From 9faa3e77c4dc472668ac3aab4375e98933255274 Mon Sep 17 00:00:00 2001 From: Leibale Date: Sun, 23 Apr 2023 07:56:15 -0400 Subject: [PATCH] WIP --- ...er-config.yml => release-drafter-base.yml} | 2 + benchmark/lib/ping/ioredis-auto-pipeline.js | 20 + benchmark/lib/ping/local-resp2.js | 21 + .../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/v4.js | 2 +- benchmark/lib/runner.js | 12 +- benchmark/package-lock.json | 448 +- benchmark/package.json | 9 +- md/RESP2-to-RESP3.md | 1 + md/Redis-todo.md | 12 + package-lock.json | 4398 ++++++++--------- package.json | 4 +- packages/bloom/lib/commands/bloom/INSERT.ts | 4 +- .../lib/commands/count-min-sketch/QUERY.ts | 4 +- packages/bloom/lib/commands/cuckoo/index.ts | 4 +- packages/bloom/lib/commands/t-digest/MERGE.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/package.json | 8 +- packages/client/.eslintrc.json | 26 +- packages/client/index.ts | 28 +- packages/client/lib/RESP/decoder-ts.ts | 1043 ++++ .../{client/RESP2 => RESP}/decoder.spec.ts | 0 packages/client/lib/RESP/decoder.ts | 1159 +++++ packages/client/lib/RESP/encoder.spec.ts | 33 + packages/client/lib/RESP/encoder.ts | 28 + packages/client/lib/RESP/types.ts | 417 ++ 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 - 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 | 510 +- packages/client/lib/client/commands.ts | 359 -- packages/client/lib/client/index.ts | 1540 +++--- packages/client/lib/client/multi-command.ts | 359 +- packages/client/lib/client/pub-sub.ts | 736 +-- packages/client/lib/client/socket.ts | 488 +- packages/client/lib/cluster/cluster-slots.ts | 1113 +++-- packages/client/lib/cluster/commands.ts | 637 --- packages/client/lib/cluster/index.ts | 941 ++-- packages/client/lib/cluster/multi-command.ts | 242 +- packages/client/lib/command-options.ts | 14 - packages/client/lib/commander.ts | 236 +- 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 | 28 +- 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 | 58 +- packages/client/lib/commands/ACL_GETUSER.ts | 76 +- packages/client/lib/commands/ACL_LIST.spec.ts | 26 +- packages/client/lib/commands/ACL_LIST.ts | 13 +- packages/client/lib/commands/ACL_LOAD.spec.ts | 18 +- packages/client/lib/commands/ACL_LOAD.ts | 13 +- packages/client/lib/commands/ACL_LOG.spec.ts | 90 +- packages/client/lib/commands/ACL_LOG.ts | 76 +- .../client/lib/commands/ACL_LOG_RESET.spec.ts | 25 +- packages/client/lib/commands/ACL_LOG_RESET.ts | 14 +- packages/client/lib/commands/ACL_SAVE.spec.ts | 18 +- packages/client/lib/commands/ACL_SAVE.ts | 13 +- packages/client/lib/commands/ACL_SETUSER.ts | 20 +- packages/client/lib/commands/ACL_USERS.ts | 13 +- packages/client/lib/commands/ACL_WHOAMI.ts | 15 +- packages/client/lib/commands/APPEND.spec.ts | 27 +- packages/client/lib/commands/APPEND.ts | 18 +- packages/client/lib/commands/ASKING.ts | 13 +- packages/client/lib/commands/AUTH.spec.ts | 40 +- packages/client/lib/commands/AUTH.ts | 25 +- packages/client/lib/commands/BGREWRITEAOF.ts | 13 +- packages/client/lib/commands/BGSAVE.ts | 25 +- packages/client/lib/commands/BITCOUNT.ts | 42 +- packages/client/lib/commands/BITFIELD.spec.ts | 83 +- packages/client/lib/commands/BITFIELD.ts | 115 +- .../client/lib/commands/BITFIELD_RO.spec.ts | 45 +- packages/client/lib/commands/BITFIELD_RO.ts | 31 +- packages/client/lib/commands/BITOP.spec.ts | 50 +- packages/client/lib/commands/BITOP.ts | 27 +- packages/client/lib/commands/BITPOS.ts | 27 +- packages/client/lib/commands/BLMOVE.ts | 37 +- packages/client/lib/commands/BLMPOP.ts | 28 +- packages/client/lib/commands/BLPOP.ts | 38 +- packages/client/lib/commands/BRPOP.ts | 26 +- packages/client/lib/commands/BRPOPLPUSH.ts | 21 +- packages/client/lib/commands/BZMPOP.ts | 14 + packages/client/lib/commands/BZPOPMAX.ts | 4 +- packages/client/lib/commands/BZPOPMIN.ts | 4 +- .../lib/commands/CLIENT_CACHING.spec.ts | 30 +- .../client/lib/commands/CLIENT_CACHING.ts | 18 +- .../lib/commands/CLIENT_GETNAME.spec.ts | 14 +- .../client/lib/commands/CLIENT_GETNAME.ts | 17 +- .../lib/commands/CLIENT_GETREDIR.spec.ts | 14 +- .../client/lib/commands/CLIENT_GETREDIR.ts | 14 +- .../client/lib/commands/CLIENT_ID.spec.ts | 26 +- packages/client/lib/commands/CLIENT_ID.ts | 12 +- packages/client/lib/commands/CLIENT_INFO.ts | 148 +- .../client/lib/commands/CLIENT_KILL.spec.ts | 208 +- packages/client/lib/commands/CLIENT_KILL.ts | 146 +- packages/client/lib/commands/CLIENT_LIST.ts | 56 +- .../client/lib/commands/CLIENT_NO-EVICT.ts | 18 +- packages/client/lib/commands/CLIENT_PAUSE.ts | 23 +- .../client/lib/commands/CLIENT_SETNAME.ts | 12 +- .../client/lib/commands/CLIENT_UNPAUSE.ts | 12 +- .../client/lib/commands/CLUSTER_ADDSLOTS.ts | 20 +- .../lib/commands/CLUSTER_ADDSLOTSRANGE.ts | 18 +- .../client/lib/commands/CLUSTER_BUMPEPOCH.ts | 12 +- .../commands/CLUSTER_COUNT-FAILURE-REPORTS.ts | 12 +- .../lib/commands/CLUSTER_COUNTKEYSINSLOT.ts | 12 +- .../client/lib/commands/CLUSTER_DELSLOTS.ts | 20 +- .../lib/commands/CLUSTER_DELSLOTSRANGE.ts | 18 +- .../client/lib/commands/CLUSTER_FAILOVER.ts | 24 +- .../client/lib/commands/CLUSTER_FLUSHSLOTS.ts | 12 +- .../client/lib/commands/CLUSTER_FORGET.ts | 12 +- .../lib/commands/CLUSTER_GETKEYSINSLOT.ts | 12 +- .../client/lib/commands/CLUSTER_KEYSLOT.ts | 11 +- packages/client/lib/commands/CLUSTER_MEET.ts | 11 +- packages/client/lib/commands/CLUSTER_MYID.ts | 11 +- .../client/lib/commands/CLUSTER_REPLICATE.ts | 11 +- packages/client/lib/commands/CLUSTER_SLOTS.ts | 71 +- packages/client/lib/commands/CONFIG_GET.ts | 13 +- .../client/lib/commands/CONFIG_RESETSTAT.ts | 11 +- .../client/lib/commands/CONFIG_REWRITE.ts | 11 +- packages/client/lib/commands/CONFIG_SET.ts | 33 +- packages/client/lib/commands/COPY.ts | 32 +- packages/client/lib/commands/DBSIZE.ts | 12 +- packages/client/lib/commands/DECR.ts | 14 +- packages/client/lib/commands/DECRBY.ts | 19 +- packages/client/lib/commands/DEL.ts | 20 +- packages/client/lib/commands/DISCARD.ts | 11 +- packages/client/lib/commands/DUMP.ts | 15 +- packages/client/lib/commands/ECHO.ts | 14 +- packages/client/lib/commands/EXISTS.ts | 23 +- packages/client/lib/commands/EXPIRE.ts | 21 +- packages/client/lib/commands/EXPIREAT.ts | 27 +- packages/client/lib/commands/EXPIRETIME.ts | 15 +- packages/client/lib/commands/FAILOVER.ts | 37 +- packages/client/lib/commands/FLUSHALL.ts | 25 +- packages/client/lib/commands/FLUSHDB.ts | 14 +- .../client/lib/commands/FUNCTION_DELETE.ts | 13 +- packages/client/lib/commands/FUNCTION_DUMP.ts | 13 +- .../client/lib/commands/FUNCTION_FLUSH.ts | 18 +- packages/client/lib/commands/FUNCTION_KILL.ts | 13 +- .../client/lib/commands/FUNCTION_STATS.ts | 137 +- packages/client/lib/commands/GEOHASH.ts | 4 +- packages/client/lib/commands/GEOPOS.ts | 4 +- packages/client/lib/commands/GET.ts | 17 +- packages/client/lib/commands/GETDEL.ts | 15 +- packages/client/lib/commands/GETEX.ts | 40 +- packages/client/lib/commands/GETRANGE.ts | 21 +- packages/client/lib/commands/GETSET.ts | 18 +- packages/client/lib/commands/HDEL.ts | 21 +- packages/client/lib/commands/HELLO.ts | 103 +- packages/client/lib/commands/HEXISTS.ts | 18 +- packages/client/lib/commands/HGET.ts | 20 +- packages/client/lib/commands/HGETALL.ts | 24 +- packages/client/lib/commands/HINCRBY.ts | 27 +- packages/client/lib/commands/HINCRBYFLOAT.ts | 27 +- packages/client/lib/commands/HKEYS.ts | 15 +- packages/client/lib/commands/HLEN.ts | 15 +- packages/client/lib/commands/HMGET.ts | 27 +- packages/client/lib/commands/HRANDFIELD.ts | 17 +- .../client/lib/commands/HRANDFIELD_COUNT.ts | 24 +- .../HRANDFIELD_COUNT_WITHVALUES.spec.ts | 21 - .../commands/HRANDFIELD_COUNT_WITHVALUES.ts | 46 +- packages/client/lib/commands/HSCAN.ts | 60 +- packages/client/lib/commands/HSET.ts | 98 +- packages/client/lib/commands/HSETNX.ts | 23 +- packages/client/lib/commands/HSTRLEN.ts | 20 +- packages/client/lib/commands/HVALS.ts | 15 +- packages/client/lib/commands/INCR.ts | 14 +- packages/client/lib/commands/INCRBY.ts | 17 +- packages/client/lib/commands/INCRBYFLOAT.ts | 17 +- packages/client/lib/commands/INFO.ts | 16 +- packages/client/lib/commands/KEYS.ts | 12 +- packages/client/lib/commands/LCS.ts | 29 +- packages/client/lib/commands/LCS_IDX.ts | 80 +- .../lib/commands/LCS_IDX_WITHMATCHLEN.ts | 82 +- packages/client/lib/commands/LCS_LEN.ts | 25 +- packages/client/lib/commands/LINDEX.ts | 20 +- packages/client/lib/commands/LINSERT.ts | 33 +- packages/client/lib/commands/LLEN.ts | 17 +- packages/client/lib/commands/LMOVE.ts | 30 +- packages/client/lib/commands/LMPOP.ts | 32 +- packages/client/lib/commands/LOLWUT.ts | 25 +- packages/client/lib/commands/LPOP.ts | 14 +- packages/client/lib/commands/LPOP_COUNT.ts | 18 +- packages/client/lib/commands/LPOS.ts | 35 +- packages/client/lib/commands/LPOS_COUNT.ts | 27 +- packages/client/lib/commands/LPUSH.ts | 20 +- packages/client/lib/commands/LPUSHX.ts | 21 +- packages/client/lib/commands/LRANGE.ts | 29 +- packages/client/lib/commands/LREM.ts | 29 +- packages/client/lib/commands/LSET.ts | 29 +- packages/client/lib/commands/LTRIM.ts | 28 +- packages/client/lib/commands/MGET.ts | 19 +- packages/client/lib/commands/MIGRATE.ts | 83 +- packages/client/lib/commands/MODULE_LIST.ts | 26 +- packages/client/lib/commands/MODULE_LOAD.ts | 20 +- packages/client/lib/commands/MODULE_UNLOAD.ts | 13 +- packages/client/lib/commands/MOVE.ts | 12 +- packages/client/lib/commands/MSET.ts | 35 +- packages/client/lib/commands/MSETNX.ts | 27 +- .../client/lib/commands/OBJECT_ENCODING.ts | 17 +- packages/client/lib/commands/OBJECT_FREQ.ts | 17 +- .../client/lib/commands/OBJECT_IDLETIME.ts | 17 +- .../client/lib/commands/OBJECT_REFCOUNT.ts | 17 +- packages/client/lib/commands/PERSIST.ts | 14 +- packages/client/lib/commands/PEXPIRE.ts | 25 +- packages/client/lib/commands/PEXPIREAT.ts | 29 +- packages/client/lib/commands/PEXPIRETIME.ts | 17 +- packages/client/lib/commands/PFADD.ts | 22 +- packages/client/lib/commands/PFCOUNT.ts | 21 +- packages/client/lib/commands/PFMERGE.ts | 21 +- packages/client/lib/commands/PING.spec.ts | 54 +- packages/client/lib/commands/PING.ts | 16 +- packages/client/lib/commands/PSETEX.ts | 30 +- packages/client/lib/commands/PTTL.ts | 17 +- packages/client/lib/commands/PUBLISH.ts | 18 +- .../client/lib/commands/PUBSUB_CHANNELS.ts | 16 +- packages/client/lib/commands/PUBSUB_NUMPAT.ts | 13 +- packages/client/lib/commands/PUBSUB_NUMSUB.ts | 33 +- .../lib/commands/PUBSUB_SHARDCHANNELS.ts | 23 +- packages/client/lib/commands/RANDOMKEY.ts | 14 +- packages/client/lib/commands/READONLY.ts | 13 +- packages/client/lib/commands/READWRITE.ts | 13 +- packages/client/lib/commands/RENAME.ts | 17 +- packages/client/lib/commands/RENAMENX.ts | 17 +- packages/client/lib/commands/REPLICAOF.ts | 13 +- packages/client/lib/commands/ROLE.ts | 132 +- packages/client/lib/commands/RPOP.ts | 14 +- packages/client/lib/commands/RPOPLPUSH.ts | 20 +- packages/client/lib/commands/RPOP_COUNT.ts | 17 +- packages/client/lib/commands/RPUSH.ts | 24 +- packages/client/lib/commands/RPUSHX.ts | 24 +- packages/client/lib/commands/SADD.ts | 24 +- packages/client/lib/commands/SCAN.ts | 33 +- packages/client/lib/commands/SCARD.ts | 13 +- packages/client/lib/commands/SCRIPT_EXISTS.ts | 4 +- packages/client/lib/commands/SDIFF.ts | 25 +- packages/client/lib/commands/SDIFFSTORE.ts | 24 +- packages/client/lib/commands/SET.ts | 58 +- packages/client/lib/commands/SETEX.ts | 28 +- packages/client/lib/commands/SETNX.ts | 17 +- packages/client/lib/commands/SETRANGE.ts | 27 +- packages/client/lib/commands/SINTER.ts | 25 +- packages/client/lib/commands/SINTERCARD.ts | 35 +- packages/client/lib/commands/SINTERSTORE.ts | 4 +- packages/client/lib/commands/SMEMBERS.ts | 15 +- packages/client/lib/commands/SREM.ts | 4 +- packages/client/lib/commands/SSCAN.ts | 39 +- packages/client/lib/commands/STRLEN.ts | 17 +- packages/client/lib/commands/SUNION.ts | 4 +- packages/client/lib/commands/SUNIONSTORE.ts | 4 +- packages/client/lib/commands/TOUCH.ts | 4 +- packages/client/lib/commands/TTL.ts | 17 +- packages/client/lib/commands/TYPE.ts | 17 +- packages/client/lib/commands/UNLINK.ts | 4 +- packages/client/lib/commands/UNWATCH.ts | 12 +- packages/client/lib/commands/WAIT.ts | 12 +- packages/client/lib/commands/WATCH.ts | 4 +- packages/client/lib/commands/XACK.ts | 4 +- packages/client/lib/commands/XCLAIM.ts | 4 +- packages/client/lib/commands/XDEL.ts | 4 +- packages/client/lib/commands/ZADD.ts | 69 +- packages/client/lib/commands/ZCARD.ts | 17 +- packages/client/lib/commands/ZCOUNT.ts | 33 +- packages/client/lib/commands/ZDIFF.ts | 23 +- packages/client/lib/commands/ZDIFFSTORE.ts | 25 +- .../client/lib/commands/ZDIFF_WITHSCORES.ts | 30 +- packages/client/lib/commands/ZINCRBY.ts | 33 +- packages/client/lib/commands/ZINTER.ts | 96 +- packages/client/lib/commands/ZINTERCARD.ts | 36 +- packages/client/lib/commands/ZINTERSTORE.ts | 43 +- .../client/lib/commands/ZINTER_WITHSCORES.ts | 45 +- packages/client/lib/commands/ZLEXCOUNT.ts | 33 +- packages/client/lib/commands/ZMPOP.ts | 72 +- packages/client/lib/commands/ZMSCORE.ts | 32 +- packages/client/lib/commands/ZREM.ts | 4 +- packages/client/lib/commands/ZSCAN.ts | 57 +- packages/client/lib/commands/ZUNION.ts | 4 +- packages/client/lib/commands/ZUNIONSTORE.ts | 4 +- .../lib/commands/generic-transformers.spec.ts | 34 +- .../lib/commands/generic-transformers.ts | 62 +- packages/client/lib/commands/index.ts | 421 +- packages/client/lib/errors.ts | 4 + packages/client/lib/lua-script.ts | 22 +- packages/client/lib/multi-command.ts | 119 +- packages/client/package.json | 18 +- packages/client/test.mjs | 338 ++ packages/graph/package.json | 8 +- packages/json/lib/commands/GET.ts | 4 +- packages/json/package.json | 8 +- packages/old/RESP3-old/decoder.ts | 423 ++ packages/old/RESP3-old/encoder.ts | 0 .../old/RESP3-old/nested-decoders/abstract.ts | 6 + .../RESP3-old/nested-decoders/map-to-array.ts | 28 + .../old/RESP3-old/nested-decoders/to-array.ts | 20 + .../old/RESP3-old/nested-decoders/to-map.ts | 28 + .../RESP3-old/nested-decoders/to-object.ts | 28 + .../old/RESP3-old/nested-decoders/to-set.ts | 20 + packages/old/RESP3-old/types.ts | 101 + packages/search/lib/commands/AGGREGATE.ts | 4 +- packages/search/lib/commands/CREATE.ts | 6 +- packages/search/lib/commands/DICTADD.ts | 4 +- packages/search/lib/commands/DICTDEL.ts | 4 +- packages/search/lib/commands/SYNUPDATE.ts | 4 +- packages/search/lib/commands/index.ts | 12 +- packages/search/package.json | 8 +- packages/test-utils/lib/dockers.ts | 348 +- packages/test-utils/lib/index.ts | 415 +- packages/test-utils/package.json | 6 +- .../time-series/lib/commands/QUERYINDEX.ts | 4 +- packages/time-series/lib/commands/index.ts | 6 +- packages/time-series/package.json | 8 +- req-res-parser/a.sh | 7 + req-res-parser/req-res-parser.mjs | 1018 ++++ 326 files changed, 14620 insertions(+), 10931 deletions(-) rename .github/{release-drafter-config.yml => release-drafter-base.yml} (97%) 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-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 md/RESP2-to-RESP3.md create mode 100644 md/Redis-todo.md create mode 100644 packages/client/lib/RESP/decoder-ts.ts rename packages/client/lib/{client/RESP2 => RESP}/decoder.spec.ts (100%) 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.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 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/HRANDFIELD_COUNT_WITHVALUES.spec.ts create mode 100644 packages/client/test.mjs create mode 100644 packages/old/RESP3-old/decoder.ts create mode 100644 packages/old/RESP3-old/encoder.ts create mode 100644 packages/old/RESP3-old/nested-decoders/abstract.ts create mode 100644 packages/old/RESP3-old/nested-decoders/map-to-array.ts create mode 100644 packages/old/RESP3-old/nested-decoders/to-array.ts create mode 100644 packages/old/RESP3-old/nested-decoders/to-map.ts create mode 100644 packages/old/RESP3-old/nested-decoders/to-object.ts create mode 100644 packages/old/RESP3-old/nested-decoders/to-set.ts create mode 100644 packages/old/RESP3-old/types.ts create mode 100644 req-res-parser/a.sh create mode 100644 req-res-parser/req-res-parser.mjs diff --git a/.github/release-drafter-config.yml b/.github/release-drafter-base.yml similarity index 97% rename from .github/release-drafter-config.yml rename to .github/release-drafter-base.yml index 9a98b1c08a..cd99c28b1f 100644 --- a/.github/release-drafter-config.yml +++ b/.github/release-drafter-base.yml @@ -1,5 +1,7 @@ name-template: 'Version $NEXT_PATCH_VERSION' tag-template: 'v$NEXT_PATCH_VERSION' +include-path: + - {{ }} autolabeler: - label: 'chore' files: diff --git a/benchmark/lib/ping/ioredis-auto-pipeline.js b/benchmark/lib/ping/ioredis-auto-pipeline.js new file mode 100644 index 0000000000..ee400fe6ca --- /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 0000000000..873698a131 --- /dev/null +++ b/benchmark/lib/ping/local-resp2.js @@ -0,0 +1,21 @@ +import { createClient } from 'redis-local'; + +export default async (host) => { + const client = createClient({ + socket: { + host + }, + RESP: 2 + }); + + await client.connect(); + + return { + benchmark() { + return client.ping(); + }, + teardown() { + return client.disconnect(); + } + }; +}; diff --git a/benchmark/lib/ping/local-resp3-module-with-flags.js b/benchmark/lib/ping/local-resp3-module-with-flags.js new file mode 100644 index 0000000000..2af849b938 --- /dev/null +++ b/benchmark/lib/ping/local-resp3-module-with-flags.js @@ -0,0 +1,27 @@ +import { createClient } from 'redis-local'; +import PING from 'redis-local/dist/lib/commands/PING.js'; + +export default async (host) => { + const client = createClient({ + socket: { + host + }, + RESP: 3, + modules: { + module: { + ping: PING.default + } + } + }); + + await client.connect(); + + return { + benchmark() { + return client.withFlags({}).module.ping(); + }, + teardown() { + return client.disconnect(); + } + }; +}; diff --git a/benchmark/lib/ping/local-resp3-module.js b/benchmark/lib/ping/local-resp3-module.js new file mode 100644 index 0000000000..66f6e3ec29 --- /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 0000000000..a4ee4f24a2 --- /dev/null +++ b/benchmark/lib/ping/local-resp3.js @@ -0,0 +1,21 @@ +import { createClient } from 'redis-local'; + +export default async (host) => { + const client = createClient({ + socket: { + host + }, + RESP: 3 + }); + + await client.connect(); + + return { + benchmark() { + return client.ping(); + }, + teardown() { + return client.disconnect(); + } + }; +}; diff --git a/benchmark/lib/ping/v4.js b/benchmark/lib/ping/v4.js index 69aa3c0692..c570aa1477 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 a96ff55cab..3787cbabd1 100644 --- a/benchmark/lib/runner.js +++ b/benchmark/lib/runner.js @@ -71,13 +71,13 @@ const benchmarkStart = process.hrtime.bigint(), histogram = await run(times), benchmarkNanoseconds = process.hrtime.bigint() - benchmarkStart, json = { - timestamp, + // timestamp, operationsPerSecond: times / Number(benchmarkNanoseconds) * 1_000_000_000, - p0: histogram.getValueAtPercentile(0), - p50: histogram.getValueAtPercentile(50), - p95: histogram.getValueAtPercentile(95), - p99: histogram.getValueAtPercentile(99), - p100: histogram.getValueAtPercentile(100) + // p0: histogram.getValueAtPercentile(0), + // p50: histogram.getValueAtPercentile(50), + // p95: histogram.getValueAtPercentile(95), + // p99: histogram.getValueAtPercentile(99), + // p100: histogram.getValueAtPercentile(100) }; console.log(`[${basename(path)}]:`); console.table(json); diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json index 828b42803e..4ec89ea2f9 100644 --- a/benchmark/package-lock.json +++ b/benchmark/package-lock.json @@ -1,31 +1,36 @@ { "name": "@redis/client-benchmark", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@redis/client-benchmark", "dependencies": { - "@redis/client": "../packages/client", "hdr-histogram-js": "3.0.0", - "ioredis": "5.3.0", - "redis-v3": "npm:redis@3.1.2", - "yargs": "17.6.2" + "ioredis": "5", + "redis-local": "file:../packages/client", + "redis-v3": "npm:redis@3", + "redis-v4": "npm:redis@4", + "yargs": "17.7.1" } }, "node_modules/@assemblyscript/loader": { "version": "0.19.23", - "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.19.23.tgz", - "integrity": "sha512-ulkCYfFbYj01ie1MDOyxv2F6SpRN1TOj7fQxbP07D6HmeR+gr2JLSmINKjga2emB+b1L2KGrFKBTc+e00p54nw==" + "license": "Apache-2.0" }, "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", + "license": "MIT" + }, + "node_modules/@redis/bloom": { + "version": "1.2.0", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } }, "node_modules/@redis/client": { - "version": "1.5.0", - "resolved": "file:../packages/client", + "version": "1.5.6", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2", @@ -36,18 +41,44 @@ "node": ">=14" } }, + "node_modules/@redis/graph": { + "version": "1.1.0", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/json": { + "version": "1.0.4", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/search": { + "version": "1.1.2", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/time-series": { + "version": "1.0.4", + "license": "MIT", + "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", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", "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==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -60,8 +91,6 @@ }, "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", @@ -75,12 +104,12 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "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==", + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -92,16 +121,14 @@ }, "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==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -111,13 +138,11 @@ }, "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/debug": { "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "license": "MIT", "dependencies": { "ms": "2.1.2" }, @@ -132,45 +157,39 @@ }, "node_modules/denque": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", "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==" + "license": "MIT" }, "node_modules/escalade": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "license": "MIT", "engines": { "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==", + "license": "MIT", "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==", + "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/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==", + "license": "BSD", "dependencies": { "@assemblyscript/loader": "^0.19.21", "base64-js": "^1.2.0", @@ -181,9 +200,8 @@ } }, "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.1", + "license": "MIT", "dependencies": { "@ioredis/commands": "^1.1.1", "cluster-key-slot": "^1.1.0", @@ -205,49 +223,55 @@ }, "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==", + "license": "MIT", "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=" + "license": "MIT" }, "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=" + "license": "MIT" }, "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==" + "license": "MIT" }, "node_modules/pako": { "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + "license": "(MIT AND Zlib)" }, "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==" + "license": "MIT" }, "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=", + "license": "MIT", "engines": { "node": ">=4" } }, + "node_modules/redis-local": { + "name": "@redis/client", + "version": "1.5.6", + "resolved": "file:../packages/client", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/redis-parser": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "license": "MIT", "dependencies": { "redis-errors": "^1.0.0" }, @@ -258,8 +282,7 @@ "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==", + "license": "MIT", "dependencies": { "denque": "^1.5.0", "redis-commands": "^1.7.0", @@ -276,29 +299,41 @@ }, "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==", + "license": "Apache-2.0", "engines": { "node": ">=0.10" } }, + "node_modules/redis-v4": { + "name": "redis", + "version": "4.6.5", + "license": "MIT", + "workspaces": [ + "./packages/*" + ], + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.5.6", + "@redis/graph": "1.1.0", + "@redis/json": "1.0.4", + "@redis/search": "1.1.2", + "@redis/time-series": "1.0.4" + } + }, "node_modules/require-directory": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "license": "MIT", "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==" + "license": "MIT" }, "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==", + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -310,8 +345,7 @@ }, "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==", + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -321,8 +355,7 @@ }, "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==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -337,21 +370,18 @@ }, "node_modules/y18n": { "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", "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==" + "license": "ISC" }, "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", + "license": "MIT", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -367,264 +397,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==", + "license": "ISC", "engines": { "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 79b0facda8..51357ce032 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -7,10 +7,11 @@ "start": "node ." }, "dependencies": { - "@redis/client": "../packages/client", "hdr-histogram-js": "3.0.0", - "ioredis": "5.3.0", - "redis-v3": "npm:redis@3.1.2", - "yargs": "17.6.2" + "ioredis": "5", + "redis-v3": "npm:redis@3", + "redis-v4": "npm:redis@4", + "redis-local": "file:../packages/client", + "yargs": "17.7.1" } } diff --git a/md/RESP2-to-RESP3.md b/md/RESP2-to-RESP3.md new file mode 100644 index 0000000000..6f34ae8f41 --- /dev/null +++ b/md/RESP2-to-RESP3.md @@ -0,0 +1 @@ +https://docs.google.com/document/d/1Bg4jxEYiWnbCl4Oa2GCk5xaWde7HLa6GcNGg1sFwDAY/edit# diff --git a/md/Redis-todo.md b/md/Redis-todo.md new file mode 100644 index 0000000000..eab52a4ce1 --- /dev/null +++ b/md/Redis-todo.md @@ -0,0 +1,12 @@ +# Missing functionality + +- `HEXISTS`: accepts one field only, should be the same as `EXISTS` + +# Replies + +`String` -> `Double`: +- `INCRBYFLOAT` +- `HINCRBYFLOAT` + +`Number` -> `Boolean`: +- `HSETNX` (deprecated) \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 3ff77cd4bc..56f66c8abc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,15 +22,14 @@ "devDependencies": { "@tsconfig/node14": "^1.0.3", "gh-pages": "^5.0.0", - "release-it": "^15.6.0", - "typescript": "^4.9.5" + "release-it": "^15.9.3", + "typescript": "^5.0.2" } }, "node_modules/@ampproject/remapping": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.1.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -41,9 +40,8 @@ }, "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==", "dev": true, + "license": "MIT", "dependencies": { "@babel/highlight": "^7.18.6" }, @@ -52,34 +50,32 @@ } }, "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.19.4", "dev": true, + "license": "MIT", "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.19.6", "dev": true, + "license": "MIT", "dependencies": { - "@ampproject/remapping": "^2.2.0", + "@ampproject/remapping": "^2.1.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/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", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", + "json5": "^2.2.1", "semver": "^6.3.0" }, "engines": { @@ -90,15 +86,21 @@ "url": "https://opencollective.com/babel" } }, - "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==", + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.19.6", + "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.21.0", + "@babel/types": "^7.19.4", "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "engines": { @@ -107,9 +109,8 @@ }, "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, + "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -120,15 +121,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "version": "7.19.3", "dev": true, + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.20.5", + "@babel/compat-data": "^7.19.3", "@babel/helper-validator-option": "^7.18.6", "browserslist": "^4.21.3", - "lru-cache": "^5.1.1", "semver": "^6.3.0" }, "engines": { @@ -138,23 +137,29 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "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", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", "dev": true, + "license": "MIT", "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.19.0", "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -162,9 +167,8 @@ }, "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==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.18.6" }, @@ -174,9 +178,8 @@ }, "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==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.18.6" }, @@ -185,31 +188,29 @@ } }, "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.19.6", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-simple-access": "^7.19.4", "@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/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.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.19.4", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.20.2" + "@babel/types": "^7.19.4" }, "engines": { "node": ">=6.9.0" @@ -217,9 +218,8 @@ }, "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==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.18.6" }, @@ -229,40 +229,36 @@ }, "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==", "dev": true, + "license": "MIT", "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==", "dev": true, + "license": "MIT", "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.18.6", "dev": true, + "license": "MIT", "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.19.4", "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.4", + "@babel/types": "^7.19.4" }, "engines": { "node": ">=6.9.0" @@ -270,9 +266,8 @@ }, "node_modules/@babel/highlight": { "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.18.6", "chalk": "^2.0.0", @@ -284,9 +279,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" }, @@ -296,9 +290,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", @@ -310,42 +303,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" }, @@ -354,10 +342,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.2.tgz", - "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==", + "version": "7.19.6", "dev": true, + "license": "MIT", "bin": { "parser": "bin/babel-parser.js" }, @@ -366,33 +353,31 @@ } }, "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.18.10", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" }, "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==", + "version": "7.19.6", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.1", + "@babel/generator": "^7.19.6", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", + "@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.21.2", - "@babel/types": "^7.21.2", + "@babel/parser": "^7.19.6", + "@babel/types": "^7.19.4", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -402,18 +387,16 @@ }, "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, + "license": "MIT", "engines": { "node": ">=4" } }, "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.19.4", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.19.4", "@babel/helper-validator-identifier": "^7.19.1", @@ -425,9 +408,8 @@ }, "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==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -437,23 +419,46 @@ }, "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, + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@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.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.4.1.tgz", + "integrity": "sha512-BISJ6ZE4xQsuL/FmsyRaiffpq977bMlsKfGHTQrOGFErfByxIe6iZTxPf/00Zon9b9a7iUykfQwejN3s2ZW/Bw==", + "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.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.1.tgz", + "integrity": "sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", + "espree": "^9.5.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -468,6 +473,15 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/js": { + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.36.0.tgz", + "integrity": "sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==", + "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", @@ -484,9 +498,8 @@ }, "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, + "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -503,15 +516,13 @@ }, "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", @@ -525,18 +536,28 @@ }, "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", + "dev": true, + "license": "MIT", + "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, + "license": "MIT", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -545,20 +566,54 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "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", + "dev": true, + "license": "MIT", + "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, + "license": "MIT", "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, + "license": "ISC", "dependencies": { "@istanbuljs/schema": "^0.1.2" }, @@ -571,18 +626,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.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.0.0", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -593,33 +646,29 @@ }, "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==", "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.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true + "dev": true, + "license": "MIT" }, "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==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "3.1.0", "@jridgewell/sourcemap-codec": "1.4.14" @@ -627,9 +676,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" @@ -640,18 +688,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" @@ -725,12 +771,12 @@ "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-5.0.1.tgz", - "integrity": "sha512-7A+rEkS70pH36Z6JivSlR7Zqepz3KVucEFVDnSrgHXzG7WLAzYwcHZbKdfTXHwuTHbkT1vKvz7dHl1+HNf6Qyw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.0.0.tgz", + "integrity": "sha512-Sq5VU1PfT6/JyuXPyt04KZNVsFOSBaYOAq2QRZUwzVlI10KFvcbUo8lR258AAQL1Et60b0WuVik+zOWKLuDZxw==", "dev": true, "dependencies": { - "@octokit/types": "^8.0.0" + "@octokit/types": "^9.0.0" }, "engines": { "node": ">= 14" @@ -739,21 +785,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 +795,12 @@ } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.8.1.tgz", - "integrity": "sha512-QrlaTm8Lyc/TbU7BL/8bO49vp+RZ6W3McxxmmQTgYxf2sWkO8ZKuj4dLhPNJD6VCUW1hetCmeIM0m6FTVpDiEg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.0.1.tgz", + "integrity": "sha512-pnCaLwZBudK5xCdrR823xHGNgqOzRnJ/mpC/76YPpNP7DybdsJtP7mdOwh+wYZxK5jqeQuhu59ogMI4NRlBUvA==", "dev": true, "dependencies": { - "@octokit/types": "^8.1.1", + "@octokit/types": "^9.0.0", "deprecation": "^2.3.1" }, "engines": { @@ -779,21 +810,6 @@ "@octokit/core": ">=3" } }, - "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz", - "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==", - "dev": true - }, - "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.2.1.tgz", - "integrity": "sha512-8oWMUji8be66q2B9PmEIUyQm00VPDPun07umUWSaCwxmeaquFBro4Hcc3ruVoDo3zkQyZBlRvhIMEYS3pBhanw==", - "dev": true, - "dependencies": { - "@octokit/openapi-types": "^14.0.0" - } - }, "node_modules/@octokit/request": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.3.tgz", @@ -846,15 +862,15 @@ } }, "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" @@ -870,10 +886,9 @@ } }, "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==", + "version": "1.0.1", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "4.2.10" }, @@ -883,9 +898,8 @@ }, "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==", "dev": true, + "license": "MIT", "dependencies": { "@pnpm/network.ca-file": "^1.0.1", "config-chain": "^1.1.11" @@ -924,9 +938,8 @@ }, "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==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" }, @@ -953,9 +966,9 @@ } }, "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", @@ -971,9 +984,8 @@ }, "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" }, @@ -983,36 +995,31 @@ }, "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, + "license": "MIT", "engines": { "node": ">= 6" } }, "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 + "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 + "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 + "dev": true, + "license": "MIT" }, "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==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/http-cache-semantics": { "version": "4.0.1", @@ -1033,9 +1040,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.14.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.1.tgz", - "integrity": "sha512-QH+37Qds3E0eDlReeboBxfHbX9omAcBCXEzswCu6jySP642jiM3cYSIkU/REqwhCUqXdonHFuBfJDiAJxMNhaQ==", + "version": "18.15.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.10.tgz", + "integrity": "sha512-9avDaQJczATcXgfmMAW3MIWArOO7A+m90vuCFLr8AotWf8igO/mRoYukrk2cqZVtv38tHs33retzHEilM7FpeQ==", "dev": true }, "node_modules/@types/semver": { @@ -1046,29 +1053,26 @@ }, "node_modules/@types/sinon": { "version": "10.0.13", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.13.tgz", - "integrity": "sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ==", "dev": true, + "license": "MIT", "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 + "dev": true, + "license": "MIT" }, "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 + "dev": true, + "license": "MIT" }, "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": "*" @@ -1076,24 +1080,23 @@ }, "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 + "dev": true, + "license": "MIT" }, "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.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.57.0.tgz", + "integrity": "sha512-itag0qpN6q2UMM6Xgk6xoHa0D0/P+M17THnr4SVgqn9Rgam5k/He33MA7/D7QoJcdMxHFyX7U9imaBonAX/6qA==", "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.57.0", + "@typescript-eslint/type-utils": "5.57.0", + "@typescript-eslint/utils": "5.57.0", "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" }, @@ -1114,48 +1117,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.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.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.53.0.tgz", - "integrity": "sha512-MKBw9i0DLYlmdOb3Oq/526+al20AJZpANdT6Ct9ffxcV8nKCHz63t/S0IhlTFNsBIHJv+GY5SFJ0XfqVeydQrQ==", + "version": "5.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.57.0.tgz", + "integrity": "sha512-orrduvpWYkgLCyAdNtR1QIWovcNZlEm6yL8nwH/eTxWLd8gsP+25pdLHYzL2QdkqrieaDwLpytHqycncv0woUQ==", "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.57.0", + "@typescript-eslint/types": "5.57.0", + "@typescript-eslint/typescript-estree": "5.57.0", "debug": "^4.3.4" }, "engines": { @@ -1175,13 +1145,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.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.57.0.tgz", + "integrity": "sha512-NANBNOQvllPlizl9LatX8+MHi7bx7WGIWYjPHDmQe5Si/0YEYfxSljJpoTyTWFTgRy3X8gLYSE4xQ2U+aCozSw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.53.0", - "@typescript-eslint/visitor-keys": "5.53.0" + "@typescript-eslint/types": "5.57.0", + "@typescript-eslint/visitor-keys": "5.57.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1192,13 +1162,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.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.57.0.tgz", + "integrity": "sha512-kxXoq9zOTbvqzLbdNKy1yFrxLC6GDJFE2Yuo3KqSwTmDOFjUGeWSakgoXT864WcK5/NAJkkONCiKb1ddsqhLXQ==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.53.0", - "@typescript-eslint/utils": "5.53.0", + "@typescript-eslint/typescript-estree": "5.57.0", + "@typescript-eslint/utils": "5.57.0", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -1219,9 +1189,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.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.57.0.tgz", + "integrity": "sha512-mxsod+aZRSyLT+jiqHw1KK6xrANm19/+VFALVFP5qa/aiJnlP38qpyaTd0fEKhWvQk6YeNZ5LGwI1pDpBRBhtQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1232,13 +1202,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.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.57.0.tgz", + "integrity": "sha512-LTzQ23TV82KpO8HPnWuxM2V7ieXW8O142I7hQTxWIHDcCEIjtkat6H96PFkYBQqGFLW/G/eVVOB9Z8rcvdY/Vw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.53.0", - "@typescript-eslint/visitor-keys": "5.53.0", + "@typescript-eslint/types": "5.57.0", + "@typescript-eslint/visitor-keys": "5.57.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1258,81 +1228,19 @@ } } }, - "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.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.53.0.tgz", - "integrity": "sha512-VUOOtPv27UNWLxFwQK/8+7kvxVC+hPHNsJjzlJyotlaHjLSIgOCKj9I0DBUjwOOA64qjBwx5afAPjksqOxMO0g==", + "version": "5.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.57.0.tgz", + "integrity": "sha512-ps/4WohXV7C+LTSgAL5CApxvxbMkl9B9AUZRtnEFonpIxZDIT7wC1xfvuJONMidrkB9scs4zhtRyIwHh4+18kw==", "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.57.0", + "@typescript-eslint/types": "5.57.0", + "@typescript-eslint/typescript-estree": "5.57.0", "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", "semver": "^7.3.7" }, "engines": { @@ -1346,46 +1254,13 @@ "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.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.53.0.tgz", - "integrity": "sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w==", + "version": "5.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.57.0.tgz", + "integrity": "sha512-ery2g3k0hv5BLiKpPuwYt9KBkAp2ugT6VvyShXdLOkax895EC55sP0Tx5L0fZaQueiK3fBLvHVvEl3jFS5ia+g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/types": "5.57.0", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -1397,10 +1272,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.8.1", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -1419,18 +1293,16 @@ }, "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, + "license": "MIT", "engines": { "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, + "license": "MIT", "dependencies": { "debug": "4" }, @@ -1440,9 +1312,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" @@ -1469,33 +1340,21 @@ }, "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-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 + "dev": true, + "license": "MIT" }, "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, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -1505,31 +1364,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", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "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.1.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.1.0.tgz", + "integrity": "sha512-bQyg9bzRntwR/8b89DOEhGwctcwCrbWW/TuqTQnpqpy5Fz3aovcOTj5i8NJV6AHc8OGNdMaqdxAWww8pz2kiKg==", "dev": true, "dependencies": { "type-fest": "^3.0.0" @@ -1541,16 +1387,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.7.1.tgz", + "integrity": "sha512-8LZNdvuztgxCF4eYpEmPYUPS0lbbByM2qHcp2oMxHZhWLIQB9QE36EeQ1PKwsUIDZXEP8HCBEmkBbT1//kLU4Q==", "dev": true, "engines": { - "node": ">=12" + "node": ">=14.16" }, "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" } }, "node_modules/ansi-sequence-parser": { @@ -1560,22 +1414,23 @@ "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", "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">=12" + "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "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==", + "version": "3.1.2", "dev": true, + "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -1586,9 +1441,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" }, @@ -1598,39 +1452,32 @@ }, "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/arg": { "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "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-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": { "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" } @@ -1656,9 +1503,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" }, @@ -1674,9 +1520,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, + "license": "MIT", "dependencies": { "retry": "0.13.1" } @@ -1695,9 +1540,8 @@ }, "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", @@ -1725,11 +1569,19 @@ "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", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -1746,10 +1598,9 @@ } }, "node_modules/boxen": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.1.tgz", - "integrity": "sha512-8k2eH6SRAK00NDl1iX5q17RJ8rfl53TajdYxE3ssMLehbg487dEVgsad4pIsZb/QqBgYWIl6JOauMTLGX2Kpkw==", + "version": "7.0.0", "dev": true, + "license": "MIT", "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^7.0.0", @@ -1768,10 +1619,9 @@ } }, "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==", + "version": "7.0.0", "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" }, @@ -1779,11 +1629,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/boxen/node_modules/chalk": { + "version": "5.1.2", + "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/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" }, @@ -1791,11 +1651,22 @@ "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", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1803,9 +1674,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" }, @@ -1815,14 +1685,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.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "version": "4.21.4", "dev": true, "funding": [ { @@ -1834,11 +1701,12 @@ "url": "https://tidelift.com/funding/github/npm/browserslist" } ], + "license": "MIT", "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.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" }, "bin": { "browserslist": "cli.js" @@ -1873,24 +1741,36 @@ }, "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 + "dev": true, + "license": "MIT" + }, + "node_modules/bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "dependencies": { + "run-applescript": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/bytes": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, + "license": "MIT", "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", - "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" } @@ -1915,9 +1795,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", @@ -1952,17 +1831,14 @@ }, "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.30001457", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001457.tgz", - "integrity": "sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==", + "version": "1.0.30001425", "dev": true, "funding": [ { @@ -1973,15 +1849,19 @@ "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/chalk": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", - "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", + "version": "4.1.2", "dev": true, + "license": "MIT", + "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" @@ -1995,8 +1875,6 @@ }, "node_modules/chokidar": { "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "funding": [ { @@ -2004,6 +1882,7 @@ "url": "https://paulmillr.com/funding/" } ], + "license": "MIT", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -2022,9 +1901,8 @@ }, "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, + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -2033,34 +1911,22 @@ } }, "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.5.0", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } + "license": "MIT" }, "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" }, @@ -2105,51 +1971,27 @@ } }, "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", "dev": true, + "license": "ISC", "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": { "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/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, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -2159,30 +2001,20 @@ "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", "dev": true, + "license": "MIT", "dependencies": { "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/wrap-ansi?sponsor=1" } }, "node_modules/clone": { @@ -2204,9 +2036,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==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -2216,33 +2047,28 @@ }, "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": "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 + "dev": true, + "license": "MIT" }, "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" @@ -2250,15 +2076,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", @@ -2275,20 +2099,18 @@ }, "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/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 + "dev": true, + "license": "MIT" }, "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,19 +2120,20 @@ }, "engines": { "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" } }, "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 + "dev": true, + "license": "MIT" }, "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", @@ -2320,11 +2143,49 @@ "node": ">= 8" } }, + "node_modules/cross-spawn-async": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz", + "integrity": "sha512-snteb3aVrxYYOX9e8BabYFK9WhCDhTlw1YQktfTthBogxri4/2r9U2nQc0ffY73ZAxezDc+U8gvHAeU1wy1ubQ==", + "deprecated": "cross-spawn no longer requires a build toolchain, use it instead", + "dev": true, + "dependencies": { + "lru-cache": "^4.0.0", + "which": "^1.2.8" + } + }, + "node_modules/cross-spawn-async/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/cross-spawn-async/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/cross-spawn-async/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true + }, "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" }, @@ -2337,9 +2198,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" }, @@ -2358,9 +2218,8 @@ }, "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" }, @@ -2375,18 +2234,16 @@ }, "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" }, @@ -2399,9 +2256,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" }, @@ -2411,24 +2267,132 @@ }, "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/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 + "dev": true, + "license": "MIT" + }, + "node_modules/default-browser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-3.1.0.tgz", + "integrity": "sha512-SOHecvSoairSAWxEHP/0qcsld/KtI3DargfEuELQDyHIYmS2EMgdGhHOTC1GxaYr+NLUV6kDroeiSBfnNHnn8w==", + "dev": true, + "dependencies": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^5.0.0", + "xdg-default-browser": "^2.1.0" + }, + "engines": { + "node": ">=12" + }, + "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-browser/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/default-browser/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/default-browser/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/default-browser/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/default-browser/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/default-browser/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/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" }, @@ -2453,20 +2417,22 @@ }, "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-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": { @@ -2487,9 +2453,8 @@ }, "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, + "license": "MIT", "dependencies": { "ast-types": "^0.13.2", "escodegen": "^1.8.1", @@ -2502,9 +2467,8 @@ }, "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" } @@ -2517,9 +2481,8 @@ }, "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" } @@ -2538,9 +2501,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, + "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -2550,9 +2512,8 @@ }, "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" }, @@ -2565,15 +2526,13 @@ }, "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.311", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.311.tgz", - "integrity": "sha512-RoDlZufvrtr2Nx3Yx5MB8jX3aHIxm8nRWPJm3yVvyHmyKaRvn90RjzB6hNnt0AkhS3IInJdyRfQb4mWhPvUjVw==", - "dev": true + "version": "1.4.284", + "dev": true, + "license": "ISC" }, "node_modules/email-addresses": { "version": "5.0.0", @@ -2583,9 +2542,8 @@ }, "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/error-ex": { "version": "1.3.2", @@ -2702,24 +2660,21 @@ }, "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/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" }, @@ -2728,12 +2683,11 @@ } }, "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", "dev": true, + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2741,9 +2695,8 @@ }, "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, + "license": "BSD-2-Clause", "dependencies": { "esprima": "^4.0.1", "estraverse": "^4.2.0", @@ -2763,9 +2716,8 @@ }, "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, + "license": "MIT", "dependencies": { "prelude-ls": "~1.1.2", "type-check": "~0.3.2" @@ -2776,9 +2728,8 @@ }, "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, + "license": "MIT", "dependencies": { "deep-is": "~0.1.3", "fast-levenshtein": "~2.0.6", @@ -2793,8 +2744,6 @@ }, "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" @@ -2802,9 +2751,8 @@ }, "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, + "license": "MIT", "dependencies": { "prelude-ls": "~1.1.2" }, @@ -2813,12 +2761,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.36.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz", + "integrity": "sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==", "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.1", + "@eslint/js": "8.36.0", "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -2829,10 +2780,9 @@ "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", + "espree": "^9.5.0", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", @@ -2853,7 +2803,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,99 +2830,22 @@ "node": ">=8.0.0" } }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz", + "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "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" + "url": "https://opencollective.com/eslint" } }, "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==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -2984,90 +2856,16 @@ }, "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, + "license": "BSD-2-Clause", "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", - "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/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.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz", + "integrity": "sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==", "dev": true, "dependencies": { "acorn": "^8.8.0", @@ -3083,9 +2881,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" @@ -3095,9 +2892,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" @@ -3117,9 +2914,8 @@ }, "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, + "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -3129,40 +2925,37 @@ }, "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, + "license": "BSD-2-Clause", "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, + "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": "6.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", - "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", + "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", "dev": true, "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.1", - "human-signals": "^3.0.1", + "human-signals": "^4.3.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", @@ -3171,12 +2964,24 @@ "strip-final-newline": "^3.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" }, "funding": { "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/execa/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -3233,15 +3038,13 @@ }, "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 + "dev": true, + "license": "MIT" }, "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.13.0", "dev": true, + "license": "ISC", "dependencies": { "reusify": "^1.0.4" } @@ -3285,11 +3088,34 @@ "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", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, + "license": "MIT", "dependencies": { "flat-cache": "^3.0.4" }, @@ -3299,27 +3125,24 @@ }, "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, + "license": "MIT", "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", - "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", @@ -3334,9 +3157,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" }, @@ -3346,9 +3168,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", @@ -3362,32 +3183,32 @@ } }, "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", "dev": true, + "license": "MIT", "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": { "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/flat-cache": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", "dev": true, + "license": "MIT", "dependencies": { "flatted": "^3.1.0", "rimraf": "^3.0.2" @@ -3398,9 +3219,8 @@ }, "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==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/for-each": { "version": "0.3.3", @@ -3413,9 +3233,8 @@ }, "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" @@ -3425,10 +3244,9 @@ } }, "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==", + "version": "2.1.3", "dev": true, + "license": "MIT", "engines": { "node": ">= 14.17" } @@ -3447,8 +3265,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": [ { @@ -3463,13 +3279,13 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "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", @@ -3481,28 +3297,11 @@ }, "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.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" - } + "license": "ISC" }, "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", @@ -3514,15 +3313,13 @@ }, "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 + "dev": true, + "license": "MIT" }, "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, + "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -3532,15 +3329,13 @@ }, "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 + "dev": true, + "license": "MIT" }, "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 + "dev": true, + "license": "MIT" }, "node_modules/function.prototype.name": { "version": "1.1.5", @@ -3571,26 +3366,23 @@ }, "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==", + "license": "MIT", "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, + "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.*" } @@ -3611,18 +3403,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": "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" }, @@ -3648,9 +3438,8 @@ }, "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, + "license": "MIT", "dependencies": { "@tootallnate/once": "1", "data-uri-to-buffer": "3", @@ -3665,9 +3454,8 @@ }, "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, + "license": "MIT", "engines": { "node": ">= 6" } @@ -3694,11 +3482,38 @@ "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", - "integrity": "sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ==", "dev": true, + "license": "MIT", "dependencies": { "is-ssh": "^1.4.0", "parse-url": "^8.1.0" @@ -3706,23 +3521,21 @@ }, "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==", "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==", + "version": "7.2.0", "dev": true, + "license": "ISC", "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" }, @@ -3735,9 +3548,8 @@ }, "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, + "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -3746,10 +3558,9 @@ } }, "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==", + "version": "3.0.0", "dev": true, + "license": "MIT", "dependencies": { "ini": "2.0.0" }, @@ -3775,18 +3586,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", @@ -3803,19 +3602,23 @@ } }, "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==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "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" + "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": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/gopd": { @@ -3831,15 +3634,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", @@ -3857,21 +3660,18 @@ }, "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/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 + "dev": true, + "license": "MIT" }, "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, + "license": "MIT", "dependencies": { "function-bind": "^1.1.1" }, @@ -3890,9 +3690,8 @@ }, "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" } @@ -3950,9 +3749,8 @@ }, "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, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -3962,9 +3760,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" @@ -3976,41 +3773,26 @@ "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", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true, + "license": "(MIT OR CC0-1.0)", "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, + "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", @@ -4020,9 +3802,8 @@ }, "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", @@ -4036,9 +3817,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, + "license": "MIT", "dependencies": { "@tootallnate/once": "1", "agent-base": "6", @@ -4049,10 +3829,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.1.11", "dev": true, + "license": "MIT", "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.2.0" @@ -4063,9 +3842,8 @@ }, "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, + "license": "MIT", "dependencies": { "agent-base": "6", "debug": "4" @@ -4075,19 +3853,18 @@ } }, "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": { "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" }, @@ -4116,10 +3893,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.2.0", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } @@ -4142,36 +3918,32 @@ }, "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" @@ -4179,43 +3951,80 @@ }, "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.1.4", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.1.4.tgz", - "integrity": "sha512-9hiJxE5gkK/cM2d1mTEnuurGTAoHebbkX0BYl3h7iEg7FYfuNIom+nDfBCSWtvSnoSrWCeBxqqBZu26xdlJlXA==", + "version": "9.1.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.1.5.tgz", + "integrity": "sha512-3ygAIh8gcZavV9bj6MTdYddG2zPSYswP808fKS46NOwlF0zZljVpnLCHODDqItWJDbDpLb3aouAxGaJbkxoppA==", "dev": true, "dependencies": { "ansi-escapes": "^6.0.0", - "chalk": "^5.1.2", + "chalk": "^5.2.0", "cli-cursor": "^4.0.0", "cli-width": "^4.0.0", "external-editor": "^3.0.3", "figures": "^5.0.0", "lodash": "^4.17.21", - "mute-stream": "0.0.8", + "mute-stream": "1.0.0", "ora": "^6.1.2", "run-async": "^2.4.0", - "rxjs": "^7.5.7", + "rxjs": "^7.8.0", "string-width": "^5.1.2", "strip-ansi": "^7.0.1", "through": "^2.3.6", - "wrap-ansi": "^8.0.1" + "wrap-ansi": "^8.1.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.18.0" + } + }, + "node_modules/inquirer/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/internal-slot": { @@ -4234,18 +4043,16 @@ }, "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", @@ -4264,13 +4071,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": { @@ -4297,9 +4104,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" }, @@ -4337,9 +4143,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" }, @@ -4349,9 +4154,8 @@ }, "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==", "dev": true, + "license": "MIT", "dependencies": { "has": "^1.0.3" }, @@ -4375,15 +4179,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" @@ -4391,27 +4195,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" }, @@ -4419,11 +4220,28 @@ "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", - "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", "dev": true, + "license": "MIT", "dependencies": { "global-dirs": "^3.0.0", "is-path-inside": "^3.0.2" @@ -4470,9 +4288,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" }, @@ -4482,9 +4299,8 @@ }, "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" } @@ -4506,27 +4322,24 @@ }, "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" } @@ -4579,20 +4392,18 @@ }, "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": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "version": "2.0.1", "dev": true, + "license": "MIT", "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4649,17 +4460,15 @@ }, "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": "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", "dev": true, + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4679,9 +4488,8 @@ }, "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" } @@ -4698,11 +4506,25 @@ "node": ">=8" } }, - "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==", + "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.0", + "dev": true, + "license": "MIT", "engines": { "node": ">=12" } @@ -4715,24 +4537,21 @@ }, "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/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, + "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" }, @@ -4742,9 +4561,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", @@ -4755,11 +4573,18 @@ "node": ">=8" } }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "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", - "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", "dev": true, + "license": "ISC", "dependencies": { "archy": "^1.0.0", "cross-spawn": "^7.0.3", @@ -4774,9 +4599,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, + "license": "BSD-3-Clause", "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^3.0.0", @@ -4788,9 +4612,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, + "license": "BSD-3-Clause", "dependencies": { "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", @@ -4802,9 +4625,8 @@ }, "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==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" @@ -4836,26 +4658,19 @@ } }, "node_modules/js-sdsl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", - "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "version": "4.1.5", "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } + "license": "MIT" }, "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" }, @@ -4865,9 +4680,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" }, @@ -4895,9 +4709,8 @@ }, "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 + "dev": true, + "license": "MIT" }, "node_modules/json5": { "version": "2.2.3", @@ -4919,9 +4732,8 @@ }, "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" } @@ -4943,9 +4755,8 @@ }, "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" }, @@ -4958,9 +4769,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, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -4976,15 +4786,17 @@ "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", "dev": true, + "license": "MIT", "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": { @@ -4995,9 +4807,8 @@ }, "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", @@ -5007,21 +4818,19 @@ }, "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 + "dev": true, + "license": "MIT" }, "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", "dev": true, + "license": "MIT", "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" @@ -5041,18 +4850,16 @@ }, "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.1.0", @@ -5068,9 +4875,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" }, @@ -5081,11 +4887,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "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 + "dev": true, + "license": "ISC" }, "node_modules/marked": { "version": "4.2.12", @@ -5174,9 +4987,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" }, @@ -5185,10 +4997,9 @@ } }, "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "version": "1.2.7", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -5233,63 +5044,18 @@ "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==", + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/mocha/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/mocha/node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "balanced-match": "^1.0.0" } }, "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, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -5298,118 +5064,13 @@ }, "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", - "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", - "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", - "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" - } + "license": "MIT" }, "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" }, @@ -5417,56 +5078,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", - "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" - } + "license": "MIT" }, "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, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -5476,23 +5096,10 @@ "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, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -5505,9 +5112,8 @@ }, "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, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -5520,20 +5126,10 @@ "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, + "license": "MIT", "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -5547,32 +5143,24 @@ "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 + "dev": true, + "license": "MIT" }, "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", - "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" }, @@ -5582,9 +5170,8 @@ }, "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 + "dev": true, + "license": "MIT" }, "node_modules/natural-compare-lite": { "version": "1.4.0", @@ -5594,18 +5181,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" }, @@ -5618,9 +5203,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" }, @@ -5661,9 +5245,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", @@ -5680,9 +5264,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" }, @@ -5691,16 +5274,14 @@ } }, "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==", - "dev": true + "version": "2.0.6", + "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" } @@ -5746,9 +5327,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", @@ -5785,20 +5365,145 @@ "node": ">=8.9" } }, - "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==", + "node_modules/nyc/node_modules/cliui": { + "version": "6.0.0", "dev": true, + "license": "ISC", + "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", + "dev": true, + "license": "MIT" + }, + "node_modules/nyc/node_modules/find-up": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "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", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "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", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/resolve-from": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "license": "MIT", + "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", + "dev": true, + "license": "MIT", + "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", + "dev": true, + "license": "ISC" + }, + "node_modules/nyc/node_modules/yargs": { + "version": "15.4.1", + "dev": true, + "license": "MIT", + "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", + "dev": true, + "license": "ISC", + "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", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -5841,9 +5546,8 @@ }, "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" } @@ -5864,17 +5568,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.0.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.0.0.tgz", + "integrity": "sha512-yerrN5WPzgwuE3T6rxAkT1UuMLDzs4Szpug7hy9s4gru3iOTnaU0yKc1AYOVYrBzvykce5gUdr9RPNB4R+Zc/A==", "dev": true, "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", + "default-browser": "^3.1.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" @@ -5882,9 +5587,8 @@ }, "node_modules/optionator": { "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", "dev": true, + "license": "MIT", "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -5898,18 +5602,18 @@ } }, "node_modules/ora": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/ora/-/ora-6.1.2.tgz", - "integrity": "sha512-EJQ3NiP5Xo94wJXIzAyOtSb0QEIAUu7m8t6UZ9krbz0vAJqr92JpcK/lEXg91q6B9pEGqrykkd2EQplnifDSBw==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-6.3.0.tgz", + "integrity": "sha512-1/D8uRFY0ay2kgBpmAwmSA404w4OoPVhHMqRqtjvrcK/dnzcEZxMJ+V4DUbyICu8IIVRclHcOf5wlD1tMY4GUQ==", "dev": true, "dependencies": { - "bl": "^5.0.0", "chalk": "^5.0.0", "cli-cursor": "^4.0.0", "cli-spinners": "^2.6.1", "is-interactive": "^2.0.0", "is-unicode-supported": "^1.1.0", "log-symbols": "^5.1.0", + "stdin-discarder": "^0.1.0", "strip-ansi": "^7.0.1", "wcwidth": "^1.0.1" }, @@ -5920,13 +5624,80 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/ora/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", + "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", "dev": true, "dependencies": { - "macos-release": "^3.0.1", + "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.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": { @@ -5947,45 +5718,44 @@ }, "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": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "version": "3.1.0", "dev": true, + "license": "MIT", "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", "dev": true, + "license": "MIT", "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": { "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" }, @@ -5995,18 +5765,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": "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, + "license": "MIT", "dependencies": { "@tootallnate/once": "1", "agent-base": "6", @@ -6024,9 +5792,8 @@ }, "node_modules/pac-resolver": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-5.0.1.tgz", - "integrity": "sha512-cy7u00ko2KVgBAjuhevqpPeHIkCIqPe1v24cydhWjmeuzaBfmUWFCZJ1iAh5TuVzVZoUzXIW7K8sMYOZ84uZ9Q==", "dev": true, + "license": "MIT", "dependencies": { "degenerator": "^3.0.2", "ip": "^1.1.5", @@ -6038,9 +5805,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", @@ -6053,9 +5819,8 @@ }, "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==", "dev": true, + "license": "MIT", "dependencies": { "got": "^12.1.0", "registry-auth-token": "^5.0.1", @@ -6069,39 +5834,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.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", @@ -6134,54 +5866,48 @@ }, "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": "1.8.0", @@ -6209,15 +5935,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" }, @@ -6227,27 +5951,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" }, @@ -6257,9 +5978,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" }, @@ -6267,20 +5987,66 @@ "node": ">=8" } }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "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", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "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", + "dev": true, + "license": "MIT", + "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, + "license": "MIT", "engines": { "node": ">= 0.8.0" } }, "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" }, @@ -6310,21 +6076,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": "5.0.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-5.0.0.tgz", - "integrity": "sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g==", "dev": true, + "license": "MIT", "dependencies": { "agent-base": "^6.0.0", "debug": "4", @@ -6341,8 +6104,13 @@ }, "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/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", "dev": true }, "node_modules/punycode": { @@ -6356,9 +6124,8 @@ }, "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" }, @@ -6371,8 +6138,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": [ { @@ -6387,13 +6152,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" }, @@ -6403,18 +6168,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/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==", + "version": "2.5.1", "dev": true, + "license": "MIT", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -6427,9 +6190,8 @@ }, "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", @@ -6442,23 +6204,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.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", @@ -6471,9 +6231,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" }, @@ -6483,8 +6242,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" @@ -6510,23 +6267,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, "node_modules/registry-auth-token": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.1.tgz", - "integrity": "sha512-UfxVOj8seK1yaIOiieV4FIP01vfBDLsY0H9sQzi9EbbUdJiuuBjJgLa1DpImXMNPnVkBD4eVxTEXcrZA6kfpJA==", "dev": true, + "license": "MIT", "dependencies": { "@pnpm/npm-conf": "^1.0.4" }, @@ -6536,9 +6280,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" }, @@ -6550,29 +6293,29 @@ } }, "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.9.3", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.9.3.tgz", + "integrity": "sha512-yFZTGJ9lH075zEAiVB4w58GgQ+bFR4by+eNClN3sdLtTLwbEm9VXrISt+8vO1fHDH83557Fns5ub1FuhAY/a3A==", "dev": true, "dependencies": { "@iarna/toml": "2.2.5", - "@octokit/rest": "19.0.5", + "@octokit/rest": "19.0.7", "async-retry": "1.3.3", - "chalk": "5.1.2", - "cosmiconfig": "8.0.0", - "execa": "6.1.0", + "chalk": "5.2.0", + "cosmiconfig": "8.1.3", + "execa": "7.1.1", "git-url-parse": "13.1.0", - "globby": "13.1.2", - "got": "12.5.3", - "inquirer": "9.1.4", + "globby": "13.1.3", + "got": "12.6.0", + "inquirer": "9.1.5", "is-ci": "3.0.1", "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.0.0", + "ora": "6.3.0", + "os-name": "5.1.0", "promise.allsettled": "1.0.6", "proxy-agent": "5.0.0", "semver": "7.3.8", @@ -6589,10 +6332,22 @@ "node": ">=14.9" } }, + "node_modules/release-it/node_modules/chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/release-it/node_modules/globby": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", - "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz", + "integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==", "dev": true, "dependencies": { "dir-glob": "^3.0.1", @@ -6608,33 +6363,6 @@ "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.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/release-it/node_modules/slash": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", @@ -6647,17 +6375,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/release-it/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "node_modules/release-it/node_modules/yargs-parser": { + "version": "21.1.1", + "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" }, @@ -6667,24 +6396,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.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, + "license": "MIT", "dependencies": { "is-core-module": "^2.9.0", "path-parse": "^1.0.7", @@ -6699,9 +6425,8 @@ }, "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": "4.0.0", @@ -6769,18 +6494,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" @@ -6788,9 +6511,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" }, @@ -6801,6 +6523,98 @@ "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/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", @@ -6812,8 +6626,6 @@ }, "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": [ { @@ -6829,6 +6641,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } @@ -6844,8 +6657,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": [ { @@ -6860,7 +6671,8 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/safe-regex-test": { "version": "1.0.0", @@ -6878,51 +6690,13 @@ }, "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.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": "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": "^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", - "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.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, + "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -6933,38 +6707,58 @@ "node": ">=10" } }, - "node_modules/semver-diff/node_modules/yallist": { + "node_modules/semver-diff": { "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": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "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/setprototypeof": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true + "dev": true, + "license": "ISC" }, "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" }, @@ -6974,18 +6768,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", @@ -7026,21 +6818,20 @@ }, "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": "15.0.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.1.tgz", - "integrity": "sha512-PZXKc08f/wcA/BMRGBze2Wmw50CWPiAH3E21EOi4B49vJ616vW4DQh4fQrqsYox2aNR/N3kCqLuB0PwwOucQrg==", + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.3.tgz", + "integrity": "sha512-si3geiRkeovP7Iel2O+qGL4NrO9vbMf3KsrJEi0ghP1l5aBkB5UxARea5j0FUsSqH3HLBh0dQPAyQ8fObRUqHw==", "dev": true, "dependencies": { - "@sinonjs/commons": "^2.0.0", - "@sinonjs/fake-timers": "10.0.2", - "@sinonjs/samsam": "^7.0.1", - "diff": "^5.0.0", - "nise": "^5.1.2", + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^10.0.2", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.4", "supports-color": "^7.2.0" }, "funding": { @@ -7048,6 +6839,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", @@ -7059,9 +6868,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" @@ -7069,9 +6877,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" @@ -7083,9 +6890,8 @@ }, "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, + "license": "MIT", "dependencies": { "agent-base": "^6.0.2", "debug": "4", @@ -7097,24 +6903,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/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, + "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -7122,9 +6925,8 @@ }, "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", @@ -7139,19 +6941,32 @@ }, "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/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.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", @@ -7175,9 +6990,8 @@ }, "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", @@ -7190,6 +7004,31 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/string-width/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/string-width/node_modules/strip-ansi": { + "version": "7.0.1", + "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/string.prototype.trimend": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", @@ -7219,29 +7058,33 @@ } }, "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", "dev": true, + "license": "MIT", "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": { "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-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/strip-final-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", @@ -7256,9 +7099,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" }, @@ -7268,9 +7110,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" }, @@ -7280,18 +7121,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": "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" }, @@ -7301,9 +7140,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" }, @@ -7313,9 +7151,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", @@ -7327,9 +7164,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 + "dev": true, + "license": "MIT" }, "node_modules/through": { "version": "2.3.8", @@ -7337,6 +7173,15 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, + "node_modules/titleize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-1.0.1.tgz", + "integrity": "sha512-rUwGDruKq1gX+FFHbTl5qjI7teVO7eOe+C8IcQ7QT+1BK3eEUXJqbZcBOeaRP4FwSC/C1A5jDoIVta0nIQ9yew==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -7351,18 +7196,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" }, @@ -7372,9 +7215,8 @@ }, "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" } @@ -7387,9 +7229,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" }, @@ -7399,18 +7240,16 @@ }, "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/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, + "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -7451,24 +7290,21 @@ }, "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, + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } }, "node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "version": "2.4.0", + "dev": true, + "license": "0BSD" }, "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, + "license": "MIT", "dependencies": { "tslib": "^1.8.1" }, @@ -7481,15 +7317,13 @@ }, "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 + "dev": true, + "license": "0BSD" }, "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, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -7507,12 +7341,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" @@ -7534,22 +7368,21 @@ }, "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.23.25", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.25.tgz", - "integrity": "sha512-O1he153qVyoCgJYSvIyY3bPP1wAJTegZfa6tL3APinSZhJOf8CSd8F/21M6ex8pUY/fuY6n0jAsT4fIuMGA6sA==", + "version": "0.23.28", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.28.tgz", + "integrity": "sha512-9x1+hZWTHEQcGoP7qFmlo4unUoVJLB0H/8vfO/7wqTnZxg4kPuji9y3uRzEu0ZKez63OJAUmiGhUrtukC6Uj3w==", "dev": true, "dependencies": { "lunr": "^2.3.9", "marked": "^4.2.12", - "minimatch": "^6.1.6", + "minimatch": "^7.1.3", "shiki": "^0.14.1" }, "bin": { @@ -7559,7 +7392,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,9 +7405,9 @@ } }, "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": "7.4.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.2.tgz", + "integrity": "sha512-xy4q7wou3vUoC9k1xGTXc+awNdGaGVHtFUaey8tiX4H1QRc04DZ/rmDFwNm2EBsuYEhAZ6SgMmYf3InGY6OauA==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -7587,16 +7420,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.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz", + "integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=12.20" } }, "node_modules/unbox-primitive": { @@ -7616,9 +7449,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" }, @@ -7637,26 +7469,31 @@ }, "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/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/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "dev": true, "funding": [ { @@ -7668,6 +7505,7 @@ "url": "https://tidelift.com/funding/github/npm/browserslist" } ], + "license": "MIT", "dependencies": { "escalade": "^3.1.1", "picocolors": "^1.0.0" @@ -7681,9 +7519,8 @@ }, "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==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "boxen": "^7.0.0", "chalk": "^5.0.1", @@ -7707,39 +7544,17 @@ "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==", + "node_modules/update-notifier/node_modules/chalk": { + "version": "5.1.2", "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, + "license": "MIT", "engines": { - "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" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "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", @@ -7751,9 +7566,8 @@ }, "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" } @@ -7766,24 +7580,21 @@ }, "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/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 + "dev": true, + "license": "MIT" }, "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.11", "dev": true, + "license": "MIT", "dependencies": { "acorn": "^8.7.0", "acorn-walk": "^8.2.0" @@ -7843,9 +7654,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" }, @@ -7874,9 +7684,8 @@ }, "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==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/which-typed-array": { "version": "1.1.9", @@ -7900,9 +7709,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" }, @@ -7915,9 +7723,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 + "dev": true, + "license": "ISC" }, "node_modules/windows-release": { "version": "5.1.0", @@ -7966,18 +7773,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", @@ -8025,18 +7820,16 @@ }, "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, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "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": "8.1.0", @@ -8055,17 +7848,51 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi/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/wrap-ansi/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/wrap-ansi/node_modules/strip-ansi": { + "version": "7.0.1", + "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/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", @@ -8075,9 +7902,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" }, @@ -8085,398 +7911,75 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/xdg-default-browser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/xdg-default-browser/-/xdg-default-browser-2.1.0.tgz", + "integrity": "sha512-HY4G725+IDQr16N8XOjAms5qJGArdJaWIuC7Q7A8UXIwj2mifqnPXephazyL7sIkQPvmEoPX3E0v2yFv6hQUNg==", + "dev": true, + "dependencies": { + "execa": "^0.2.2", + "titleize": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/xdg-default-browser/node_modules/execa": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.2.2.tgz", + "integrity": "sha512-zmBGzLd3nhA/NB9P7VLoceAO6vyYPftvl809Vjwe5U2fYI9tYWbeKqP3wZlAw9WS+znnkogf/bhSU+Gcn2NbkQ==", + "dev": true, + "dependencies": { + "cross-spawn-async": "^2.1.1", + "npm-run-path": "^1.0.0", + "object-assign": "^4.0.1", + "path-key": "^1.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/xdg-default-browser/node_modules/npm-run-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-1.0.0.tgz", + "integrity": "sha512-PrGAi1SLlqNvKN5uGBjIgnrTb8fl0Jz0a3JJmeMcGnIBh7UE9Gc4zsAMlwDajOMg2b1OgP6UPvoLUboTmMZPFA==", + "dev": true, + "dependencies": { + "path-key": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/xdg-default-browser/node_modules/path-key": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-1.0.0.tgz", + "integrity": "sha512-T3hWy7tyXlk3QvPFnT+o2tmXRzU4GkitkUWLp/WZ0S/FXd7XMx176tRurgTvHTNMJOQzTcesHNpBqetH86mQ9g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/xregexp": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", - "integrity": "sha512-xl/50/Cf32VsGq/1R8jJE5ajH1yMCQkpmoS10QbFZWl2Oor4H0Me64Pu2yxvsRWK3m6soJbmGfzSR7BYmDcWAA==", "dev": true, - "engines": { - "node": "*" - } + "license": "MIT" }, "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", + "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": "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/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/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.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", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "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", - "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/bloom": { - "name": "@redis/bloom", - "version": "1.2.0", - "license": "MIT", - "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@redis/test-utils": "*", - "@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.25", - "typescript": "^4.9.5" - }, - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "packages/client": { - "name": "@redis/client", - "version": "1.5.6", - "license": "MIT", - "dependencies": { - "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.14.1", - "@types/sinon": "^10.0.13", - "@types/yallist": "^4.0.1", - "@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.25", - "typescript": "^4.9.5" - }, - "engines": { - "node": ">=14" - } - }, - "packages/client/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "packages/graph": { - "name": "@redis/graph", - "version": "1.1.0", - "license": "MIT", - "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@redis/test-utils": "*", - "@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.25", - "typescript": "^4.9.5" - }, - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "packages/json": { - "name": "@redis/json", - "version": "1.0.4", - "license": "MIT", - "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@redis/test-utils": "*", - "@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.25", - "typescript": "^4.9.5" - }, - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "packages/search": { - "name": "@redis/search", - "version": "1.1.2", - "license": "MIT", - "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@redis/test-utils": "*", - "@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.25", - "typescript": "^4.9.5" - }, - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "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", - "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" - }, - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "packages/test-utils/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "packages/test-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "packages/test-utils/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "packages/test-utils/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "packages/test-utils/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "packages/test-utils/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "packages/test-utils/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "packages/test-utils/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "packages/test-utils/node_modules/yargs": { "version": "17.7.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", @@ -8494,6 +7997,223 @@ "node": ">=12" } }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "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.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs-unparser/node_modules/decamelize": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "license": "MIT", + "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/yargs-parser": { + "version": "21.1.1", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/bloom": { + "name": "@redis/bloom", + "version": "1.2.0", + "license": "MIT", + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@redis/test-utils": "*", + "@types/node": "^18.15.10", + "nyc": "^15.1.0", + "release-it": "^15.9.3", + "source-map-support": "^0.5.21", + "ts-node": "^10.9.1", + "typedoc": "^0.23.28", + "typescript": "^5.0.2" + }, + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "packages/client": { + "name": "@redis/client", + "version": "1.5.6", + "license": "MIT", + "dependencies": { + "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.15.10", + "@types/sinon": "^10.0.13", + "@types/yallist": "^4.0.1", + "@typescript-eslint/eslint-plugin": "^5.57.0", + "@typescript-eslint/parser": "^5.57.0", + "eslint": "^8.36.0", + "nyc": "^15.1.0", + "release-it": "^15.9.3", + "sinon": "^15.0.3", + "source-map-support": "^0.5.21", + "ts-node": "^10.9.1", + "typedoc": "^0.23.28", + "typescript": "^5.0.2" + }, + "engines": { + "node": ">=14" + } + }, + "packages/client/node_modules/yallist": { + "version": "4.0.0", + "license": "ISC" + }, + "packages/graph": { + "name": "@redis/graph", + "version": "1.1.0", + "license": "MIT", + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@redis/test-utils": "*", + "@types/node": "^18.15.10", + "nyc": "^15.1.0", + "release-it": "^15.9.3", + "source-map-support": "^0.5.21", + "ts-node": "^10.9.1", + "typedoc": "^0.23.28", + "typescript": "^5.0.2" + }, + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "packages/json": { + "name": "@redis/json", + "version": "1.0.4", + "license": "MIT", + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@redis/test-utils": "*", + "@types/node": "^18.15.10", + "nyc": "^15.1.0", + "release-it": "^15.9.3", + "source-map-support": "^0.5.21", + "ts-node": "^10.9.1", + "typedoc": "^0.23.28", + "typescript": "^5.0.2" + }, + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "packages/search": { + "name": "@redis/search", + "version": "1.1.2", + "license": "MIT", + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@redis/test-utils": "*", + "@types/node": "^18.15.10", + "nyc": "^15.1.0", + "release-it": "^15.9.3", + "source-map-support": "^0.5.21", + "ts-node": "^10.9.1", + "typedoc": "^0.23.28", + "typescript": "^5.0.2" + }, + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "packages/test-utils": { + "name": "@redis/test-utils", + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@types/mocha": "^10.0.1", + "@types/node": "^18.15.10", + "@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.2", + "yargs": "^17.7.1" + }, + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, "packages/time-series": { "name": "@redis/time-series", "version": "1.0.4", @@ -8501,13 +8221,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", + "@types/node": "^18.15.10", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.9.3", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.23.28", + "typescript": "^5.0.2" }, "peerDependencies": { "@redis/client": "^1.0.0" diff --git a/package.json b/package.json index def009747c..3be6fe2175 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,8 @@ "devDependencies": { "@tsconfig/node14": "^1.0.3", "gh-pages": "^5.0.0", - "release-it": "^15.6.0", - "typescript": "^4.9.5" + "release-it": "^15.9.3", + "typescript": "^5.0.2" }, "repository": { "type": "git", diff --git a/packages/bloom/lib/commands/bloom/INSERT.ts b/packages/bloom/lib/commands/bloom/INSERT.ts index f6deb7a861..4c3cec0f79 100644 --- a/packages/bloom/lib/commands/bloom/INSERT.ts +++ b/packages/bloom/lib/commands/bloom/INSERT.ts @@ -1,4 +1,4 @@ -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; export const FIRST_KEY_INDEX = 1; @@ -39,7 +39,7 @@ export function transformArguments( } args.push('ITEMS'); - return pushVerdictArguments(args, items); + return pushVariadicArguments(args, items); } export { transformBooleanArrayReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/bloom/lib/commands/count-min-sketch/QUERY.ts b/packages/bloom/lib/commands/count-min-sketch/QUERY.ts index 13a71c9b1d..a34a9e9b9a 100644 --- a/packages/bloom/lib/commands/count-min-sketch/QUERY.ts +++ b/packages/bloom/lib/commands/count-min-sketch/QUERY.ts @@ -1,5 +1,5 @@ import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -9,7 +9,7 @@ export function transformArguments( key: string, items: string | Array ): RedisCommandArguments { - return pushVerdictArguments(['CMS.QUERY', key], items); + return pushVariadicArguments(['CMS.QUERY', key], items); } export declare function transformReply(): Array; diff --git a/packages/bloom/lib/commands/cuckoo/index.ts b/packages/bloom/lib/commands/cuckoo/index.ts index 96b4453bc3..c34bdde503 100644 --- a/packages/bloom/lib/commands/cuckoo/index.ts +++ b/packages/bloom/lib/commands/cuckoo/index.ts @@ -10,7 +10,7 @@ import * as INSERTNX from './INSERTNX'; import * as LOADCHUNK from './LOADCHUNK'; import * as RESERVE from './RESERVE'; import * as SCANDUMP from './SCANDUMP'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; export default { @@ -58,5 +58,5 @@ export function pushInsertOptions( } args.push('ITEMS'); - return pushVerdictArguments(args, items); + return pushVariadicArguments(args, items); } diff --git a/packages/bloom/lib/commands/t-digest/MERGE.ts b/packages/bloom/lib/commands/t-digest/MERGE.ts index 5119d0b9e1..1aebe6fa47 100644 --- a/packages/bloom/lib/commands/t-digest/MERGE.ts +++ b/packages/bloom/lib/commands/t-digest/MERGE.ts @@ -1,5 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { CompressionOption, pushCompressionArgument } from '.'; export const FIRST_KEY_INDEX = 1; @@ -13,7 +13,7 @@ export function transformArguments( srcKeys: RedisCommandArgument | Array, options?: MergeOptions ): RedisCommandArguments { - const args = pushVerdictArgument( + const args = pushVariadicArgument( ['TDIGEST.MERGE', destKey], srcKeys ); diff --git a/packages/bloom/lib/commands/top-k/ADD.ts b/packages/bloom/lib/commands/top-k/ADD.ts index beee3a2206..125c2e71be 100644 --- a/packages/bloom/lib/commands/top-k/ADD.ts +++ b/packages/bloom/lib/commands/top-k/ADD.ts @@ -1,5 +1,5 @@ import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,7 +7,7 @@ export function transformArguments( key: string, items: string | Array ): RedisCommandArguments { - return pushVerdictArguments(['TOPK.ADD', key], items); + return pushVariadicArguments(['TOPK.ADD', key], items); } export declare function transformReply(): Array; diff --git a/packages/bloom/lib/commands/top-k/COUNT.ts b/packages/bloom/lib/commands/top-k/COUNT.ts index fc8cf557dc..cb877ecc25 100644 --- a/packages/bloom/lib/commands/top-k/COUNT.ts +++ b/packages/bloom/lib/commands/top-k/COUNT.ts @@ -1,5 +1,5 @@ import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -9,7 +9,7 @@ export function transformArguments( key: string, items: string | Array ): RedisCommandArguments { - return pushVerdictArguments(['TOPK.COUNT', key], items); + return pushVariadicArguments(['TOPK.COUNT', key], items); } export declare function transformReply(): Array; diff --git a/packages/bloom/lib/commands/top-k/QUERY.ts b/packages/bloom/lib/commands/top-k/QUERY.ts index 94943a26fd..55451df3d4 100644 --- a/packages/bloom/lib/commands/top-k/QUERY.ts +++ b/packages/bloom/lib/commands/top-k/QUERY.ts @@ -1,5 +1,5 @@ import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -9,7 +9,7 @@ export function transformArguments( key: string, items: string | Array ): RedisCommandArguments { - return pushVerdictArguments(['TOPK.QUERY', key], items); + return pushVariadicArguments(['TOPK.QUERY', key], items); } export declare function transformReply(): Array; diff --git a/packages/bloom/package.json b/packages/bloom/package.json index d4eef193d8..aae71bb442 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.14.1", + "@types/node": "^18.15.10", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.9.3", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.23.28", + "typescript": "^5.0.2" } } diff --git a/packages/client/.eslintrc.json b/packages/client/.eslintrc.json index 4536bc3133..3037932fd9 100644 --- a/packages/client/.eslintrc.json +++ b/packages/client/.eslintrc.json @@ -1,15 +1,15 @@ { - "root": true, - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ], - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended" - ], - "rules": { - "semi": [2, "always"] - } + "root": true, + "parser": "@typescript-eslint/parser", + "plugins": [ + "@typescript-eslint" + ], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + "semi": [2, "always"] } +} diff --git a/packages/client/index.ts b/packages/client/index.ts index 8b21c5d5a3..7bb2f23946 100644 --- a/packages/client/index.ts +++ b/packages/client/index.ts @@ -1,24 +1,18 @@ -import RedisClient from './lib/client'; -import RedisCluster from './lib/cluster'; - -export { RedisClientType, RedisClientOptions } from './lib/client'; - -export { RedisModules, RedisFunctions, RedisScripts } from './lib/commands'; +export { RedisModules, RedisFunctions, RedisScripts, RespVersions } from './lib/RESP/types'; +export { VerbatimString } from './lib/RESP/verbatim-string'; +export { defineScript } from './lib/lua-script'; +// export * from './lib/errors'; +import RedisClient, { RedisClientType, RedisClientOptions } from './lib/client'; +export { RedisClientType, RedisClientOptions }; export const createClient = RedisClient.create; -export const commandOptions = RedisClient.commandOptions; - -export { RedisClusterType, RedisClusterOptions } from './lib/cluster'; - +import RedisCluster, { RedisClusterType, RedisClusterOptions } from './lib/cluster'; +export { RedisClusterType, RedisClusterOptions }; export const createCluster = RedisCluster.create; -export { defineScript } from './lib/lua-script'; +// export { GeoReplyWith } from './lib/commands/generic-transformers'; -export * from './lib/errors'; +// export { SetOptions } from './lib/commands/SET'; -export { GeoReplyWith } from './lib/commands/generic-transformers'; - -export { SetOptions } from './lib/commands/SET'; - -export { RedisFlushModes } from './lib/commands/FLUSHALL'; +// export { RedisFlushModes } from './lib/commands/FLUSHALL'; diff --git a/packages/client/lib/RESP/decoder-ts.ts b/packages/client/lib/RESP/decoder-ts.ts new file mode 100644 index 0000000000..0586940f69 --- /dev/null +++ b/packages/client/lib/RESP/decoder-ts.ts @@ -0,0 +1,1043 @@ +// import { ErrorReply } from '../../errors'; +// import { Flags, ReplyUnion, RespTypesUnion, ReplyWithFlags, RespTypes, Flag } from './types'; + +// // https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md +// export const TYPES = { +// NULL: 95, // _ +// BOOLEAN: 35, // # +// NUMBER: 58, // : +// BIG_NUMBER: 40, // ( +// DOUBLE: 44, // , +// SIMPLE_STRING: 43, // + +// BLOB_STRING: 36, // $ +// VERBATIM_STRING: 61, // = +// SIMPLE_ERROR: 45, // - +// BLOB_ERROR: 33, // ! +// ARRAY: 42, // * +// SET: 126, // ~ +// MAP: 37, // % +// PUSH: 62 // > +// } as const; + +// const ASCII = { +// '\r': 13, +// 't': 116, +// '-': 45, +// '0': 48, +// '.': 46, +// 'i': 105, +// 'n': 110 +// } as const; + +// // this was written with performance in mind, so it's not very readable... sorry :( + +// interface DecoderConfig { +// // TODO: on parser error? +// // TODO: types +// onReply(reply: unknown): unknown; +// onErrorReply(reply: unknown): unknown; +// onPush(push: unknown): unknown; +// getFlags(): Flags; +// } + +// type ValueCb = DecoderConfig['onReply'] | DecoderConfig['onErrorReply'] | DecoderConfig['onPush']; + +// type Next = (chunk: Buffer) => ValueOrNext; + +// type ValueOrNext = T | Next; + +// export class Decoder { +// #config: DecoderConfig; + +// #cursor = 0; + +// #next: Next | undefined; + +// constructor(config: DecoderConfig) { +// this.#config = config; +// } + +// reset() { +// this.#cursor = 0; +// this.#next = undefined; +// } + +// write(chunk: Buffer) { +// if (this.#cursor >= chunk.length) { +// this.#cursor -= chunk.length; +// return; +// } + +// if (this.#next) { +// if (this.#next(chunk) || this.#cursor >= chunk.length) { +// this.#cursor -= chunk.length; +// return; +// } +// } + +// do { +// const flags = this.#config.getFlags(), +// type = chunk[this.#cursor] as RespTypesUnion; + +// if (++this.#cursor === chunk.length) { +// this.#next = this.#continueDecodeTypeValue.bind(this, type, flags); +// break; +// } + +// if (this.#decodeTypeValue(type, flags, chunk)) { +// break; +// } +// } while (this.#cursor < chunk.length); +// this.#cursor -= chunk.length; +// } + +// #continueDecodeTypeValue( +// type: RespTypesUnion, +// flags: Flags, +// chunk: Buffer +// ) { +// this.#next = undefined; +// return this.#decodeTypeValue(type, flags, chunk); +// } + +// #decodeTypeValue( +// type: RespTypesUnion, +// flags: Flags, +// chunk: Buffer +// ) { +// switch (type) { +// case TYPES.NULL: +// this.#config.onReply(this.#decodeNull()); +// return false; + +// case TYPES.BOOLEAN: +// return this.#handleDecodedValue( +// this.#config.onReply, +// this.#decodeBoolean(chunk) +// ); + +// case TYPES.NUMBER: +// return this.#handleDecodedValue( +// this.#config.onReply, +// this.#decodeNumber(chunk) +// ); + +// case TYPES.BIG_NUMBER: +// return this.#handleDecodedValue( +// this.#config.onReply, +// this.#decodeBigNumber(flags[TYPES.BIG_NUMBER], chunk) +// ); + +// case TYPES.DOUBLE: +// return this.#handleDecodedValue( +// this.#config.onReply, +// this.#decodeDouble(flags[TYPES.DOUBLE], chunk) +// ); + +// case TYPES.SIMPLE_STRING: +// return this.#handleDecodedValue( +// this.#config.onReply, +// this.#decodeDouble(flags[TYPES.DOUBLE], chunk) +// ); + +// case TYPES.BLOB_STRING: +// return this.#handleDecodedValue( +// this.#config.onReply, +// this.#decodeBlobString(flags[TYPES.BLOB_STRING], chunk) +// ); + +// case TYPES.VERBATIM_STRING: +// throw new Error('TODO: verbatim string'); + +// case TYPES.SIMPLE_ERROR: +// return this.#handleDecodedValue( +// this.#config.onErrorReply, +// this.#decodeSimpleError(chunk) +// ); + +// case TYPES.BLOB_ERROR: +// return this.#handleDecodedValue( +// this.#config.onErrorReply, +// this.#decodeBlobError(chunk) +// ); + +// case TYPES.ARRAY: +// return this.#handleDecodedValue( +// this.#config.onReply, +// this.#decodeArray(flags, chunk) +// ); + +// case TYPES.SET: +// return this.#handleDecodedValue( +// this.#config.onReply, +// this.#decodeSet(flags, chunk) +// ); + +// case TYPES.MAP: +// return this.#handleDecodedValue( +// this.#config.onReply, +// this.#decodeMap(flags, chunk) +// ); + +// case TYPES.PUSH: +// return this.#handleDecodedValue( +// this.#config.onPush, +// this.#decodeArray(flags, chunk) +// ); +// } +// } + +// #handleDecodedValue(cb: ValueCb, value: ValueOrNext) { +// if (typeof value === 'function') { +// this.#next = this.#continueDecodeValue.bind(this, cb, value); +// return true; +// } + +// cb(value); +// return false; +// } + +// #continueDecodeValue( +// cb: ValueCb, +// next: Next, +// chunk: Buffer +// ) { +// this.#next = undefined; +// return this.#handleDecodedValue(cb, next(chunk)); +// } + +// #decodeNull() { +// this.#cursor += 2; // skip \r\n +// return null; +// } + +// #decodeBoolean(chunk: Buffer) { +// const boolean = chunk[this.#cursor] === ASCII['t']; +// this.#cursor += 3; // skip {t | f}\r\n +// return boolean; +// } + +// #decodeNumber(chunk: Buffer) { +// const isNegative = chunk[this.#cursor] === ASCII['-']; +// if (isNegative && ++this.#cursor === chunk.length) { +// return this.#continueDecodeNumber.bind( +// this, +// isNegative, +// this.#decodeUnsingedNumber.bind(this, 0) +// ); +// } + +// const number = this.#decodeUnsingedNumber(0, chunk); +// return typeof number === 'function' ? +// this.#continueDecodeNumber.bind(this, isNegative, number) : +// isNegative ? -number : number; +// } + +// #continueDecodeNumber( +// isNegative: boolean, +// numberCb: Next, +// chunk: Buffer +// ): ValueOrNext { +// const number = numberCb(chunk); +// return typeof number === 'function' ? +// this.#continueDecodeNumber.bind(this, isNegative, number) : +// isNegative ? -number : number; +// } + +// #decodeUnsingedNumber( +// number: number, +// chunk: Buffer +// ): ValueOrNext { +// let cursor = this.#cursor; +// do { +// const byte = chunk[cursor]; +// if (byte === ASCII['\r']) { +// this.#cursor = cursor + 2; // skip \r\n +// return number; +// } +// number = number * 10 + byte - ASCII['0']; +// } while (++cursor < chunk.length); + +// this.#cursor = cursor; +// return this.#decodeUnsingedNumber.bind(this, number); +// } + +// #decodeBigNumber(flag: Flags[typeof TYPES.BIG_NUMBER], chunk: Buffer) { +// if (flag === String) { +// return this.#decodeSimpleString(String, chunk); +// } + +// const isNegative = chunk[this.#cursor] === ASCII['-']; +// if (isNegative && ++this.#cursor === chunk.length) { +// return this.#continueDecodeBigNumber.bind( +// this, +// isNegative, +// this.#decodeUnsingedBigNumber.bind(this, 0n) +// ); +// } + +// const bigNumber = this.#decodeUnsingedBigNumber(0n, chunk); +// return typeof bigNumber === 'function' ? +// this.#continueDecodeBigNumber.bind(this, isNegative, bigNumber) : +// isNegative ? -bigNumber : bigNumber; +// } + +// #continueDecodeBigNumber( +// isNegative: boolean, +// bigNumberCb: Next, +// chunk: Buffer +// ): ValueOrNext { +// const bigNumber = bigNumberCb(chunk); +// return typeof bigNumber === 'function' ? +// this.#continueDecodeBigNumber.bind(this, isNegative, bigNumber) : +// isNegative ? -bigNumber : bigNumber; +// } + +// #decodeUnsingedBigNumber( +// bigNumber: bigint, +// chunk: Buffer +// ): ValueOrNext { +// let cursor = this.#cursor; +// do { +// const byte = chunk[cursor]; +// if (byte === ASCII['\r']) { +// this.#cursor = cursor + 2; // skip \r\n +// return bigNumber; +// } +// bigNumber = bigNumber * 10n + BigInt(byte - ASCII['0']); +// } while (++cursor < chunk.length); + +// this.#cursor = cursor; +// return this.#decodeUnsingedBigNumber.bind(this, bigNumber); +// } + +// #decodeDouble(flag: Flags[RespTypes['DOUBLE']], chunk: Buffer) { +// if (flag === String) { +// return this.#decodeSimpleString(String, chunk); +// } + +// switch (chunk[this.#cursor]) { +// case ASCII['n']: +// this.#cursor += 5; // skip nan\r\n +// return NaN; + +// case ASCII['-']: +// return ++this.#cursor === chunk.length ? +// this.#decodeDoubleInteger.bind(this, true, 0, chunk) : +// this.#decodeDoubleInteger(true, 0, chunk); + +// default: +// return this.#decodeDoubleInteger(false, 0, chunk); +// } +// } + +// #decodeDoubleInteger( +// isNegative: boolean, +// integer: number, +// chunk: Buffer +// ) { +// if (chunk[this.#cursor] === ASCII['i']) { +// this.#cursor += 5; // skip inf\r\n +// return isNegative ? -Infinity : Infinity; +// } + +// return this.#continueDecodeDoubleInteger(isNegative, integer, chunk); +// } + +// #continueDecodeDoubleInteger( +// isNegative: boolean, +// integer: number, +// chunk: Buffer +// ): ValueOrNext { +// let cursor = this.#cursor; +// do { +// const byte = chunk[cursor]; +// switch (byte) { +// case ASCII['.']: +// this.#cursor = ++cursor; +// return cursor < chunk.length ? +// this.#decodeDoubleDecimal(isNegative, 0, integer, chunk) : +// this.#decodeDoubleDecimal.bind(this, isNegative, 0, integer); + +// case ASCII['\r']: +// this.#cursor = cursor + 2; // skip \r\n +// return isNegative ? -integer : integer; + +// default: +// integer = integer * 10 + byte - ASCII['0']; +// } +// } while (++cursor < chunk.length); + +// this.#cursor = cursor; +// return this.#continueDecodeDoubleInteger.bind(this, isNegative, integer); +// } + +// // Precalculated multipliers for decimal points to improve performance +// // "A Number only keeps about 17 decimal places of precision" +// // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number +// static #DOUBLE_DECIMAL_MULTIPLIERS = [ +// 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, +// 1e-7, 1e-8, 1e-9, 1e-10, 1e-11, 1e-12, +// 1e-13, 1e-14, 1e-15, 1e-16, 1e-17 +// ]; + +// #decodeDoubleDecimal( +// isNegative: boolean, +// decimalIndex: number, +// double: number, +// chunk: Buffer +// ): ValueOrNext { +// let cursor = this.#cursor; +// do { +// const byte = chunk[cursor]; +// if (byte === ASCII['\r']) { +// this.#cursor = cursor + 2; // skip \r\n +// return isNegative ? -double : double; +// } + +// if (decimalIndex < Decoder.#DOUBLE_DECIMAL_MULTIPLIERS.length) { +// double += (byte - ASCII['0']) * Decoder.#DOUBLE_DECIMAL_MULTIPLIERS[decimalIndex++]; +// } +// } while (++cursor < chunk.length); + +// this.#cursor = cursor; +// return this.#decodeDoubleDecimal.bind(this, isNegative, decimalIndex, double); +// } + +// #findCRLF(chunk: Buffer, cursor: number) { +// while (chunk[cursor] !== ASCII['\r']) { +// if (++cursor === chunk.length) { +// this.#cursor = chunk.length; +// return -1; +// } +// } + +// this.#cursor = cursor + 2; // skip \r\n +// return cursor; +// } + +// #decodeSimpleString( +// flag: F, +// chunk: Buffer +// ): ValueOrNext ? T : never> { +// const start = this.#cursor, +// crlfIndex = this.#findCRLF(chunk, start); +// if (crlfIndex === -1) { +// return this.#continueDecodeSimpleString.bind( +// this, +// [chunk.subarray(start)], +// flag +// ); +// } + +// const slice = chunk.subarray(start, crlfIndex); +// return (flag === Buffer ? +// slice : +// slice.toString()) as (F extends Flag ? T : never); +// } + +// #continueDecodeSimpleString( +// chunks: Array, +// flag: F, +// chunk: Buffer +// ): ValueOrNext ? T : never> { +// const start = this.#cursor, +// crlfIndex = this.#findCRLF(chunk, start); +// if (crlfIndex === -1) { +// chunks.push(chunk.subarray(start)); +// return this.#continueDecodeSimpleString.bind(this, chunks, flag) as Next ? T : never>; +// } + +// chunks.push(chunk.subarray(start, crlfIndex)); +// return (flag === Buffer ? +// Buffer.concat(chunks) : +// chunks.join('')) as ValueOrNext ? T : never>; +// } + +// #decodeBlobString(flag: Flags[RespTypes['BLOB_STRING']], chunk: Buffer) { +// // RESP 2 bulk string null +// // https://github.com/redis/redis-specifications/blob/master/protocol/RESP2.md#resp-bulk-strings +// if (chunk[this.#cursor] === ASCII['-']) { +// this.#cursor += 4; // skip -1\r\n +// return null; +// } + +// const length = this.#decodeUnsingedNumber(0, chunk); +// if (typeof length === 'function') { +// return this.#continueDecodeBlobStringLength.bind(this, length, flag); +// } else if (this.#cursor >= chunk.length) { +// return this.#decodeBlobStringWithLength.bind(this, length, flag); +// } + +// return this.#decodeBlobStringWithLength(length, flag, chunk); +// } + +// #continueDecodeBlobStringLength( +// lengthCb: Next, +// flag: Flags[RespTypes['BLOB_STRING']], +// chunk: Buffer +// ): ValueOrNext { +// const length = lengthCb(chunk); +// if (typeof length === 'function') { +// return this.#continueDecodeBlobStringLength.bind(this, length, flag); +// } else if (this.#cursor >= chunk.length) { +// return this.#decodeBlobStringWithLength.bind(this, length, flag); +// } + +// return this.#decodeBlobStringWithLength(length, flag, chunk); +// } + +// #decodeBlobStringWithLength( +// length: number, +// flag: Flags[RespTypes['BLOB_STRING']], +// chunk: Buffer +// ) { +// const end = this.#cursor + length; +// if (end >= chunk.length) { +// const slice = chunk.subarray(this.#cursor); +// this.#cursor = chunk.length; +// return this.#continueDecodeBlobStringWithLength.bind( +// this, +// length - slice.length, +// [slice], +// flag +// ); +// } + +// const slice = chunk.subarray(this.#cursor, end); +// this.#cursor = end + 2; // skip ${string}\r\n +// return flag === Buffer ? +// slice : +// slice.toString(); +// } + +// #continueDecodeBlobStringWithLength( +// length: number, +// chunks: Array, +// flag: Flags[RespTypes['BLOB_STRING']], +// chunk: Buffer +// ): ValueOrNext { +// const end = this.#cursor + length; +// if (end >= chunk.length) { +// const slice = chunk.subarray(this.#cursor); +// chunks.push(slice); +// this.#cursor = chunk.length; +// return this.#continueDecodeBlobStringWithLength.bind( +// this, +// length - slice.length, +// chunks, +// flag +// ); +// } + +// chunks.push(chunk.subarray(this.#cursor, end)); +// this.#cursor = end + 2; // skip ${string}\r\n +// return flag === Buffer ? +// Buffer.concat(chunks) : +// chunks.join(''); +// } + +// #decodeSimpleError(chunk: Buffer) { +// const string = this.#decodeSimpleString(String, chunk); +// return typeof string === 'function' ? +// this.#continueDecodeSimpleError.bind(this, string) : +// new Error(string); // TODO use custom error +// } + +// #continueDecodeSimpleError(stringCb, chunk) { +// const string = stringCb(chunk); +// return typeof string === 'function' ? +// this.#continueDecodeSimpleError.bind(this, string) : +// new Error(string); // TODO use custom error +// } + +// #decodeBlobError(chunk) { +// const string = this.#decodeBlobString(String, chunk); +// return typeof string === 'function' ? +// this.#continueDecodeBlobError.bind(this, string) : +// new Error(string); // TODO use custom error +// } + +// #continueDecodeBlobError(stringCb, chunk) { +// const string = stringCb(chunk); +// return typeof string === 'function' ? +// this.#continueDecodeBlobError.bind(this, string) : +// new Error(string); // TODO use custom error +// } + +// #decodeNestedType(flags, chunk) { +// const type = chunk[this.#cursor]; +// return ++this.#cursor === chunk.length ? +// this.#decodeReplyValue.bind(this, type, flags) : +// this.#decodeReplyValue(type, flags, chunk); +// } + +// #decodeArray(flags, chunk) { +// // RESP 2 null +// // https://github.com/redis/redis-specifications/blob/master/protocol/RESP2.md#resp-arrays +// if (chunk[this.#cursor] === ASCII['-']) { +// this.#cursor += 4; // skip -1\r\n +// return null; +// } + +// return this.#decodeArrayWithLength( +// this.#decodeUnsingedNumber(0, chunk), +// flags, +// chunk +// ); +// } + +// #decodeArrayWithLength(length, flags, chunk) { +// return typeof length === 'function' ? +// this.#continueDecodeArrayLength.bind(this, length, flags) : +// this.#decodeArrayItems( +// new Array(length), +// 0, +// flags, +// chunk +// ); +// } + +// #continueDecodeArrayLength(lengthCb, flags, chunk) { +// return this.#decodeArrayWithLength( +// lengthCb(chunk), +// flags, +// chunk +// ); +// } + +// #decodeArrayItems(array, filled, flags, chunk) { +// for (let i = filled; i < array.length; i++) { +// if (this.#cursor >= chunk.length) { +// return this.#decodeArrayItems.bind( +// this, +// array, +// i, +// flags +// ); +// } + +// const item = this.#decodeNestedType(flags, chunk); +// if (typeof item === 'function') { +// return this.#continueDecodeArrayItems.bind( +// this, +// array, +// i, +// item, +// flags +// ); +// } + +// array[i] = item; +// } + +// return array; +// } + +// #continueDecodeArrayItems(array, filled, itemCb, flags, chunk) { +// const item = itemCb(chunk); +// if (typeof item === 'function') { +// return this.#continueDecodeArrayItems.bind( +// this, +// array, +// filled, +// item, +// flags +// ); +// } + +// array[filled++] = item; + +// return this.#decodeArrayItems(array, filled, flags, chunk); +// } + +// #decodeSet(flags, chunk) { +// const length = this.#decodeUnsingedNumber(0, chunk); +// if (typeof length === 'function') { +// return this.#continueDecodeSetLength.bind(this, length, flags); +// } + +// return this.#decodeSetItems( +// length, +// flags, +// chunk +// ); +// } + +// #continueDecodeSetLength(lengthCb, flags, chunk) { +// const length = lengthCb(chunk); +// return typeof length === 'function' ? +// this.#continueDecodeSetLength.bind(this, length, flags) : +// this.#decodeSetItems(length, flags, chunk); +// } + +// #decodeSetItems(length, flags, chunk) { +// return flags[TYPES.SET] === Set ? +// this.#decodeSetAsSet( +// new Set(), +// length, +// flags, +// chunk +// ) : +// this.#decodeArrayItems( +// new Array(length), +// 0, +// flags, +// chunk +// ); +// } + +// #decodeSetAsSet(set, remaining, flags, chunk) { +// // using `remaining` instead of `length` & `set.size` to make it work even if the set contains duplicates +// while (remaining > 0) { +// if (this.#cursor >= chunk.length) { +// return this.#decodeSetAsSet.bind( +// this, +// set, +// remaining, +// flags +// ); +// } + +// const item = this.#decodeNestedType(flags, chunk); +// if (typeof item === 'function') { +// return this.#continueDecodeSetAsSet.bind( +// this, +// set, +// remaining, +// item, +// flags +// ); +// } + +// set.add(item); +// --remaining; +// } + +// return set; +// } + +// #continueDecodeSetAsSet(set, remaining, itemCb, flags, chunk) { +// const item = itemCb(chunk); +// if (typeof item === 'function') { +// return this.#continueDecodeSetAsSet.bind( +// this, +// set, +// remaining, +// item, +// flags +// ); +// } + +// set.add(item); + +// return this.#decodeSetAsSet(set, remaining - 1, flags, chunk); +// } + +// #decodeMap(flags, chunk) { +// const length = this.#decodeUnsingedNumber(0, chunk); +// if (typeof length === 'function') { +// return this.#continueDecodeMapLength.bind(this, length, flags); +// } + +// return this.#decodeMapItems( +// length, +// flags, +// chunk +// ); +// } + +// #continueDecodeMapLength(lengthCb, flags, chunk) { +// const length = lengthCb(chunk); +// return typeof length === 'function' ? +// this.#continueDecodeMapLength.bind(this, length, flags) : +// this.#decodeMapItems(length, flags, chunk); +// } + +// #decodeMapItems(length, flags, chunk) { +// switch (flags[TYPES.MAP]) { +// case Map: +// return this.#decodeMapAsMap( +// new Map(), +// length, +// flags, +// chunk +// ); + +// case Array: +// return this.#decodeArrayItems( +// new Array(length * 2), +// 0, +// flags, +// chunk +// ); + +// default: +// return this.#decodeMapAsObject( +// Object.create(null), +// length, +// flags, +// chunk +// ); +// } +// } + +// #decodeMapAsMap(map, remaining, flags, chunk) { +// // using `remaining` instead of `length` & `map.size` to make it work even if the map contains duplicate keys +// while (remaining > 0) { +// if (this.#cursor >= chunk.length) { +// return this.#decodeMapAsMap.bind( +// this, +// map, +// remaining, +// flags +// ); +// } + +// const key = this.#decodeMapKey(flags, chunk); +// if (typeof key === 'function') { +// return this.#continueDecodeMapKey.bind( +// this, +// map, +// remaining, +// key, +// flags +// ); +// } + +// if (this.#cursor >= chunk.length) { +// return this.#continueDecodeMapValue.bind( +// this, +// map, +// remaining, +// key, +// this.#decodeNestedType.bind(this, flags), +// flags +// ); +// } + +// const value = this.#decodeNestedType(flags, chunk); +// if (typeof value === 'function') { +// return this.#continueDecodeMapValue.bind( +// this, +// map, +// remaining, +// key, +// value, +// flags +// ); +// } + +// map.set(key, value); +// --remaining; +// } + +// return map; +// } + +// #decodeMapKey(flags, chunk) { +// const type = chunk[this.#cursor]; +// return ++this.#cursor === chunk.length ? +// this.#decodeMapKeyValue.bind(this, type, flags) : +// this.#decodeMapKeyValue(type, flags, chunk); +// } + +// #decodeMapKeyValue(type, flags, chunk) { +// switch (type) { +// // decode simple string map key as string (and not as buffer) +// case TYPES.SIMPLE_STRING: +// return this.#decodeSimpleString(String, chunk); + +// // decode blob string map key as string (and not as buffer) +// case TYPES.BLOB_STRING: +// return this.#decodeBlobString(String, chunk); + +// default: +// return this.#decodeReplyValue(type, flags, chunk); +// } +// } + +// #continueDecodeMapKey(map, remaining, keyCb, flags, chunk) { +// const key = keyCb(chunk); +// if (typeof key === 'function') { +// return this.#continueDecodeMapKey.bind( +// this, +// map, +// remaining, +// key, +// flags +// ); +// } + +// if (this.#cursor >= chunk.length) { +// return this.#continueDecodeMapValue.bind( +// this, +// map, +// remaining, +// key, +// this.#decodeNestedType.bind(this, flags), +// flags +// ); +// } + +// const value = this.#decodeNestedType(flags, chunk); +// if (typeof value === 'function') { +// return this.#continueDecodeMapValue.bind( +// this, +// map, +// remaining, +// key, +// value, +// flags +// ); +// } + +// map.set(key, value); +// return this.#decodeMapAsMap(map, remaining - 1, flags, chunk); +// } + +// #continueDecodeMapValue(map, remaining, key, valueCb, flags, chunk) { +// const value = valueCb(chunk); +// if (typeof value === 'function') { +// return this.#continueDecodeMapValue.bind( +// this, +// map, +// remaining, +// key, +// value, +// flags +// ); +// } + +// map.set(key, value); + +// return this.#decodeMapAsMap(map, remaining - 1, flags, chunk); +// } + +// #decodeMapAsObject(object, remaining, flags, chunk) { +// while (remaining > 0) { +// if (this.#cursor >= chunk.length) { +// return this.#decodeMapAsObject.bind( +// this, +// object, +// remaining, +// flags +// ); +// } + +// const key = this.#decodeMapKey(flags, chunk); +// if (typeof key === 'function') { +// return this.#continueDecodeMapAsObjectKey.bind( +// this, +// object, +// remaining, +// key, +// flags +// ); +// } + +// if (this.#cursor >= chunk.length) { +// return this.#continueDecodeMapAsObjectValue.bind( +// this, +// object, +// remaining, +// key, +// this.#decodeNestedType.bind(this, flags), +// flags +// ); +// } + +// const value = this.#decodeNestedType(flags, chunk); +// if (typeof value === 'function') { +// return this.#continueDecodeMapAsObjectValue.bind( +// this, +// object, +// remaining, +// key, +// value, +// flags +// ); +// } + +// object[key] = value; +// --remaining; +// } + +// return object; +// } + +// #continueDecodeMapAsObjectKey(object, remaining, keyCb, flags, chunk) { +// const key = keyCb(chunk); +// if (typeof key === 'function') { +// return this.#continueDecodeMapAsObjectKey.bind( +// this, +// object, +// remaining, +// key, +// flags +// ); +// } + +// if (this.#cursor >= chunk.length) { +// return this.#continueDecodeMapAsObjectValue.bind( +// this, +// object, +// remaining, +// key, +// this.#decodeNestedType.bind(this, flags), +// flags +// ); +// } + +// const value = this.#decodeNestedType(flags, chunk); +// if (typeof value === 'function') { +// return this.#continueDecodeMapAsObjectValue.bind( +// this, +// object, +// remaining, +// key, +// value, +// flags +// ); +// } + +// object[key] = value; + +// return this.#decodeMapAsObject(object, remaining - 1, flags, chunk); +// } + +// #continueDecodeMapAsObjectValue(object, remaining, key, valueCb, flags, chunk) { +// const value = valueCb(chunk); +// if (typeof value === 'function') { +// return this.#continueDecodeMapAsObjectValue.bind( +// this, +// object, +// remaining, +// key, +// value, +// flags +// ); +// } + +// object[key] = value; + +// return this.#decodeMapAsObject(object, remaining - 1, flags, chunk); +// } +// } + +// const a = new Decoder({ +// onReply(reply) { +// console.log('[REPLY]', reply); +// }, +// onErrorReply(err) { +// console.log('[ERROR REPLY]', err); +// }, +// onPush(push) { +// console.log('[PUSH]', push); +// }, +// getFlags() { +// return {}; +// } +// }); + +// a.write(Buffer.from('+PONG\r\n')); diff --git a/packages/client/lib/client/RESP2/decoder.spec.ts b/packages/client/lib/RESP/decoder.spec.ts similarity index 100% rename from packages/client/lib/client/RESP2/decoder.spec.ts rename to packages/client/lib/RESP/decoder.spec.ts diff --git a/packages/client/lib/RESP/decoder.ts b/packages/client/lib/RESP/decoder.ts new file mode 100644 index 0000000000..950387966e --- /dev/null +++ b/packages/client/lib/RESP/decoder.ts @@ -0,0 +1,1159 @@ +// @ts-nocheck +import { VerbatimString } from './verbatim-string'; +import { SimpleError, BlobError, ErrorReply } from '../errors'; +import { Flags } from './types'; + +// https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md +export const TYPES = { + NULL: 95, // _ + BOOLEAN: 35, // # + NUMBER: 58, // : + BIG_NUMBER: 40, // ( + DOUBLE: 44, // , + SIMPLE_STRING: 43, // + + BLOB_STRING: 36, // $ + VERBATIM_STRING: 61, // = + SIMPLE_ERROR: 45, // - + BLOB_ERROR: 33, // ! + ARRAY: 42, // * + SET: 126, // ~ + MAP: 37, // % + PUSH: 62 // > +} as const; + +const ASCII = { + '\r': 13, + 't': 116, + '+': 43, + '-': 45, + '0': 48, + '.': 46, + 'i': 105, + 'n': 110, + 'E': 69, + 'e': 101 +} as const; + +export const PUSH_FLAGS = { + [TYPES.BLOB_STRING]: Buffer +}; + +// this was written with performance in mind, so it's not very readable... sorry :( +// using `private _` instead of `#` to boost performance + +interface DecoderOptions { + onReply(reply: any): unknown; + onErrorReply(err: ErrorReply): unknown; + onPush(push: Array): unknown; + getFlags(): Flags; +} + +export class Decoder { + private _config; + + private _cursor = 0; + + private _next; + + constructor(config: DecoderOptions) { + this._config = config; + } + + reset() { + this._cursor = 0; + this._next = undefined; + } + + write(chunk) { + if (this._cursor >= chunk.length) { + this._cursor -= chunk.length; + return; + } + + if (this._next) { + if (this._next(chunk) || this._cursor >= chunk.length) { + this._cursor -= chunk.length; + return; + } + } + + do { + const type = chunk[this._cursor]; + if (++this._cursor === chunk.length) { + this._next = this._continueDecodeTypeValue.bind(this, type); + break; + } + + if (this._decodeTypeValue(type, chunk)) { + break; + } + } while (this._cursor < chunk.length); + this._cursor -= chunk.length; + } + + private _continueDecodeTypeValue(type, chunk) { + this._next = undefined; + return this._decodeTypeValue(type, chunk); + } + + private _decodeTypeValue(type, chunk) { + switch (type) { + case TYPES.NULL: + this._config.onReply(this._decodeNull()); + return false; + + case TYPES.BOOLEAN: + return this._handleDecodedValue( + this._config.onReply, + this._decodeBoolean(chunk) + ); + + case TYPES.NUMBER: + return this._handleDecodedValue( + this._config.onReply, + this._decodeNumber(chunk) + ); + + case TYPES.BIG_NUMBER: + return this._handleDecodedValue( + this._config.onReply, + this._decodeBigNumber( + this._config.getFlags()[TYPES.BIG_NUMBER], + chunk + ) + ); + + case TYPES.DOUBLE: + return this._handleDecodedValue( + this._config.onReply, + this._decodeDouble( + this._config.getFlags()[TYPES.DOUBLE], + chunk + ) + ); + + case TYPES.SIMPLE_STRING: + return this._handleDecodedValue( + this._config.onReply, + this._decodeSimpleString( + this._config.getFlags()[TYPES.SIMPLE_STRING], + chunk + ) + ); + + case TYPES.BLOB_STRING: + return this._handleDecodedValue( + this._config.onReply, + this._decodeBlobString( + this._config.getFlags()[TYPES.BLOB_STRING], + chunk + ) + ); + + case TYPES.VERBATIM_STRING: + return this._handleDecodedValue( + this._config.onReply, + this._decodeVerbatimString( + this._config.getFlags()[TYPES.VERBATIM_STRING], + chunk + ) + ); + + case TYPES.SIMPLE_ERROR: + return this._handleDecodedValue( + this._config.onErrorReply, + this._decodeSimpleError(chunk) + ); + + case TYPES.BLOB_ERROR: + return this._handleDecodedValue( + this._config.onErrorReply, + this._decodeBlobError(chunk) + ); + + case TYPES.ARRAY: + return this._handleDecodedValue( + this._config.onReply, + this._decodeArray(this._config.getFlags(), chunk) + ); + + case TYPES.SET: + return this._handleDecodedValue( + this._config.onReply, + this._decodeSet(this._config.getFlags(), chunk) + ); + + case TYPES.MAP: + return this._handleDecodedValue( + this._config.onReply, + this._decodeMap(this._config.getFlags(), chunk) + ); + + case TYPES.PUSH: + return this._handleDecodedValue( + this._config.onPush, + this._decodeArray(PUSH_FLAGS, chunk) + ); + } + } + + private _handleDecodedValue(cb, value) { + if (typeof value === 'function') { + this._next = this._continueDecodeValue.bind(this, cb, value); + return true; + } + + cb(value); + return false; + } + + private _continueDecodeValue(cb, next, chunk) { + this._next = undefined; + return this._handleDecodedValue(cb, next(chunk)); + } + + private _decodeNull() { + this._cursor += 2; // skip \r\n + return null; + } + + private _decodeBoolean(chunk) { + const boolean = chunk[this._cursor] === ASCII.t; + this._cursor += 3; // skip {t | f}\r\n + return boolean; + } + + private _decodeNumber(chunk) { + switch (chunk[this._cursor]) { + case ASCII['+']: + return this._maybeDecodeNumberValue(false, chunk); + + case ASCII['-']: + return this._maybeDecodeNumberValue(true, chunk); + + default: + return this._decodeNumberValue( + false, + this._decodeUnsingedNumber.bind(this, 0), + chunk + ); + } + } + + private _maybeDecodeNumberValue(isNegative, chunk) { + const cb = this._decodeUnsingedNumber.bind(this, 0); + return this._cursor === chunk.length ? + this._decodeNumberValue.bind(isNegative, cb) : + this._decodeNumberValue(isNegative, cb, chunk); + } + + private _decodeNumberValue(isNegative, numberCb, chunk) { + const number = numberCb(chunk); + return typeof number === 'function' ? + this._decodeNumberValue.bind(this, isNegative, number) : + isNegative ? -number : number; + } + + private _decodeUnsingedNumber(number, chunk) { + let cursor = this._cursor; + do { + const byte = chunk[cursor]; + if (byte === ASCII['\r']) { + this._cursor = cursor + 2; // skip \r\n + return number; + } + number = number * 10 + byte - ASCII['0']; + } while (++cursor < chunk.length); + + this._cursor = cursor; + return this._decodeUnsingedNumber.bind(this, number); + } + + private _decodeBigNumber(flag, chunk) { + if (flag === String) { + return this._decodeSimpleString(String, chunk); + } + + switch (chunk[this._cursor]) { + case ASCII['+']: + return this._maybeDecodeBigNumberValue(false, chunk); + + case ASCII['-']: + return this._maybeDecodeBigNumberValue(true, chunk); + + default: + return this._decodeBigNumberValue( + false, + this._decodeUnsingedBigNumber.bind(this, 0n), + chunk + ); + } + } + + private _maybeDecodeBigNumberValue(isNegative, chunk) { + const cb = this._decodeUnsingedBigNumber.bind(this, 0n); + return this._cursor === chunk.length ? + this._decodeBigNumberValue.bind(isNegative, cb) : + this._decodeBigNumberValue(isNegative, cb, chunk); + } + + private _decodeBigNumberValue(isNegative, bigNumberCb, chunk) { + const bigNumber = bigNumberCb(chunk); + return typeof bigNumber === 'function' ? + this._decodeBigNumberValue.bind(this, isNegative, bigNumber) : + isNegative ? -bigNumber : bigNumber; + } + + private _decodeUnsingedBigNumber(bigNumber, chunk) { + let cursor = this._cursor; + do { + const byte = chunk[cursor]; + if (byte === ASCII['\r']) { + this._cursor = cursor + 2; // skip \r\n + return bigNumber; + } + bigNumber = bigNumber * 10n + BigInt(byte - ASCII['0']); + } while (++cursor < chunk.length); + + this._cursor = cursor; + return this._decodeUnsingedBigNumber.bind(this, bigNumber); + } + + private _decodeDouble(flag, chunk) { + if (flag === String) { + return this._decodeSimpleString(String, chunk); + } + + switch (chunk[this._cursor]) { + case ASCII.n: + this._cursor += 5; // skip nan\r\n + return NaN; + + case ASCII['+']: + return this._maybeDecodeDoubleInteger(false, chunk); + + case ASCII['-']: + return this._maybeDecodeDoubleInteger(true, chunk); + + default: + return this._decodeDoubleInteger(false, 0, chunk); + } + } + + private _maybeDecodeDoubleInteger(isNegative, chunk) { + return ++this._cursor === chunk.length ? + this._decodeDoubleInteger.bind(this, isNegative, 0) : + this._decodeDoubleInteger(true, 0, chunk); + } + + private _decodeDoubleInteger(isNegative, integer, chunk) { + if (chunk[this._cursor] === ASCII.i) { + this._cursor += 5; // skip inf\r\n + return isNegative ? -Infinity : Infinity; + } + + return this._continueDecodeDoubleInteger(isNegative, integer, chunk); + } + + private _continueDecodeDoubleInteger(isNegative, integer, chunk) { + let cursor = this._cursor; + do { + const byte = chunk[cursor]; + switch (byte) { + case ASCII['.']: + this._cursor = cursor + 1; // skip . + return cursor < chunk.length ? + this._decodeDoubleDecimal(isNegative, 0, integer, chunk) : + this._decodeDoubleDecimal.bind(this, isNegative, 0, integer); + + case ASCII.E: + case ASCII.e: + this._cursor = cursor + 1; // skip e + return this._decodeDoubleExponent(isNegative ? -integer : integer, chunk); + + case ASCII['\r']: + this._cursor = cursor + 2; // skip \r\n + return isNegative ? -integer : integer; + + default: + integer = integer * 10 + byte - ASCII['0']; + } + } while (++cursor < chunk.length); + + this._cursor = cursor; + return this._continueDecodeDoubleInteger.bind(this, isNegative, integer); + } + + // Precalculated multipliers for decimal points to improve performance + // "A Number only keeps about 17 decimal places of precision" + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number + static _DOUBLE_DECIMAL_MULTIPLIERS = [ + 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, + 1e-7, 1e-8, 1e-9, 1e-10, 1e-11, 1e-12, + 1e-13, 1e-14, 1e-15, 1e-16, 1e-17 + ]; + + private _decodeDoubleDecimal(isNegative, decimalIndex, double, chunk) { + let cursor = this._cursor; + do { + const byte = chunk[cursor]; + switch (byte) { + case ASCII.E: + case ASCII.e: + this._cursor = cursor + 1; // skip e + const d = isNegative ? -double : double; + return this._cursor === chunk.length ? + this._decodeDoubleExponent.bind(this, d, false, 0) : + this._decodeDoubleExponent(d, false, 0, chunk); + + case ASCII['\r']: + this._cursor = cursor + 2; // skip \r\n + return isNegative ? -double : double; + } + + if (decimalIndex < Decoder._DOUBLE_DECIMAL_MULTIPLIERS.length) { + double += (byte - ASCII['0']) * Decoder._DOUBLE_DECIMAL_MULTIPLIERS[decimalIndex++]; + } + } while (++cursor < chunk.length); + + this._cursor = cursor; + return this._decodeDoubleDecimal.bind(this, isNegative, decimalIndex, double); + } + + private _decodeDoubleExponent(double, chunk) { + switch (chunk[this._cursor]) { + case ASCII['+']: + return ++this._cursor === chunk.length ? + this._continueDecodeDoubleExponent.bind(this, false, double, 0) : + this._continueDecodeDoubleExponent(false, double, 0, chunk); + + case ASCII['-']: + return ++this._cursor === chunk.length ? + this._continueDecodeDoubleExponent.bind(this, true, double, 0) : + this._continueDecodeDoubleExponent(true, double, 0, chunk); + } + + return this._continueDecodeDoubleExponent(false, double, 0, chunk); + } + + private _continueDecodeDoubleExponent(isNegative, double, exponent, chunk) { + let cursor = this._cursor; + do { + const byte = chunk[cursor]; + if (byte === ASCII['\r']) { + this._cursor = cursor + 2; // skip \r\n + return double * 10 ** (isNegative ? -exponent : exponent); + } + + exponent = exponent * 10 + byte - ASCII['0']; + } while (++cursor < chunk.length); + + this._cursor = cursor; + return this._continueDecodeDoubleExponent.bind(this, isNegative, double, exponent); + } + + private _findCRLF(chunk, cursor) { + while (chunk[cursor] !== ASCII['\r']) { + if (++cursor === chunk.length) { + this._cursor = chunk.length; + return -1; + } + } + + this._cursor = cursor + 2; // skip \r\n + return cursor; + } + + private _decodeSimpleString(flag, chunk) { + const start = this._cursor, + crlfIndex = this._findCRLF(chunk, start); + if (crlfIndex === -1) { + return this._continueDecodeSimpleString.bind( + this, + [chunk.subarray(start)], + flag + ); + } + + const slice = chunk.subarray(start, crlfIndex); + return flag === Buffer ? + slice : + slice.toString(); + } + + private _continueDecodeSimpleString(chunks, flag, chunk) { + const start = this._cursor, + crlfIndex = this._findCRLF(chunk, start); + if (crlfIndex === -1) { + chunks.push(chunk.subarray(start)); + return this._continueDecodeSimpleString.bind(this, chunks, flag); + } + + chunks.push(chunk.subarray(start, crlfIndex)); + return flag === Buffer ? + Buffer.concat(chunks) : + chunks.join(''); + } + + private _decodeBlobString(flag, chunk) { + // RESP 2 bulk string null + // https://github.com/redis/redis-specifications/blob/master/protocol/RESP2.md#resp-bulk-strings + if (chunk[this._cursor] === ASCII['-']) { + this._cursor += 4; // skip -1\r\n + return null; + } + + const length = this._decodeUnsingedNumber(0, chunk); + if (typeof length === 'function') { + return this._continueDecodeBlobStringLength.bind(this, length, flag); + } else if (this._cursor >= chunk.length) { + return this._decodeBlobStringWithLength.bind(this, length, flag); + } + + return this._decodeBlobStringWithLength(length, flag, chunk); + } + + private _continueDecodeBlobStringLength(lengthCb, flag, chunk) { + const length = lengthCb(chunk); + if (typeof length === 'function') { + return this._continueDecodeBlobStringLength.bind(this, length, flag); + } else if (this._cursor >= chunk.length) { + return this._decodeBlobStringWithLength.bind(this, length, flag); + } + + return this._decodeBlobStringWithLength(length, flag, chunk); + } + + private _decodeStringWithLength(length, skip, flag, chunk) { + const end = this._cursor + length; + if (end >= chunk.length) { + const slice = chunk.subarray(this._cursor); + this._cursor = chunk.length; + return this._continueDecodeStringWithLength.bind( + this, + length - slice.length, + [slice], + skip, + flag + ); + } + + const slice = chunk.subarray(this._cursor, end); + this._cursor = end + skip; + return flag === Buffer ? + slice : + slice.toString(); + } + + private _continueDecodeStringWithLength(length, chunks, skip, flag, chunk) { + const end = this._cursor + length; + if (end >= chunk.length) { + const slice = chunk.subarray(this._cursor); + chunks.push(slice); + this._cursor = chunk.length; + return this._continueDecodeStringWithLength.bind( + this, + length - slice.length, + chunks, + flag + ); + } + + chunks.push(chunk.subarray(this._cursor, end)); + this._cursor = end + skip; + return flag === Buffer ? + Buffer.concat(chunks) : + chunks.join(''); + } + + private _decodeBlobStringWithLength(length, flag, chunk) { + return this._decodeStringWithLength(length, 2, flag, chunk); + } + + private _decodeVerbatimString(flag, chunk) { + return this._continueDecodeVerbatimStringLength( + this._decodeUnsingedNumber.bind(this, 0), + flag, + chunk + ); + } + + private _continueDecodeVerbatimStringLength(lengthCb, flag, chunk) { + const length = lengthCb(chunk); + return typeof length === 'function'? + this._continueDecodeVerbatimStringLength.bind(this, length, flag) : + this._decodeVerbatimStringWithLength(length, flag, chunk); + } + + private _decodeVerbatimStringWithLength(length, flag, chunk) { + const stringLength = length - 4; // skip : + if (flag === VerbatimString) { + return this._decodeVerbatimStringFormat(stringLength, chunk); + } + + this._cursor += 4; // skip : + return this._cursor >= chunk.length ? + this._decodeBlobStringWithLength.bind(this, stringLength, flag) : + this._decodeBlobStringWithLength(stringLength, flag, chunk); + } + + private _decodeVerbatimStringFormat(stringLength, chunk) { + return this._continueDecodeVerbatimStringFormat( + stringLength, + this._decodeStringWithLength.bind(this, 3, 1, String), + chunk + ); + } + + private _continueDecodeVerbatimStringFormat(stringLength, formatCb, chunk) { + const format = formatCb(chunk); + return typeof format === 'function' ? + this._continueDecodeVerbatimStringFormat.bind(this, stringLength, format) : + this._decodeVerbatimStringWithFormat(stringLength, format, chunk); + } + + private _decodeVerbatimStringWithFormat(stringLength, format, chunk) { + return this._continueDecodeVerbatimStringWithFormat( + format, + this._decodeBlobStringWithLength.bind(this, stringLength, String), + chunk + ); + } + + private _continueDecodeVerbatimStringWithFormat(format, stringCb, chunk) { + const string = stringCb(chunk); + return typeof string === 'function' ? + this._continueDecodeVerbatimStringWithFormat.bind(this, format, string) : + new VerbatimString(format, string); + } + + private _decodeSimpleError(chunk) { + const string = this._decodeSimpleString(String, chunk); + return typeof string === 'function' ? + this._continueDecodeSimpleError.bind(this, string) : + new SimpleError(string); + } + + private _continueDecodeSimpleError(stringCb, chunk) { + const string = stringCb(chunk); + return typeof string === 'function' ? + this._continueDecodeSimpleError.bind(this, string) : + new SimpleError(string); + } + + private _decodeBlobError(chunk) { + const string = this._decodeBlobString(String, chunk); + return typeof string === 'function' ? + this._continueDecodeBlobError.bind(this, string) : + new BlobError(string); + } + + private _continueDecodeBlobError(stringCb, chunk) { + const string = stringCb(chunk); + return typeof string === 'function' ? + this._continueDecodeBlobError.bind(this, string) : + new BlobError(string); + } + + private _decodeNestedType(flags, chunk) { + const type = chunk[this._cursor]; + return ++this._cursor === chunk.length ? + this._decodeNestedTypeValue.bind(this, type, flags) : + this._decodeNestedTypeValue(type, flags, chunk); + } + + private _decodeNestedTypeValue(type, flags, chunk) { + switch (type) { + case TYPES.NULL: + return this._decodeNull(); + + case TYPES.BOOLEAN: + return this._decodeBoolean(chunk); + + case TYPES.NUMBER: + return this._decodeNumber(chunk); + + case TYPES.BIG_NUMBER: + return this._decodeBigNumber(flags[TYPES.BIG_NUMBER], chunk); + + case TYPES.DOUBLE: + return this._decodeDouble(flags[TYPES.DOUBLE], chunk); + + case TYPES.SIMPLE_STRING: + return this._decodeSimpleString(flags[TYPES.SIMPLE_STRING], chunk); + + case TYPES.BLOB_STRING: + return this._decodeBlobString(flags[TYPES.BLOB_STRING], chunk); + + case TYPES.VERBATIM_STRING: + return this._decodeVerbatimString(flags[TYPES.VERBATIM_STRING], chunk); + + case TYPES.SIMPLE_ERROR: + return this._decodeSimpleError(chunk); + + case TYPES.BLOB_ERROR: + return this._decodeBlobError(chunk); + + case TYPES.ARRAY: + return this._decodeArray(flags, chunk); + + case TYPES.SET: + return this._decodeSet(flags, chunk); + + case TYPES.MAP: + return this._decodeMap(flags, chunk); + } + } + + private _decodeArray(flags, chunk) { + // RESP 2 null + // https://github.com/redis/redis-specifications/blob/master/protocol/RESP2.md#resp-arrays + if (chunk[this._cursor] === ASCII['-']) { + this._cursor += 4; // skip -1\r\n + return null; + } + + return this._decodeArrayWithLength( + this._decodeUnsingedNumber(0, chunk), + flags, + chunk + ); + } + + private _decodeArrayWithLength(length, flags, chunk) { + return typeof length === 'function' ? + this._continueDecodeArrayLength.bind(this, length, flags) : + this._decodeArrayItems( + new Array(length), + 0, + flags, + chunk + ); + } + + private _continueDecodeArrayLength(lengthCb, flags, chunk) { + return this._decodeArrayWithLength( + lengthCb(chunk), + flags, + chunk + ); + } + + private _decodeArrayItems(array, filled, flags, chunk) { + for (let i = filled; i < array.length; i++) { + if (this._cursor >= chunk.length) { + return this._decodeArrayItems.bind( + this, + array, + i, + flags + ); + } + + const item = this._decodeNestedType(flags, chunk); + if (typeof item === 'function') { + return this._continueDecodeArrayItems.bind( + this, + array, + i, + item, + flags + ); + } + + array[i] = item; + } + + return array; + } + + private _continueDecodeArrayItems(array, filled, itemCb, flags, chunk) { + const item = itemCb(chunk); + if (typeof item === 'function') { + return this._continueDecodeArrayItems.bind( + this, + array, + filled, + item, + flags + ); + } + + array[filled++] = item; + + return this._decodeArrayItems(array, filled, flags, chunk); + } + + private _decodeSet(flags, chunk) { + const length = this._decodeUnsingedNumber(0, chunk); + if (typeof length === 'function') { + return this._continueDecodeSetLength.bind(this, length, flags); + } + + return this._decodeSetItems( + length, + flags, + chunk + ); + } + + private _continueDecodeSetLength(lengthCb, flags, chunk) { + const length = lengthCb(chunk); + return typeof length === 'function' ? + this._continueDecodeSetLength.bind(this, length, flags) : + this._decodeSetItems(length, flags, chunk); + } + + private _decodeSetItems(length, flags, chunk) { + return flags[TYPES.SET] === Set ? + this._decodeSetAsSet( + new Set(), + length, + flags, + chunk + ) : + this._decodeArrayItems( + new Array(length), + 0, + flags, + chunk + ); + } + + private _decodeSetAsSet(set, remaining, flags, chunk) { + // using `remaining` instead of `length` & `set.size` to make it work even if the set contains duplicates + while (remaining > 0) { + if (this._cursor >= chunk.length) { + return this._decodeSetAsSet.bind( + this, + set, + remaining, + flags + ); + } + + const item = this._decodeNestedType(flags, chunk); + if (typeof item === 'function') { + return this._continueDecodeSetAsSet.bind( + this, + set, + remaining, + item, + flags + ); + } + + set.add(item); + --remaining; + } + + return set; + } + + private _continueDecodeSetAsSet(set, remaining, itemCb, flags, chunk) { + const item = itemCb(chunk); + if (typeof item === 'function') { + return this._continueDecodeSetAsSet.bind( + this, + set, + remaining, + item, + flags + ); + } + + set.add(item); + + return this._decodeSetAsSet(set, remaining - 1, flags, chunk); + } + + private _decodeMap(flags, chunk) { + const length = this._decodeUnsingedNumber(0, chunk); + if (typeof length === 'function') { + return this._continueDecodeMapLength.bind(this, length, flags); + } + + return this._decodeMapItems( + length, + flags, + chunk + ); + } + + private _continueDecodeMapLength(lengthCb, flags, chunk) { + const length = lengthCb(chunk); + return typeof length === 'function' ? + this._continueDecodeMapLength.bind(this, length, flags) : + this._decodeMapItems(length, flags, chunk); + } + + private _decodeMapItems(length, flags, chunk) { + switch (flags[TYPES.MAP]) { + case Map: + return this._decodeMapAsMap( + new Map(), + length, + flags, + chunk + ); + + case Array: + return this._decodeArrayItems( + new Array(length * 2), + 0, + flags, + chunk + ); + + default: + return this._decodeMapAsObject( + Object.create(null), + length, + flags, + chunk + ); + } + } + + private _decodeMapAsMap(map, remaining, flags, chunk) { + // using `remaining` instead of `length` & `map.size` to make it work even if the map contains duplicate keys + while (remaining > 0) { + if (this._cursor >= chunk.length) { + return this._decodeMapAsMap.bind( + this, + map, + remaining, + flags + ); + } + + const key = this._decodeMapKey(flags, chunk); + if (typeof key === 'function') { + return this._continueDecodeMapKey.bind( + this, + map, + remaining, + key, + flags + ); + } + + if (this._cursor >= chunk.length) { + return this._continueDecodeMapValue.bind( + this, + map, + remaining, + key, + this._decodeNestedType.bind(this, flags), + flags + ); + } + + const value = this._decodeNestedType(flags, chunk); + if (typeof value === 'function') { + return this._continueDecodeMapValue.bind( + this, + map, + remaining, + key, + value, + flags + ); + } + + map.set(key, value); + --remaining; + } + + return map; + } + + private _decodeMapKey(flags, chunk) { + const type = chunk[this._cursor]; + return ++this._cursor === chunk.length ? + this._decodeMapKeyValue.bind(this, type, flags) : + this._decodeMapKeyValue(type, flags, chunk); + } + + private _decodeMapKeyValue(type, flags, chunk) { + switch (type) { + // decode simple string map key as string (and not as buffer) + case TYPES.SIMPLE_STRING: + return this._decodeSimpleString(String, chunk); + + // decode blob string map key as string (and not as buffer) + case TYPES.BLOB_STRING: + return this._decodeBlobString(String, chunk); + + default: + return this._decodeNestedTypeValue(type, flags, chunk); + } + } + + private _continueDecodeMapKey(map, remaining, keyCb, flags, chunk) { + const key = keyCb(chunk); + if (typeof key === 'function') { + return this._continueDecodeMapKey.bind( + this, + map, + remaining, + key, + flags + ); + } + + if (this._cursor >= chunk.length) { + return this._continueDecodeMapValue.bind( + this, + map, + remaining, + key, + this._decodeNestedType.bind(this, flags), + flags + ); + } + + const value = this._decodeNestedType(flags, chunk); + if (typeof value === 'function') { + return this._continueDecodeMapValue.bind( + this, + map, + remaining, + key, + value, + flags + ); + } + + map.set(key, value); + return this._decodeMapAsMap(map, remaining - 1, flags, chunk); + } + + private _continueDecodeMapValue(map, remaining, key, valueCb, flags, chunk) { + const value = valueCb(chunk); + if (typeof value === 'function') { + return this._continueDecodeMapValue.bind( + this, + map, + remaining, + key, + value, + flags + ); + } + + map.set(key, value); + + return this._decodeMapAsMap(map, remaining - 1, flags, chunk); + } + + private _decodeMapAsObject(object, remaining, flags, chunk) { + while (remaining > 0) { + if (this._cursor >= chunk.length) { + return this._decodeMapAsObject.bind( + this, + object, + remaining, + flags + ); + } + + const key = this._decodeMapKey(flags, chunk); + if (typeof key === 'function') { + return this._continueDecodeMapAsObjectKey.bind( + this, + object, + remaining, + key, + flags + ); + } + + if (this._cursor >= chunk.length) { + return this._continueDecodeMapAsObjectValue.bind( + this, + object, + remaining, + key, + this._decodeNestedType.bind(this, flags), + flags + ); + } + + const value = this._decodeNestedType(flags, chunk); + if (typeof value === 'function') { + return this._continueDecodeMapAsObjectValue.bind( + this, + object, + remaining, + key, + value, + flags + ); + } + + object[key] = value; + --remaining; + } + + return object; + } + + private _continueDecodeMapAsObjectKey(object, remaining, keyCb, flags, chunk) { + const key = keyCb(chunk); + if (typeof key === 'function') { + return this._continueDecodeMapAsObjectKey.bind( + this, + object, + remaining, + key, + flags + ); + } + + if (this._cursor >= chunk.length) { + return this._continueDecodeMapAsObjectValue.bind( + this, + object, + remaining, + key, + this._decodeNestedType.bind(this, flags), + flags + ); + } + + const value = this._decodeNestedType(flags, chunk); + if (typeof value === 'function') { + return this._continueDecodeMapAsObjectValue.bind( + this, + object, + remaining, + key, + value, + flags + ); + } + + object[key] = value; + + return this._decodeMapAsObject(object, remaining - 1, flags, chunk); + } + + private _continueDecodeMapAsObjectValue(object, remaining, key, valueCb, flags, chunk) { + const value = valueCb(chunk); + if (typeof value === 'function') { + return this._continueDecodeMapAsObjectValue.bind( + this, + object, + remaining, + key, + value, + flags + ); + } + + object[key] = value; + + return this._decodeMapAsObject(object, remaining - 1, flags, chunk); + } +} diff --git a/packages/client/lib/RESP/encoder.spec.ts b/packages/client/lib/RESP/encoder.spec.ts new file mode 100644 index 0000000000..f8f20d783b --- /dev/null +++ b/packages/client/lib/RESP/encoder.spec.ts @@ -0,0 +1,33 @@ +import { strict as assert } from 'assert'; +import { describe } from 'mocha'; +import encodeCommand from './encoder'; + +describe('RESP Encoder', () => { + it('1 byte', () => { + assert.deepEqual( + encodeCommand(['a', 'z']), + ['*2\r\n$1\r\na\r\n$1\r\nz\r\n'] + ); + }); + + it('2 bytes', () => { + assert.deepEqual( + encodeCommand(['א', 'ת']), + ['*2\r\n$2\r\nא\r\n$2\r\nת\r\n'] + ); + }); + + it('4 bytes', () => { + assert.deepEqual( + [...encodeCommand(['🐣', '🐤'])], + ['*2\r\n$4\r\n🐣\r\n$4\r\n🐤\r\n'] + ); + }); + + it('buffer', () => { + assert.deepEqual( + encodeCommand([Buffer.from('string')]), + ['*1\r\n$6\r\n', Buffer.from('string'), '\r\n'] + ); + }); +}); diff --git a/packages/client/lib/RESP/encoder.ts b/packages/client/lib/RESP/encoder.ts new file mode 100644 index 0000000000..b1db1c7023 --- /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 0000000000..88ca88606d --- /dev/null +++ b/packages/client/lib/RESP/types.ts @@ -0,0 +1,417 @@ +import { RedisScriptConfig, SHA1 } from '../lua-script'; +import { TYPES } from './decoder'; +import { VerbatimString } from './verbatim-string'; + +export type RespTypes = typeof TYPES; + +export type RespTypesUnion = RespTypes[keyof RespTypes]; + +type RespType< + RESP_TYPE extends RespTypesUnion, + DEFAULT, + TYPES = never, + FLAG_TYPES = DEFAULT | TYPES +> = (DEFAULT | TYPES) & { + RESP_TYPE: RESP_TYPE; + DEFAULT: DEFAULT; + TYPES: TYPES; + FLAG: Flag; +}; + +export type NullReply = RespType< + RespTypes['NULL'], + null +>; +export type BooleanReply< + T extends boolean = boolean +> = RespType< + RespTypes['BOOLEAN'], + T +>; +export type NumberReply< + T extends number = number +> = RespType< + RespTypes['NUMBER'], + T, + `${T}`, + number | string +>; +export type BigNumberReply< + T extends bigint = bigint +> = RespType< + RespTypes['BIG_NUMBER'], + T, + number | `${T}`, + bigint | number | string +>; +export type DoubleReply< + T extends number = number +> = RespType< + RespTypes['DOUBLE'], + T, + `${T}`, + number | string +>; +export type SimpleStringReply< + T extends string = string +> = RespType< + RespTypes['SIMPLE_STRING'], + T, + Buffer, + string | Buffer +>; +export type BlobStringReply< + T extends string = string +> = RespType< + RespTypes['BLOB_STRING'], + T, + Buffer, + string | Buffer +>; +export type VerbatimStringReply< + T extends string = string +> = RespType< + RespTypes['VERBATIM_STRING'], + T, + Buffer | VerbatimString, + string | Buffer | VerbatimString +>; +export type SimpleErrorReply = RespType< + RespTypes['SIMPLE_ERROR'], + Buffer +>; +export type BlobErrorReply = RespType< + RespTypes['BLOB_ERROR'], + Buffer +>; +export type ArrayReply = RespType< + RespTypes['ARRAY'], + Array, + never, + Array +>; +export type TuplesReply]> = RespType< + RespTypes['ARRAY'], + T, + never, + Array +>; +export type SetReply = RespType< + RespTypes['SET'], + Array, + Set, + Array | Set +>; +export type MapReply = RespType< + RespTypes['MAP'], + { [key: string]: V }, + Map | Array, + Map | Array +>; + +type MapKeyValue = [key: BlobStringReply, value: unknown]; + +type MapTuples = Array; + +export type TuplesToMapReply = RespType< + RespTypes['MAP'], + { + [P in T[number] as P[0] extends BlobStringReply ? S : never]: P[1]; + }, + Map | FlattenTuples +>; + +type FlattenTuples = ( + T extends [] ? [] : + T extends [MapKeyValue] ? T[0] : + T extends [MapKeyValue, ...infer R] ? [ + ...T[0], + ...FlattenTuples + ] : + never +); + +export type ReplyUnion = NullReply | BooleanReply | NumberReply | BigNumberReply | DoubleReply | SimpleStringReply | BlobStringReply | VerbatimStringReply | SimpleErrorReply | BlobErrorReply | + // cannot reuse ArrayReply, SetReply and MapReply because of circular reference + RespType< + RespTypes['ARRAY'], + Array + > | + RespType< + RespTypes['SET'], + Array, + Set + > | + RespType< + RespTypes['MAP'], + { [key: string]: ReplyUnion }, + Map | Array + >; + +export type Reply = ReplyWithFlags; + +export type Flag = ((...args: any) => T) | (new (...args: any) => T); + +type RespTypeUnion = T extends RespType ? FLAG_TYPES : never; + +export type Flags = { + [P in RespTypesUnion]?: Flag>>>; +}; + +type MapKey< + T, + FLAGS extends Flags +> = ReplyWithFlags; + +export type ReplyWithFlags< + REPLY, + FLAGS extends Flags +> = ( + // if REPLY is a type, extract the coresponding type from FLAGS or use the default type + REPLY extends RespType ? + FLAGS[RESP_TYPE] extends Flag ? + ReplyWithFlags, FLAGS> : + ReplyWithFlags + : ( + // if REPLY is a known generic type, convert its generic arguments + // TODO: tuples? + REPLY extends Array ? Array> : + REPLY extends Set ? Set> : + REPLY extends Map ? Map, ReplyWithFlags> : + // `Date` & `Buffer` are supersets of `Record`, so they need to be checked first + REPLY extends Date ? REPLY : + REPLY extends Buffer ? REPLY : + REPLY extends Record ? { + [P in keyof REPLY]: ReplyWithFlags; + } : + // otherwise, just return the REPLY as is + REPLY + ) +); + +export type TransformReply = (this: void, reply: any, preserve?: any) => any; // TODO; + +export type RedisArgument = string | Buffer; + +export type CommandArguments = Array & { preserve?: unknown }; + +export const REQUEST_POLICIES = { + /** + * TODO + */ + ALL_NODES: 'all_nodes', + /** + * TODO + */ + ALL_SHARDS: 'all_shards', + /** + * TODO + */ + SPECIAL: 'special' +} as const; + +export type REQUEST_POLICIES = typeof REQUEST_POLICIES; + +export type RequestPolicies = REQUEST_POLICIES[keyof REQUEST_POLICIES]; + +export const RESPONSE_POLICIES = { + /** + * TODO + */ + ONE_SUCCEEDED: 'one_succeeded', + /** + * TODO + */ + ALL_SUCCEEDED: 'all_succeeded', + /** + * TODO + */ + LOGICAL_AND: 'agg_logical_and', + /** + * TODO + */ + SPECIAL: 'special' +} as const; + +export type RESPONSE_POLICIES = typeof RESPONSE_POLICIES; + +export type ResponsePolicies = RESPONSE_POLICIES[keyof RESPONSE_POLICIES]; + +export type CommandPolicies = { + request?: RequestPolicies | null; + response?: ResponsePolicies | null; +}; + +export type Command = { + FIRST_KEY_INDEX?: number | ((this: void, ...args: Array) => RedisArgument | undefined); + IS_READ_ONLY?: boolean; + POLICIES?: CommandPolicies; + transformArguments(this: void, ...args: Array): CommandArguments; + TRANSFORM_LEGACY_REPLY?: boolean; + transformReply: TransformReply | Record; +}; + +export type RedisCommands = Record; + +export type RedisModules = Record; + +export interface RedisFunction extends Command { + NUMBER_OF_KEYS?: number; +} + +export type RedisFunctions = Record>; + +export type RedisScript = RedisScriptConfig & SHA1; + +export type RedisScripts = Record; + +// TODO: move to Commander? +export interface CommanderConfig< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions +> { + modules?: M; + functions?: F; + scripts?: S; + /** + * TODO + */ + RESP?: RESP; +} + +type Resp2Array = ( + T extends [] ? [] : + T extends [infer ITEM] ? [Resp2Reply] : + T extends [infer ITEM, ...infer REST] ? [ + Resp2Reply, + ...Resp2Array + ] : + T extends Array ? Array> : + never +); + +export type Resp2Reply = ( + RESP3REPLY extends RespType ? + // TODO: RESP3 only scalar types + RESP_TYPE extends RespTypes['DOUBLE'] ? BlobStringReply : + RESP_TYPE extends RespTypes['ARRAY'] | RespTypes['SET'] ? RespType< + RESP_TYPE, + Resp2Array + > : + RESP_TYPE extends RespTypes['MAP'] ? RespType< + RespTypes['ARRAY'], + Resp2Array>> + > : + RespType< + RESP_TYPE, + DEFAULT, + TYPES + > : + RESP3REPLY +); + +export type RespVersions = 2 | 3; + +export type CommandReply< + COMMAND extends Command, + RESP extends RespVersions +> = ( + // if transformReply is a function, use its return type + COMMAND['transformReply'] extends (...args: any) => infer T ? T : + // if transformReply[RESP] is a function, use its return type + COMMAND['transformReply'] extends Record infer T> ? T : + // otherwise use the generic reply type + Reply +); + +export type CommandSignature< + COMMAND extends Command, + RESP extends RespVersions, + FLAGS extends Flags +> = (...args: Parameters) => Promise, FLAGS>>; + +export type CommandWithPoliciesSignature< + COMMAND extends Command, + RESP extends RespVersions, + FLAGS extends Flags, + POLICIES extends CommandPolicies +> = (...args: Parameters) => Promise< + ReplyWithPolicy< + ReplyWithFlags, FLAGS>, + MergePolicies + > +>; + +export type MergePolicies< + COMMAND extends Command, + POLICIES extends CommandPolicies +> = Omit & POLICIES; + +type ReplyWithPolicy< + REPLY, + POLICIES extends CommandPolicies, +> = ( + POLICIES['request'] extends REQUEST_POLICIES['SPECIAL'] ? never : + POLICIES['request'] extends null | undefined ? REPLY : + unknown extends POLICIES['request'] ? REPLY : + POLICIES['response'] extends RESPONSE_POLICIES['SPECIAL'] ? never : + POLICIES['response'] extends RESPONSE_POLICIES['ALL_SUCCEEDED' | 'ONE_SUCCEEDED' | 'LOGICAL_AND'] ? REPLY : + // otherwise, return array of replies + Array +); + +const SAME = { + transformArguments(key: string): Array { + return ['GET', key]; + }, + transformReply: () => 'default' as const +} satisfies Command; + +type SAME_DEFAULT = CommandWithPoliciesSignature< + typeof SAME, + 2, + {}, + { + request: REQUEST_POLICIES['ALL_NODES']; + response: RESPONSE_POLICIES['SPECIAL']; + } +>; + +// type SAME_RESP2 = CommandReply; +// type SAME_COMMAND_RESP2 = CommandSignuture; +// type SAME_RESP3 = CommandReply; +// type SAME_COMMAND_RESP3 = CommandSignuture; + +// interface Test { +// /** +// * This is a test +// */ +// a: 'a'; +// } + +// const DIFFERENT = { +// transformArguments(key: string): Array { +// return ['GET', key]; +// }, +// transformReply: { +// 2: () => null as any as Test, +// 3: () => '3' as const +// } +// } satisfies Command; + +// type DIFFERENT_RESP2 = CommandReply; +// type DIFFERENT_COMMAND_RESP2 = CommandSignuture; +// type DIFFERENT_RESP3 = CommandReply; +// type DIFFERENT_COMMAND_RESP3 = CommandSignuture; + +// const a = null as any as DIFFERENT_COMMAND_RESP2; + +// const b = await a('a'); + +// b.a \ No newline at end of file diff --git a/packages/client/lib/RESP/verbatim-string.ts b/packages/client/lib/RESP/verbatim-string.ts new file mode 100644 index 0000000000..92ff4fe3fb --- /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 f57c369fec..0000000000 --- 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 4affb4283e..0000000000 --- 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 0fc8f03141..0000000000 --- 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 9dd26aae02..0000000000 --- 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 0cd8f00e95..0000000000 --- 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.ts b/packages/client/lib/client/RESP2/decoder.ts deleted file mode 100644 index 525f118bf3..0000000000 --- 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 486259472a..0000000000 --- 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 217fbc714b..0000000000 --- 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 7fffed8658..9d215799e0 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -1,263 +1,319 @@ import * as LinkedList from 'yallist'; -import { AbortError, ErrorReply } from '../errors'; -import { RedisCommandArguments, RedisCommandRawReply } from '../commands'; -import RESP2Decoder from './RESP2/decoder'; -import encodeCommand from './RESP2/encoder'; +import encodeCommand from '../RESP/encoder'; +import { Decoder, PUSH_FLAGS, TYPES } from '../RESP/decoder'; +import { CommandArguments, Flags, ReplyUnion, RespVersions } from '../RESP/types'; import { ChannelListeners, PubSub, PubSubCommand, PubSubListener, PubSubType, PubSubTypeListeners } from './pub-sub'; +import { AbortError, ErrorReply } from '../errors'; +import { EventEmitter } from 'stream'; export interface QueueCommandOptions { - asap?: boolean; - chainId?: symbol; - signal?: AbortSignal; - returnBuffers?: boolean; + asap?: boolean; + chainId?: symbol; + signal?: AbortSignal; + flags?: Flags; } export interface CommandWaitingToBeSent extends CommandWaitingForReply { - args: RedisCommandArguments; - chainId?: symbol; - abort?: { - signal: AbortSignal; - listener(): void; - }; + args: CommandArguments; + chainId?: symbol; + removeAbortListener?(): void; } interface CommandWaitingForReply { - resolve(reply?: unknown): void; - reject(err: unknown): void; - channelsCounter?: number; - returnBuffers?: boolean; + resolve(reply?: unknown): void; + reject(err: unknown): void; + channelsCounter?: number; + flags?: Flags; } -const PONG = Buffer.from('pong'); - export type OnShardedChannelMoved = (channel: string, listeners: ChannelListeners) => void; +const PONG = Buffer.from('pong'); + +const RESP2_PUSH_FLAGS = { + ...PUSH_FLAGS, + [TYPES.SIMPLE_STRING]: Buffer +}; + export default class RedisCommandsQueue { - static #flushQueue(queue: LinkedList, err: Error): void { - while (queue.length) { - queue.shift()!.reject(err); - } + private readonly _maxLength: number | null | undefined; + private readonly _waitingToBeSent = new LinkedList(); + private readonly _waitingForReply = new LinkedList(); + private readonly _onShardedChannelMoved: OnShardedChannelMoved; + + private readonly _pubSub = new PubSub(); + + get isPubSubActive() { + return this._pubSub.isActive; + } + + private _chainInExecution: symbol | undefined; + + decoder: Decoder; + + constructor( + respVersion: RespVersions | null | undefined, + maxLength: number | null | undefined, + onShardedChannelMoved: EventEmitter['emit'] + ) { + this.decoder = this._initiateDecoder(respVersion); + this._maxLength = maxLength; + this._onShardedChannelMoved = onShardedChannelMoved; + } + + private _initiateDecoder(respVersion: RespVersions | null | undefined) { + return respVersion === 3 ? + this._initiateResp3Decoder() : + this._initiateResp2Decoder(); + } + + private _onReply(reply: ReplyUnion) { + this._waitingForReply.shift()!.resolve(reply); + } + + private _onErrorReply(err: ErrorReply) { + this._waitingForReply.shift()!.reject(err); + } + + private _onPush(push: Array) { + // TODO: type + if (this._pubSub.handleMessageReply(push)) return true; + + const isShardedUnsubscribe = PubSub.isShardedUnsubscribe(push); + if (isShardedUnsubscribe && !this._waitingForReply.length) { + const channel = push[1].toString(); + this._onShardedChannelMoved( + channel, + this._pubSub.removeShardedListeners(channel) + ); + return true; + } else if (isShardedUnsubscribe || PubSub.isStatusReply(push)) { + const head = this._waitingForReply.head!.value; + if ( + (Number.isNaN(head.channelsCounter!) && push[2] === 0) || + --head.channelsCounter! === 0 + ) { + this._waitingForReply.shift()!.resolve(); + } + return true; } + } - readonly #maxLength: number | null | undefined; - readonly #waitingToBeSent = new LinkedList(); - readonly #waitingForReply = new LinkedList(); - readonly #onShardedChannelMoved: OnShardedChannelMoved; + private _getFlags() { + return this._waitingForReply.head!.value.flags ?? {}; + } - readonly #pubSub = new PubSub(); + private _initiateResp3Decoder() { + return new Decoder({ + onReply: reply => this._onReply(reply), + onErrorReply: err => this._onErrorReply(err), + onPush: push => { + if (!this._onPush(push)) { - get isPubSubActive() { - return this.#pubSub.isActive; - } - - #chainInExecution: symbol | undefined; - - #decoder = new RESP2Decoder({ - returnStringsAsBuffers: () => { - return !!this.#waitingForReply.head?.value.returnBuffers || - this.#pubSub.isActive; - }, - onReply: reply => { - if (this.#pubSub.isActive && Array.isArray(reply)) { - if (this.#pubSub.handleMessageReply(reply as Array)) return; - - const isShardedUnsubscribe = PubSub.isShardedUnsubscribe(reply as Array); - if (isShardedUnsubscribe && !this.#waitingForReply.length) { - const channel = (reply[1] as Buffer).toString(); - this.#onShardedChannelMoved( - channel, - this.#pubSub.removeShardedListeners(channel) - ); - return; - } else if (isShardedUnsubscribe || PubSub.isStatusReply(reply as Array)) { - const head = this.#waitingForReply.head!.value; - if ( - (Number.isNaN(head.channelsCounter!) && reply[2] === 0) || - --head.channelsCounter! === 0 - ) { - this.#waitingForReply.shift()!.resolve(); - } - return; - } - if (PONG.equals(reply[0] as Buffer)) { - const { resolve, returnBuffers } = this.#waitingForReply.shift()!, - buffer = ((reply[1] as Buffer).length === 0 ? reply[0] : reply[1]) as Buffer; - resolve(returnBuffers ? buffer : buffer.toString()); - return; - } - } - - const { resolve, reject } = this.#waitingForReply.shift()!; - if (reply instanceof ErrorReply) { - reject(reply); - } else { - resolve(reply); - } } + }, + getFlags: () => this._getFlags() }); + } - constructor( - maxLength: number | null | undefined, - onShardedChannelMoved: OnShardedChannelMoved - ) { - this.#maxLength = maxLength; - this.#onShardedChannelMoved = onShardedChannelMoved; - } - - addCommand(args: RedisCommandArguments, options?: QueueCommandOptions): Promise { - if (this.#maxLength && this.#waitingToBeSent.length + this.#waitingForReply.length >= this.#maxLength) { - return Promise.reject(new Error('The queue is full')); - } else if (options?.signal?.aborted) { - return Promise.reject(new AbortError()); - } - - return new Promise((resolve, reject) => { - const node = new LinkedList.Node({ - args, - chainId: options?.chainId, - returnBuffers: options?.returnBuffers, - resolve, - reject - }); - - if (options?.signal) { - const listener = () => { - this.#waitingToBeSent.removeNode(node); - node.value.reject(new AbortError()); - }; - node.value.abort = { - signal: options.signal, - listener - }; - // AbortSignal type is incorrent - (options.signal as any).addEventListener('abort', listener, { - once: true - }); - } - - if (options?.asap) { - this.#waitingToBeSent.unshiftNode(node); - } else { - this.#waitingToBeSent.pushNode(node); - } - }); - } - - subscribe( - type: PubSubType, - channels: string | Array, - listener: PubSubListener, - returnBuffers?: T - ) { - return this.#pushPubSubCommand( - this.#pubSub.subscribe(type, channels, listener, returnBuffers) - ); - } - - unsubscribe( - type: PubSubType, - channels?: string | Array, - listener?: PubSubListener, - returnBuffers?: T - ) { - return this.#pushPubSubCommand( - this.#pubSub.unsubscribe(type, channels, listener, returnBuffers) - ); - } - - resubscribe(): Promise | undefined { - const commands = this.#pubSub.resubscribe(); - if (!commands.length) return; - - return Promise.all( - commands.map(command => this.#pushPubSubCommand(command)) - ); - } - - extendPubSubChannelListeners( - type: PubSubType, - channel: string, - listeners: ChannelListeners - ) { - return this.#pushPubSubCommand( - this.#pubSub.extendChannelListeners(type, channel, listeners) - ); - } - - extendPubSubListeners(type: PubSubType, listeners: PubSubTypeListeners) { - return this.#pushPubSubCommand( - this.#pubSub.extendTypeListeners(type, listeners) - ); - } - - getPubSubListeners(type: PubSubType) { - return this.#pubSub.getTypeListeners(type); - } - - #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); - } - }); - }); - } - - getCommandToSend(): RedisCommandArguments | undefined { - const toSend = this.#waitingToBeSent.shift(); - if (!toSend) return; - - let encoded: RedisCommandArguments; - try { - encoded = encodeCommand(toSend.args); - } catch (err) { - toSend.reject(err); + private _initiateResp2Decoder() { + return new Decoder({ + onReply: reply => { + if (this._pubSub.isActive && Array.isArray(reply)) { + if (this._onPush(reply)) return; + + if (PONG.equals(reply[0] as Buffer)) { + const { resolve, flags } = this._waitingForReply.shift()!, + buffer = ((reply[1] as Buffer).length === 0 ? reply[0] : reply[1]) as Buffer; + resolve(flags?.[TYPES.SIMPLE_STRING] === Buffer ? buffer : buffer.toString()); return; + } } - this.#waitingForReply.push({ - resolve: toSend.resolve, - reject: toSend.reject, - channelsCounter: toSend.channelsCounter, - returnBuffers: toSend.returnBuffers - }); - this.#chainInExecution = toSend.chainId; - return encoded; + this._onReply(reply); + }, + onErrorReply: err => this._onErrorReply(err), + // PUSH type does not exist in RESP2 + // PubSub is handled in onReply + // @ts-expect-error + onPush: undefined, + getFlags: () => { + // PubSub push is an Array in RESP2 + return this._pubSub.isActive ? + RESP2_PUSH_FLAGS : + this._getFlags(); + } + }); + } + + addCommand(args: CommandArguments, options?: QueueCommandOptions): Promise { + if (this._maxLength && this._waitingToBeSent.length + this._waitingForReply.length >= this._maxLength) { + return Promise.reject(new Error('The queue is full')); + } else if (options?.signal?.aborted) { + return Promise.reject(new AbortError()); } - onReplyChunk(chunk: Buffer): void { - this.#decoder.write(chunk); - } + return new Promise((resolve, reject) => { + const node = new LinkedList.Node({ + args, + chainId: options?.chainId, + flags: options?.flags, + resolve, + reject + }); - flushWaitingForReply(err: Error): void { - this.#decoder.reset(); - this.#pubSub.reset(); - RedisCommandsQueue.#flushQueue(this.#waitingForReply, err); + if (options?.signal) { + const listener = () => { + this._waitingToBeSent.removeNode(node); + node.value.reject(new AbortError()); + }; - if (!this.#chainInExecution) return; + node.value.removeAbortListener = () => options.signal?.removeEventListener('abort', listener); - while (this.#waitingToBeSent.head?.value.chainId === this.#chainInExecution) { - this.#waitingToBeSent.shift(); + options.signal.addEventListener('abort', listener, { once: true }); + } + + if (options?.asap) { + this._waitingToBeSent.unshiftNode(node); + } else { + this._waitingToBeSent.pushNode(node); + } + }); + } + + subscribe( + type: PubSubType, + channels: string | Array, + listener: PubSubListener, + returnBuffers?: T + ) { + return this._pushPubSubCommand( + this._pubSub.subscribe(type, channels, listener, returnBuffers) + ); + } + + unsubscribe( + type: PubSubType, + channels?: string | Array, + listener?: PubSubListener, + returnBuffers?: T + ) { + return this._pushPubSubCommand( + this._pubSub.unsubscribe(type, channels, listener, returnBuffers) + ); + } + + resubscribe(): Promise | undefined { + const commands = this._pubSub.resubscribe(); + if (!commands.length) return; + + return Promise.all( + commands.map(command => this._pushPubSubCommand(command)) + ); + } + + extendPubSubChannelListeners( + type: PubSubType, + channel: string, + listeners: ChannelListeners + ) { + return this._pushPubSubCommand( + this._pubSub.extendChannelListeners(type, channel, listeners) + ); + } + + extendPubSubListeners(type: PubSubType, listeners: PubSubTypeListeners) { + return this._pushPubSubCommand( + this._pubSub.extendTypeListeners(type, listeners) + ); + } + + getPubSubListeners(type: PubSubType) { + return this._pubSub.getTypeListeners(type); + } + + private _pushPubSubCommand(command: PubSubCommand) { + if (command === undefined) return; + + return new Promise((resolve, reject) => { + this._waitingToBeSent.push({ + args: command.args, + channelsCounter: command.channelsCounter, + flags: PUSH_FLAGS, + resolve: () => { + command.resolve(); + resolve(); + }, + reject: err => { + command.reject?.(); + reject(err); } + }); + }); + } - this.#chainInExecution = undefined; + getCommandToSend(): CommandArguments | undefined { + const toSend = this._waitingToBeSent.shift(); + if (!toSend) return; + + let encoded: CommandArguments; + try { + encoded = encodeCommand(toSend.args); + } catch (err) { + toSend.reject(err); + return; } - flushAll(err: Error): void { - this.#decoder.reset(); - this.#pubSub.reset(); - RedisCommandsQueue.#flushQueue(this.#waitingForReply, err); - RedisCommandsQueue.#flushQueue(this.#waitingToBeSent, err); + // TODO + // reuse `toSend` + (toSend.args as any) = undefined; + if (toSend.removeAbortListener) { + toSend.removeAbortListener(); + (toSend.removeAbortListener as any) = undefined; } + this._waitingForReply.push(toSend); + this._chainInExecution = toSend.chainId; + return encoded; + } + + #flushWaitingForReply(err: Error): void { + while (this._waitingForReply.head) { + this._waitingForReply.shift()!.reject(err); + } + } + + static #flushWaitingToBeSent(command: CommandWaitingToBeSent, err: Error) { + command.removeAbortListener?.(); + command.reject(err); + } + + flushWaitingForReply(err: Error): void { + this.decoder.reset(); + this._pubSub.reset(); + + this.#flushWaitingForReply(err); + + if (!this._chainInExecution) return; + + while (this._waitingToBeSent.head?.value.chainId === this._chainInExecution) { + RedisCommandsQueue.#flushWaitingToBeSent( + this._waitingToBeSent.shift()!, + err + ); + } + + this._chainInExecution = undefined; + } + + flushAll(err: Error): void { + this.decoder.reset(); + this._pubSub.reset(); + this.#flushWaitingForReply(err); + while (this._waitingToBeSent.head) { + RedisCommandsQueue.#flushWaitingToBeSent( + this._waitingToBeSent.shift()!, + err + ); + } + } } diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts deleted file mode 100644 index 2605962432..0000000000 --- a/packages/client/lib/client/commands.ts +++ /dev/null @@ -1,359 +0,0 @@ -import CLUSTER_COMMANDS from '../cluster/commands'; -import * as ACL_CAT from '../commands/ACL_CAT'; -import * as ACL_DELUSER from '../commands/ACL_DELUSER'; -import * as ACL_DRYRUN from '../commands/ACL_DRYRUN'; -import * as ACL_GENPASS from '../commands/ACL_GENPASS'; -import * as ACL_GETUSER from '../commands/ACL_GETUSER'; -import * as ACL_LIST from '../commands/ACL_LIST'; -import * as ACL_LOAD from '../commands/ACL_LOAD'; -import * as ACL_LOG_RESET from '../commands/ACL_LOG_RESET'; -import * as ACL_LOG from '../commands/ACL_LOG'; -import * as ACL_SAVE from '../commands/ACL_SAVE'; -import * as ACL_SETUSER from '../commands/ACL_SETUSER'; -import * as ACL_USERS from '../commands/ACL_USERS'; -import * as ACL_WHOAMI from '../commands/ACL_WHOAMI'; -import * as ASKING from '../commands/ASKING'; -import * as AUTH from '../commands/AUTH'; -import * as BGREWRITEAOF from '../commands/BGREWRITEAOF'; -import * as BGSAVE from '../commands/BGSAVE'; -import * as CLIENT_CACHING from '../commands/CLIENT_CACHING'; -import * as CLIENT_GETNAME from '../commands/CLIENT_GETNAME'; -import * as CLIENT_GETREDIR from '../commands/CLIENT_GETREDIR'; -import * as CLIENT_ID from '../commands/CLIENT_ID'; -import * as CLIENT_KILL from '../commands/CLIENT_KILL'; -import * as CLIENT_LIST from '../commands/CLIENT_LIST'; -import * as CLIENT_NO_EVICT from '../commands/CLIENT_NO-EVICT'; -import * as CLIENT_PAUSE from '../commands/CLIENT_PAUSE'; -import * as CLIENT_SETNAME from '../commands/CLIENT_SETNAME'; -import * as CLIENT_TRACKING from '../commands/CLIENT_TRACKING'; -import * as CLIENT_TRACKINGINFO from '../commands/CLIENT_TRACKINGINFO'; -import * as CLIENT_UNPAUSE from '../commands/CLIENT_UNPAUSE'; -import * as CLIENT_INFO from '../commands/CLIENT_INFO'; -import * as CLUSTER_ADDSLOTS from '../commands/CLUSTER_ADDSLOTS'; -import * as CLUSTER_ADDSLOTSRANGE from '../commands/CLUSTER_ADDSLOTSRANGE'; -import * as CLUSTER_BUMPEPOCH from '../commands/CLUSTER_BUMPEPOCH'; -import * as CLUSTER_COUNT_FAILURE_REPORTS from '../commands/CLUSTER_COUNT-FAILURE-REPORTS'; -import * as CLUSTER_COUNTKEYSINSLOT from '../commands/CLUSTER_COUNTKEYSINSLOT'; -import * as CLUSTER_DELSLOTS from '../commands/CLUSTER_DELSLOTS'; -import * as CLUSTER_DELSLOTSRANGE from '../commands/CLUSTER_DELSLOTSRANGE'; -import * as CLUSTER_FAILOVER from '../commands/CLUSTER_FAILOVER'; -import * as CLUSTER_FLUSHSLOTS from '../commands/CLUSTER_FLUSHSLOTS'; -import * as CLUSTER_FORGET from '../commands/CLUSTER_FORGET'; -import * as CLUSTER_GETKEYSINSLOT from '../commands/CLUSTER_GETKEYSINSLOT'; -import * as CLUSTER_INFO from '../commands/CLUSTER_INFO'; -import * as CLUSTER_KEYSLOT from '../commands/CLUSTER_KEYSLOT'; -import * as CLUSTER_LINKS from '../commands/CLUSTER_LINKS'; -import * as CLUSTER_MEET from '../commands/CLUSTER_MEET'; -import * as CLUSTER_MYID from '../commands/CLUSTER_MYID'; -import * as CLUSTER_NODES from '../commands/CLUSTER_NODES'; -import * as CLUSTER_REPLICAS from '../commands/CLUSTER_REPLICAS'; -import * as CLUSTER_REPLICATE from '../commands/CLUSTER_REPLICATE'; -import * as CLUSTER_RESET from '../commands/CLUSTER_RESET'; -import * as CLUSTER_SAVECONFIG from '../commands/CLUSTER_SAVECONFIG'; -import * as CLUSTER_SET_CONFIG_EPOCH from '../commands/CLUSTER_SET-CONFIG-EPOCH'; -import * as CLUSTER_SETSLOT from '../commands/CLUSTER_SETSLOT'; -import * as CLUSTER_SLOTS from '../commands/CLUSTER_SLOTS'; -import * as COMMAND_COUNT from '../commands/COMMAND_COUNT'; -import * as COMMAND_GETKEYS from '../commands/COMMAND_GETKEYS'; -import * as COMMAND_GETKEYSANDFLAGS from '../commands/COMMAND_GETKEYSANDFLAGS'; -import * as COMMAND_INFO from '../commands/COMMAND_INFO'; -import * as COMMAND_LIST from '../commands/COMMAND_LIST'; -import * as COMMAND from '../commands/COMMAND'; -import * as CONFIG_GET from '../commands/CONFIG_GET'; -import * as CONFIG_RESETASTAT from '../commands/CONFIG_RESETSTAT'; -import * as CONFIG_REWRITE from '../commands/CONFIG_REWRITE'; -import * as CONFIG_SET from '../commands/CONFIG_SET'; -import * as DBSIZE from '../commands/DBSIZE'; -import * as DISCARD from '../commands/DISCARD'; -import * as ECHO from '../commands/ECHO'; -import * as FAILOVER from '../commands/FAILOVER'; -import * as FLUSHALL from '../commands/FLUSHALL'; -import * as FLUSHDB from '../commands/FLUSHDB'; -import * as FUNCTION_DELETE from '../commands/FUNCTION_DELETE'; -import * as FUNCTION_DUMP from '../commands/FUNCTION_DUMP'; -import * as FUNCTION_FLUSH from '../commands/FUNCTION_FLUSH'; -import * as FUNCTION_KILL from '../commands/FUNCTION_KILL'; -import * as FUNCTION_LIST_WITHCODE from '../commands/FUNCTION_LIST_WITHCODE'; -import * as FUNCTION_LIST from '../commands/FUNCTION_LIST'; -import * as FUNCTION_LOAD from '../commands/FUNCTION_LOAD'; -import * as FUNCTION_RESTORE from '../commands/FUNCTION_RESTORE'; -import * as FUNCTION_STATS from '../commands/FUNCTION_STATS'; -import * as HELLO from '../commands/HELLO'; -import * as INFO from '../commands/INFO'; -import * as KEYS from '../commands/KEYS'; -import * as LASTSAVE from '../commands/LASTSAVE'; -import * as LATENCY_DOCTOR from '../commands/LATENCY_DOCTOR'; -import * as LATENCY_GRAPH from '../commands/LATENCY_GRAPH'; -import * as LOLWUT from '../commands/LOLWUT'; -import * as MEMORY_DOCTOR from '../commands/MEMORY_DOCTOR'; -import * as MEMORY_MALLOC_STATS from '../commands/MEMORY_MALLOC-STATS'; -import * as MEMORY_PURGE from '../commands/MEMORY_PURGE'; -import * as MEMORY_STATS from '../commands/MEMORY_STATS'; -import * as MEMORY_USAGE from '../commands/MEMORY_USAGE'; -import * as MODULE_LIST from '../commands/MODULE_LIST'; -import * as MODULE_LOAD from '../commands/MODULE_LOAD'; -import * as MODULE_UNLOAD from '../commands/MODULE_UNLOAD'; -import * as MOVE from '../commands/MOVE'; -import * as PING from '../commands/PING'; -import * as PUBSUB_CHANNELS from '../commands/PUBSUB_CHANNELS'; -import * as PUBSUB_NUMPAT from '../commands/PUBSUB_NUMPAT'; -import * as PUBSUB_NUMSUB from '../commands/PUBSUB_NUMSUB'; -import * as PUBSUB_SHARDCHANNELS from '../commands/PUBSUB_SHARDCHANNELS'; -import * as RANDOMKEY from '../commands/RANDOMKEY'; -import * as READONLY from '../commands/READONLY'; -import * as READWRITE from '../commands/READWRITE'; -import * as REPLICAOF from '../commands/REPLICAOF'; -import * as RESTORE_ASKING from '../commands/RESTORE-ASKING'; -import * as ROLE from '../commands/ROLE'; -import * as SAVE from '../commands/SAVE'; -import * as SCAN from '../commands/SCAN'; -import * as SCRIPT_DEBUG from '../commands/SCRIPT_DEBUG'; -import * as SCRIPT_EXISTS from '../commands/SCRIPT_EXISTS'; -import * as SCRIPT_FLUSH from '../commands/SCRIPT_FLUSH'; -import * as SCRIPT_KILL from '../commands/SCRIPT_KILL'; -import * as SCRIPT_LOAD from '../commands/SCRIPT_LOAD'; -import * as SHUTDOWN from '../commands/SHUTDOWN'; -import * as SWAPDB from '../commands/SWAPDB'; -import * as TIME from '../commands/TIME'; -import * as UNWATCH from '../commands/UNWATCH'; -import * as WAIT from '../commands/WAIT'; - -export default { - ...CLUSTER_COMMANDS, - ACL_CAT, - aclCat: ACL_CAT, - ACL_DELUSER, - aclDelUser: ACL_DELUSER, - ACL_DRYRUN, - aclDryRun: ACL_DRYRUN, - ACL_GENPASS, - aclGenPass: ACL_GENPASS, - ACL_GETUSER, - aclGetUser: ACL_GETUSER, - ACL_LIST, - aclList: ACL_LIST, - ACL_LOAD, - aclLoad: ACL_LOAD, - ACL_LOG_RESET, - aclLogReset: ACL_LOG_RESET, - ACL_LOG, - aclLog: ACL_LOG, - ACL_SAVE, - aclSave: ACL_SAVE, - ACL_SETUSER, - aclSetUser: ACL_SETUSER, - ACL_USERS, - aclUsers: ACL_USERS, - ACL_WHOAMI, - aclWhoAmI: ACL_WHOAMI, - ASKING, - asking: ASKING, - AUTH, - auth: AUTH, - BGREWRITEAOF, - bgRewriteAof: BGREWRITEAOF, - BGSAVE, - bgSave: BGSAVE, - CLIENT_CACHING, - clientCaching: CLIENT_CACHING, - CLIENT_GETNAME, - clientGetName: CLIENT_GETNAME, - CLIENT_GETREDIR, - clientGetRedir: CLIENT_GETREDIR, - CLIENT_ID, - clientId: CLIENT_ID, - CLIENT_KILL, - clientKill: CLIENT_KILL, - 'CLIENT_NO-EVICT': CLIENT_NO_EVICT, - clientNoEvict: CLIENT_NO_EVICT, - CLIENT_LIST, - clientList: CLIENT_LIST, - CLIENT_PAUSE, - clientPause: CLIENT_PAUSE, - CLIENT_SETNAME, - clientSetName: CLIENT_SETNAME, - CLIENT_TRACKING, - clientTracking: CLIENT_TRACKING, - CLIENT_TRACKINGINFO, - clientTrackingInfo: CLIENT_TRACKINGINFO, - CLIENT_UNPAUSE, - clientUnpause: CLIENT_UNPAUSE, - CLIENT_INFO, - clientInfo: CLIENT_INFO, - CLUSTER_ADDSLOTS, - clusterAddSlots: CLUSTER_ADDSLOTS, - CLUSTER_ADDSLOTSRANGE, - clusterAddSlotsRange: CLUSTER_ADDSLOTSRANGE, - CLUSTER_BUMPEPOCH, - clusterBumpEpoch: CLUSTER_BUMPEPOCH, - CLUSTER_COUNT_FAILURE_REPORTS, - clusterCountFailureReports: CLUSTER_COUNT_FAILURE_REPORTS, - CLUSTER_COUNTKEYSINSLOT, - clusterCountKeysInSlot: CLUSTER_COUNTKEYSINSLOT, - CLUSTER_DELSLOTS, - clusterDelSlots: CLUSTER_DELSLOTS, - CLUSTER_DELSLOTSRANGE, - clusterDelSlotsRange: CLUSTER_DELSLOTSRANGE, - CLUSTER_FAILOVER, - clusterFailover: CLUSTER_FAILOVER, - CLUSTER_FLUSHSLOTS, - clusterFlushSlots: CLUSTER_FLUSHSLOTS, - CLUSTER_FORGET, - clusterForget: CLUSTER_FORGET, - CLUSTER_GETKEYSINSLOT, - clusterGetKeysInSlot: CLUSTER_GETKEYSINSLOT, - CLUSTER_INFO, - clusterInfo: CLUSTER_INFO, - CLUSTER_KEYSLOT, - clusterKeySlot: CLUSTER_KEYSLOT, - CLUSTER_LINKS, - clusterLinks: CLUSTER_LINKS, - CLUSTER_MEET, - clusterMeet: CLUSTER_MEET, - CLUSTER_MYID, - clusterMyId: CLUSTER_MYID, - CLUSTER_NODES, - clusterNodes: CLUSTER_NODES, - CLUSTER_REPLICAS, - clusterReplicas: CLUSTER_REPLICAS, - CLUSTER_REPLICATE, - clusterReplicate: CLUSTER_REPLICATE, - CLUSTER_RESET, - clusterReset: CLUSTER_RESET, - CLUSTER_SAVECONFIG, - clusterSaveConfig: CLUSTER_SAVECONFIG, - CLUSTER_SET_CONFIG_EPOCH, - clusterSetConfigEpoch: CLUSTER_SET_CONFIG_EPOCH, - CLUSTER_SETSLOT, - clusterSetSlot: CLUSTER_SETSLOT, - CLUSTER_SLOTS, - clusterSlots: CLUSTER_SLOTS, - COMMAND_COUNT, - commandCount: COMMAND_COUNT, - COMMAND_GETKEYS, - commandGetKeys: COMMAND_GETKEYS, - COMMAND_GETKEYSANDFLAGS, - commandGetKeysAndFlags: COMMAND_GETKEYSANDFLAGS, - COMMAND_INFO, - commandInfo: COMMAND_INFO, - COMMAND_LIST, - commandList: COMMAND_LIST, - COMMAND, - command: COMMAND, - CONFIG_GET, - configGet: CONFIG_GET, - CONFIG_RESETASTAT, - configResetStat: CONFIG_RESETASTAT, - CONFIG_REWRITE, - configRewrite: CONFIG_REWRITE, - CONFIG_SET, - configSet: CONFIG_SET, - DBSIZE, - dbSize: DBSIZE, - DISCARD, - discard: DISCARD, - ECHO, - echo: ECHO, - FAILOVER, - failover: FAILOVER, - FLUSHALL, - flushAll: FLUSHALL, - FLUSHDB, - flushDb: FLUSHDB, - FUNCTION_DELETE, - functionDelete: FUNCTION_DELETE, - FUNCTION_DUMP, - functionDump: FUNCTION_DUMP, - FUNCTION_FLUSH, - functionFlush: FUNCTION_FLUSH, - FUNCTION_KILL, - functionKill: FUNCTION_KILL, - FUNCTION_LIST_WITHCODE, - functionListWithCode: FUNCTION_LIST_WITHCODE, - FUNCTION_LIST, - functionList: FUNCTION_LIST, - FUNCTION_LOAD, - functionLoad: FUNCTION_LOAD, - FUNCTION_RESTORE, - functionRestore: FUNCTION_RESTORE, - FUNCTION_STATS, - functionStats: FUNCTION_STATS, - HELLO, - hello: HELLO, - INFO, - info: INFO, - KEYS, - keys: KEYS, - LASTSAVE, - lastSave: LASTSAVE, - LATENCY_DOCTOR, - latencyDoctor: LATENCY_DOCTOR, - LATENCY_GRAPH, - latencyGraph: LATENCY_GRAPH, - LOLWUT, - lolwut: LOLWUT, - MEMORY_DOCTOR, - memoryDoctor: MEMORY_DOCTOR, - 'MEMORY_MALLOC-STATS': MEMORY_MALLOC_STATS, - memoryMallocStats: MEMORY_MALLOC_STATS, - MEMORY_PURGE, - memoryPurge: MEMORY_PURGE, - MEMORY_STATS, - memoryStats: MEMORY_STATS, - MEMORY_USAGE, - memoryUsage: MEMORY_USAGE, - MODULE_LIST, - moduleList: MODULE_LIST, - MODULE_LOAD, - moduleLoad: MODULE_LOAD, - MODULE_UNLOAD, - moduleUnload: MODULE_UNLOAD, - MOVE, - move: MOVE, - PING, - ping: PING, - PUBSUB_CHANNELS, - pubSubChannels: PUBSUB_CHANNELS, - PUBSUB_NUMPAT, - pubSubNumPat: PUBSUB_NUMPAT, - PUBSUB_NUMSUB, - pubSubNumSub: PUBSUB_NUMSUB, - PUBSUB_SHARDCHANNELS, - pubSubShardChannels: PUBSUB_SHARDCHANNELS, - RANDOMKEY, - randomKey: RANDOMKEY, - READONLY, - readonly: READONLY, - READWRITE, - readwrite: READWRITE, - REPLICAOF, - replicaOf: REPLICAOF, - 'RESTORE-ASKING': RESTORE_ASKING, - restoreAsking: RESTORE_ASKING, - ROLE, - role: ROLE, - SAVE, - save: SAVE, - SCAN, - scan: SCAN, - SCRIPT_DEBUG, - scriptDebug: SCRIPT_DEBUG, - SCRIPT_EXISTS, - scriptExists: SCRIPT_EXISTS, - SCRIPT_FLUSH, - scriptFlush: SCRIPT_FLUSH, - SCRIPT_KILL, - scriptKill: SCRIPT_KILL, - SCRIPT_LOAD, - scriptLoad: SCRIPT_LOAD, - SHUTDOWN, - shutdown: SHUTDOWN, - SWAPDB, - swapDb: SWAPDB, - TIME, - time: TIME, - UNWATCH, - unwatch: UNWATCH, - WAIT, - wait: WAIT -}; diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 5dd386647e..7d5e18a4c1 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -1,823 +1,919 @@ -import COMMANDS from './commands'; -import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, RedisCommandSignature, ConvertArgumentType, RedisFunction, ExcludeMappedString, RedisCommands } from '../commands'; +import COMMANDS from '../commands'; import RedisSocket, { RedisSocketOptions, RedisTlsSocketOptions } from './socket'; import RedisCommandsQueue, { QueueCommandOptions } from './commands-queue'; -import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; -import { RedisMultiQueuedCommand } from '../multi-command'; import { EventEmitter } from 'events'; -import { CommandOptions, commandOptions, isCommandOptions } from '../command-options'; -import { ScanOptions, ZMember } from '../commands/generic-transformers'; -import { ScanCommandOptions } from '../commands/SCAN'; -import { HScanTuple } from '../commands/HSCAN'; -import { attachCommands, attachExtensions, fCallArguments, transformCommandArguments, transformCommandReply, transformLegacyCommandArguments } from '../commander'; -import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; +import { attachConfig, functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander'; import { ClientClosedError, ClientOfflineError, DisconnectsClientError } from '../errors'; import { URL } from 'url'; import { TcpSocketConnectOpts } from 'net'; import { PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub'; -import { callbackify } from 'util'; +import { Command, CommandArguments, CommandSignature, Flags, CommanderConfig, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts, ReplyUnion, RespVersions, RedisArgument } from '../RESP/types'; +import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; +import { RedisMultiQueuedCommand } from '../multi-command'; +import HELLO, { HelloOptions } from '../commands/HELLO'; +import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; +import { ReplyWithFlags, BlobStringReply } from '../RESP/types'; +import { ScanCommandOptions } from '../commands/SCAN'; +import { HScanEntry } from '../commands/HSCAN'; +import { ScanOptions, ZMember } 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; + M extends RedisModules = RedisModules, + F extends RedisFunctions = RedisFunctions, + S extends RedisScripts = RedisScripts, + RESP extends RespVersions = RespVersions +> extends CommanderConfig { + /** + * `redis[s]://[[username][:password]@][host][:port][/db-number]` + * See [`redis`](https://www.iana.org/assignments/uri-schemes/prov/redis) and [`rediss`](https://www.iana.org/assignments/uri-schemes/prov/rediss) IANA registration for more details + */ + url?: string; + /** + * Socket connection properties + */ + socket?: RedisSocketOptions; + /** + * ACL username ([see ACL guide](https://redis.io/topics/acl)) + */ + username?: string; + /** + * ACL password or the old "--requirepass" password + */ + password?: string; + /** + * Client name ([see `CLIENT SETNAME`](https://redis.io/commands/client-setname)) + */ + name?: string; + /** + * Redis database number (see [`SELECT`](https://redis.io/commands/select) command) + */ + database?: number; + /** + * Maximum length of the client's internal command queue + */ + commandsQueueMaxLength?: number; + /** + * When `true`, commands are rejected when the client is reconnecting. + * When `false`, commands are queued for execution after reconnection. + */ + disableOfflineQueue?: boolean; + /** + * Connect in [`READONLY`](https://redis.io/commands/readonly) mode + */ + readonly?: boolean; + /** + * TODO + */ + legacyMode?: boolean; + /** + * TODO + */ + isolationPoolOptions?: PoolOptions; + /** + * Send `PING` command at interval (in ms). + * Useful with Redis deployments that do not use TCP Keep-Alive. + */ + pingInterval?: number; } -type WithCommands = { - [P in keyof typeof COMMANDS]: RedisCommandSignature<(typeof COMMANDS)[P]>; +type WithCommands< + RESP extends RespVersions, + FLAGS extends Flags +> = { + [P in keyof typeof COMMANDS]: CommandSignature<(typeof COMMANDS)[P], RESP, FLAGS>; }; -export type WithModules = { - [P in keyof M as ExcludeMappedString

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

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

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

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

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

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

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

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

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

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

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

]: RedisClusterMultiCommandSignature; +// }; -export type RedisClusterMultiCommandType< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = RedisClusterMultiCommand & WithCommands & WithModules & WithFunctions & WithScripts; +// export type 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 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 type RedisClusterMultiExecutor = (queue: Array, firstKey?: RedisCommandArgument, chainId?: symbol) => Promise>; -export default class RedisClusterMultiCommand { - readonly #multi = new RedisMultiCommand(); - readonly #executor: RedisClusterMultiExecutor; - #firstKey: RedisCommandArgument | undefined; +// 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 - }); - } +// 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; - } +// 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); - } +// 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; - } +// 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; - } +// 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; - } +// 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(); - } +// async exec(execAsPipeline = false): Promise> { +// if (execAsPipeline) { +// return this.execAsPipeline(); +// } - return this.#multi.handleExecReplies( - await this.#executor(this.#multi.queue, this.#firstKey, RedisMultiCommand.generateChainId()) - ); - } +// return this.#multi.handleExecReplies( +// await this.#executor(this.#multi.queue, this.#firstKey, RedisMultiCommand.generateChainId()) +// ); +// } - EXEC = this.exec; +// EXEC = this.exec; - async execAsPipeline(): Promise> { - return this.#multi.transformReplies( - await this.#executor(this.#multi.queue, this.#firstKey) - ); - } -} +// async execAsPipeline(): Promise> { +// return this.#multi.transformReplies( +// await this.#executor(this.#multi.queue, this.#firstKey) +// ); +// } +// } -attachCommands({ - BaseClass: RedisClusterMultiCommand, - commands: COMMANDS, - executor: RedisClusterMultiCommand.prototype.commandsExecutor -}); +// attachCommands({ +// BaseClass: RedisClusterMultiCommand, +// commands: COMMANDS, +// executor: RedisClusterMultiCommand.prototype.commandsExecutor +// }); diff --git a/packages/client/lib/command-options.ts b/packages/client/lib/command-options.ts deleted file mode 100644 index 8f91130b55..0000000000 --- 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 1407a80344..b1db7e701f 100644 --- a/packages/client/lib/commander.ts +++ b/packages/client/lib/commander.ts @@ -1,163 +1,115 @@ +import { Command, CommanderConfig, RedisCommands, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts, RespVersions } from './RESP/types'; -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; +interface AttachConfigOptions< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions +> { + BaseClass: new (...args: any) => any; + commands: RedisCommands; + createCommand(command: Command, resp: RespVersions): (...args: any) => any; + createModuleCommand(command: Command, resp: RespVersions): (...args: any) => any; + createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions): (...args: any) => any; + createScriptCommand(script: RedisScript, resp: RespVersions): (...args: any) => any; + config?: CommanderConfig; } -export function attachCommands({ - BaseClass, - commands, - executor -}: AttachCommandsConfig): void { - for (const [name, command] of Object.entries(commands)) { - BaseClass.prototype[name] = function (...args: Array): unknown { - return executor.call(this, command, args, name); - }; +export function attachConfig< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions +>({ + BaseClass, + commands, + createCommand, + createModuleCommand, + createFunctionCommand, + createScriptCommand, + config +}: AttachConfigOptions) { + const RESP = config?.RESP ?? 2, + Class: any = class extends BaseClass {}; + + for (const [name, command] of Object.entries(commands)) { + Class.prototype[name] = createCommand(command, RESP); + } + + if (config?.modules) { + for (const [moduleName, module] of Object.entries(config.modules)) { + const fns = Object.create(null); + for (const [name, command] of Object.entries(module)) { + fns[name] = createModuleCommand(command, RESP); + } + + attachNamespace(Class.prototype, moduleName, fns); } -} + } -interface AttachExtensionsConfig { - BaseClass: T; - modulesExecutor: CommandsExecutor; - modules?: RedisModules; - functionsExecutor: CommandsExecutor; - functions?: RedisFunctions; - scriptsExecutor: CommandsExecutor; - scripts?: RedisScripts; -} + 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); + } -export function attachExtensions(config: AttachExtensionsConfig): any { - let Commander; - - if (config.modules) { - Commander = attachWithNamespaces({ - BaseClass: config.BaseClass, - namespaces: config.modules, - executor: config.modulesExecutor - }); + attachNamespace(Class.prototype, library, fns); } + } - if (config.functions) { - Commander = attachWithNamespaces({ - BaseClass: Commander ?? config.BaseClass, - namespaces: config.functions, - executor: config.functionsExecutor - }); + if (config?.scripts) { + for (const [name, script] of Object.entries(config.scripts)) { + Class.prototype[name] = createScriptCommand(script, RESP); } + } - if (config.scripts) { - Commander ??= class extends config.BaseClass {}; - attachCommands({ - BaseClass: Commander, - commands: config.scripts, - executor: config.scriptsExecutor - }); + return Class; +} + +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 ?? config.BaseClass; + }); } -interface AttachWithNamespacesConfig { - BaseClass: Instantiable; - namespaces: Record>; - executor: CommandsExecutor; +export function getTransformReply(command: Command, resp: RespVersions) { + switch (typeof command.transformReply) { + case 'function': + return command.transformReply; + + case 'object': + return command.transformReply[resp]; + } } -function attachWithNamespaces({ - BaseClass, - namespaces, - executor -}: AttachWithNamespacesConfig): any { - const Commander = class extends BaseClass { - constructor(...args: Array) { - super(...args); +export function functionArgumentsPrefix(name: string, fn: RedisFunction) { + const prefix: Array = [ + fn.IS_READ_ONLY ? 'FCALL_RO' : 'FCALL', + name + ]; - for (const namespace of Object.keys(namespaces)) { - this[namespace] = Object.create(this[namespace], { - self: { - value: this - } - }); - } - } - }; + if (fn.NUMBER_OF_KEYS !== undefined) { + prefix.push(fn.NUMBER_OF_KEYS.toString()); + } - 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); - }; - } - } - - return Commander; + return prefix; } -export function transformCommandArguments( - command: RedisCommand, - args: Array -): { - args: RedisCommandArguments; - options: CommandOptions | undefined; -} { - let options; - if (isCommandOptions(args[0])) { - options = args[0]; - args = args.slice(1); - } +export function scriptArgumentsPrefix(script: RedisScript) { + const prefix: Array = [ + script.IS_READ_ONLY ? 'EVALSHA_RO' : 'EVALSHA', + script.SHA1 + ]; - return { - args: command.transformArguments(...args), - options - }; -} - -export function transformLegacyCommandArguments(args: Array): Array { - return args.flat().map(arg => { - return typeof arg === 'number' || arg instanceof Date ? - arg.toString() : - arg; - }); -} - -export function transformCommandReply( - command: C, - rawReply: unknown, - preserved: unknown -): RedisCommandReply { - if (!command.transformReply) { - return rawReply as RedisCommandReply; - } - - return command.transformReply(rawReply, preserved); -} - -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()); - } - - actualArgs.push(...args); - - return actualArgs; + if (script.NUMBER_OF_KEYS !== undefined) { + prefix.push(script.NUMBER_OF_KEYS.toString()); + } + + return prefix; } diff --git a/packages/client/lib/commands/ACL_CAT.spec.ts b/packages/client/lib/commands/ACL_CAT.spec.ts index 521871a1c6..0fc8b08b2f 100644 --- a/packages/client/lib/commands/ACL_CAT.spec.ts +++ b/packages/client/lib/commands/ACL_CAT.spec.ts @@ -1,23 +1,31 @@ import { strict as assert } from 'assert'; -import testUtils from '../test-utils'; -import { transformArguments } from './ACL_CAT'; +import testUtils, { GLOBAL } from '../test-utils'; +import ACL_CAT from './ACL_CAT'; describe('ACL CAT', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['ACL', 'CAT'] - ); - }); - - it('with categoryName', () => { - assert.deepEqual( - transformArguments('dangerous'), - ['ACL', 'CAT', 'dangerous'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + ACL_CAT.transformArguments(), + ['ACL', 'CAT'] + ); }); + + 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 161546cfbe..dd4762239a 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 5c5ea2fa2a..bccd2ccdc5 100644 --- a/packages/client/lib/commands/ACL_DELUSER.spec.ts +++ b/packages/client/lib/commands/ACL_DELUSER.spec.ts @@ -1,30 +1,30 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ACL_DELUSER'; +import ACL_DELUSER from './ACL_DELUSER'; describe('ACL DELUSER', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('username'), - ['ACL', 'DELUSER', 'username'] - ); - }); - - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['ACL', 'DELUSER', '1', '2'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + ACL_DELUSER.transformArguments('username'), + ['ACL', 'DELUSER', 'username'] + ); }); - testUtils.testWithClient('client.aclDelUser', async client => { - assert.equal( - await client.aclDelUser('dosenotexists'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + it('array', () => { + assert.deepEqual( + ACL_DELUSER.transformArguments(['1', '2']), + ['ACL', 'DELUSER', '1', '2'] + ); + }); + }); + + 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 25ed1a1030..c0f8e15d67 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 3154689c29..051e6a561d 100644 --- a/packages/client/lib/commands/ACL_DRYRUN.spec.ts +++ b/packages/client/lib/commands/ACL_DRYRUN.spec.ts @@ -1,21 +1,21 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ACL_DRYRUN'; +import ACL_DRYRUN from './ACL_DRYRUN'; describe('ACL DRYRUN', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('default', ['GET', 'key']), - ['ACL', 'DRYRUN', 'default', 'GET', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ACL_DRYRUN.transformArguments('default', ['GET', 'key']), + ['ACL', 'DRYRUN', 'default', 'GET', 'key'] + ); + }); - testUtils.testWithClient('client.aclDryRun', async client => { - assert.equal( - await client.aclDryRun('default', ['GET', 'key']), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.aclDryRun', async client => { + assert.equal( + await client.aclDryRun('default', ['GET', 'key']), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ACL_DRYRUN.ts b/packages/client/lib/commands/ACL_DRYRUN.ts index 95eed95066..257f0fe61e 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 3b2a022f97..7b489f1e9e 100644 --- a/packages/client/lib/commands/ACL_GENPASS.spec.ts +++ b/packages/client/lib/commands/ACL_GENPASS.spec.ts @@ -1,23 +1,30 @@ import { strict as assert } from 'assert'; -import testUtils from '../test-utils'; -import { transformArguments } from './ACL_GENPASS'; +import testUtils, { GLOBAL } from '../test-utils'; +import ACL_GENPASS from './ACL_GENPASS'; describe('ACL GENPASS', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['ACL', 'GENPASS'] - ); - }); - - it('with bits', () => { - assert.deepEqual( - transformArguments(128), - ['ACL', 'GENPASS', '128'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + ACL_GENPASS.transformArguments(), + ['ACL', 'GENPASS'] + ); }); + + 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 91a71e220e..be89ff90a9 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 f91f2ff9e5..6a0948d6f0 100644 --- a/packages/client/lib/commands/ACL_GETUSER.spec.ts +++ b/packages/client/lib/commands/ACL_GETUSER.spec.ts @@ -1,44 +1,34 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ACL_GETUSER'; +import ACL_GETUSER from './ACL_GETUSER'; describe('ACL GETUSER', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('username'), - ['ACL', 'GETUSER', 'username'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ACL_GETUSER.transformArguments('username'), + ['ACL', 'GETUSER', 'username'] + ); + }); - testUtils.testWithClient('client.aclGetUser', async client => { - const expectedReply: any = { - passwords: [], - commands: '+@all', - }; + testUtils.testWithClient('client.aclGetUser', async client => { + const reply = await client.aclGetUser('default'); - if (testUtils.isVersionGreaterThan([7])) { - expectedReply.flags = ['on', 'nopass']; - expectedReply.keys = '~*'; - expectedReply.channels = '&*'; - expectedReply.selectors = []; - } else { - expectedReply.keys = ['*']; - expectedReply.selectors = undefined; + assert.ok(Array.isArray(reply.passwords)); + assert.equal(typeof reply.commands, 'string'); + assert.ok(Array.isArray(reply.flags)); - if (testUtils.isVersionGreaterThan([6, 2])) { - expectedReply.flags = ['on', 'allkeys', 'allchannels', 'allcommands', 'nopass']; - expectedReply.channels = ['*']; - } else { - expectedReply.flags = ['on', 'allkeys', 'allcommands', 'nopass']; - expectedReply.channels = undefined; - } - } + if (testUtils.isVersionGreaterThan([7])) { + assert.equal(typeof reply.keys, 'string'); + assert.equal(typeof reply.channels, 'string'); + assert.ok(Array.isArray(reply.selectors)); + } else { + assert.ok(Array.isArray(reply.keys)); - assert.deepEqual( - await client.aclGetUser('default'), - expectedReply - ); - }, GLOBAL.SERVERS.OPEN); + if (testUtils.isVersionGreaterThan([6, 2])) { + assert.ok(Array.isArray(reply.channels)); + } + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ACL_GETUSER.ts b/packages/client/lib/commands/ACL_GETUSER.ts index 818a945bac..c9489abaf5 100644 --- a/packages/client/lib/commands/ACL_GETUSER.ts +++ b/packages/client/lib/commands/ACL_GETUSER.ts @@ -1,40 +1,40 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, Resp2Reply, Command } from '../RESP/types'; -export function transformArguments(username: RedisCommandArgument): RedisCommandArguments { +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] + ]>>], +]>; + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(username: RedisArgument) { return ['ACL', 'GETUSER', username]; -} - -type AclGetUserRawReply = [ - '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 | 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] - }; -} + }, + transformReply: { + 2: (reply: Resp2Reply) => ({ + flags: reply[1], + passwords: reply[3], + commands: reply[5], + keys: reply[7], + channels: reply[9], + selectors: reply[11]?.map(selector => ({ + commands: selector[1], + keys: selector[3], + channels: selector[5] + })) + }), + 3: undefined as unknown as () => AclUser + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_LIST.spec.ts b/packages/client/lib/commands/ACL_LIST.spec.ts index 9f9156db7a..8b2d875bda 100644 --- a/packages/client/lib/commands/ACL_LIST.spec.ts +++ b/packages/client/lib/commands/ACL_LIST.spec.ts @@ -1,14 +1,22 @@ import { strict as assert } from 'assert'; -import testUtils from '../test-utils'; -import { transformArguments } from './ACL_LIST'; +import testUtils, { GLOBAL } from '../test-utils'; +import ACL_LIST from './ACL_LIST'; describe('ACL LIST', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['ACL', 'LIST'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ACL_LIST.transformArguments(), + ['ACL', 'LIST'] + ); + }); + + testUtils.testWithClient('client.aclList', async client => { + const users = await client.aclList(); + assert.ok(Array.isArray(users)); + for (const user of users) { + assert.equal(typeof user, 'string'); + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ACL_LIST.ts b/packages/client/lib/commands/ACL_LIST.ts index ae523fe9ce..1a831a4987 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 703d5eeb25..66afcf356a 100644 --- a/packages/client/lib/commands/ACL_LOAD.spec.ts +++ b/packages/client/lib/commands/ACL_LOAD.spec.ts @@ -1,14 +1,14 @@ import { strict as assert } from 'assert'; import testUtils from '../test-utils'; -import { transformArguments } from './ACL_SAVE'; +import ACL_LOAD from './ACL_LOAD'; -describe('ACL SAVE', () => { - testUtils.isVersionGreaterThanHook([6]); +describe('ACL LOAD', () => { + testUtils.isVersionGreaterThanHook([6]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['ACL', 'SAVE'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ACL_LOAD.transformArguments(), + ['ACL', 'LOAD'] + ); + }); }); diff --git a/packages/client/lib/commands/ACL_LOAD.ts b/packages/client/lib/commands/ACL_LOAD.ts index 88309102b9..39587829b1 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 a8296d31da..6b63b59f81 100644 --- a/packages/client/lib/commands/ACL_LOG.spec.ts +++ b/packages/client/lib/commands/ACL_LOG.spec.ts @@ -1,53 +1,53 @@ import { strict as assert } from 'assert'; -import testUtils from '../test-utils'; -import { transformArguments, transformReply } from './ACL_LOG'; +import testUtils, { GLOBAL } from '../test-utils'; +import ACL_LOG from './ACL_LOG'; describe('ACL LOG', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['ACL', 'LOG'] - ); - }); - - it('with count', () => { - assert.deepEqual( - transformArguments(10), - ['ACL', 'LOG', '10'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + ACL_LOG.transformArguments(), + ['ACL', 'LOG'] + ); }); - it('transformReply', () => { - assert.deepEqual( - transformReply([[ - 'count', - 1, - 'reason', - 'auth', - 'context', - 'toplevel', - 'object', - 'AUTH', - 'username', - 'someuser', - 'age-seconds', - '4.096', - 'client-info', - 'id=6 addr=127.0.0.1:63026 fd=8 name= age=9 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=48 qbuf-free=32720 obl=0 oll=0 omem=0 events=r cmd=auth user=default' - ]]), - [{ - count: 1, - reason: 'auth', - context: 'toplevel', - object: 'AUTH', - username: 'someuser', - ageSeconds: 4.096, - clientInfo: 'id=6 addr=127.0.0.1:63026 fd=8 name= age=9 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=48 qbuf-free=32720 obl=0 oll=0 omem=0 events=r cmd=auth user=default' - }] - ); + it('with count', () => { + assert.deepEqual( + ACL_LOG.transformArguments(10), + ['ACL', 'LOG', '10'] + ); }); + }); + + testUtils.testWithClient('client.aclLog', async client => { + // make sure to create at least one log + await Promise.all([ + client.aclSetUser('test', 'on >test'), + client.auth({ + username: 'test', + password: 'test' + }), + client.auth({ + username: 'default', + password: '' + }) + ]); + + const logs = await client.aclLog(); + assert.ok(Array.isArray(logs)); + for (const log of logs) { + + assert.equal(typeof log.count, 'number'); + assert.equal(typeof log.timestamp, 'number'); + assert.equal(typeof log.username, 'string'); + assert.equal(typeof log.clientId, 'string'); + assert.equal(typeof log.command, 'string'); + assert.equal(typeof log.args, 'string'); + assert.equal(typeof log.key, 'string'); + assert.equal(typeof log.result, 'number'); + assert.equal(typeof log.duration, 'number'); + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ACL_LOG.ts b/packages/client/lib/commands/ACL_LOG.ts index 0fd9aa6f19..4140111ecf 100644 --- a/packages/client/lib/commands/ACL_LOG.ts +++ b/packages/client/lib/commands/ACL_LOG.ts @@ -1,50 +1,38 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { Resp2Reply } from '../RESP/types'; +import { ArrayReply, BlobStringReply, Command, NumberReply, TuplesToMapReply } from '../RESP/types'; -export function transformArguments(count?: number): RedisCommandArguments { +export type AclLogReply = ArrayReply, NumberReply], + [BlobStringReply<'reason'>, BlobStringReply], + [BlobStringReply<'context'>, BlobStringReply], + [BlobStringReply<'object'>, BlobStringReply], + [BlobStringReply<'username'>, BlobStringReply], + [BlobStringReply<'age-seconds'>, BlobStringReply], + [BlobStringReply<'client-info'>, BlobStringReply] +]>>; + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(count?: number) { const args = ['ACL', 'LOG']; - if (count) { - args.push(count.toString()); + if (count !== undefined) { + args.push(count.toString()); } return args; -} - -type AclLogRawReply = [ - _: RedisCommandArgument, - count: number, - _: RedisCommandArgument, - reason: RedisCommandArgument, - _: RedisCommandArgument, - context: RedisCommandArgument, - _: RedisCommandArgument, - object: RedisCommandArgument, - _: RedisCommandArgument, - username: RedisCommandArgument, - _: RedisCommandArgument, - ageSeconds: RedisCommandArgument, - _: RedisCommandArgument, - clientInfo: RedisCommandArgument -]; - -interface AclLog { - count: number; - reason: RedisCommandArgument; - context: RedisCommandArgument; - object: RedisCommandArgument; - username: RedisCommandArgument; - ageSeconds: number; - clientInfo: RedisCommandArgument; -} - -export function transformReply(reply: Array): Array { - return reply.map(log => ({ - count: log[1], - reason: log[3], - context: log[5], - object: log[7], - username: log[9], - ageSeconds: Number(log[11]), - clientInfo: log[13] - })); -} + }, + transformReply: { + 2: (reply: Resp2Reply) => ({ + count: Number(reply[1]), + reason: reply[3], + context: reply[5], + object: reply[7], + username: reply[9], + 'age-seconds': Number(reply[11]), + 'client-info': reply[13] + }), + 3: undefined as unknown as () => AclLogReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_LOG_RESET.spec.ts b/packages/client/lib/commands/ACL_LOG_RESET.spec.ts index 5d26e45d04..7f3fbf1ade 100644 --- a/packages/client/lib/commands/ACL_LOG_RESET.spec.ts +++ b/packages/client/lib/commands/ACL_LOG_RESET.spec.ts @@ -1,14 +1,21 @@ import { strict as assert } from 'assert'; -import testUtils from '../test-utils'; -import { transformArguments } from './ACL_LOG_RESET'; +import testUtils, { GLOBAL } from '../test-utils'; +import ACL_LOG_RESET from './ACL_LOG_RESET'; describe('ACL LOG RESET', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['ACL', 'LOG', 'RESET'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ACL_LOG_RESET.transformArguments(), + ['ACL', 'LOG', 'RESET'] + ); + }); + + testUtils.testWithClient('client.aclLogReset', async client => { + assert.equal( + await client.aclLogReset(), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ACL_LOG_RESET.ts b/packages/client/lib/commands/ACL_LOG_RESET.ts index 8ff0be4f8b..91d58d538e 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 f4de312bb7..065b75f93b 100644 --- a/packages/client/lib/commands/ACL_SAVE.spec.ts +++ b/packages/client/lib/commands/ACL_SAVE.spec.ts @@ -1,14 +1,14 @@ import { strict as assert } from 'assert'; import testUtils from '../test-utils'; -import { transformArguments } from './ACL_LOAD'; +import ACL_SAVE from './ACL_SAVE'; -describe('ACL LOAD', () => { - testUtils.isVersionGreaterThanHook([6]); +describe('ACL SAVE', () => { + testUtils.isVersionGreaterThanHook([6]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['ACL', 'LOAD'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ACL_SAVE.transformArguments(), + ['ACL', 'SAVE'] + ); + }); }); diff --git a/packages/client/lib/commands/ACL_SAVE.ts b/packages/client/lib/commands/ACL_SAVE.ts index e57cd69729..8c2e2dab11 100644 --- a/packages/client/lib/commands/ACL_SAVE.ts +++ b/packages/client/lib/commands/ACL_SAVE.ts @@ -1,7 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['ACL', 'SAVE']; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_SETUSER.ts b/packages/client/lib/commands/ACL_SETUSER.ts index a12cc8ed24..c99fec3d9b 100644 --- a/packages/client/lib/commands/ACL_SETUSER.ts +++ b/packages/client/lib/commands/ACL_SETUSER.ts @@ -1,11 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export function transformArguments( - username: RedisCommandArgument, - rule: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['ACL', 'SETUSER', username], rule); -} - -export declare function transformReply(): RedisCommandArgument; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(username: RedisArgument, rule: RedisVariadicArgument) { + return pushVariadicArguments(['ACL', 'SETUSER', username], rule); + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_USERS.ts b/packages/client/lib/commands/ACL_USERS.ts index 7970a262e2..ee8c619f25 100644 --- a/packages/client/lib/commands/ACL_USERS.ts +++ b/packages/client/lib/commands/ACL_USERS.ts @@ -1,7 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['ACL', 'USERS']; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_WHOAMI.ts b/packages/client/lib/commands/ACL_WHOAMI.ts index 3c41171638..5aa56c4d35 100644 --- a/packages/client/lib/commands/ACL_WHOAMI.ts +++ b/packages/client/lib/commands/ACL_WHOAMI.ts @@ -1,7 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { BlobStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { - return ['ACL', 'WHOAMI']; -} - -export declare function transformReply(): RedisCommandArgument; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['ACL', 'USERS']; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/APPEND.spec.ts b/packages/client/lib/commands/APPEND.spec.ts index 2335386684..4481c0571e 100644 --- a/packages/client/lib/commands/APPEND.spec.ts +++ b/packages/client/lib/commands/APPEND.spec.ts @@ -1,11 +1,22 @@ import { strict as assert } from 'assert'; -import { transformArguments } from './APPEND'; +import testUtils, { GLOBAL } from '../test-utils'; +import APPEND from './APPEND'; -describe('APPEND', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'value'), - ['APPEND', 'key', 'value'] - ); - }); +describe.only('APPEND', () => { + 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 66f7fc8479..1bc0102499 100644 --- a/packages/client/lib/commands/APPEND.ts +++ b/packages/client/lib/commands/APPEND.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - value: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, value: RedisArgument) { return ['APPEND', key, value]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ASKING.ts b/packages/client/lib/commands/ASKING.ts index 8a87806fe6..c6ada477ee 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 1907488346..290ba936bf 100644 --- a/packages/client/lib/commands/AUTH.spec.ts +++ b/packages/client/lib/commands/AUTH.spec.ts @@ -1,25 +1,25 @@ import { strict as assert } from 'assert'; -import { transformArguments } from './AUTH'; +import AUTH from './AUTH'; describe('AUTH', () => { - describe('transformArguments', () => { - it('password only', () => { - assert.deepEqual( - transformArguments({ - password: 'password' - }), - ['AUTH', 'password'] - ); - }); - - it('username & password', () => { - assert.deepEqual( - transformArguments({ - username: 'username', - password: 'password' - }), - ['AUTH', 'username', 'password'] - ); - }); + describe('transformArguments', () => { + it('password only', () => { + assert.deepEqual( + AUTH.transformArguments({ + password: 'password' + }), + ['AUTH', '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 49b0df6d31..4c7a0b0c76 100644 --- a/packages/client/lib/commands/AUTH.ts +++ b/packages/client/lib/commands/AUTH.ts @@ -1,16 +1,23 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export interface AuthOptions { - username?: RedisCommandArgument; - password: RedisCommandArgument; + username?: RedisArgument; + password: RedisArgument; } -export function transformArguments({ username, password }: AuthOptions): RedisCommandArguments { - if (!username) { - return ['AUTH', password]; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments({ username, password }: AuthOptions) { + const args: Array = ['AUTH']; + + if (username !== undefined) { + args.push(username); } - return ['AUTH', username, password]; -} + args.push(password); -export declare function transformReply(): RedisCommandArgument; + return args; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/BGREWRITEAOF.ts b/packages/client/lib/commands/BGREWRITEAOF.ts index be4ec2546a..5f9a46e318 100644 --- a/packages/client/lib/commands/BGREWRITEAOF.ts +++ b/packages/client/lib/commands/BGREWRITEAOF.ts @@ -1,7 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['BGREWRITEAOF']; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BGSAVE.ts b/packages/client/lib/commands/BGSAVE.ts index 9c90f3485b..1cc60cb5da 100644 --- a/packages/client/lib/commands/BGSAVE.ts +++ b/packages/client/lib/commands/BGSAVE.ts @@ -1,17 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -interface BgSaveOptions { - SCHEDULE?: true; -} - -export function transformArguments(options?: BgSaveOptions): RedisCommandArguments { - const args = ['BGSAVE']; - - if (options?.SCHEDULE) { - args.push('SCHEDULE'); - } - - return args; -} - -export declare function transformReply(): RedisCommandArgument; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['BGSAVE']; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BITCOUNT.ts b/packages/client/lib/commands/BITCOUNT.ts index 4bbd4f0091..6ec6b89be8 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() - ); + args.push( + range.start.toString(), + range.end.toString() + ); - if (range.mode) { - args.push(range.mode); - } + 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 aaf0f93e50..9b94b19d24 100644 --- a/packages/client/lib/commands/BITFIELD.spec.ts +++ b/packages/client/lib/commands/BITFIELD.spec.ts @@ -1,46 +1,49 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './BITFIELD'; +import BITFIELD from './BITFIELD'; describe('BITFIELD', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', [{ - operation: 'OVERFLOW', - behavior: 'WRAP' - }, { - operation: 'GET', - encoding: 'i8', - offset: 0 - }, { - operation: 'OVERFLOW', - behavior: 'SAT' - }, { - operation: 'SET', - encoding: 'i16', - offset: 1, - value: 0 - }, { - operation: 'OVERFLOW', - behavior: 'FAIL' - }, { - operation: 'INCRBY', - encoding: 'i32', - offset: 2, - increment: 1 - }]), - ['BITFIELD', 'key', 'OVERFLOW', 'WRAP', 'GET', 'i8', '0', 'OVERFLOW', 'SAT', 'SET', 'i16', '1', '0', 'OVERFLOW', 'FAIL', 'INCRBY', 'i32', '2', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + BITFIELD.transformArguments('key', [{ + operation: 'OVERFLOW', + behavior: 'WRAP' + }, { + operation: 'GET', + encoding: 'i8', + offset: 0 + }, { + operation: 'OVERFLOW', + behavior: 'SAT' + }, { + operation: 'SET', + encoding: 'i16', + offset: 1, + value: 0 + }, { + operation: 'OVERFLOW', + behavior: 'FAIL' + }, { + operation: 'INCRBY', + encoding: 'i32', + offset: 2, + increment: 1 + }]), + ['BITFIELD', 'key', 'OVERFLOW', 'WRAP', 'GET', 'i8', '0', 'OVERFLOW', 'SAT', 'SET', 'i16', '1', '0', 'OVERFLOW', 'FAIL', 'INCRBY', 'i32', '2', '1'] + ); + }); - testUtils.testWithClient('client.bitField', async client => { - assert.deepEqual( - await client.bitField('key', [{ - operation: 'GET', - encoding: 'i8', - offset: 0 - }]), - [0] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('bitField', async client => { + assert.deepEqual( + await client.bitField('key', [{ + operation: 'GET', + encoding: 'i8', + offset: 0 + }]), + [0] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/BITFIELD.ts b/packages/client/lib/commands/BITFIELD.ts index 6a477b89f0..5d7d4bf728 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 98399d5f23..2857155a60 100644 --- a/packages/client/lib/commands/BITFIELD_RO.spec.ts +++ b/packages/client/lib/commands/BITFIELD_RO.spec.ts @@ -1,27 +1,30 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './BITFIELD_RO'; +import BITFIELD_RO from './BITFIELD_RO'; -describe('BITFIELD RO', () => { - testUtils.isVersionGreaterThanHook([6, 2]); +describe('BITFIELD_RO', () => { + testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', [{ - encoding: 'i8', - offset: 0 - }]), - ['BITFIELD_RO', 'key', 'GET', 'i8', '0'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + BITFIELD_RO.transformArguments('key', [{ + encoding: 'i8', + offset: 0 + }]), + ['BITFIELD_RO', 'key', 'GET', 'i8', '0'] + ); + }); - testUtils.testWithClient('client.bitFieldRo', async client => { - assert.deepEqual( - await client.bitFieldRo('key', [{ - encoding: 'i8', - offset: 0 - }]), - [0] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('bitFieldRo', async client => { + assert.deepEqual( + await client.bitFieldRo('key', [{ + encoding: 'i8', + offset: 0 + }]), + [0] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/BITFIELD_RO.ts b/packages/client/lib/commands/BITFIELD_RO.ts index efd4eac188..99e500abc0 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 554530d56f..c89d5ea0b3 100644 --- a/packages/client/lib/commands/BITOP.spec.ts +++ b/packages/client/lib/commands/BITOP.spec.ts @@ -1,35 +1,31 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './BITOP'; +import BITOP from './BITOP'; describe('BITOP', () => { - describe('transformArguments', () => { - it('single key', () => { - assert.deepEqual( - transformArguments('AND', 'destKey', 'key'), - ['BITOP', 'AND', 'destKey', 'key'] - ); - }); - - it('multiple keys', () => { - assert.deepEqual( - transformArguments('AND', 'destKey', ['1', '2']), - ['BITOP', 'AND', 'destKey', '1', '2'] - ); - }); + describe('transformArguments', () => { + it('single key', () => { + assert.deepEqual( + BITOP.transformArguments('AND', 'destKey', 'key'), + ['BITOP', 'AND', 'destKey', 'key'] + ); }); - testUtils.testWithClient('client.bitOp', async client => { - assert.equal( - await client.bitOp('AND', 'destKey', 'key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + it('multiple keys', () => { + assert.deepEqual( + BITOP.transformArguments('AND', 'destKey', ['1', '2']), + ['BITOP', 'AND', 'destKey', '1', '2'] + ); + }); + }); - testUtils.testWithCluster('cluster.bitOp', async cluster => { - assert.equal( - await cluster.bitOp('AND', '{tag}destKey', '{tag}key'), - 0 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('client.bitOp', async client => { + assert.equal( + await client.bitOp('AND', '{tag}destKey', '{tag}key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/BITOP.ts b/packages/client/lib/commands/BITOP.ts index e2953303d4..4c34845699 100644 --- a/packages/client/lib/commands/BITOP.ts +++ b/packages/client/lib/commands/BITOP.ts @@ -1,16 +1,17 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { NumberReply, Command, RedisArgument } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 2; +export type BitOperations = 'AND' | 'OR' | 'XOR' | 'NOT'; -type BitOperations = 'AND' | 'OR' | 'XOR' | 'NOT'; - -export function transformArguments( +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: false, + transformArguments( operation: BitOperations, - destKey: RedisCommandArgument, - key: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['BITOP', operation, destKey], key); -} - -export declare function transformReply(): number; + destKey: RedisArgument, + key: RedisVariadicArgument + ) { + return pushVariadicArguments(['BITOP', operation, destKey], key); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BITPOS.ts b/packages/client/lib/commands/BITPOS.ts index a9a035fd9f..5f8a1031a4 100644 --- a/packages/client/lib/commands/BITPOS.ts +++ b/packages/client/lib/commands/BITPOS.ts @@ -1,32 +1,31 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { BitValue } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, bit: BitValue, start?: number, end?: number, mode?: 'BYTE' | 'BIT' -): RedisCommandArguments { + ) { const args = ['BITPOS', key, bit.toString()]; if (typeof start === 'number') { - args.push(start.toString()); + args.push(start.toString()); } if (typeof end === 'number') { - args.push(end.toString()); + args.push(end.toString()); } if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BLMOVE.ts b/packages/client/lib/commands/BLMOVE.ts index ee808e70fc..c7e4844375 100644 --- a/packages/client/lib/commands/BLMOVE.ts +++ b/packages/client/lib/commands/BLMOVE.ts @@ -1,23 +1,24 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; import { ListSide } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - source: RedisCommandArgument, - destination: RedisCommandArgument, - sourceDirection: ListSide, - destinationDirection: ListSide, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + source: RedisArgument, + destination: RedisArgument, + sourceSide: ListSide, + destinationSide: ListSide, timeout: number -): RedisCommandArguments { + ) { return [ - 'BLMOVE', - source, - destination, - sourceDirection, - destinationDirection, - timeout.toString() + 'BLMOVE', + source, + destination, + sourceSide, + destinationSide, + timeout.toString() ]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BLMPOP.ts b/packages/client/lib/commands/BLMPOP.ts index 11bfad8b99..32d249899b 100644 --- a/packages/client/lib/commands/BLMPOP.ts +++ b/packages/client/lib/commands/BLMPOP.ts @@ -1,20 +1,22 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, Command } from '../RESP/types'; import { transformLMPopArguments, LMPopOptions, ListSide } from './generic-transformers'; +import LMPOP from './LMPOP'; -export const FIRST_KEY_INDEX = 3; - -export function transformArguments( +export default { + FIRST_KEY_INDEX: 3, + IS_READ_ONLY: false, + transformArguments( timeout: number, - keys: RedisCommandArgument | Array, + keys: RedisArgument | Array, side: ListSide, options?: LMPopOptions -): RedisCommandArguments { + ) { return transformLMPopArguments( - ['BLMPOP', timeout.toString()], - keys, - side, - options + ['BLMPOP', timeout.toString()], + keys, + side, + options ); -} - -export { transformReply } from './LMPOP'; + }, + transformReply: LMPOP.transformReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BLPOP.ts b/packages/client/lib/commands/BLPOP.ts index 46ef41ad6f..feb0fd6512 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'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; +import { pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - keys: RedisCommandArgument | Array, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument | Array, timeout: number -): RedisCommandArguments { - const args = pushVerdictArguments(['BLPOP'], keys); - + ) { + const args = pushVariadicArguments(['BRPOP'], key); args.push(timeout.toString()); - return args; -} - -type BLPopRawReply = null | [RedisCommandArgument, RedisCommandArgument]; - -type BLPopReply = null | { - key: RedisCommandArgument; - element: RedisCommandArgument; -}; - -export function transformReply(reply: BLPopRawReply): BLPopReply { + }, + transformReply(reply: NullReply | [BlobStringReply, BlobStringReply]) { if (reply === null) return null; return { - key: reply[0], - element: reply[1] + key: reply[0], + element: reply[1] }; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/BRPOP.ts b/packages/client/lib/commands/BRPOP.ts index b30e7e2cc2..64e8ed8404 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 { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; +import { 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: RedisArgument | Array, timeout: number -): RedisCommandArguments { - const args = pushVerdictArguments(['BRPOP'], key); - + ) { + const args = pushVariadicArguments(['BRPOP'], key); args.push(timeout.toString()); - return args; -} - -export { transformReply } from './BLPOP'; + }, + transformReply: BLPOP.transformReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BRPOPLPUSH.ts b/packages/client/lib/commands/BRPOPLPUSH.ts index 72c3e4aa5b..d342ea7572 100644 --- a/packages/client/lib/commands/BRPOPLPUSH.ts +++ b/packages/client/lib/commands/BRPOPLPUSH.ts @@ -1,13 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - source: RedisCommandArgument, - destination: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + source: RedisArgument, + destination: RedisArgument, timeout: number -): RedisCommandArguments { + ) { return ['BRPOPLPUSH', source, destination, timeout.toString()]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BZMPOP.ts b/packages/client/lib/commands/BZMPOP.ts index e4e9699cbd..936ccf800c 100644 --- a/packages/client/lib/commands/BZMPOP.ts +++ b/packages/client/lib/commands/BZMPOP.ts @@ -18,3 +18,17 @@ export function transformArguments( } export { transformReply } from './ZMPOP'; + + +import { Command } from '../RESP/types'; +import ZMPOP from './ZMPOP'; + +export default { + IS_READ_ONLY: false, + FIRST_KEY_INDEX: 3, + transformArguments() { + return ['BZMPOP']; + }, + transformReply: ZMPOP.transformReply +} as const satisfies Command; + diff --git a/packages/client/lib/commands/BZPOPMAX.ts b/packages/client/lib/commands/BZPOPMAX.ts index 94a30fb8dc..be92c194ef 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, transformNumberInfinityReply, ZMember } from './generic-transformers'; +import { pushVariadicArguments, transformNumberInfinityReply, ZMember } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,7 +7,7 @@ export function transformArguments( key: RedisCommandArgument | Array, timeout: number ): RedisCommandArguments { - const args = pushVerdictArguments(['BZPOPMAX'], key); + const args = pushVariadicArguments(['BZPOPMAX'], key); args.push(timeout.toString()); diff --git a/packages/client/lib/commands/BZPOPMIN.ts b/packages/client/lib/commands/BZPOPMIN.ts index 40cb3d5dc7..02d9f9215b 100644 --- a/packages/client/lib/commands/BZPOPMIN.ts +++ b/packages/client/lib/commands/BZPOPMIN.ts @@ -1,5 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { pushVariadicArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,7 +7,7 @@ export function transformArguments( key: RedisCommandArgument | Array, timeout: number ): RedisCommandArguments { - const args = pushVerdictArguments(['BZPOPMIN'], key); + const args = pushVariadicArguments(['BZPOPMIN'], key); args.push(timeout.toString()); diff --git a/packages/client/lib/commands/CLIENT_CACHING.spec.ts b/packages/client/lib/commands/CLIENT_CACHING.spec.ts index d9cb9a3f79..bf8e4ed12b 100644 --- a/packages/client/lib/commands/CLIENT_CACHING.spec.ts +++ b/packages/client/lib/commands/CLIENT_CACHING.spec.ts @@ -1,20 +1,20 @@ import { strict as assert } from 'assert'; -import { transformArguments } from './CLIENT_CACHING'; +import CLIENT_CACHING from './CLIENT_CACHING'; describe('CLIENT CACHING', () => { - describe('transformArguments', () => { - it('true', () => { - assert.deepEqual( - transformArguments(true), - ['CLIENT', 'CACHING', 'YES'] - ); - }); - - it('false', () => { - assert.deepEqual( - transformArguments(false), - ['CLIENT', 'CACHING', 'NO'] - ); - }); + describe('transformArguments', () => { + it('true', () => { + assert.deepEqual( + CLIENT_CACHING.transformArguments(true), + ['CLIENT', 'CACHING', 'YES'] + ); }); + + 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 bc2fbe41e9..35edf889fa 100644 --- a/packages/client/lib/commands/CLIENT_CACHING.ts +++ b/packages/client/lib/commands/CLIENT_CACHING.ts @@ -1,11 +1,13 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(value: boolean): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments(value: boolean) { return [ - 'CLIENT', - 'CACHING', - value ? 'YES' : 'NO' + 'CLIENT', + 'CACHING', + value ? 'YES' : 'NO' ]; -} - -export declare function transformReply(): 'OK' | Buffer; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_GETNAME.spec.ts b/packages/client/lib/commands/CLIENT_GETNAME.spec.ts index 0a09713882..cbd6576891 100644 --- a/packages/client/lib/commands/CLIENT_GETNAME.spec.ts +++ b/packages/client/lib/commands/CLIENT_GETNAME.spec.ts @@ -1,11 +1,11 @@ import { strict as assert } from 'assert'; -import { transformArguments } from './CLIENT_GETNAME'; +import CLIENT_GETNAME from './CLIENT_GETNAME'; describe('CLIENT GETNAME', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CLIENT', 'GETNAME'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLIENT_GETNAME.transformArguments(), + ['CLIENT', 'GETNAME'] + ); + }); }); diff --git a/packages/client/lib/commands/CLIENT_GETNAME.ts b/packages/client/lib/commands/CLIENT_GETNAME.ts index da00539d7f..5e3272553e 100644 --- a/packages/client/lib/commands/CLIENT_GETNAME.ts +++ b/packages/client/lib/commands/CLIENT_GETNAME.ts @@ -1,7 +1,12 @@ -import { RedisCommandArguments } from '.'; +import { BlobStringReply, NullReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { - return ['CLIENT', 'GETNAME']; -} - -export declare function transformReply(): string | null; +export default { + IS_READ_ONLY: true, + transformArguments() { + return [ + 'CLIENT', + 'GETNAME' + ]; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_GETREDIR.spec.ts b/packages/client/lib/commands/CLIENT_GETREDIR.spec.ts index 09dd9677e3..057eb23712 100644 --- a/packages/client/lib/commands/CLIENT_GETREDIR.spec.ts +++ b/packages/client/lib/commands/CLIENT_GETREDIR.spec.ts @@ -1,11 +1,11 @@ import { strict as assert } from 'assert'; -import { transformArguments } from './CLIENT_GETREDIR'; +import CLIENT_GETREDIR from './CLIENT_GETREDIR'; describe('CLIENT GETREDIR', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CLIENT', 'GETREDIR'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLIENT_GETREDIR.transformArguments(), + ['CLIENT', 'GETREDIR'] + ); + }); }); diff --git a/packages/client/lib/commands/CLIENT_GETREDIR.ts b/packages/client/lib/commands/CLIENT_GETREDIR.ts index d192adf284..c2e68de5b6 100644 --- a/packages/client/lib/commands/CLIENT_GETREDIR.ts +++ b/packages/client/lib/commands/CLIENT_GETREDIR.ts @@ -1,7 +1,9 @@ -import { RedisCommandArguments } from '.'; +import { NumberReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { - return ['CLIENT', 'GETREDIR']; -} - -export declare function transformReply(): number; +export default { + IS_READ_ONLY: true, + transformArguments() { + return ['CLIENT', 'GETREDIR'] + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_ID.spec.ts b/packages/client/lib/commands/CLIENT_ID.spec.ts index 6792a8c31b..28865a4ade 100644 --- a/packages/client/lib/commands/CLIENT_ID.spec.ts +++ b/packages/client/lib/commands/CLIENT_ID.spec.ts @@ -1,19 +1,19 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CLIENT_ID'; +import CLIENT_ID from './CLIENT_ID'; describe('CLIENT ID', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CLIENT', 'ID'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLIENT_ID.transformArguments(), + ['CLIENT', 'ID'] + ); + }); - testUtils.testWithClient('client.clientId', async client => { - assert.equal( - typeof (await client.clientId()), - 'number' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.clientId', async client => { + assert.equal( + typeof (await client.clientId()), + 'number' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/CLIENT_ID.ts b/packages/client/lib/commands/CLIENT_ID.ts index a57e392ade..cc298ed122 100644 --- a/packages/client/lib/commands/CLIENT_ID.ts +++ b/packages/client/lib/commands/CLIENT_ID.ts @@ -1,7 +1,9 @@ -export const IS_READ_ONLY = true; +import { NumberReply, Command } from '../RESP/types'; -export function transformArguments(): Array { +export default { + IS_READ_ONLY: true, + transformArguments() { return ['CLIENT', 'ID']; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_INFO.ts b/packages/client/lib/commands/CLIENT_INFO.ts index 7f6b6e1884..1bd9fc2f25 100644 --- a/packages/client/lib/commands/CLIENT_INFO.ts +++ b/packages/client/lib/commands/CLIENT_INFO.ts @@ -1,89 +1,115 @@ -export const IS_READ_ONLY = true; - -export function transformArguments(): Array { - return ['CLIENT', 'INFO']; -} +import { Command, VerbatimStringReply } from '../RESP/types'; export interface ClientInfoReply { - id: number; - addr: string; - laddr?: string; // 6.2 - fd: number; - name: string; - age: number; - idle: number; - flags: string; - db: number; - sub: number; - psub: number; - ssub?: number; // 7.0.3 - multi: number; - qbuf: number; - qbufFree: number; - argvMem?: number; // 6.0 - multiMem?: number; // 7.0 - obl: number; - oll: number; - omem: number; - totMem?: number; // 6.0 - events: string; - cmd: string; - user?: string; // 6.0 - redir?: number; // 6.2 - resp?: number; // 7.0 + id: number; + addr: string; + /** + * available since 6.2 + */ + laddr?: string; + fd: number; + name: string; + age: number; + idle: number; + flags: string; + db: number; + sub: number; + psub: number; + /** + * available since 7.0.3 + */ + ssub?: number; + multi: number; + qbuf: number; + qbufFree: number; + /** + * available since 6.0 + */ + argvMem?: number; + /** + * available since 7.0 + */ + multiMem?: number; + obl: number; + oll: number; + omem: number; + /** + * available since 6.0 + */ + totMem?: number; + events: string; + cmd: string; + /** + * available since 6.0 + */ + user?: string; + /** + * available since 6.2 + */ + redir?: number; + /** + * available since 7.0 + */ + resp?: number; } const CLIENT_INFO_REGEX = /([^\s=]+)=([^\s]*)/g; -export function transformReply(rawReply: string): ClientInfoReply { +export default { + IS_READ_ONLY: true, + transformArguments() { + return ['CLIENT', 'INFO'] + }, + transformReply(rawReply: VerbatimStringReply) { const map: Record = {}; - for (const item of rawReply.matchAll(CLIENT_INFO_REGEX)) { - map[item[1]] = item[2]; + for (const item of rawReply.toString().matchAll(CLIENT_INFO_REGEX)) { + map[item[1]] = item[2]; } const reply: ClientInfoReply = { - id: Number(map.id), - addr: map.addr, - fd: Number(map.fd), - name: map.name, - age: Number(map.age), - idle: Number(map.idle), - flags: map.flags, - db: Number(map.db), - sub: Number(map.sub), - psub: Number(map.psub), - multi: Number(map.multi), - qbuf: Number(map.qbuf), - qbufFree: Number(map['qbuf-free']), - argvMem: Number(map['argv-mem']), - obl: Number(map.obl), - oll: Number(map.oll), - omem: Number(map.omem), - totMem: Number(map['tot-mem']), - events: map.events, - cmd: map.cmd, - user: map.user + id: Number(map.id), + addr: map.addr, + fd: Number(map.fd), + name: map.name, + age: Number(map.age), + idle: Number(map.idle), + flags: map.flags, + db: Number(map.db), + sub: Number(map.sub), + psub: Number(map.psub), + multi: Number(map.multi), + qbuf: Number(map.qbuf), + qbufFree: Number(map['qbuf-free']), + argvMem: Number(map['argv-mem']), + obl: Number(map.obl), + oll: Number(map.oll), + omem: Number(map.omem), + totMem: Number(map['tot-mem']), + events: map.events, + cmd: map.cmd, + user: map.user }; if (map.laddr !== undefined) { - reply.laddr = map.laddr; + reply.laddr = map.laddr; } if (map.redir !== undefined) { - reply.redir = Number(map.redir); + reply.redir = Number(map.redir); } if (map.ssub !== undefined) { - reply.ssub = Number(map.ssub); + reply.ssub = Number(map.ssub); } if (map['multi-mem'] !== undefined) { - reply.multiMem = Number(map['multi-mem']); + reply.multiMem = Number(map['multi-mem']); } if (map.resp !== undefined) { - reply.resp = Number(map.resp); + reply.resp = Number(map.resp); } return reply; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_KILL.spec.ts b/packages/client/lib/commands/CLIENT_KILL.spec.ts index 2fe894f361..4a64cb2c6e 100644 --- a/packages/client/lib/commands/CLIENT_KILL.spec.ts +++ b/packages/client/lib/commands/CLIENT_KILL.spec.ts @@ -2,109 +2,109 @@ import { strict as assert } from 'assert'; import { ClientKillFilters, transformArguments } from './CLIENT_KILL'; describe('CLIENT KILL', () => { - describe('transformArguments', () => { - it('ADDRESS', () => { - assert.deepEqual( - transformArguments({ - filter: ClientKillFilters.ADDRESS, - address: 'ip:6379' - }), - ['CLIENT', 'KILL', 'ADDR', 'ip:6379'] - ); - }); - - 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'] - ); - }); - }); - - it('TYPE & SKIP_ME', () => { - assert.deepEqual( - transformArguments([ - { - filter: ClientKillFilters.TYPE, - type: 'master' - }, - ClientKillFilters.SKIP_ME - ]), - ['CLIENT', 'KILL', 'TYPE', 'master', 'SKIPME'] - ); - }); + 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'] + ); + }); + }); + + it('TYPE & SKIP_ME', () => { + assert.deepEqual( + transformArguments([ + { + filter: ClientKillFilters.TYPE, + type: 'master' + }, + ClientKillFilters.SKIP_ME + ]), + ['CLIENT', 'KILL', 'TYPE', 'master', 'SKIPME'] + ); + }); + }); }); diff --git a/packages/client/lib/commands/CLIENT_KILL.ts b/packages/client/lib/commands/CLIENT_KILL.ts index adb2a5a656..c1663f80e3 100644 --- a/packages/client/lib/commands/CLIENT_KILL.ts +++ b/packages/client/lib/commands/CLIENT_KILL.ts @@ -1,95 +1,99 @@ -import { RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export enum ClientKillFilters { - ADDRESS = 'ADDR', - LOCAL_ADDRESS = 'LADDR', - ID = 'ID', - TYPE = 'TYPE', - USER = 'USER', - SKIP_ME = 'SKIPME' +export const CLIENT_KILL_FILTERS = { + ADDRESS: 'ADDR', + LOCAL_ADDRESS: 'LADDR', + ID: 'ID', + TYPE: 'TYPE', + USER: 'USER', + SKIP_ME: 'SKIPME' +} 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; }); -type KillFilters = KillAddress | KillLocalAddress | KillId | KillType | KillUser | KillSkipMe; +export type ClientKillFilter = ClientKillAddress | ClientKillLocalAddress | ClientKillId | ClientKillType | ClientKillUser | ClientKillSkipMe; -export function transformArguments(filters: KillFilters | Array): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments(filters: ClientKillFilter | Array) { const args = ['CLIENT', 'KILL']; if (Array.isArray(filters)) { - for (const filter of filters) { - pushFilter(args, filter); - } + for (const filter of filters) { + pushFilter(args, filter); + } } else { - pushFilter(args, filters); + pushFilter(args, filters); } return args; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; + +function pushFilter(args: Array, filter: ClientKillFilter): void { + if (filter === CLIENT_KILL_FILTERS.SKIP_ME) { + args.push('SKIPME'); + return; + } + + args.push(filter.filter); + + switch (filter.filter) { + case CLIENT_KILL_FILTERS.ADDRESS: + args.push(filter.address); + break; + + case CLIENT_KILL_FILTERS.LOCAL_ADDRESS: + args.push(filter.localAddress); + break; + + case CLIENT_KILL_FILTERS.ID: + args.push( + typeof filter.id === 'number' ? + filter.id.toString() : + filter.id + ); + break; + + case CLIENT_KILL_FILTERS.TYPE: + args.push(filter.type); + break; + + case CLIENT_KILL_FILTERS.USER: + args.push(filter.username); + break; + + case CLIENT_KILL_FILTERS.SKIP_ME: + args.push(filter.skipMe ? 'yes' : 'no'); + break; + } } - -function pushFilter(args: RedisCommandArguments, filter: KillFilters): void { - if (filter === ClientKillFilters.SKIP_ME) { - args.push('SKIPME'); - return; - } - - args.push(filter.filter); - - switch(filter.filter) { - case ClientKillFilters.ADDRESS: - args.push(filter.address); - break; - - case ClientKillFilters.LOCAL_ADDRESS: - args.push(filter.localAddress); - break; - - case ClientKillFilters.ID: - args.push( - typeof filter.id === 'number' ? - filter.id.toString() : - filter.id - ); - break; - - case ClientKillFilters.TYPE: - args.push(filter.type); - break; - - case ClientKillFilters.USER: - args.push(filter.username); - break; - - case ClientKillFilters.SKIP_ME: - args.push(filter.skipMe ? 'yes' : 'no'); - break; - } -} - -export declare function transformReply(): number; diff --git a/packages/client/lib/commands/CLIENT_LIST.ts b/packages/client/lib/commands/CLIENT_LIST.ts index 6f71dc7d99..b1c2e5d2db 100644 --- a/packages/client/lib/commands/CLIENT_LIST.ts +++ b/packages/client/lib/commands/CLIENT_LIST.ts @@ -1,43 +1,43 @@ -import { RedisCommandArguments, RedisCommandArgument } from '.'; -import { pushVerdictArguments } from './generic-transformers'; -import { transformReply as transformClientInfoReply, ClientInfoReply } from './CLIENT_INFO'; +import { RedisArgument, VerbatimStringReply, Command } from '../RESP/types'; +import { pushVariadicArguments } from './generic-transformers'; +import CLIENT_INFO, { ClientInfoReply } from './CLIENT_INFO'; -interface ListFilterType { - TYPE: 'NORMAL' | 'MASTER' | 'REPLICA' | 'PUBSUB'; - ID?: never; +export interface ListFilterType { + TYPE: 'NORMAL' | 'MASTER' | 'REPLICA' | 'PUBSUB'; + ID?: never; } -interface ListFilterId { - ID: Array; - TYPE?: never; +export interface ListFilterId { + ID: Array; + TYPE?: never; } export type ListFilter = ListFilterType | ListFilterId; -export const IS_READ_ONLY = true; - -export function transformArguments(filter?: ListFilter): RedisCommandArguments { - let args: RedisCommandArguments = ['CLIENT', 'LIST']; +export default { + IS_READ_ONLY: true, + transformArguments(filter?: ListFilter) { + let args: Array = ['CLIENT', 'LIST']; if (filter) { - if (filter.TYPE !== undefined) { - args.push('TYPE', filter.TYPE); - } else { - args.push('ID'); - args = pushVerdictArguments(args, filter.ID); - } + if (filter.TYPE !== undefined) { + args.push('TYPE', filter.TYPE); + } else { + args.push('ID'); + args = pushVariadicArguments(args, filter.ID); + } } return args; -} - -export function transformReply(rawReply: string): Array { - const split = rawReply.split('\n'), - length = split.length - 1, - reply: Array = []; + }, + transformReply(rawReply: VerbatimStringReply): Array { + const split = rawReply.toString().split('\n'), + length = split.length - 1, + reply: Array = []; for (let i = 0; i < length; i++) { - reply.push(transformClientInfoReply(split[i])); + reply.push(CLIENT_INFO.transformReply(split[i] as VerbatimStringReply)); } - + return reply; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_NO-EVICT.ts b/packages/client/lib/commands/CLIENT_NO-EVICT.ts index 86edbde1d2..226405d032 100644 --- a/packages/client/lib/commands/CLIENT_NO-EVICT.ts +++ b/packages/client/lib/commands/CLIENT_NO-EVICT.ts @@ -1,11 +1,13 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(value: boolean): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments(value: boolean) { return [ - 'CLIENT', - 'NO-EVICT', - value ? 'ON' : 'OFF' + 'CLIENT', + 'NO-EVICT', + value ? 'ON' : 'OFF' ]; -} - -export declare function transformReply(): 'OK' | Buffer; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_PAUSE.ts b/packages/client/lib/commands/CLIENT_PAUSE.ts index 090002272c..b01e356cf5 100644 --- a/packages/client/lib/commands/CLIENT_PAUSE.ts +++ b/packages/client/lib/commands/CLIENT_PAUSE.ts @@ -1,20 +1,19 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments( - timeout: number, - mode?: 'WRITE' | 'ALL' -): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments(timeout: number, mode?: 'WRITE' | 'ALL') { const args = [ - 'CLIENT', - 'PAUSE', - timeout.toString() + 'CLIENT', + 'PAUSE', + timeout.toString() ]; if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export declare function transformReply(): 'OK' | Buffer; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_SETNAME.ts b/packages/client/lib/commands/CLIENT_SETNAME.ts index f5cf1c786f..9208bba924 100644 --- a/packages/client/lib/commands/CLIENT_SETNAME.ts +++ b/packages/client/lib/commands/CLIENT_SETNAME.ts @@ -1,7 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(name: RedisCommandArgument): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments(name: RedisArgument) { return ['CLIENT', 'SETNAME', name]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_UNPAUSE.ts b/packages/client/lib/commands/CLIENT_UNPAUSE.ts index e139436d00..b2baf9c4be 100644 --- a/packages/client/lib/commands/CLIENT_UNPAUSE.ts +++ b/packages/client/lib/commands/CLIENT_UNPAUSE.ts @@ -1,7 +1,9 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments() { return ['CLIENT', 'UNPAUSE']; -} - -export declare function transformReply(): 'OK' | Buffer; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts b/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts index 6cd357fb82..0841b42eca 100644 --- a/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts @@ -1,11 +1,13 @@ -import { RedisCommandArguments } from '.'; -import { pushVerdictNumberArguments } from './generic-transformers'; +import { SimpleStringReply, Command } from '../RESP/types'; +import { pushVariadicNumberArguments } from './generic-transformers'; -export function transformArguments(slots: number | Array): RedisCommandArguments { - return pushVerdictNumberArguments( - ['CLUSTER', 'ADDSLOTS'], - slots +export default { + IS_READ_ONLY: true, + transformArguments(slots: number | Array) { + return pushVariadicNumberArguments( + ['CLUSTER', 'ADDSLOTS'], + slots ); -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts b/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts index 6a8d6dc668..ebbd4529e1 100644 --- a/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts +++ b/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts @@ -1,13 +1,13 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; import { pushSlotRangesArguments, SlotRange } from './generic-transformers'; -export function transformArguments( - ranges: SlotRange | Array -): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments(ranges: SlotRange | Array) { return pushSlotRangesArguments( - ['CLUSTER', 'ADDSLOTSRANGE'], - ranges + ['CLUSTER', 'ADDSLOTSRANGE'], + ranges ); -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts b/packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts index 7f81c8fdc4..c2587e82ae 100644 --- a/packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts +++ b/packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts @@ -1,5 +1,9 @@ -export function transformArguments(): Array { - return ['CLUSTER', 'BUMPEPOCH']; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): 'BUMPED' | 'STILL'; +export default { + IS_READ_ONLY: true, + transformArguments() { + return ['CLUSTER', 'BUMPEPOCH']; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'BUMPED' | 'STILL'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts b/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts index 3fbc33052f..1c33ca7453 100644 --- a/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts +++ b/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts @@ -1,5 +1,9 @@ -export function transformArguments(nodeId: string): Array { - return ['CLUSTER', 'COUNT-FAILURE-REPORTS', nodeId]; -} +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export declare function transformReply(): number; +export default { + IS_READ_ONLY: true, + transformArguments(nodeId: RedisArgument) { + return ['CLUSTER', 'COUNT-FAILURE-REPORTS', nodeId]; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts b/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts index a5ff75e58a..939d09d208 100644 --- a/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts +++ b/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts @@ -1,5 +1,9 @@ -export function transformArguments(slot: number): Array { - return ['CLUSTER', 'COUNTKEYSINSLOT', slot.toString()]; -} +import { NumberReply, Command } from '../RESP/types'; -export declare function transformReply(): number; +export default { + IS_READ_ONLY: true, + transformArguments(slot: number) { + return ['CLUSTER', 'COUNT-FAILURE-REPORTS', slot.toString()]; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_DELSLOTS.ts b/packages/client/lib/commands/CLUSTER_DELSLOTS.ts index bf8d9c1890..f9f408fa93 100644 --- a/packages/client/lib/commands/CLUSTER_DELSLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_DELSLOTS.ts @@ -1,11 +1,13 @@ -import { RedisCommandArguments } from '.'; -import { pushVerdictNumberArguments } from './generic-transformers'; +import { SimpleStringReply, Command } from '../RESP/types'; +import { pushVariadicNumberArguments } from './generic-transformers'; -export function transformArguments(slots: number | Array): RedisCommandArguments { - return pushVerdictNumberArguments( - ['CLUSTER', 'DELSLOTS'], - slots +export default { + IS_READ_ONLY: true, + transformArguments(slots: number | Array) { + return pushVariadicNumberArguments( + ['CLUSTER', 'DELSLOTS'], + slots ); -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts b/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts index b136113c65..81e46ba2dc 100644 --- a/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts +++ b/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts @@ -1,13 +1,13 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; import { pushSlotRangesArguments, SlotRange } from './generic-transformers'; -export function transformArguments( - ranges: SlotRange | Array -): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments(ranges: SlotRange | Array) { return pushSlotRangesArguments( - ['CLUSTER', 'DELSLOTSRANGE'], - ranges + ['CLUSTER', 'DELSLOTSRANGE'], + ranges ); -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_FAILOVER.ts b/packages/client/lib/commands/CLUSTER_FAILOVER.ts index 9bc4b69f34..948d1f4864 100644 --- a/packages/client/lib/commands/CLUSTER_FAILOVER.ts +++ b/packages/client/lib/commands/CLUSTER_FAILOVER.ts @@ -1,16 +1,22 @@ -export enum FailoverModes { - FORCE = 'FORCE', - TAKEOVER = 'TAKEOVER' -} +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(mode?: FailoverModes): Array { +export const FAILOVER_MODES = { + FORCE: 'FORCE', + TAKEOVER: 'TAKEOVER' +} as const; + +export type FailoverModes = typeof FAILOVER_MODES[keyof typeof FAILOVER_MODES]; + +export default { + IS_READ_ONLY: true, + transformArguments(mode?: FailoverModes) { const args = ['CLUSTER', 'FAILOVER']; if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts b/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts index dfb1e1ccde..d4f783cd11 100644 --- a/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts @@ -1,5 +1,9 @@ -export function transformArguments(): Array { - return ['CLUSTER', 'FLUSHSLOTS']; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): 'OK'; +export default { + IS_READ_ONLY: true, + transformArguments() { + return ['CLUSTER', 'FLUSHSLOTS']; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_FORGET.ts b/packages/client/lib/commands/CLUSTER_FORGET.ts index fc557073ae..19bc4657e4 100644 --- a/packages/client/lib/commands/CLUSTER_FORGET.ts +++ b/packages/client/lib/commands/CLUSTER_FORGET.ts @@ -1,5 +1,9 @@ -export function transformArguments(nodeId: string): Array { - return ['CLUSTER', 'FORGET', nodeId]; -} +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): 'OK'; +export default { + IS_READ_ONLY: true, + transformArguments(nodeId: RedisArgument) { + return ['CLUSTER', 'FORGET', nodeId]; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts index ec75b7b733..b9970a07db 100644 --- a/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts +++ b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts @@ -1,5 +1,9 @@ -export function transformArguments(slot: number, count: number): Array { - return ['CLUSTER', 'GETKEYSINSLOT', slot.toString(), count.toString()]; -} +import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export declare function transformReply(): Array; +export default { + IS_READ_ONLY: true, + transformArguments(slot: number, count: number) { + return ['CLUSTER', 'GETKEYSINSLOT', slot.toString(), count.toString()]; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_KEYSLOT.ts b/packages/client/lib/commands/CLUSTER_KEYSLOT.ts index 0af524ff12..fc68287eae 100644 --- a/packages/client/lib/commands/CLUSTER_KEYSLOT.ts +++ b/packages/client/lib/commands/CLUSTER_KEYSLOT.ts @@ -1,5 +1,8 @@ -export function transformArguments(key: string): Array { - return ['CLUSTER', 'KEYSLOT', key]; -} +import { Command, NumberReply, RedisArgument } from '../RESP/types'; -export declare function transformReply(): number; +export default { + transformArguments(key: RedisArgument) { + return ['CLUSTER', 'KEYSLOT', key]; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_MEET.ts b/packages/client/lib/commands/CLUSTER_MEET.ts index e6ce1c1fce..3e5d343b6f 100644 --- a/packages/client/lib/commands/CLUSTER_MEET.ts +++ b/packages/client/lib/commands/CLUSTER_MEET.ts @@ -1,5 +1,8 @@ -export function transformArguments(ip: string, port: number): Array { - return ['CLUSTER', 'MEET', ip, port.toString()]; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): 'OK'; +export default { + transformArguments(host: string, port: number) { + return ['CLUSTER', 'MEET', host, port.toString()]; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_MYID.ts b/packages/client/lib/commands/CLUSTER_MYID.ts index 2b61684634..a7a4184f9c 100644 --- a/packages/client/lib/commands/CLUSTER_MYID.ts +++ b/packages/client/lib/commands/CLUSTER_MYID.ts @@ -1,5 +1,8 @@ -export function transformArguments(): Array { - return ['CLUSTER', 'MYID']; -} +import { BlobStringReply, Command } from "../RESP/types"; -export declare function transformReply(): string; +export default { + transformArguments() { + return ['CLUSTER', 'MYID']; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_REPLICATE.ts b/packages/client/lib/commands/CLUSTER_REPLICATE.ts index c74e1ec596..c589ad33d7 100644 --- a/packages/client/lib/commands/CLUSTER_REPLICATE.ts +++ b/packages/client/lib/commands/CLUSTER_REPLICATE.ts @@ -1,5 +1,8 @@ -export function transformArguments(nodeId: string): Array { - return ['CLUSTER', 'REPLICATE', nodeId]; -} +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): 'OK'; +export default { + transformArguments(nodeId: RedisArgument) { + return ['CLUSTER', 'REPLICATE', nodeId]; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_SLOTS.ts b/packages/client/lib/commands/CLUSTER_SLOTS.ts index 20d9782dd9..5d4e5d7d0b 100644 --- a/packages/client/lib/commands/CLUSTER_SLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_SLOTS.ts @@ -1,46 +1,39 @@ -import { RedisCommandArguments } from '.'; +import { NumberReply, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { - return ['CLUSTER', 'SLOTS']; -} +type RawNode = [ + host: BlobStringReply, + port: NumberReply, + id: BlobStringReply +]; -type ClusterSlotsRawNode = [ip: string, port: number, id: string]; - -type ClusterSlotsRawReply = Array<[ - from: number, - to: number, - master: ClusterSlotsRawNode, - ...replicas: Array +type ClusterSlotsRawReply = ArrayReply<[ + from: NumberReply, + to: NumberReply, + master: RawNode, + ...replicas: Array ]>; -export interface ClusterSlotsNode { - ip: string; - port: number; - id: string; -}; +export type ClusterSlotsNode = ReturnType; -export type ClusterSlotsReply = Array<{ - from: number; - to: number; - master: ClusterSlotsNode; - replicas: Array; -}>; +export default { + IS_READ_ONLY: true, + transformArguments() { + return ['CLUSTER', 'SLOTS']; + }, + transformReply(reply: ClusterSlotsRawReply) { + return reply.map(([from, to, master, ...replicas]) => ({ + from, + to, + master: transformNode(master), + replicas: replicas.map(transformNode) + })); + } +} as const satisfies Command; -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 - }; +function transformNode([host, port, id ]: RawNode) { + return { + host, + port, + id + }; } diff --git a/packages/client/lib/commands/CONFIG_GET.ts b/packages/client/lib/commands/CONFIG_GET.ts index 3afc0eddfd..45b8e61ac4 100644 --- a/packages/client/lib/commands/CONFIG_GET.ts +++ b/packages/client/lib/commands/CONFIG_GET.ts @@ -1,5 +1,10 @@ -export function transformArguments(parameter: string): Array { - return ['CONFIG', 'GET', parameter]; -} +import { RedisArgument, Command } from '../RESP/types'; +import { transformTuplesReply } from './generic-transformers'; -export { transformTuplesReply as transformReply } from './generic-transformers'; +export default { + IS_READ_ONLY: true, + transformArguments(parameter: RedisArgument) { + return ['CONFIG', 'GET', parameter]; + }, + transformReply: transformTuplesReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CONFIG_RESETSTAT.ts b/packages/client/lib/commands/CONFIG_RESETSTAT.ts index aba54bc3c7..119bbd6051 100644 --- a/packages/client/lib/commands/CONFIG_RESETSTAT.ts +++ b/packages/client/lib/commands/CONFIG_RESETSTAT.ts @@ -1,5 +1,8 @@ -export function transformArguments(): Array { - return ['CONFIG', 'RESETSTAT']; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + transformArguments() { + return ['CONFIG', 'RESETSTAT']; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CONFIG_REWRITE.ts b/packages/client/lib/commands/CONFIG_REWRITE.ts index 67984adf30..63f9acdb90 100644 --- a/packages/client/lib/commands/CONFIG_REWRITE.ts +++ b/packages/client/lib/commands/CONFIG_REWRITE.ts @@ -1,5 +1,8 @@ -export function transformArguments(): Array { - return ['CONFIG', 'REWRITE']; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + transformArguments() { + return ['CONFIG', 'REWRITE']; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CONFIG_SET.ts b/packages/client/lib/commands/CONFIG_SET.ts index 41f40d035d..426a314fc1 100644 --- a/packages/client/lib/commands/CONFIG_SET.ts +++ b/packages/client/lib/commands/CONFIG_SET.ts @@ -1,23 +1,24 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { SimpleStringReply, Command, RedisArgument } from '../RESP/types'; -type SingleParameter = [parameter: RedisCommandArgument, value: RedisCommandArgument]; +type SingleParameter = [parameter: RedisArgument, value: RedisArgument]; -type MultipleParameters = [config: Record]; +type MultipleParameters = [config: Record]; -export function transformArguments( +export default { + transformArguments( ...[parameterOrConfig, value]: SingleParameter | MultipleParameters -): RedisCommandArguments { - const args: RedisCommandArguments = ['CONFIG', 'SET']; - - if (typeof parameterOrConfig === 'string') { - args.push(parameterOrConfig, value!); + ) { + const args: Array = ['CONFIG', 'SET']; + + if (typeof parameterOrConfig === 'string' || Buffer.isBuffer(parameterOrConfig)) { + args.push(parameterOrConfig, value!); } else { - for (const [key, value] of Object.entries(parameterOrConfig)) { - args.push(key, value); - } + for (const [key, value] of Object.entries(parameterOrConfig)) { + args.push(key, value); + } } - + return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/COPY.ts b/packages/client/lib/commands/COPY.ts index b1e212a995..2ef3c3224f 100644 --- a/packages/client/lib/commands/COPY.ts +++ b/packages/client/lib/commands/COPY.ts @@ -1,28 +1,24 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -interface CopyCommandOptions { - destinationDb?: number; - replace?: boolean; +export interface CopyCommandOptions { + DB?: number; + REPLACE?: boolean; } -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - source: RedisCommandArgument, - destination: RedisCommandArgument, - options?: CopyCommandOptions -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(source: RedisArgument, destination: RedisArgument, options?: CopyCommandOptions) { const args = ['COPY', source, destination]; - if (options?.destinationDb) { - args.push('DB', options.destinationDb.toString()); + if (options?.DB) { + args.push('DB', options.DB.toString()); } - if (options?.replace) { - args.push('REPLACE'); + if (options?.REPLACE) { + args.push('REPLACE'); } return args; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/DBSIZE.ts b/packages/client/lib/commands/DBSIZE.ts index 6b442ec33a..c4d3c8d857 100644 --- a/packages/client/lib/commands/DBSIZE.ts +++ b/packages/client/lib/commands/DBSIZE.ts @@ -1,7 +1,9 @@ -export const IS_READ_ONLY = true; +import { NumberReply, Command } from '../RESP/types'; -export function transformArguments(): Array { +export default { + IS_READ_ONLY: true, + transformArguments() { return ['DBSIZE']; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/DECR.ts b/packages/client/lib/commands/DECR.ts index 2b5f2c4bb5..540148b7ee 100644 --- a/packages/client/lib/commands/DECR.ts +++ b/packages/client/lib/commands/DECR.ts @@ -1,9 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument) { return ['DECR', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/DECRBY.ts b/packages/client/lib/commands/DECRBY.ts index afe4d79f0a..4a22aeb34c 100644 --- a/packages/client/lib/commands/DECRBY.ts +++ b/packages/client/lib/commands/DECRBY.ts @@ -1,12 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - decrement: number -): RedisCommandArguments { - return ['DECRBY', key, decrement.toString()]; -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, decrement: number) { + return ['DECR', key, decrement.toString()]; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/DEL.ts b/packages/client/lib/commands/DEL.ts index d60abe0f28..faba0712cd 100644 --- a/packages/client/lib/commands/DEL.ts +++ b/packages/client/lib/commands/DEL.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - keys: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['DEL'], keys); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + transformArguments(keys: RedisArgument | Array) { + return pushVariadicArguments(['DEL'], keys); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/DISCARD.ts b/packages/client/lib/commands/DISCARD.ts index acad8a722e..e153070c98 100644 --- a/packages/client/lib/commands/DISCARD.ts +++ b/packages/client/lib/commands/DISCARD.ts @@ -1,7 +1,8 @@ -import { RedisCommandArgument } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(): Array { +export default { + transformArguments() { return ['DISCARD']; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/DUMP.ts b/packages/client/lib/commands/DUMP.ts index fd4354db45..06b12f06d0 100644 --- a/packages/client/lib/commands/DUMP.ts +++ b/packages/client/lib/commands/DUMP.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['DUMP', key]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ECHO.ts b/packages/client/lib/commands/ECHO.ts index 7a837307e2..828ab0382e 100644 --- a/packages/client/lib/commands/ECHO.ts +++ b/packages/client/lib/commands/ECHO.ts @@ -1,9 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; -export const IS_READ_ONLY = true; - -export function transformArguments(message: RedisCommandArgument): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments(message: RedisArgument) { return ['ECHO', message]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/EXISTS.ts b/packages/client/lib/commands/EXISTS.ts index 3bbc72ada4..2dfa5963d5 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'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { pushVariadicArguments } 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; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(keys: RedisArgument | Array) { + return pushVariadicArguments(['EXISTS'], keys); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/EXPIRE.ts b/packages/client/lib/commands/EXPIRE.ts index d9e85595c3..3e57769bd6 100644 --- a/packages/client/lib/commands/EXPIRE.ts +++ b/packages/client/lib/commands/EXPIRE.ts @@ -1,19 +1,20 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, seconds: number, mode?: 'NX' | 'XX' | 'GT' | 'LT' -): RedisCommandArguments { + ) { const args = ['EXPIRE', key, seconds.toString()]; if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/EXPIREAT.ts b/packages/client/lib/commands/EXPIREAT.ts index 84e7760fe7..b9b5986cac 100644 --- a/packages/client/lib/commands/EXPIREAT.ts +++ b/packages/client/lib/commands/EXPIREAT.ts @@ -1,24 +1,21 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { transformEXAT } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, timestamp: number | Date, mode?: 'NX' | 'XX' | 'GT' | 'LT' -): RedisCommandArguments { - const args = [ - 'EXPIREAT', - key, - transformEXAT(timestamp) - ]; + ) { + const args = ['EXPIRE', key, transformEXAT(timestamp)]; if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/EXPIRETIME.ts b/packages/client/lib/commands/EXPIRETIME.ts index 8c1bb07599..d6ac35aeb3 100644 --- a/packages/client/lib/commands/EXPIRETIME.ts +++ b/packages/client/lib/commands/EXPIRETIME.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['EXPIRETIME', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FAILOVER.ts b/packages/client/lib/commands/FAILOVER.ts index c31dbc063d..0c1e710d32 100644 --- a/packages/client/lib/commands/FAILOVER.ts +++ b/packages/client/lib/commands/FAILOVER.ts @@ -1,33 +1,36 @@ +import { SimpleStringReply, Command } from '../RESP/types'; + interface FailoverOptions { - TO?: { - host: string; - port: number; - FORCE?: true; - }; - ABORT?: true; - TIMEOUT?: number; + TO?: { + host: string; + port: number; + FORCE?: true; + }; + ABORT?: true; + TIMEOUT?: number; } -export function transformArguments(options?: FailoverOptions): Array { +export default { + transformArguments(options?: FailoverOptions) { const args = ['FAILOVER']; if (options?.TO) { - args.push('TO', options.TO.host, options.TO.port.toString()); + args.push('TO', options.TO.host, options.TO.port.toString()); - if (options.TO.FORCE) { - args.push('FORCE'); - } + if (options.TO.FORCE) { + args.push('FORCE'); + } } if (options?.ABORT) { - args.push('ABORT'); + args.push('ABORT'); } if (options?.TIMEOUT) { - args.push('TIMEOUT', options.TIMEOUT.toString()); + args.push('TIMEOUT', options.TIMEOUT.toString()); } return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FLUSHALL.ts b/packages/client/lib/commands/FLUSHALL.ts index 967096bb9b..bc41e24e0d 100644 --- a/packages/client/lib/commands/FLUSHALL.ts +++ b/packages/client/lib/commands/FLUSHALL.ts @@ -1,16 +1,21 @@ -export enum RedisFlushModes { - ASYNC = 'ASYNC', - SYNC = 'SYNC' -} +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(mode?: RedisFlushModes): Array { +export const REDIS_FLUSH_MODES = { + ASYNC: 'ASYNC', + SYNC: 'SYNC' +} as const; + +export type RedisFlushModes = typeof REDIS_FLUSH_MODES[keyof typeof REDIS_FLUSH_MODES]; + +export default { + transformArguments(mode?: RedisFlushModes) { const args = ['FLUSHALL']; - + if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FLUSHDB.ts b/packages/client/lib/commands/FLUSHDB.ts index 5b8060df9e..3dade5c0c5 100644 --- a/packages/client/lib/commands/FLUSHDB.ts +++ b/packages/client/lib/commands/FLUSHDB.ts @@ -1,13 +1,15 @@ +import { SimpleStringReply, Command } from '../RESP/types'; import { RedisFlushModes } from './FLUSHALL'; -export function transformArguments(mode?: RedisFlushModes): Array { +export default { + transformArguments(mode?: RedisFlushModes) { const args = ['FLUSHDB']; - + if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_DELETE.ts b/packages/client/lib/commands/FUNCTION_DELETE.ts index 4aa6be40e1..b614c5a9ec 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 { + IS_READ_ONLY: true, + FIRST_KEY_INDEX: undefined, + transformArguments(library: RedisArgument) { return ['FUNCTION', 'DELETE', library]; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_DUMP.ts b/packages/client/lib/commands/FUNCTION_DUMP.ts index f608e078c2..cd53312caf 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 { + IS_READ_ONLY: true, + FIRST_KEY_INDEX: undefined, + transformArguments() { return ['FUNCTION', 'DUMP']; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_FLUSH.ts b/packages/client/lib/commands/FUNCTION_FLUSH.ts index 143282de97..b421181bf3 100644 --- a/packages/client/lib/commands/FUNCTION_FLUSH.ts +++ b/packages/client/lib/commands/FUNCTION_FLUSH.ts @@ -1,13 +1,17 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; +import { RedisFlushModes } from './FLUSHALL'; -export function transformArguments(mode?: 'ASYNC' | 'SYNC'): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + FIRST_KEY_INDEX: undefined, + transformArguments(mode?: RedisFlushModes) { const args = ['FUNCTION', 'FLUSH']; - + if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_KILL.ts b/packages/client/lib/commands/FUNCTION_KILL.ts index 517272e837..2e89746d59 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 { + IS_READ_ONLY: true, + FIRST_KEY_INDEX: undefined, + 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_STATS.ts b/packages/client/lib/commands/FUNCTION_STATS.ts index bb5c957e78..eacbcc19ad 100644 --- a/packages/client/lib/commands/FUNCTION_STATS.ts +++ b/packages/client/lib/commands/FUNCTION_STATS.ts @@ -1,56 +1,87 @@ -import { RedisCommandArguments } from '.'; +// import { RedisCommandArguments } from '.'; -export function transformArguments(): RedisCommandArguments { +// export function transformArguments(): RedisCommandArguments { +// return ['FUNCTION', 'STATS']; +// } + +// type FunctionStatsRawReply = [ +// 'running_script', +// null | [ +// 'name', +// string, +// 'command', +// string, +// 'duration_ms', +// number +// ], +// 'engines', +// Array // "flat tuples" (there is no way to type that) +// // ...[string, [ +// // 'libraries_count', +// // number, +// // 'functions_count', +// // number +// // ]] +// ]; + +// interface FunctionStatsReply { +// runningScript: null | { +// name: string; +// command: string; +// durationMs: number; +// }; +// engines: Record; +// } + +// export function transformReply(reply: FunctionStatsRawReply): FunctionStatsReply { +// const engines = Object.create(null); +// for (let i = 0; i < reply[3].length; i++) { +// engines[reply[3][i]] = { +// librariesCount: reply[3][++i][1], +// functionsCount: reply[3][i][3] +// }; +// } + +// return { +// runningScript: reply[1] === null ? null : { +// name: reply[1][1], +// command: reply[1][3], +// durationMs: reply[1][5] +// }, +// engines +// }; +// } + + +// #!LUA name=math \n redis.register_function{ function_name = "square", callback = function(keys, args) return args[1] * args[1] end, flags = { "no-writes" } } + +import { Command, TuplesToMapReply, BlobStringReply, NullReply, NumberReply, MapReply } from '../RESP/types'; + +type FunctionStatsReply = TuplesToMapReply<[ + [BlobStringReply<'running_script'>, NullReply | TuplesToMapReply<[ + [BlobStringReply<'name'>, BlobStringReply], + [BlobStringReply<'command'>, BlobStringReply], + [BlobStringReply<'duration_ms'>, NumberReply] + ]>], + [BlobStringReply<'engines'>, MapReply, NumberReply], + [BlobStringReply<'functions_count'>, NumberReply] + ]>>] +]>; + +export default { + IS_READ_ONLY: true, + FIRST_KEY_INDEX: undefined, + transformArguments() { return ['FUNCTION', 'STATS']; -} + }, + transformReply: { + 2: (reply) => { -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 - }; -} + }, + 3: undefined as unknown as () => + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/GEOHASH.ts b/packages/client/lib/commands/GEOHASH.ts index 55e22c497e..12bc9368eb 100644 --- a/packages/client/lib/commands/GEOHASH.ts +++ b/packages/client/lib/commands/GEOHASH.ts @@ -1,5 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { pushVariadicArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -9,7 +9,7 @@ export function transformArguments( key: RedisCommandArgument, member: RedisCommandArgument | Array ): RedisCommandArguments { - return pushVerdictArguments(['GEOHASH', key], member); + return pushVariadicArguments(['GEOHASH', key], member); } export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/GEOPOS.ts b/packages/client/lib/commands/GEOPOS.ts index 0a5f079dee..c2e0056155 100644 --- a/packages/client/lib/commands/GEOPOS.ts +++ b/packages/client/lib/commands/GEOPOS.ts @@ -1,5 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { pushVariadicArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -9,7 +9,7 @@ export function transformArguments( key: RedisCommandArgument, member: RedisCommandArgument | Array ): RedisCommandArguments { - return pushVerdictArguments(['GEOPOS', key], member); + return pushVariadicArguments(['GEOPOS', key], member); } type GeoCoordinatesRawReply = Array<[RedisCommandArgument, RedisCommandArgument] | null>; diff --git a/packages/client/lib/commands/GET.ts b/packages/client/lib/commands/GET.ts index 127b0a5634..bb3db4f76d 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/GETDEL.ts b/packages/client/lib/commands/GETDEL.ts index 2d91e6cc02..c11fd047df 100644 --- a/packages/client/lib/commands/GETDEL.ts +++ b/packages/client/lib/commands/GETDEL.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['GETDEL', key]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GETEX.ts b/packages/client/lib/commands/GETEX.ts index 5b3cec6d88..201a888208 100644 --- a/packages/client/lib/commands/GETEX.ts +++ b/packages/client/lib/commands/GETEX.ts @@ -1,39 +1,37 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; import { transformEXAT, transformPXAT } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -type GetExModes = { - EX: number; +export type GetExModes = { + EX: number; } | { - PX: number; + PX: number; } | { - EXAT: number | Date; + EXAT: number | Date; } | { - PXAT: number | Date; + PXAT: number | Date; } | { - PERSIST: true; + PERSIST: true; }; -export function transformArguments( - key: RedisCommandArgument, - mode: GetExModes -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, mode: GetExModes) { const args = ['GETEX', key]; if ('EX' in mode) { - args.push('EX', mode.EX.toString()); + args.push('EX', mode.EX.toString()); } else if ('PX' in mode) { - args.push('PX', mode.PX.toString()); + args.push('PX', mode.PX.toString()); } else if ('EXAT' in mode) { - args.push('EXAT', transformEXAT(mode.EXAT)); + args.push('EXAT', transformEXAT(mode.EXAT)); } else if ('PXAT' in mode) { - args.push('PXAT', transformPXAT(mode.PXAT)); + args.push('PXAT', transformPXAT(mode.PXAT)); } else { // PERSIST - args.push('PERSIST'); + args.push('PERSIST'); } return args; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GETRANGE.ts b/packages/client/lib/commands/GETRANGE.ts index 2d12d937cc..e5357cd120 100644 --- a/packages/client/lib/commands/GETRANGE.ts +++ b/packages/client/lib/commands/GETRANGE.ts @@ -1,15 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - start: number, - end: number -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, start: number, end: number) { return ['GETRANGE', key, start.toString(), end.toString()]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GETSET.ts b/packages/client/lib/commands/GETSET.ts index 87d111792c..bbe920181b 100644 --- a/packages/client/lib/commands/GETSET.ts +++ b/packages/client/lib/commands/GETSET.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - value: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, value: RedisArgument) { return ['GETSET', key, value]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HDEL.ts b/packages/client/lib/commands/HDEL.ts index 1a994e109d..64aa55edda 100644 --- a/packages/client/lib/commands/HDEL.ts +++ b/packages/client/lib/commands/HDEL.ts @@ -1,13 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - field: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['HDEL', key], field); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, field: RedisVariadicArgument) { + return pushVariadicArguments(['HDEL', key], field); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HELLO.ts b/packages/client/lib/commands/HELLO.ts index d943f2e4c3..d4784b67c4 100644 --- a/packages/client/lib/commands/HELLO.ts +++ b/packages/client/lib/commands/HELLO.ts @@ -1,65 +1,54 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { AuthOptions } from './AUTH'; +import { RedisArgument, ArrayReply, BlobStringReply, Command, NumberReply, Resp2Reply, RespVersions, TuplesToMapReply } from '../RESP/types'; -interface HelloOptions { - protover: number; - auth?: Required; - clientName?: string; +export interface HelloOptions { + AUTH?: { + username: RedisArgument; + password: RedisArgument; + }; + SETNAME?: string; } -export function transformArguments(options?: HelloOptions): RedisCommandArguments { - const args: RedisCommandArguments = ['HELLO']; +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] +]>; - if (options) { - args.push(options.protover.toString()); +export default { + transformArguments(protoover: RespVersions, options?: HelloOptions) { + const args: Array = ['HELLO', protoover.toString()]; - if (options.auth) { - args.push('AUTH', options.auth.username, options.auth.password); - } - - if (options.clientName) { - args.push('SETNAME', options.clientName); - } + if (options?.AUTH) { + args.push( + 'AUTH', + options.AUTH.username, + options.AUTH.password + ); } + if (options?.SETNAME) { + args.push( + 'SETNAME', + options.SETNAME + ); + } + return args; -} - -type HelloRawReply = [ - _: never, - server: RedisCommandArgument, - _: never, - version: RedisCommandArgument, - _: never, - proto: number, - _: never, - id: number, - _: never, - mode: RedisCommandArgument, - _: never, - role: RedisCommandArgument, - _: never, - modules: Array -]; - -interface HelloTransformedReply { - server: RedisCommandArgument; - version: RedisCommandArgument; - proto: number; - id: number; - mode: RedisCommandArgument; - role: RedisCommandArgument; - modules: Array; -} - -export function transformReply(reply: HelloRawReply): HelloTransformedReply { - return { - server: reply[1], - version: reply[3], - proto: reply[5], - id: reply[7], - mode: reply[9], - role: reply[11], - modules: reply[13] - }; -} + }, + transformReply: { + 2: (reply: Resp2Reply) => ({ + server: reply[1], + version: reply[3], + proto: reply[5], + id: reply[7], + mode: reply[9], + role: reply[11], + modules: reply[13] + }), + 3: undefined as unknown as () => HelloReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/HEXISTS.ts b/packages/client/lib/commands/HEXISTS.ts index 289be20aa8..dc7e937ee7 100644 --- a/packages/client/lib/commands/HEXISTS.ts +++ b/packages/client/lib/commands/HEXISTS.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - field: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, field: RedisArgument) { return ['HEXISTS', key, field]; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply<0 | 1> +} as const satisfies Command; diff --git a/packages/client/lib/commands/HGET.ts b/packages/client/lib/commands/HGET.ts index fcfd31e617..d83f84e24f 100644 --- a/packages/client/lib/commands/HGET.ts +++ b/packages/client/lib/commands/HGET.ts @@ -1,14 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - field: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, field: RedisArgument) { return ['HGET', key, field]; -} - -export declare function transformReply(): RedisCommandArgument | undefined; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HGETALL.ts b/packages/client/lib/commands/HGETALL.ts index bf51760ff0..f1f0ac50bc 100644 --- a/packages/client/lib/commands/HGETALL.ts +++ b/packages/client/lib/commands/HGETALL.ts @@ -1,13 +1,15 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, MapReply, BlobStringReply, Command } from '../RESP/types'; +import { transformTuplesReply } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export const TRANSFORM_LEGACY_REPLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['HGETALL', key]; -} - -export { transformTuplesReply as transformReply } from './generic-transformers'; + }, + TRANSFORM_LEGACY_REPLY: true, + transformReply: { + 2: transformTuplesReply, + 3: undefined as unknown as () => MapReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/HINCRBY.ts b/packages/client/lib/commands/HINCRBY.ts index b2cf6eefe8..cb7f62ebef 100644 --- a/packages/client/lib/commands/HINCRBY.ts +++ b/packages/client/lib/commands/HINCRBY.ts @@ -1,13 +1,18 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - field: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, + field: RedisArgument, increment: number -): RedisCommandArguments { - return ['HINCRBY', key, field, increment.toString()]; -} - -export declare function transformReply(): number; + ) { + return [ + 'HINCRBY', + key, + field, + increment.toString() + ]; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HINCRBYFLOAT.ts b/packages/client/lib/commands/HINCRBYFLOAT.ts index 0e2de6e9b2..a4eea75c82 100644 --- a/packages/client/lib/commands/HINCRBYFLOAT.ts +++ b/packages/client/lib/commands/HINCRBYFLOAT.ts @@ -1,13 +1,18 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - field: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, + field: RedisArgument, increment: number -): RedisCommandArguments { - return ['HINCRBYFLOAT', key, field, increment.toString()]; -} - -export declare function transformReply(): number; + ) { + return [ + 'HINCRBYFLOAT', + key, + field, + increment.toString() + ]; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HKEYS.ts b/packages/client/lib/commands/HKEYS.ts index 3d629733d0..00af43f7a4 100644 --- a/packages/client/lib/commands/HKEYS.ts +++ b/packages/client/lib/commands/HKEYS.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['HKEYS', key]; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HLEN.ts b/packages/client/lib/commands/HLEN.ts index 15a93d408d..8f156d303e 100644 --- a/packages/client/lib/commands/HLEN.ts +++ b/packages/client/lib/commands/HLEN.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['HLEN', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HMGET.ts b/packages/client/lib/commands/HMGET.ts index 64b4014abe..1c4acafdee 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'; +import { RedisArgument, ArrayReply, BlobStringReply, NullReply, 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, - fields: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['HMGET', key], fields); -} - -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + fields: RedisVariadicArgument + ) { + return pushVariadicArguments(['HMGET'], fields); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HRANDFIELD.ts b/packages/client/lib/commands/HRANDFIELD.ts index a2c70aabd5..be878e244a 100644 --- a/packages/client/lib/commands/HRANDFIELD.ts +++ b/packages/client/lib/commands/HRANDFIELD.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['HRANDFIELD', key]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HRANDFIELD_COUNT.ts b/packages/client/lib/commands/HRANDFIELD_COUNT.ts index 01b8df6327..4b6f42a115 100644 --- a/packages/client/lib/commands/HRANDFIELD_COUNT.ts +++ b/packages/client/lib/commands/HRANDFIELD_COUNT.ts @@ -1,16 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformArguments as transformHRandFieldArguments } from './HRANDFIELD'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './HRANDFIELD'; - -export function transformArguments( - key: RedisCommandArgument, - count: number -): RedisCommandArguments { - return [ - ...transformHRandFieldArguments(key), - count.toString() - ]; -} - -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, count: number) { + return ['HRANDFIELD', key, count.toString()]; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts deleted file mode 100644 index c4e6409a72..0000000000 --- a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HRANDFIELD_COUNT_WITHVALUES'; - -describe('HRANDFIELD COUNT WITHVALUES', () => { - testUtils.isVersionGreaterThanHook([6, 2, 5]); - - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1), - ['HRANDFIELD', 'key', '1', 'WITHVALUES'] - ); - }); - - testUtils.testWithClient('client.hRandFieldCountWithValues', async client => { - assert.deepEqual( - await client.hRandFieldCountWithValues('key', 1), - Object.create(null) - ); - }, GLOBAL.SERVERS.OPEN); -}); diff --git a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts index 3e09dbb9a1..2c7b17d576 100644 --- a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts +++ b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts @@ -1,16 +1,36 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformArguments as transformHRandFieldCountArguments } from './HRANDFIELD_COUNT'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './HRANDFIELD_COUNT'; +export type HRandFieldCountWithValuesReply = Array<{ + field: BlobStringReply; + value: BlobStringReply; +}>; -export function transformArguments( - key: RedisCommandArgument, - count: number -): RedisCommandArguments { - return [ - ...transformHRandFieldCountArguments(key, count), - 'WITHVALUES' - ]; -} +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, count: number) { + return ['HRANDFIELD', key, count.toString(), 'WITHVALUES']; + }, + transformReply: { + 2: (rawReply: ArrayReply) => { + const reply: HRandFieldCountWithValuesReply = []; -export { transformTuplesReply as transformReply } from './generic-transformers'; + let i = 0; + while (i < rawReply.length) { + reply.push({ + field: rawReply[i++], + value: rawReply[i++] + }); + } + + return reply; + }, + 3: (reply: ArrayReply<[BlobStringReply, BlobStringReply]>) => { + return reply.map(([field, value]) => ({ + field, + value + })) satisfies HRandFieldCountWithValuesReply; + } + } +} as const satisfies Command; + \ No newline at end of file diff --git a/packages/client/lib/commands/HSCAN.ts b/packages/client/lib/commands/HSCAN.ts index ba18fb986b..a23063e34f 100644 --- a/packages/client/lib/commands/HSCAN.ts +++ b/packages/client/lib/commands/HSCAN.ts @@ -1,44 +1,34 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; import { ScanOptions, pushScanArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +export interface HScanEntry { + field: BlobStringReply; + value: BlobStringReply; +} -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, cursor: number, options?: ScanOptions -): RedisCommandArguments { - return pushScanArguments([ - 'HSCAN', - key - ], cursor, options); -} - -type HScanRawReply = [RedisCommandArgument, Array]; - -export interface HScanTuple { - field: RedisCommandArgument; - value: RedisCommandArgument; -} - -interface HScanReply { - cursor: number; - tuples: Array; -} - -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] - }); + ) { + return pushScanArguments(['HSCAN', key], cursor, options); + }, + transformReply([cursor, rawEntries]: [BlobStringReply, Array]) { + const entries = []; + let i = 0; + while (i < rawEntries.length) { + entries.push({ + field: rawEntries[i++], + value: rawEntries[i++] + } satisfies HScanEntry); } return { - cursor: Number(cursor), - tuples: parsedTuples + cursor: Number(cursor), + entries }; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/HSET.ts b/packages/client/lib/commands/HSET.ts index 261ef98c77..7cb98c827b 100644 --- a/packages/client/lib/commands/HSET.ts +++ b/packages/client/lib/commands/HSET.ts @@ -1,73 +1,75 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; +export type HashTypes = RedisArgument | number; -type Types = RedisCommandArgument | number; +type HSETObject = Record; -type HSETObject = Record; +type HSETMap = Map; -type HSETMap = Map; +type HSETTuples = Array<[HashTypes, HashTypes]> | Array; -type HSETTuples = Array<[Types, Types]> | Array; +type GenericArguments = [key: RedisArgument]; -type GenericArguments = [key: RedisCommandArgument]; - -type SingleFieldArguments = [...generic: GenericArguments, field: Types, value: Types]; +type SingleFieldArguments = [...generic: GenericArguments, field: HashTypes, value: HashTypes]; type MultipleFieldsArguments = [...generic: GenericArguments, value: HSETObject | HSETMap | HSETTuples]; -export function transformArguments(...[ key, value, fieldValue ]: SingleFieldArguments | MultipleFieldsArguments): RedisCommandArguments { - const args: RedisCommandArguments = ['HSET', key]; +export type HSETArguments = SingleFieldArguments | MultipleFieldsArguments; + +export default { + FIRST_KEY_INDEX: 1, + transformArguments(...[key, value, fieldValue]: SingleFieldArguments | MultipleFieldsArguments) { + const args: Array = ['HSET', key]; if (typeof value === 'string' || typeof value === 'number' || Buffer.isBuffer(value)) { - args.push( - convertValue(value), - convertValue(fieldValue!) - ); + args.push( + convertValue(value), + convertValue(fieldValue!) + ); } else if (value instanceof Map) { - pushMap(args, value); + pushMap(args, value); } else if (Array.isArray(value)) { - pushTuples(args, value); + pushTuples(args, value); } else { - pushObject(args, value); + pushObject(args, value); } return args; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; + +function pushMap(args: Array, map: HSETMap): void { + for (const [key, value] of map.entries()) { + args.push( + convertValue(key), + convertValue(value) + ); + } } -function pushMap(args: RedisCommandArguments, map: HSETMap): void { - for (const [key, value] of map.entries()) { - args.push( - convertValue(key), - convertValue(value) - ); +function pushTuples(args: Array, tuples: HSETTuples): void { + for (const tuple of tuples) { + if (Array.isArray(tuple)) { + pushTuples(args, tuple); + continue; } + + args.push(convertValue(tuple)); + } } -function pushTuples(args: RedisCommandArguments, tuples: HSETTuples): void { - for (const tuple of tuples) { - if (Array.isArray(tuple)) { - pushTuples(args, tuple); - continue; - } - - args.push(convertValue(tuple)); - } +function pushObject(args: Array, object: HSETObject): void { + for (const key of Object.keys(object)) { + args.push( + convertValue(key), + convertValue(object[key]) + ); + } } -function pushObject(args: RedisCommandArguments, object: HSETObject): void { - for (const key of Object.keys(object)) { - args.push( - convertValue(key), - convertValue(object[key]) - ); - } +function convertValue(value: HashTypes): RedisArgument { + return typeof value === 'number' ? + value.toString() : + value; } - -function convertValue(value: Types): RedisCommandArgument { - return typeof value === 'number' ? - value.toString() : - value; -} - -export declare function transformReply(): number; diff --git a/packages/client/lib/commands/HSETNX.ts b/packages/client/lib/commands/HSETNX.ts index 9ac6ef0edd..d26c42a76b 100644 --- a/packages/client/lib/commands/HSETNX.ts +++ b/packages/client/lib/commands/HSETNX.ts @@ -1,13 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, Command, NumberReply } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - field: RedisCommandArgument, - value: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + field: RedisArgument, + value: RedisArgument + ) { return ['HSETNX', key, field, value]; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply<0 | 1> +} as const satisfies Command; diff --git a/packages/client/lib/commands/HSTRLEN.ts b/packages/client/lib/commands/HSTRLEN.ts index a820e6c564..61e2e1b1ac 100644 --- a/packages/client/lib/commands/HSTRLEN.ts +++ b/packages/client/lib/commands/HSTRLEN.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - field: RedisCommandArgument -): RedisCommandArguments { - return ['HSTRLEN', key, field]; -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, field: RedisArgument) { + return ['HSETLEN', key, field]; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HVALS.ts b/packages/client/lib/commands/HVALS.ts index ef63fdc7f8..ee4193f5ce 100644 --- a/packages/client/lib/commands/HVALS.ts +++ b/packages/client/lib/commands/HVALS.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['HVALS', key]; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/INCR.ts b/packages/client/lib/commands/INCR.ts index 2f9a9adfe2..eb38256c9d 100644 --- a/packages/client/lib/commands/INCR.ts +++ b/packages/client/lib/commands/INCR.ts @@ -1,9 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument) { return ['INCR', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/INCRBY.ts b/packages/client/lib/commands/INCRBY.ts index 75c61156d6..5e94348fe6 100644 --- a/packages/client/lib/commands/INCRBY.ts +++ b/packages/client/lib/commands/INCRBY.ts @@ -1,12 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - increment: number -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, increment: number) { return ['INCRBY', key, increment.toString()]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/INCRBYFLOAT.ts b/packages/client/lib/commands/INCRBYFLOAT.ts index ace3702339..16f59e68c1 100644 --- a/packages/client/lib/commands/INCRBYFLOAT.ts +++ b/packages/client/lib/commands/INCRBYFLOAT.ts @@ -1,12 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - increment: number -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, increment: number) { return ['INCRBYFLOAT', key, increment.toString()]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/INFO.ts b/packages/client/lib/commands/INFO.ts index 8ab24221b2..3adc3efd33 100644 --- a/packages/client/lib/commands/INFO.ts +++ b/packages/client/lib/commands/INFO.ts @@ -1,13 +1,15 @@ -export const IS_READ_ONLY = true; +import { RedisArgument, VerbatimStringReply, Command } from '../RESP/types'; -export function transformArguments(section?: string): Array { - const args = ['INFO']; +export default { + IS_READ_ONLY: true, + transformArguments(section?: RedisArgument) { + const args: Array = ['INFO']; if (section) { - args.push(section); + args.push(section); } return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => VerbatimStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/KEYS.ts b/packages/client/lib/commands/KEYS.ts index c96ee00143..507681a56a 100644 --- a/packages/client/lib/commands/KEYS.ts +++ b/packages/client/lib/commands/KEYS.ts @@ -1,7 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export function transformArguments(pattern: RedisCommandArgument): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments(pattern: RedisArgument) { return ['KEYS', pattern]; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LCS.ts b/packages/client/lib/commands/LCS.ts index b075b73e8a..b798f12aa3 100644 --- a/packages/client/lib/commands/LCS.ts +++ b/packages/client/lib/commands/LCS.ts @@ -1,18 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; -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; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key1: RedisArgument, + key2: RedisArgument + ) { + return ['LCS', key1, key2]; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LCS_IDX.ts b/packages/client/lib/commands/LCS_IDX.ts index 262a02ba4c..b996183057 100644 --- a/packages/client/lib/commands/LCS_IDX.ts +++ b/packages/client/lib/commands/LCS_IDX.ts @@ -1,42 +1,48 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { RangeReply, RawRangeReply, transformRangeReply } from './generic-transformers'; -import { transformArguments as transformLcsArguments } from './LCS'; +// import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, NumberReply, Resp2Reply, Command, TuplesReply } from '../RESP/types'; +// import LCS from './LCS'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './LCS'; +// export interface LcsIdxOptions { +// MINMATCHLEN?: number; +// } -export function transformArguments( - key1: RedisCommandArgument, - key2: RedisCommandArgument -): RedisCommandArguments { - const args = transformLcsArguments(key1, key2); - args.push('IDX'); - return args; -} +// export type LcsIdxRange = TuplesReply<[ +// start: NumberReply, +// end: NumberReply +// ]>; -type RawReply = [ - 'matches', - Array<[ - key1: RawRangeReply, - key2: RawRangeReply - ]>, - 'len', - number -]; +// export type LcsIdxMatches = ArrayReply< +// TuplesReply<[ +// key1: LcsIdxRange, +// key2: LcsIdxRange +// ]> +// >; -interface Reply { - matches: Array<{ - key1: RangeReply; - key2: RangeReply; - }>; - length: number; -} +// export type LcsIdxReply = TuplesToMapReply<[ +// [BlobStringReply<'matches'>, LcsIdxMatches], +// [BlobStringReply<'len'>, NumberReply] +// ]>; -export function transformReply(reply: RawReply): Reply { - return { - matches: reply[1].map(([key1, key2]) => ({ - key1: transformRangeReply(key1), - key2: transformRangeReply(key2) - })), - length: reply[3] - }; -} +// export default { +// FIRST_KEY_INDEX: LCS.FIRST_KEY_INDEX, +// IS_READ_ONLY: LCS.IS_READ_ONLY, +// transformArguments( +// key1: RedisArgument, +// key2: RedisArgument, +// options?: LcsIdxOptions +// ) { +// const args = LCS.transformArguments(key1, key2); + +// if (options?.MINMATCHLEN) { +// args.push('MINMATCHLEN', options.MINMATCHLEN.toString()); +// } + +// return args; +// }, +// transformReply: { +// 2: (reply: Resp2Reply) => ({ +// matches: reply[1], +// len: reply[2] +// }), +// 3: undefined as unknown as () => LcsIdxReply +// } +// } as const satisfies Command; diff --git a/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts b/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts index 989870d6ca..2b6a87672a 100644 --- a/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts +++ b/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts @@ -1,45 +1,47 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { RangeReply, RawRangeReply, transformRangeReply } from './generic-transformers'; -import { transformArguments as transformLcsArguments } from './LCS'; +// import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, NumberReply, Resp2Reply, Command, TuplesReply } from '../RESP/types'; +// import LCS from './LCS'; +// import { LcsIdxRange } from './LCS_IDX'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './LCS'; +// interface LcsIdxOptions { +// MINMATCHLEN?: number; +// } -export function transformArguments( - key1: RedisCommandArgument, - key2: RedisCommandArgument -): RedisCommandArguments { - const args = transformLcsArguments(key1, key2); - args.push('IDX', 'WITHMATCHLEN'); - return args; -} +// export type LcsIdxWithMatchLenMatches = ArrayReply< +// TuplesReply<[ +// key1: LcsIdxRange, +// key2: LcsIdxRange, +// len: NumberReply +// ]> +// >; -type RawReply = [ - 'matches', - Array<[ - key1: RawRangeReply, - key2: RawRangeReply, - length: number - ]>, - 'len', - number -]; +// export type LcsIdxReply = TuplesToMapReply<[ +// [BlobStringReply<'matches'>, LcsIdxWithMatchLenMatches], +// [BlobStringReply<'len'>, NumberReply] +// ]>; -interface Reply { - matches: Array<{ - key1: RangeReply; - key2: RangeReply; - length: number; - }>; - length: number; -} +// export default { +// FIRST_KEY_INDEX: LCS.FIRST_KEY_INDEX, +// IS_READ_ONLY: LCS.IS_READ_ONLY, +// transformArguments( +// key1: RedisArgument, +// key2: RedisArgument, +// options?: LcsIdxOptions +// ) { +// const args = LCS.transformArguments(key1, key2); -export function transformReply(reply: RawReply): Reply { - return { - matches: reply[1].map(([key1, key2, length]) => ({ - key1: transformRangeReply(key1), - key2: transformRangeReply(key2), - length - })), - length: reply[3] - }; -} +// if (options?.MINMATCHLEN) { +// args.push('MINMATCHLEN', options.MINMATCHLEN.toString()); +// } + +// args.push('WITHMATCHLEN'); + +// return args; +// }, +// transformReply: { +// 2: (reply: Resp2Reply) => ({ +// matches: reply[1], +// len: reply[2] +// }), +// 3: undefined as unknown as () => LcsIdxReply +// } +// } as const satisfies Command; diff --git a/packages/client/lib/commands/LCS_LEN.ts b/packages/client/lib/commands/LCS_LEN.ts index a5121e4c13..d5d0e77e4d 100644 --- a/packages/client/lib/commands/LCS_LEN.ts +++ b/packages/client/lib/commands/LCS_LEN.ts @@ -1,15 +1,16 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformArguments as transformLcsArguments } from './LCS'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import LCS from './LCS'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './LCS'; - -export function transformArguments( - key1: RedisCommandArgument, - key2: RedisCommandArgument -): RedisCommandArguments { - const args = transformLcsArguments(key1, key2); +export default { + FIRST_KEY_INDEX: LCS.FIRST_KEY_INDEX, + IS_READ_ONLY: LCS.IS_READ_ONLY, + transformArguments( + key1: RedisArgument, + key2: RedisArgument + ) { + const args = LCS.transformArguments(key1, key2); args.push('LEN'); return args; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LINDEX.ts b/packages/client/lib/commands/LINDEX.ts index 8e74ad8aae..0478bf9dc4 100644 --- a/packages/client/lib/commands/LINDEX.ts +++ b/packages/client/lib/commands/LINDEX.ts @@ -1,14 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - index: number -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, index: number) { return ['LINDEX', key, index.toString()]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LINSERT.ts b/packages/client/lib/commands/LINSERT.ts index 0a8e1f32ba..4bdc77de5a 100644 --- a/packages/client/lib/commands/LINSERT.ts +++ b/packages/client/lib/commands/LINSERT.ts @@ -1,22 +1,23 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; - -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; type LInsertPosition = 'BEFORE' | 'AFTER'; -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, position: LInsertPosition, - pivot: RedisCommandArgument, - element: RedisCommandArgument -): RedisCommandArguments { + pivot: RedisArgument, + element: RedisArgument + ) { return [ - 'LINSERT', - key, - position, - pivot, - element + 'LINSERT', + key, + position, + pivot, + element ]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LLEN.ts b/packages/client/lib/commands/LLEN.ts index 3410e57d42..dda59ddf29 100644 --- a/packages/client/lib/commands/LLEN.ts +++ b/packages/client/lib/commands/LLEN.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['LLEN', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LMOVE.ts b/packages/client/lib/commands/LMOVE.ts index 849c6385f5..3ec118b990 100644 --- a/packages/client/lib/commands/LMOVE.ts +++ b/packages/client/lib/commands/LMOVE.ts @@ -1,21 +1,21 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; import { ListSide } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - source: RedisCommandArgument, - destination: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + source: RedisArgument, + destination: RedisArgument, sourceSide: ListSide, destinationSide: ListSide -): RedisCommandArguments { + ) { return [ - 'LMOVE', - source, - destination, - sourceSide, - destinationSide, + 'LMOVE', + source, + destination, + sourceSide, + destinationSide, ]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LMPOP.ts b/packages/client/lib/commands/LMPOP.ts index 29d868b982..d52245251b 100644 --- a/packages/client/lib/commands/LMPOP.ts +++ b/packages/client/lib/commands/LMPOP.ts @@ -1,22 +1,22 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NullReply, TuplesReply, BlobStringReply, Command } from '../RESP/types'; import { transformLMPopArguments, LMPopOptions, ListSide } from './generic-transformers'; -export const FIRST_KEY_INDEX = 2; - -export function transformArguments( - keys: RedisCommandArgument | Array, +export default { + FIRST_KEY_INDEX: 2, + transformArguments( + keys: RedisArgument | Array, side: ListSide, options?: LMPopOptions -): RedisCommandArguments { + ) { return transformLMPopArguments( - ['LMPOP'], - keys, - side, - options + ['LMPOP'], + keys, + side, + options ); -} - -export declare function transformReply(): null | [ - key: string, - elements: Array -]; + }, + transformReply: undefined as unknown as () => NullReply | TuplesReply<[ + key: BlobStringReply, + elements: Array + ]> +} as const satisfies Command; diff --git a/packages/client/lib/commands/LOLWUT.ts b/packages/client/lib/commands/LOLWUT.ts index 5d5fc72606..8536711d32 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 { + IS_READ_ONLY: true, + FIRST_KEY_INDEX: undefined, + 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.ts b/packages/client/lib/commands/LPOP.ts index 5dd1bea519..7c85c30f9a 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.ts b/packages/client/lib/commands/LPOP_COUNT.ts index 021517b018..6cbc8564ab 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 { + IS_READ_ONLY: false, + FIRST_KEY_INDEX: 2, + 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.ts b/packages/client/lib/commands/LPOS.ts index 1f2e34ab88..047d80d7c2 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.ts b/packages/client/lib/commands/LPOS_COUNT.ts index 0549df82db..91bf602f39 100644 --- a/packages/client/lib/commands/LPOS_COUNT.ts +++ b/packages/client/lib/commands/LPOS_COUNT.ts @@ -1,27 +1,28 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { LPosOptions } from './LPOS'; +import { RedisArgument, ArrayReply, NumberReply, Command } from '../RESP/types'; +import LPOS, { LPosOptions } from './LPOS'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './LPOS'; - -export function transformArguments( - key: RedisCommandArgument, - element: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: LPOS.FIRST_KEY_INDEX, + IS_READ_ONLY: LPOS.IS_READ_ONLY, + transformArguments( + key: RedisArgument, + element: RedisArgument, count: number, options?: LPosOptions -): RedisCommandArguments { + ) { const args = ['LPOS', key, element]; if (typeof options?.RANK === 'number') { - args.push('RANK', options.RANK.toString()); + args.push('RANK', options.RANK.toString()); } args.push('COUNT', count.toString()); if (typeof options?.MAXLEN === 'number') { - args.push('MAXLEN', options.MAXLEN.toString()); + args.push('MAXLEN', options.MAXLEN.toString()); } return args; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LPUSH.ts b/packages/client/lib/commands/LPUSH.ts index 7144b146e2..72d4d7b676 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 { 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: RedisArgument | Array) { + return pushVariadicArguments(['LPUSH', key], elements); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LPUSHX.ts b/packages/client/lib/commands/LPUSHX.ts index 0b518add6d..1265c56a26 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 { 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: RedisArgument | Array) { + return pushVariadicArguments(['LPUSHX', key], elements); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LRANGE.ts b/packages/client/lib/commands/LRANGE.ts index df12c57d80..4b4b3488ba 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.ts b/packages/client/lib/commands/LREM.ts index b495133488..dbd2002be8 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.ts b/packages/client/lib/commands/LSET.ts index 33c7b4cc06..3c5b786746 100644 --- a/packages/client/lib/commands/LSET.ts +++ b/packages/client/lib/commands/LSET.ts @@ -1,18 +1,19 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, index: number, - element: RedisCommandArgument -): RedisCommandArguments { + element: RedisArgument + ) { return [ - 'LSET', - key, - index.toString(), - element + 'LREM', + key, + index.toString(), + element ]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LTRIM.ts b/packages/client/lib/commands/LTRIM.ts index 668497cdde..d4c6951316 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 { + stop: RedisArgument + ) { return [ - 'LTRIM', - key, - start.toString(), - stop.toString() + 'LREM', + key, + start.toString(), + stop.toString() ]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/MGET.ts b/packages/client/lib/commands/MGET.ts index 6635a2ca20..0f0f9e52ff 100644 --- a/packages/client/lib/commands/MGET.ts +++ b/packages/client/lib/commands/MGET.ts @@ -1,13 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - keys: Array -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(keys: Array) { return ['MGET', ...keys]; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => Array +} as const satisfies Command; diff --git a/packages/client/lib/commands/MIGRATE.ts b/packages/client/lib/commands/MIGRATE.ts index aaff316408..adb1ae8d19 100644 --- a/packages/client/lib/commands/MIGRATE.ts +++ b/packages/client/lib/commands/MIGRATE.ts @@ -1,65 +1,66 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; import { AuthOptions } from './AUTH'; interface MigrateOptions { - COPY?: true; - REPLACE?: true; - AUTH?: AuthOptions; + COPY?: true; + REPLACE?: true; + AUTH?: AuthOptions; } -export function transformArguments( - host: RedisCommandArgument, +export default { + transformArguments( + host: RedisArgument, port: number, - key: RedisCommandArgument | Array, + key: RedisArgument | Array, destinationDb: number, timeout: number, options?: MigrateOptions -): RedisCommandArguments { + ) { const args = ['MIGRATE', host, port.toString()], - isKeyArray = Array.isArray(key); - + isKeyArray = Array.isArray(key); + if (isKeyArray) { - args.push(''); + args.push(''); } else { - args.push(key); + args.push(key); } - + args.push( - destinationDb.toString(), - timeout.toString() + destinationDb.toString(), + timeout.toString() ); - + if (options?.COPY) { - args.push('COPY'); + args.push('COPY'); } - + if (options?.REPLACE) { - args.push('REPLACE'); + args.push('REPLACE'); } - + if (options?.AUTH) { - if (options.AUTH.username) { - args.push( - 'AUTH2', - options.AUTH.username, - options.AUTH.password - ); - } else { - args.push( - 'AUTH', - options.AUTH.password - ); - } - } - - if (isKeyArray) { + if (options.AUTH.username) { args.push( - 'KEYS', - ...key + 'AUTH2', + options.AUTH.username, + options.AUTH.password ); + } else { + args.push( + 'AUTH', + options.AUTH.password + ); + } } - + + if (isKeyArray) { + args.push( + 'KEYS', + ...key + ); + } + return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/MODULE_LIST.ts b/packages/client/lib/commands/MODULE_LIST.ts index d75b242830..56a6109528 100644 --- a/packages/client/lib/commands/MODULE_LIST.ts +++ b/packages/client/lib/commands/MODULE_LIST.ts @@ -1,5 +1,23 @@ -export function transformArguments(): Array { - return ['MODULE', 'LIST']; -} +import { ArrayReply, BlobStringReply, NumberReply, Command, Resp2Reply, TuplesToMapReply } from '../RESP/types'; -export declare function transformReply(): string; +export type ModuleListReply = ArrayReply, BlobStringReply], + [BlobStringReply<'version'>, NumberReply], +]>>; + +export default { + IS_READ_ONLY: true, + FIRST_KEY_INDEX: undefined, + transformArguments() { + return ['MODULE', 'LIST']; + }, + transformReply: { + 2: (reply: Resp2Reply) => { + return reply.map(module => ({ + name: module[1], + version: module[3] + })); + }, + 3: undefined as unknown as () => ModuleListReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/MODULE_LOAD.ts b/packages/client/lib/commands/MODULE_LOAD.ts index b44b4b57ce..ed0520adf4 100644 --- a/packages/client/lib/commands/MODULE_LOAD.ts +++ b/packages/client/lib/commands/MODULE_LOAD.ts @@ -1,11 +1,11 @@ -export function transformArguments(path: string, moduleArgs?: Array): Array { - const args = ['MODULE', 'LOAD', path]; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; +import { pushVariadicArguments } from './generic-transformers'; - if (moduleArgs) { - args.push(...moduleArgs); - } - - return args; -} - -export declare function transformReply(): string; +export default { + IS_READ_ONLY: true, + FIRST_KEY_INDEX: undefined, + transformArguments(path: RedisArgument, moduleArguments?: Array) { + return pushVariadicArguments(['MODULE', 'LOAD', path], moduleArguments); + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/MODULE_UNLOAD.ts b/packages/client/lib/commands/MODULE_UNLOAD.ts index d5927778fe..4f47abbdea 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 { + IS_READ_ONLY: true, + FIRST_KEY_INDEX: undefined, + transformArguments(name: RedisArgument) { + return ['MODULE', 'UNLOAD', name]; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/MOVE.ts b/packages/client/lib/commands/MOVE.ts index 17cc6742c5..60aac4b145 100644 --- a/packages/client/lib/commands/MOVE.ts +++ b/packages/client/lib/commands/MOVE.ts @@ -1,7 +1,9 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export function transformArguments(key: string, db: number): Array { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, db: number) { return ['MOVE', key, db.toString()]; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/MSET.ts b/packages/client/lib/commands/MSET.ts index bd7111659d..136fde3e7f 100644 --- a/packages/client/lib/commands/MSET.ts +++ b/packages/client/lib/commands/MSET.ts @@ -1,24 +1,27 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; - -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export type MSetArguments = - Array<[RedisCommandArgument, RedisCommandArgument]> | - Array | - Record; + Array<[RedisArgument, RedisArgument]> | + Array | + Record; -export function transformArguments(toSet: MSetArguments): RedisCommandArguments { - const args: RedisCommandArguments = ['MSET']; +export function mSetArguments(command: string, toSet: MSetArguments) { + const args: Array = [command]; - if (Array.isArray(toSet)) { - args.push(...toSet.flat()); - } else { - for (const key of Object.keys(toSet)) { - args.push(key, toSet[key]); - } + if (Array.isArray(toSet)) { + args.push(...toSet.flat()); + } else { + for (const tuple of Object.entries(toSet)) { + args.push(...tuple); } + } - return args; + return args; } -export declare function transformReply(): RedisCommandArgument; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments: mSetArguments.bind(undefined, 'MSET'), + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/MSETNX.ts b/packages/client/lib/commands/MSETNX.ts index 0ef3393611..4867b3ea09 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'; +import { SimpleStringReply, Command } from '../RESP/types'; +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'; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments: mSetArguments.bind(undefined, 'MSETNX'), + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/OBJECT_ENCODING.ts b/packages/client/lib/commands/OBJECT_ENCODING.ts index ac219ae89e..e74c3f99eb 100644 --- a/packages/client/lib/commands/OBJECT_ENCODING.ts +++ b/packages/client/lib/commands/OBJECT_ENCODING.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 2; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['OBJECT', 'ENCODING', key]; -} - -export declare function transformReply(): string | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/OBJECT_FREQ.ts b/packages/client/lib/commands/OBJECT_FREQ.ts index 071d16f274..b6757c6c57 100644 --- a/packages/client/lib/commands/OBJECT_FREQ.ts +++ b/packages/client/lib/commands/OBJECT_FREQ.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 2; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['OBJECT', 'FREQ', key]; -} - -export declare function transformReply(): number | null; + }, + transformReply: undefined as unknown as () => NumberReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/OBJECT_IDLETIME.ts b/packages/client/lib/commands/OBJECT_IDLETIME.ts index 38847d6f4c..f0fef24e2d 100644 --- a/packages/client/lib/commands/OBJECT_IDLETIME.ts +++ b/packages/client/lib/commands/OBJECT_IDLETIME.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 2; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['OBJECT', 'IDLETIME', key]; -} - -export declare function transformReply(): number | null; + }, + transformReply: undefined as unknown as () => NumberReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/OBJECT_REFCOUNT.ts b/packages/client/lib/commands/OBJECT_REFCOUNT.ts index 9fd259b5b9..c8381a76bf 100644 --- a/packages/client/lib/commands/OBJECT_REFCOUNT.ts +++ b/packages/client/lib/commands/OBJECT_REFCOUNT.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 2; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['OBJECT', 'REFCOUNT', key]; -} - -export declare function transformReply(): number | null; + }, + transformReply: undefined as unknown as () => NumberReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PERSIST.ts b/packages/client/lib/commands/PERSIST.ts index d7c9f8623e..64637fabc1 100644 --- a/packages/client/lib/commands/PERSIST.ts +++ b/packages/client/lib/commands/PERSIST.ts @@ -1,9 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument) { return ['PERSIST', key]; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PEXPIRE.ts b/packages/client/lib/commands/PEXPIRE.ts index cbb5666a51..4d64281e92 100644 --- a/packages/client/lib/commands/PEXPIRE.ts +++ b/packages/client/lib/commands/PEXPIRE.ts @@ -1,19 +1,20 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - milliseconds: number, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + ms: number, mode?: 'NX' | 'XX' | 'GT' | 'LT' -): RedisCommandArguments { - const args = ['PEXPIRE', key, milliseconds.toString()]; + ) { + const args = ['PEXPIRE', key, ms.toString()]; if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PEXPIREAT.ts b/packages/client/lib/commands/PEXPIREAT.ts index da912ec4fc..cbae589fba 100644 --- a/packages/client/lib/commands/PEXPIREAT.ts +++ b/packages/client/lib/commands/PEXPIREAT.ts @@ -1,24 +1,21 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { transformPXAT } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - millisecondsTimestamp: number | Date, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + msTimestamp: number | Date, mode?: 'NX' | 'XX' | 'GT' | 'LT' -): RedisCommandArguments { - const args = [ - 'PEXPIREAT', - key, - transformPXAT(millisecondsTimestamp) - ]; + ) { + const args = ['PEXPIREAT', key, transformPXAT(msTimestamp)]; if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PEXPIRETIME.ts b/packages/client/lib/commands/PEXPIRETIME.ts index 4c1acba8f0..49877701b6 100644 --- a/packages/client/lib/commands/PEXPIRETIME.ts +++ b/packages/client/lib/commands/PEXPIRETIME.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { - return ['PEXPIRETIME', key]; -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { + return ['PEXPIREAT', key]; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PFADD.ts b/packages/client/lib/commands/PFADD.ts index 8c8985de89..bdde0648f3 100644 --- a/packages/client/lib/commands/PFADD.ts +++ b/packages/client/lib/commands/PFADD.ts @@ -1,13 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - element: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['PFADD', key], element); -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, element?: RedisArgument | Array) { + return pushVariadicArguments(['PFADD', key], element); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PFCOUNT.ts b/packages/client/lib/commands/PFCOUNT.ts index a4cf2dbcb2..d2370d3228 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 { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { 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?: RedisArgument | Array) { + return pushVariadicArguments(['PFCOUNT'], key); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PFMERGE.ts b/packages/client/lib/commands/PFMERGE.ts index e934062b3f..5735e51bba 100644 --- a/packages/client/lib/commands/PFMERGE.ts +++ b/packages/client/lib/commands/PFMERGE.ts @@ -1,10 +1,13 @@ -import { RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; +import { pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(destination: string, source: string | Array): RedisCommandArguments { - return pushVerdictArguments(['PFMERGE', destination], source); -} - -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + destination: RedisArgument, + source?: RedisArgument | Array + ) { + return pushVariadicArguments(['PFMERGE', destination], 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 06cbae43a1..fa2a8b172f 100644 --- a/packages/client/lib/commands/PING.spec.ts +++ b/packages/client/lib/commands/PING.spec.ts @@ -1,37 +1,31 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './PING'; +import PING from './PING'; -describe('PING', () => { - describe('transformArguments', () => { - it('default', () => { - assert.deepEqual( - transformArguments(), - ['PING'] - ); - }); - - it('with message', () => { - assert.deepEqual( - transformArguments('message'), - ['PING', 'message'] - ); - }); +describe.only('PING', () => { + 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 95fa006122..5ca95f932d 100644 --- a/packages/client/lib/commands/PING.ts +++ b/packages/client/lib/commands/PING.ts @@ -1,12 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(message?: RedisCommandArgument): RedisCommandArguments { - const args: RedisCommandArguments = ['PING']; +export default { + IS_READ_ONLY: true, + transformArguments(message?: RedisArgument) { + const args: Array = ['PING']; if (message) { - args.push(message); + args.push(message); } return args; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PSETEX.ts b/packages/client/lib/commands/PSETEX.ts index f2739b6e27..4e345a1a1c 100644 --- a/packages/client/lib/commands/PSETEX.ts +++ b/packages/client/lib/commands/PSETEX.ts @@ -1,18 +1,18 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - milliseconds: number, - value: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, + ms: number, + value: RedisArgument + ) { return [ - 'PSETEX', - key, - milliseconds.toString(), - value + 'PSETEX', + key, + ms.toString(), + value ]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/PTTL.ts b/packages/client/lib/commands/PTTL.ts index a2975623f7..3585433787 100644 --- a/packages/client/lib/commands/PTTL.ts +++ b/packages/client/lib/commands/PTTL.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['PTTL', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PUBLISH.ts b/packages/client/lib/commands/PUBLISH.ts index 7862a0936c..1038dea52f 100644 --- a/packages/client/lib/commands/PUBLISH.ts +++ b/packages/client/lib/commands/PUBLISH.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const IS_READ_ONLY = true; - -export function transformArguments( - channel: RedisCommandArgument, - message: RedisCommandArgument -): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + FIRST_KEY_INDEX: undefined, + transformArguments(channel: RedisArgument, message: RedisArgument) { return ['PUBLISH', channel, message]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PUBSUB_CHANNELS.ts b/packages/client/lib/commands/PUBSUB_CHANNELS.ts index 86a144ede8..c828ebefce 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 { + IS_READ_ONLY: true, + FIRST_KEY_INDEX: undefined, + transformArguments(pattern?: RedisArgument) { + const args: Array = ['PUBSUB', 'CHANNELS']; if (pattern) { - args.push(pattern); + args.push(pattern); } return args; -} + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; -export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/PUBSUB_NUMPAT.ts b/packages/client/lib/commands/PUBSUB_NUMPAT.ts index 15be6aa1b1..b800b719dc 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 { + IS_READ_ONLY: true, + FIRST_KEY_INDEX: undefined, + transformArguments() { return ['PUBSUB', 'NUMPAT']; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PUBSUB_NUMSUB.ts b/packages/client/lib/commands/PUBSUB_NUMSUB.ts index f47238f888..f0da89069d 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 { RedisArgument, ArrayReply, BlobStringReply, NumberReply, Command } from '../RESP/types'; +import { pushVariadicArguments } from './generic-transformers'; -export const IS_READ_ONLY = true; - -export function transformArguments( - channels?: Array | RedisCommandArgument -): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + FIRST_KEY_INDEX: undefined, + transformArguments(channels?: RedisArgument | Array) { const args = ['PUBSUB', 'NUMSUB']; - if (channels) return pushVerdictArguments(args, channels); + if (channels) return pushVariadicArguments(args, channels); return args; -} - -export function transformReply(rawReply: Array): Record { - const transformedReply = Object.create(null); - - for (let i = 0; i < rawReply.length; i +=2) { - transformedReply[rawReply[i]] = rawReply[i + 1]; + }, + transformReply(rawReply: ArrayReply) { + const reply = Object.create(null); + let i = 0; + while (i < rawReply.length) { + reply[rawReply[i++].toString()] = rawReply[i++].toString(); } - return transformedReply; -} + return reply as Record; + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.ts b/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.ts index e998677848..8acf29c15b 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 { + IS_READ_ONLY: true, + FIRST_KEY_INDEX: undefined, + transformArguments(pattern?: RedisArgument) { + const args: Array = ['PUBSUB', 'bb']; + + if (pattern) { + args.push(pattern); + } -export function transformArguments( - pattern?: RedisCommandArgument -): RedisCommandArguments { - const args: RedisCommandArguments = ['PUBSUB', 'SHARDCHANNELS']; - if (pattern) args.push(pattern); return args; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/RANDOMKEY.ts b/packages/client/lib/commands/RANDOMKEY.ts index f2d511d4de..81d956209e 100644 --- a/packages/client/lib/commands/RANDOMKEY.ts +++ b/packages/client/lib/commands/RANDOMKEY.ts @@ -1,9 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { NumberReply, Command } from '../RESP/types'; -export const IS_READ_ONLY = true; - -export function transformArguments(): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments() { return ['RANDOMKEY']; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/READONLY.ts b/packages/client/lib/commands/READONLY.ts index db7db88162..094ff1cc02 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 { + IS_READ_ONLY: true, + FIRST_KEY_INDEX: undefined, + transformArguments() { + return ['READONLY']; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/READWRITE.ts b/packages/client/lib/commands/READWRITE.ts index 60dc865e89..b370ec7417 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 { + IS_READ_ONLY: true, + FIRST_KEY_INDEX: undefined, + transformArguments() { + return ['READWRITE']; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/RENAME.ts b/packages/client/lib/commands/RENAME.ts index 2d1134084f..ded9e7e0b2 100644 --- a/packages/client/lib/commands/RENAME.ts +++ b/packages/client/lib/commands/RENAME.ts @@ -1,12 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - newKey: RedisCommandArgument -): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, newKey: RedisArgument) { return ['RENAME', key, newKey]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/RENAMENX.ts b/packages/client/lib/commands/RENAMENX.ts index 322ff0a88c..01dedc8779 100644 --- a/packages/client/lib/commands/RENAMENX.ts +++ b/packages/client/lib/commands/RENAMENX.ts @@ -1,12 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command, NumberReply } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - newKey: RedisCommandArgument -): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, newKey: RedisArgument) { return ['RENAMENX', key, newKey]; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/REPLICAOF.ts b/packages/client/lib/commands/REPLICAOF.ts index bd452e0f37..4024e94bc4 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 { + IS_READ_ONLY: true, + FIRST_KEY_INDEX: undefined, + 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/ROLE.ts b/packages/client/lib/commands/ROLE.ts index b1d6041fdf..49196cb279 100644 --- a/packages/client/lib/commands/ROLE.ts +++ b/packages/client/lib/commands/ROLE.ts @@ -1,75 +1,67 @@ -export const IS_READ_ONLY = true; +import { ArrayReply, BlobStringReply, Command, NumberReply } from '../RESP/types'; -export function transformArguments(): Array { +type MasterRole = [ + role: BlobStringReply<'master'>, + replicationOffest: NumberReply, + replicas: ArrayReply<[host: BlobStringReply, port: NumberReply, replicationOffest: NumberReply]>, +]; + +type SlaveRole = [ + role: BlobStringReply<'slave'>, + masterHost: BlobStringReply, + masterPort: NumberReply, + state: BlobStringReply<'connect' | 'connecting' | 'sync' | 'connected'>, + dataReceived: NumberReply +]; + +type SentinelRole = [ + role: BlobStringReply<'sentinel'>, + masterNames: ArrayReply +]; + +type Role = MasterRole | SlaveRole | SentinelRole; + +export default { + IS_READ_ONLY: true, + FIRST_KEY_INDEX: undefined, + transformArguments() { return ['ROLE']; -} + }, + transformReply(reply: Role) { + switch (reply[0] as Role[0]['DEFAULT']) { + case 'master': { + const [role, replicationOffest, replicas] = reply as MasterRole; + return { + role, + replicationOffest, + replicas: replicas.map(([host, port, replicationOffest]) => ({ + host, + port, + replicationOffest, + })), + }; + } -interface RoleReplyInterface { - role: T; -} + case 'slave': { + const [role, masterHost, masterPort, state, dataReceived] = reply as SlaveRole; + return { + role, + master: { + host: masterHost, + port: masterPort, + }, + state, + dataReceived, + }; + } -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] - }; + case 'sentinel': { + const [role, masterNames] = reply as SentinelRole; + return { + role, + masterNames, + }; + } } -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/RPOP.ts b/packages/client/lib/commands/RPOP.ts index ed696b6d52..f7d0b33d3a 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.ts b/packages/client/lib/commands/RPOPLPUSH.ts index da45f6f602..1a5e1cc59b 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.ts b/packages/client/lib/commands/RPOP_COUNT.ts index b3bc778ee5..b60dec6ab9 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.ts b/packages/client/lib/commands/RPUSH.ts index 15e282f089..6dca8c81ce 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 { 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: RedisArgument + ) { + return pushVariadicArguments(['RPUSH', key], element); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/RPUSHX.ts b/packages/client/lib/commands/RPUSHX.ts index 29253cd6ed..ce38ba6b5f 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 { 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: RedisArgument + ) { + return pushVariadicArguments(['RPUSHX', key], element); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SADD.ts b/packages/client/lib/commands/SADD.ts index 7d7121e539..d3f164e7af 100644 --- a/packages/client/lib/commands/SADD.ts +++ b/packages/client/lib/commands/SADD.ts @@ -1,13 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - members: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['SADD', key], members); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, + members: Array | RedisArgument + ) { + return pushVariadicArguments(['SADD', key], members); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SCAN.ts b/packages/client/lib/commands/SCAN.ts index ee5908eb9b..b1660ddbbc 100644 --- a/packages/client/lib/commands/SCAN.ts +++ b/packages/client/lib/commands/SCAN.ts @@ -1,34 +1,25 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, ArrayReply, Command } from '../RESP/types'; import { ScanOptions, pushScanArguments } from './generic-transformers'; -export const IS_READ_ONLY = true; export interface ScanCommandOptions extends ScanOptions { - TYPE?: RedisCommandArgument; + TYPE?: RedisArgument; } -export function transformArguments( - cursor: number, - options?: ScanCommandOptions -): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments(cursor: number, options?: ScanCommandOptions) { const args = pushScanArguments(['SCAN'], cursor, options); if (options?.TYPE) { - args.push('TYPE', options.TYPE); + args.push('TYPE', options.TYPE); } return args; -} - -type ScanRawReply = [string, Array]; - -export interface ScanReply { - cursor: number; - keys: Array; -} - -export function transformReply([cursor, keys]: ScanRawReply): ScanReply { + }, + transformReply([cursor, keys]: [BlobStringReply, ArrayReply]) { return { - cursor: Number(cursor), - keys + cursor: Number(cursor), + keys }; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/SCARD.ts b/packages/client/lib/commands/SCARD.ts index 0d3ce49b6b..c13d042ba6 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_EXISTS.ts b/packages/client/lib/commands/SCRIPT_EXISTS.ts index cee889215d..72123806e8 100644 --- a/packages/client/lib/commands/SCRIPT_EXISTS.ts +++ b/packages/client/lib/commands/SCRIPT_EXISTS.ts @@ -1,8 +1,8 @@ import { RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { pushVariadicArguments } from './generic-transformers'; export function transformArguments(sha1: string | Array): RedisCommandArguments { - return pushVerdictArguments(['SCRIPT', 'EXISTS'], sha1); + return pushVariadicArguments(['SCRIPT', 'EXISTS'], sha1); } export { transformBooleanArrayReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/SDIFF.ts b/packages/client/lib/commands/SDIFF.ts index 9c4f3b4820..895a48da26 100644 --- a/packages/client/lib/commands/SDIFF.ts +++ b/packages/client/lib/commands/SDIFF.ts @@ -1,14 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { pushVariadicArguments } 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; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + keys: Array | RedisArgument + ) { + return pushVariadicArguments(['SDIFF'], keys); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SDIFFSTORE.ts b/packages/client/lib/commands/SDIFFSTORE.ts index a927e12ef0..526469d69b 100644 --- a/packages/client/lib/commands/SDIFFSTORE.ts +++ b/packages/client/lib/commands/SDIFFSTORE.ts @@ -1,13 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - destination: RedisCommandArgument, - keys: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['SDIFFSTORE', destination], keys); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + destination: RedisArgument, + keys: Array | RedisArgument + ) { + return pushVariadicArguments(['SDIFFSTORE', destination], keys); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SET.ts b/packages/client/lib/commands/SET.ts index 08ae56552b..bb39bd8960 100644 --- a/packages/client/lib/commands/SET.ts +++ b/packages/client/lib/commands/SET.ts @@ -1,63 +1,59 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; - -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, SimpleStringReply, BlobStringReply, NullReply, Command } from '../RESP/types'; type MaximumOneOf = - K extends keyof T ? { [P in K]?: T[K] } & Partial, never>> : never; + 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; + EX: number; + PX: number; + EXAT: number; + PXAT: number; + KEEPTTL: true; }>; type SetGuards = MaximumOneOf<{ - NX: true; - XX: true; + NX: true; + XX: true; }>; interface SetCommonOptions { - GET?: true; + GET?: true; } 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()); + 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'); + 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/SETEX.ts b/packages/client/lib/commands/SETEX.ts index bb3068501f..bbd77e5d99 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.ts b/packages/client/lib/commands/SETNX.ts index b01d45dc32..0940efad69 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.ts b/packages/client/lib/commands/SETRANGE.ts index 038a8a5dd7..1951a82c07 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/SINTER.ts b/packages/client/lib/commands/SINTER.ts index fe1feee7ad..d5c0fbc98d 100644 --- a/packages/client/lib/commands/SINTER.ts +++ b/packages/client/lib/commands/SINTER.ts @@ -1,14 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { pushVariadicArguments } 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; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + keys: Array | RedisArgument + ) { + return pushVariadicArguments(['SINTER'], keys); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SINTERCARD.ts b/packages/client/lib/commands/SINTERCARD.ts index ddb7e5b00e..967db96110 100644 --- a/packages/client/lib/commands/SINTERCARD.ts +++ b/packages/client/lib/commands/SINTERCARD.ts @@ -1,21 +1,26 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArgument } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 2; +export interface SInterCardOptions { + LIMIT?: number; +} -export const IS_READ_ONLY = true; +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments( + keys: Array | RedisArgument, + options?: SInterCardOptions | number // `number` for backwards compatibility + ) { + const args = pushVariadicArguments(['SINTERCARD'], keys); -export function transformArguments( - keys: Array | RedisCommandArgument, - limit?: number -): RedisCommandArguments { - const args = pushVerdictArgument(['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.ts b/packages/client/lib/commands/SINTERSTORE.ts index 02bf9d061a..8ab9c0065e 100644 --- a/packages/client/lib/commands/SINTERSTORE.ts +++ b/packages/client/lib/commands/SINTERSTORE.ts @@ -1,5 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { pushVariadicArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,7 +7,7 @@ export function transformArguments( destination: RedisCommandArgument, keys: RedisCommandArgument | Array ): RedisCommandArguments { - return pushVerdictArguments(['SINTERSTORE', destination], keys); + return pushVariadicArguments(['SINTERSTORE', destination], keys); } export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/SMEMBERS.ts b/packages/client/lib/commands/SMEMBERS.ts index 7950a4c073..fdab118e86 100644 --- a/packages/client/lib/commands/SMEMBERS.ts +++ b/packages/client/lib/commands/SMEMBERS.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SetReply, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['SMEMBERS', key]; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => SetReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SREM.ts b/packages/client/lib/commands/SREM.ts index 34aebdf02e..f08e147dd8 100644 --- a/packages/client/lib/commands/SREM.ts +++ b/packages/client/lib/commands/SREM.ts @@ -1,5 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { pushVariadicArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,7 +7,7 @@ export function transformArguments( key: RedisCommandArgument, members: RedisCommandArgument | Array ): RedisCommandArguments { - return pushVerdictArguments(['SREM', key], members); + return pushVariadicArguments(['SREM', key], members); } export declare function transformReply(): number; diff --git a/packages/client/lib/commands/SSCAN.ts b/packages/client/lib/commands/SSCAN.ts index 9b3938f159..fe648bff5e 100644 --- a/packages/client/lib/commands/SSCAN.ts +++ b/packages/client/lib/commands/SSCAN.ts @@ -1,31 +1,20 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; import { ScanOptions, pushScanArguments } 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, 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 { + ) { + return pushScanArguments(['SSCAN', key], cursor, options); + }, + transformReply([cursor, members]: [BlobStringReply, Array]) { return { - cursor: Number(cursor), - members + cursor: Number(cursor), + members }; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/STRLEN.ts b/packages/client/lib/commands/STRLEN.ts index de88340d8b..594530ff6b 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.ts b/packages/client/lib/commands/SUNION.ts index 52c112e661..97d8b41d70 100644 --- a/packages/client/lib/commands/SUNION.ts +++ b/packages/client/lib/commands/SUNION.ts @@ -1,5 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { pushVariadicArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -8,7 +8,7 @@ export const IS_READ_ONLY = true; export function transformArguments( keys: RedisCommandArgument | Array ): RedisCommandArguments { - return pushVerdictArguments(['SUNION'], keys); + return pushVariadicArguments(['SUNION'], keys); } export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/SUNIONSTORE.ts b/packages/client/lib/commands/SUNIONSTORE.ts index 94df6771a0..27161ba077 100644 --- a/packages/client/lib/commands/SUNIONSTORE.ts +++ b/packages/client/lib/commands/SUNIONSTORE.ts @@ -1,5 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { pushVariadicArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,7 +7,7 @@ export function transformArguments( destination: RedisCommandArgument, keys: RedisCommandArgument | Array ): RedisCommandArguments { - return pushVerdictArguments(['SUNIONSTORE', destination], keys); + return pushVariadicArguments(['SUNIONSTORE', destination], keys); } export declare function transformReply(): number; diff --git a/packages/client/lib/commands/TOUCH.ts b/packages/client/lib/commands/TOUCH.ts index e67dff8e93..c28f7a4629 100644 --- a/packages/client/lib/commands/TOUCH.ts +++ b/packages/client/lib/commands/TOUCH.ts @@ -1,12 +1,12 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { pushVariadicArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export function transformArguments( key: RedisCommandArgument | Array ): RedisCommandArguments { - return pushVerdictArguments(['TOUCH'], key); + return pushVariadicArguments(['TOUCH'], key); } export declare function transformReply(): number; diff --git a/packages/client/lib/commands/TTL.ts b/packages/client/lib/commands/TTL.ts index 29586f31fa..65c3b7b026 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.ts b/packages/client/lib/commands/TYPE.ts index 10cd3f99b0..09f6887492 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.ts b/packages/client/lib/commands/UNLINK.ts index 53b0360e2d..4455d42bcf 100644 --- a/packages/client/lib/commands/UNLINK.ts +++ b/packages/client/lib/commands/UNLINK.ts @@ -1,12 +1,12 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { pushVariadicArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export function transformArguments( key: RedisCommandArgument | Array ): RedisCommandArguments { - return pushVerdictArguments(['UNLINK'], key); + return pushVariadicArguments(['UNLINK'], key); } export declare function transformReply(): number; diff --git a/packages/client/lib/commands/UNWATCH.ts b/packages/client/lib/commands/UNWATCH.ts index ce42e7697b..3e80adeb1d 100644 --- a/packages/client/lib/commands/UNWATCH.ts +++ b/packages/client/lib/commands/UNWATCH.ts @@ -1,5 +1,9 @@ -export function transformArguments(): Array { - return ['UNWATCH']; -} +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { + return ['WATCH', key]; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/WAIT.ts b/packages/client/lib/commands/WAIT.ts index dff51ed968..da9df89ff0 100644 --- a/packages/client/lib/commands/WAIT.ts +++ b/packages/client/lib/commands/WAIT.ts @@ -1,7 +1,9 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, SimpleStringReply, Command, NumberReply } from '../RESP/types'; -export function transformArguments(numberOfReplicas: number, timeout: number): Array { +export default { + 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.ts b/packages/client/lib/commands/WATCH.ts index 58c6dfd1da..9a7b16fd74 100644 --- a/packages/client/lib/commands/WATCH.ts +++ b/packages/client/lib/commands/WATCH.ts @@ -1,10 +1,10 @@ import { RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { pushVariadicArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string | Array): RedisCommandArguments { - return pushVerdictArguments(['WATCH'], key); + return pushVariadicArguments(['WATCH'], key); } export declare function transformReply(): string; diff --git a/packages/client/lib/commands/XACK.ts b/packages/client/lib/commands/XACK.ts index 670d810fc0..1d35c8d13b 100644 --- a/packages/client/lib/commands/XACK.ts +++ b/packages/client/lib/commands/XACK.ts @@ -1,5 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { pushVariadicArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -8,7 +8,7 @@ export function transformArguments( group: RedisCommandArgument, id: RedisCommandArgument | Array ): RedisCommandArguments { - return pushVerdictArguments(['XACK', key, group], id); + return pushVariadicArguments(['XACK', key, group], id); } export declare function transformReply(): number; diff --git a/packages/client/lib/commands/XCLAIM.ts b/packages/client/lib/commands/XCLAIM.ts index bc38f9b9e9..43ad78cfb6 100644 --- a/packages/client/lib/commands/XCLAIM.ts +++ b/packages/client/lib/commands/XCLAIM.ts @@ -1,5 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { pushVariadicArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -18,7 +18,7 @@ export function transformArguments( id: RedisCommandArgument | Array, options?: XClaimOptions ): RedisCommandArguments { - const args = pushVerdictArguments( + const args = pushVariadicArguments( ['XCLAIM', key, group, consumer, minIdleTime.toString()], id ); diff --git a/packages/client/lib/commands/XDEL.ts b/packages/client/lib/commands/XDEL.ts index 82b30d2109..80b429c4e1 100644 --- a/packages/client/lib/commands/XDEL.ts +++ b/packages/client/lib/commands/XDEL.ts @@ -1,5 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { pushVariadicArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,7 +7,7 @@ export function transformArguments( key: RedisCommandArgument, id: RedisCommandArgument | Array ): RedisCommandArguments { - return pushVerdictArguments(['XDEL', key], id); + return pushVariadicArguments(['XDEL', key], id); } export declare function transformReply(): number; diff --git a/packages/client/lib/commands/ZADD.ts b/packages/client/lib/commands/ZADD.ts index 9ac67d59cc..9372c9217e 100644 --- a/packages/client/lib/commands/ZADD.ts +++ b/packages/client/lib/commands/ZADD.ts @@ -1,71 +1,76 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformNumberInfinityArgument, ZMember } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, NumberReply, DoubleReply, Command } from '../RESP/types'; +import { ZMember, transformNumberInfinityArgument, transformNumberInfinityReply } from './generic-transformers'; interface NX { - NX?: true; + NX?: boolean; } interface XX { - XX?: true; + XX?: boolean; } interface LT { - LT?: true; + LT?: boolean; } interface GT { - GT?: true; + GT?: boolean; } interface CH { - CH?: true; + CH?: boolean; } interface INCR { - INCR?: true; + INCR?: boolean; } -type ZAddOptions = (NX | (XX & LT & GT)) & CH & INCR; +export type ZAddOptions = (NX | (XX & LT & GT)) & CH & INCR; -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, members: ZMember | Array, options?: ZAddOptions -): RedisCommandArguments { + ) { const args = ['ZADD', key]; if ((options)?.NX) { - args.push('NX'); + args.push('NX'); } else { - if ((options)?.XX) { - args.push('XX'); - } + if ((options)?.XX) { + args.push('XX'); + } - if ((options)?.GT) { - args.push('GT'); - } else if ((options)?.LT) { - args.push('LT'); - } + if ((options)?.GT) { + args.push('GT'); + } + + if ((options)?.LT) { + args.push('LT'); + } } if ((options)?.CH) { - args.push('CH'); + args.push('CH'); } if ((options)?.INCR) { - args.push('INCR'); + args.push('INCR'); } for (const { score, value } of (Array.isArray(members) ? members : [members])) { - args.push( - transformNumberInfinityArgument(score), - value - ); + args.push( + transformNumberInfinityArgument(score), + value + ); } return args; -} - -export { transformNumberInfinityReply as transformReply } from './generic-transformers'; + }, + transformReply: { + 2: transformNumberInfinityReply, + 3: undefined as unknown as () => NumberReply | DoubleReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZCARD.ts b/packages/client/lib/commands/ZCARD.ts index f208c18136..c11cb69db3 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.ts b/packages/client/lib/commands/ZCOUNT.ts index f9700cc909..06bd4b915a 100644 --- a/packages/client/lib/commands/ZCOUNT.ts +++ b/packages/client/lib/commands/ZCOUNT.ts @@ -1,21 +1,20 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { transformStringNumberInfinityArgument } 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, + transformStringNumberInfinityArgument(min), + transformStringNumberInfinityArgument(max) ]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZDIFF.ts b/packages/client/lib/commands/ZDIFF.ts index f3818a139f..f16c8717cd 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'; +import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArgument } 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; +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.ts b/packages/client/lib/commands/ZDIFFSTORE.ts index 3b9af9511c..e4614a1cb1 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.ts b/packages/client/lib/commands/ZDIFF_WITHSCORES.ts index 9caa13c9f8..0fa1a6d271 100644 --- a/packages/client/lib/commands/ZDIFF_WITHSCORES.ts +++ b/packages/client/lib/commands/ZDIFF_WITHSCORES.ts @@ -1,13 +1,19 @@ -import { RedisCommandArguments } from '.'; -import { transformArguments as transformZDiffArguments } from './ZDIFF'; +import { ArrayReply, BlobStringReply, Command, NumberReply } from '../RESP/types'; +import ZDIFF from './ZDIFF'; +import { transformSortedSetWithScoresReply } from './generic-transformers'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZDIFF'; - -export function transformArguments(...args: Parameters): RedisCommandArguments { - return [ - ...transformZDiffArguments(...args), - 'WITHSCORES' - ]; -} - -export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments(keys: Parameters[0]) { + const args = ZDIFF.transformArguments(keys); + args.push('WITHSCORES'); + return args; + }, + transformReply: { + 2: transformSortedSetWithScoresReply, + 3: (reply: ArrayReply<[BlobStringReply, NumberReply]>) => { + return reply.map(([value, score]) => ({ value, score })); + } + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZINCRBY.ts b/packages/client/lib/commands/ZINCRBY.ts index 68d8935139..66b3dfa1f2 100644 --- a/packages/client/lib/commands/ZINCRBY.ts +++ b/packages/client/lib/commands/ZINCRBY.ts @@ -1,19 +1,22 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformNumberInfinityArgument } from './generic-transformers'; +import { RedisArgument, DoubleReply, Command } from '../RESP/types'; +import { transformNumberInfinityArgument, transformNumberInfinityReply } 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, + transformNumberInfinityArgument(increment), + member ]; -} - -export { transformNumberInfinityReply as transformReply } from './generic-transformers'; + }, + transformReply: { + 2: transformNumberInfinityReply, + 3: undefined as unknown as () => DoubleReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZINTER.ts b/packages/client/lib/commands/ZINTER.ts index 88d7f80188..8c185c03c7 100644 --- a/packages/client/lib/commands/ZINTER.ts +++ b/packages/client/lib/commands/ZINTER.ts @@ -1,33 +1,75 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArgument } from './generic-transformers'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { transformNumberInfinityArgument } 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, +export function pushZInterArguments( + args: Array, + keys: ZInterKeys | ZInterKeys, + options?: ZInterOptions +) { + if (Array.isArray(keys)) { + args.push(keys.length.toString()); + + if (keys.length) { + if (isPlainKeys(keys)) { + args = args.concat(keys); + } else { + const start = args.length; + args[start + keys.length] = 'WEIGHTS'; + for (let i = 0; i < keys.length; i++) { + const index = start + i; + args[index] = keys[i].key; + args[index + 1 + keys.length] = transformNumberInfinityArgument(keys[i].weight); + } + } + } + } else { + args.push('1'); + + if (isPlainKey(keys)) { + args.push(keys); + } else { + args.push( + keys.key, + 'WEIGHTS', + transformNumberInfinityArgument(keys.weight) + ); + } + } + + if (options?.AGGREGATE) { + args.push('AGGREGATE', options.AGGREGATE); + } + + return args; +} + +function isPlainKey(key: RedisArgument | ZInterKeyAndWeight): key is RedisArgument { + return typeof key === 'string' || Buffer.isBuffer(key); +} + +function isPlainKeys(keys: Array | Array): keys is Array { + return isPlainKey(keys[0]); +} + +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments( + keys: ZInterKeys | ZInterKeys, options?: ZInterOptions -): RedisCommandArguments { - 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 declare function transformReply(): Array; + ) { + return pushZInterArguments(['ZINTER'], keys, options); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZINTERCARD.ts b/packages/client/lib/commands/ZINTERCARD.ts index ff45ab2aa0..9953d88eec 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 interface ZInterCardOptions { + LIMIT?: number; +} -export const IS_READ_ONLY = true; +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments( + keys: RedisVariadicArgument, + options?: ZInterCardOptions['LIMIT'] | ZInterCardOptions + ) { + const args = pushVariadicArgument(['ZINTERCARD'], keys); -export function transformArguments( - keys: Array | RedisCommandArgument, - limit?: number -): RedisCommandArguments { - const args = pushVerdictArgument(['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.ts b/packages/client/lib/commands/ZINTERSTORE.ts index 540f10ae2d..9188b0d90f 100644 --- a/packages/client/lib/commands/ZINTERSTORE.ts +++ b/packages/client/lib/commands/ZINTERSTORE.ts @@ -1,32 +1,15 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { pushZInterArguments, ZInterKeyAndWeight, ZInterKeys, ZInterOptions } from './ZINTER'; -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, + transformArguments( + destination: RedisArgument, + keys: ZInterKeys | ZInterKeys, + options?: ZInterOptions + ) { + return pushZInterArguments(['ZINTERSTORE', destination], keys, options); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZINTER_WITHSCORES.ts b/packages/client/lib/commands/ZINTER_WITHSCORES.ts index c9416e9222..0761625145 100644 --- a/packages/client/lib/commands/ZINTER_WITHSCORES.ts +++ b/packages/client/lib/commands/ZINTER_WITHSCORES.ts @@ -1,13 +1,38 @@ -import { RedisCommandArguments } from '.'; -import { transformArguments as transformZInterArguments } from './ZINTER'; +// import { RedisCommandArguments } from '.'; +// import { transformArguments as transformZInterArguments } from './ZINTER'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZINTER'; +// export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZINTER'; -export function transformArguments(...args: Parameters): RedisCommandArguments { - return [ - ...transformZInterArguments(...args), - 'WITHSCORES' - ]; -} +// export function transformArguments(...args: Parameters): RedisCommandArguments { +// return [ +// ...transformZInterArguments(...args), +// 'WITHSCORES' +// ]; +// } -export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; + + +// // transformSortedSetWithScoresReply + +import { ArrayReply, BlobStringReply, Command, DoubleReply } from '../RESP/types'; +import ZINTER from './ZINTER'; +import { transformSortedSetWithScoresReply } from './generic-transformers'; + +export default { + FIRST_KEY_INDEX: ZINTER.FIRST_KEY_INDEX, + IS_READ_ONLY: ZINTER.IS_READ_ONLY, + transformArguments(...args: Parameters) { + const redisArgs = ZINTER.transformArguments(...args); + redisArgs.push('WITHSCORES'); + return redisArgs; + }, + transformReply: { + 2: transformSortedSetWithScoresReply, + 3: (reply: ArrayReply<[BlobStringReply, DoubleReply]>) => { + return reply.map(([member, score]) => ({ + member, + score + })); + } + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZLEXCOUNT.ts b/packages/client/lib/commands/ZLEXCOUNT.ts index e2fbcdbb42..26c9e0d70a 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.ts b/packages/client/lib/commands/ZMPOP.ts index 0baa46bbf0..d430b1cbc4 100644 --- a/packages/client/lib/commands/ZMPOP.ts +++ b/packages/client/lib/commands/ZMPOP.ts @@ -1,34 +1,54 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { SortedSetSide, transformSortedSetMemberReply, transformZMPopArguments, ZMember, ZMPopOptions } from './generic-transformers'; +import { NullReply, TuplesReply, BlobStringReply, DoubleReply, ArrayReply, Resp2Reply, Command } from '../RESP/types'; +import { pushVariadicArgument, RedisVariadicArgument, SortedSetSide } from './generic-transformers'; -export const FIRST_KEY_INDEX = 2; +export interface ZMPopOptions { + COUNT?: number; +} -export function transformArguments( - keys: RedisCommandArgument | Array, +export type ZMPopRawReply = NullReply | TuplesReply<[ + key: BlobStringReply, + elements: ArrayReply> +]>; + +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: false, + transformArguments( + keys: RedisVariadicArgument, side: SortedSetSide, options?: ZMPopOptions -): RedisCommandArguments { - return transformZMPopArguments( - ['ZMPOP'], - keys, - side, - options - ); -} + ) { + const args = pushVariadicArgument(['ZMPOP'], keys); -type ZMPopRawReply = null | [ - key: string, - elements: Array<[RedisCommandArgument, RedisCommandArgument]> -]; + args.push(side); -type ZMPopReply = null | { - key: string, - elements: Array -}; + if (options?.COUNT) { + args.push('COUNT', options.COUNT.toString()); + } -export function transformReply(reply: ZMPopRawReply): ZMPopReply { - return reply === null ? null : { + return args; + }, + transformReply: { + 2: (reply: Resp2Reply) => { + return reply === null ? null : { key: reply[0], - elements: reply[1].map(transformSortedSetMemberReply) - }; -} + elements: reply[1].map(([member, score]) => ({ + member, + score: Number(score) + })) + }; + }, + 3: (reply: ZMPopRawReply) => { + return reply === null ? null : { + key: reply[0], + elements: reply[1].map(([member, score]) => ({ + member, + score + })) + }; + }, + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZMSCORE.ts b/packages/client/lib/commands/ZMSCORE.ts index 6c8c9dace3..55f71e0d3a 100644 --- a/packages/client/lib/commands/ZMSCORE.ts +++ b/packages/client/lib/commands/ZMSCORE.ts @@ -1,15 +1,19 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, ArrayReply, NullReply, BlobStringReply, DoubleReply, Command } from '../RESP/types'; +import { pushVariadicArgument, 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 pushVariadicArgument(['ZMSCORE', key], member); + }, + transformReply: { + 2: (reply: ArrayReply) => { + return reply.map(score => score === null ? null : Number(score)); + }, + 3: undefined as unknown as () => ArrayReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZREM.ts b/packages/client/lib/commands/ZREM.ts index 7ab92c4a78..a5e1ff559c 100644 --- a/packages/client/lib/commands/ZREM.ts +++ b/packages/client/lib/commands/ZREM.ts @@ -1,5 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { pushVariadicArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,7 +7,7 @@ export function transformArguments( key: RedisCommandArgument, member: RedisCommandArgument | Array ): RedisCommandArguments { - return pushVerdictArguments(['ZREM', key], member); + return pushVariadicArguments(['ZREM', key], member); } export declare function transformReply(): number; diff --git a/packages/client/lib/commands/ZSCAN.ts b/packages/client/lib/commands/ZSCAN.ts index f6fa17c2d4..2377b90aac 100644 --- a/packages/client/lib/commands/ZSCAN.ts +++ b/packages/client/lib/commands/ZSCAN.ts @@ -1,39 +1,34 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { ScanOptions, transformNumberInfinityReply, pushScanArguments, ZMember } from './generic-transformers'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; +import { ScanOptions, ZMember, pushScanArguments, transformNumberInfinityReply } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +export interface HScanEntry { + field: BlobStringReply; + value: BlobStringReply; +} -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, cursor: number, options?: ScanOptions -): RedisCommandArguments { - return pushScanArguments([ - 'ZSCAN', - key - ], cursor, options); -} - -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]) - }); + ) { + return pushScanArguments(['ZSCAN', key], cursor, options); + }, + transformReply([cursor, rawMembers]: [BlobStringReply, Array]) { + const members = []; + let i = 0; + while (i < rawMembers.length) { + members.push({ + value: rawMembers[i++], + score: transformNumberInfinityReply(rawMembers[i++]) + } satisfies ZMember); } return { - cursor: Number(cursor), - members: parsedMembers + cursor: Number(cursor), + members }; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZUNION.ts b/packages/client/lib/commands/ZUNION.ts index f329348cc8..3f57c5ba72 100644 --- a/packages/client/lib/commands/ZUNION.ts +++ b/packages/client/lib/commands/ZUNION.ts @@ -1,5 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArgument } from './generic-transformers'; +import { pushVariadicArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; @@ -14,7 +14,7 @@ export function transformArguments( keys: Array | RedisCommandArgument, options?: ZUnionOptions ): RedisCommandArguments { - const args = pushVerdictArgument(['ZUNION'], keys); + const args = pushVariadicArgument(['ZUNION'], keys); if (options?.WEIGHTS) { args.push('WEIGHTS', ...options.WEIGHTS.map(weight => weight.toString())); diff --git a/packages/client/lib/commands/ZUNIONSTORE.ts b/packages/client/lib/commands/ZUNIONSTORE.ts index 2a42e21bc8..4795161d00 100644 --- a/packages/client/lib/commands/ZUNIONSTORE.ts +++ b/packages/client/lib/commands/ZUNIONSTORE.ts @@ -1,5 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArgument } from './generic-transformers'; +import { pushVariadicArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -13,7 +13,7 @@ export function transformArguments( keys: Array | RedisCommandArgument, options?: ZUnionOptions ): RedisCommandArguments { - const args = pushVerdictArgument(['ZUNIONSTORE', destination], keys); + const args = pushVariadicArgument(['ZUNIONSTORE', destination], keys); if (options?.WEIGHTS) { args.push('WEIGHTS', ...options.WEIGHTS.map(weight => weight.toString())); diff --git a/packages/client/lib/commands/generic-transformers.spec.ts b/packages/client/lib/commands/generic-transformers.spec.ts index 301cab0a75..0c64a2175f 100644 --- a/packages/client/lib/commands/generic-transformers.spec.ts +++ b/packages/client/lib/commands/generic-transformers.spec.ts @@ -18,10 +18,10 @@ import { transformEXAT, transformPXAT, pushEvalArguments, - pushVerdictArguments, - pushVerdictNumberArguments, - pushVerdictArgument, - pushOptionalVerdictArgument, + pushVariadicArguments, + pushVariadicNumberArguments, + pushVariadicArgument, + pushOptionalVariadicArgument, transformCommandReply, CommandFlags, CommandCategories, @@ -564,72 +564,72 @@ describe('Generic Transformers', () => { }); }); - describe('pushVerdictArguments', () => { + describe('pushVariadicArguments', () => { it('string', () => { assert.deepEqual( - pushVerdictArguments([], 'string'), + pushVariadicArguments([], 'string'), ['string'] ); }); it('array', () => { assert.deepEqual( - pushVerdictArguments([], ['1', '2']), + pushVariadicArguments([], ['1', '2']), ['1', '2'] ); }); }); - describe('pushVerdictNumberArguments', () => { + describe('pushVariadicNumberArguments', () => { it('number', () => { assert.deepEqual( - pushVerdictNumberArguments([], 0), + pushVariadicNumberArguments([], 0), ['0'] ); }); it('array', () => { assert.deepEqual( - pushVerdictNumberArguments([], [0, 1]), + pushVariadicNumberArguments([], [0, 1]), ['0', '1'] ); }); }); - describe('pushVerdictArgument', () => { + describe('pushVariadicArgument', () => { it('string', () => { assert.deepEqual( - pushVerdictArgument([], 'string'), + pushVariadicArgument([], 'string'), ['1', 'string'] ); }); it('array', () => { assert.deepEqual( - pushVerdictArgument([], ['1', '2']), + pushVariadicArgument([], ['1', '2']), ['2', '1', '2'] ); }); }); - describe('pushOptionalVerdictArgument', () => { + describe('pushOptionalVariadicArgument', () => { it('undefined', () => { assert.deepEqual( - pushOptionalVerdictArgument([], 'name', undefined), + pushOptionalVariadicArgument([], 'name', undefined), [] ); }); it('string', () => { assert.deepEqual( - pushOptionalVerdictArgument([], 'name', 'string'), + pushOptionalVariadicArgument([], 'name', 'string'), ['name', '1', 'string'] ); }); it('array', () => { assert.deepEqual( - pushOptionalVerdictArgument([], 'name', ['1', '2']), + pushOptionalVariadicArgument([], 'name', ['1', '2']), ['name', '2', '1', '2'] ); }); diff --git a/packages/client/lib/commands/generic-transformers.ts b/packages/client/lib/commands/generic-transformers.ts index 5048de9399..dd89610718 100644 --- a/packages/client/lib/commands/generic-transformers.ts +++ b/packages/client/lib/commands/generic-transformers.ts @@ -1,4 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { ArrayReply, BlobStringReply, NullReply, RedisArgument } from '../RESP/types'; export function transformBooleanReply(reply: number): boolean { return reply === 1; @@ -33,7 +34,7 @@ export function pushScanArguments( return args; } -export function transformNumberInfinityReply(reply: RedisCommandArgument): number { +export function transformNumberInfinityReply(reply: BlobStringReply): number { switch (reply.toString()) { case '+inf': return Infinity; @@ -46,13 +47,13 @@ export function transformNumberInfinityReply(reply: RedisCommandArgument): numbe } } -export function transformNumberInfinityNullReply(reply: RedisCommandArgument | null): number | null { +export function transformNumberInfinityNullReply(reply: BlobStringReply | NullReply): number | null { if (reply === null) return null; return transformNumberInfinityReply(reply); } -export function transformNumberInfinityNullArrayReply(reply: Array): Array { +export function transformNumberInfinityNullArrayReply(reply: Array): Array { return reply.map(transformNumberInfinityNullReply); } @@ -76,8 +77,8 @@ export function transformStringNumberInfinityArgument(num: RedisCommandArgument } export function transformTuplesReply( - reply: Array -): Record { + reply: ArrayReply +): Record { const message = Object.create(null); for (let i = 0; i < reply.length; i += 2) { @@ -123,11 +124,11 @@ export function transformStreamsMessagesReply(reply: Array | null): Streams export interface ZMember { score: number; - value: RedisCommandArgument; + value: RedisArgument; } export function transformSortedSetMemberNullReply( - reply: [RedisCommandArgument, RedisCommandArgument] | [] + reply: [BlobStringReply, BlobStringReply] | [] ): ZMember | null { if (!reply.length) return null; @@ -135,7 +136,7 @@ export function transformSortedSetMemberNullReply( } export function transformSortedSetMemberReply( - reply: [RedisCommandArgument, RedisCommandArgument] + reply: [BlobStringReply, BlobStringReply] ): ZMember { return { value: reply[0], @@ -143,7 +144,7 @@ export function transformSortedSetMemberReply( }; } -export function transformSortedSetWithScoresReply(reply: Array): Array { +export function transformSortedSetWithScoresReply(reply: ArrayReply): Array { const members = []; for (let i = 0; i < reply.length; i += 2) { @@ -156,31 +157,10 @@ 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 type SortedSetSide = 'MIN' | 'MAX'; + export interface LMPopOptions { COUNT?: number; } @@ -191,7 +171,7 @@ export function transformLMPopArguments( side: ListSide, options?: LMPopOptions ): RedisCommandArguments { - pushVerdictArgument(args, keys); + pushVariadicArgument(args, keys); args.push(side); @@ -425,7 +405,7 @@ export function pushEvalArguments(args: Array, options?: EvalOptions): A return args; } -export function pushVerdictArguments(args: RedisCommandArguments, value: RedisCommandArgument | Array): RedisCommandArguments { +export function pushVariadicArguments(args: RedisCommandArguments, value: RedisCommandArgument | Array): RedisCommandArguments { if (Array.isArray(value)) { // https://github.com/redis/node-redis/pull/2160 args = args.concat(value); @@ -436,7 +416,7 @@ export function pushVerdictArguments(args: RedisCommandArguments, value: RedisCo return args; } -export function pushVerdictNumberArguments( +export function pushVariadicNumberArguments( args: RedisCommandArguments, value: number | Array ): RedisCommandArguments { @@ -451,9 +431,11 @@ export function pushVerdictNumberArguments( return args; } -export function pushVerdictArgument( - args: RedisCommandArguments, - value: RedisCommandArgument | Array +export type RedisVariadicArgument = RedisArgument | Array; + +export function pushVariadicArgument( + args: Array, + value: RedisVariadicArgument ): RedisCommandArguments { if (Array.isArray(value)) { args.push(value.length.toString(), ...value); @@ -464,7 +446,7 @@ export function pushVerdictArgument( return args; } -export function pushOptionalVerdictArgument( +export function pushOptionalVariadicArgument( args: RedisCommandArguments, name: RedisCommandArgument, value: undefined | RedisCommandArgument | Array @@ -473,7 +455,7 @@ export function pushOptionalVerdictArgument( args.push(name); - return pushVerdictArgument(args, value); + return pushVariadicArgument(args, value); } export enum CommandFlags { diff --git a/packages/client/lib/commands/index.ts b/packages/client/lib/commands/index.ts index 60f9720c8d..db7ca279a7 100644 --- a/packages/client/lib/commands/index.ts +++ b/packages/client/lib/commands/index.ts @@ -1,91 +1,332 @@ -import { ClientCommandOptions } from '../client'; -import { CommandOptions } from '../command-options'; -import { RedisScriptConfig, SHA1 } from '../lua-script'; +import ACL_CAT from './ACL_CAT'; +import ACL_DRYRUN from './ACL_DRYRUN'; +import ACL_GENPASS from './ACL_GENPASS'; +import ACL_GETUSER from './ACL_GETUSER'; +import ACL_LIST from './ACL_LIST'; +import ACL_LOAD from './ACL_LOAD'; +import ACL_LOG_RESET from './ACL_LOG_RESET'; +import ACL_LOG from './ACL_LOG'; +import ACL_SAVE from './ACL_SAVE'; +import ACL_SETUSER from './ACL_SETUSER'; +import ACL_USERS from './ACL_USERS'; +import ACL_WHOAMI from './ACL_WHOAMI'; +import APPEND from './APPEND'; +import ASKING from './ASKING'; +import AUTH from './AUTH'; +import BGREWRITEAOF from './BGREWRITEAOF'; +import BGSAVE from './BGSAVE'; +import BITCOUNT from './BITCOUNT'; +import BITFIELD_RO from './BITFIELD_RO'; +import BITFIELD from './BITFIELD'; +import BITOP from './BITOP'; +import BITPOS from './BITPOS'; +import BLMOVE from './BLMOVE'; +import BLMPOP from './BLMPOP'; +import BLPOP from './BLPOP'; +import BRPOP from './BRPOP'; +import BRPOPLPUSH from './BRPOPLPUSH'; +import CLIENT_CACHING from './CLIENT_CACHING'; +import CLIENT_GETNAME from './CLIENT_GETNAME'; +import CLIENT_GETREDIR from './CLIENT_GETREDIR'; +import CLIENT_ID from './CLIENT_ID'; +import CLIENT_INFO from './CLIENT_INFO'; +import CLIENT_KILL from './CLIENT_KILL'; +import CLIENT_LIST from './CLIENT_LIST'; +import CLIENT_NO_EVICT from './CLIENT_NO-EVICT'; +import CLIENT_PAUSE from './CLIENT_PAUSE'; +import CLIENT_SETNAME from './CLIENT_SETNAME'; +import CLUSTER_ADDSLOTS from './CLUSTER_ADDSLOTS'; +import CLUSTER_SLOTS from './CLUSTER_SLOTS'; +import CLUSTER_MEET from './CLUSTER_MEET'; +import CLUSTER_MYID from './CLUSTER_MYID'; +import CLUSTER_REPLICATE from './CLUSTER_REPLICATE'; +import DECR from './DECR'; +import DECRBY from './DECRBY'; +import GET from './GET'; +import GETDEL from './GETDEL'; +import GETEX from './GETEX'; +import GETRANGE from './GETRANGE'; +import GETSET from './GETSET'; +import FLUSHALL from './FLUSHALL'; +import HDEL from './HDEL'; +import HEXISTS from './HEXISTS'; +import HGET from './HGET'; +import HGETALL from './HGETALL'; +import HINCRBY from './HINCRBY'; +import HINCRBYFLOAT from './HINCRBYFLOAT'; +import HKEYS from './HKEYS'; +import HLEN from './HLEN' +import HMGET from './HMGET'; +import HRANDFIELD_COUNT_WITHVALUES from './HRANDFIELD_COUNT_WITHVALUES'; +import HRANDFIELD_COUNT from './HRANDFIELD_COUNT'; +import HRANDFIELD from './HRANDFIELD'; +import HSCAN from './HSCAN'; +import HSET from './HSET'; +import HSETNX from './HSETNX'; +import HSTRLEN from './HSTRLEN'; +import HVALS from './HVALS'; +import INCR from './INCR'; +import INCRBY from './INCRBY'; +import INCRBYFLOAT from './INCRBYFLOAT'; +import INFO from './INFO'; +// import LCS_IDX_WITHMATCHLEN from './LCS_IDX_WITHMATCHLEN'; +// import LCS_IDX from './LCS_IDX'; +import LCS_LEN from './LCS_LEN'; +import LCS from './LCS'; +import LINDEX from './LINDEX'; +import LINSERT from './LINSERT'; +import LLEN from './LLEN'; +import LMOVE from './LMOVE'; +import LMPOP from './LMPOP'; +import LPOP from './LPOP'; +import LPOS from './LPOS'; +import LPUSH from './LPUSH'; +import LPUSHX from './LPUSHX'; +import LRANGE from './LRANGE'; +import LREM from './LREM'; +import LSET from './LSET'; +import LTRIM from './LTRIM'; +import MGET from './MGET'; +import MSET from './MSET'; +import MSETNX from './MSETNX'; +import PFADD from './PFADD'; +import PFCOUNT from './PFCOUNT'; +import PFMERGE from './PFMERGE'; +import PING from './PING'; +import PSETEX from './PSETEX'; +import RPOP from './RPOP'; +import RPOPLPUSH from './RPOPLPUSH'; +import RPUSH from './RPUSH'; +import RPUSHX from './RPUSHX'; +import SCAN from './SCAN'; +import SET from './SET'; +import SETEX from './SETEX'; +import SETNX from './SETNX'; +import SETRANGE from './SETRANGE'; +import SMEMBERS from './SMEMBERS'; +import SSCAN from './SSCAN'; +import STRLEN from './STRLEN'; +import ZSCAN from './ZSCAN'; +import { Command } from '../RESP/types'; -export type RedisCommandRawReply = string | number | Buffer | null | undefined | Array; - -export type RedisCommandArgument = string | Buffer; - -export type RedisCommandArguments = Array & { preserve?: unknown }; - -export interface RedisCommand { - FIRST_KEY_INDEX?: number | ((...args: Array) => RedisCommandArgument | undefined); - IS_READ_ONLY?: boolean; - TRANSFORM_LEGACY_REPLY?: boolean; - transformArguments(this: void, ...args: Array): RedisCommandArguments; - transformReply?(this: void, reply: any, preserved?: any): any; -} - -export type RedisCommandReply = - C['transformReply'] extends (...args: any) => infer T ? T : RedisCommandRawReply; - -export type ConvertArgumentType = - Type extends RedisCommandArgument ? ( - Type extends (string & ToType) ? Type : ToType - ) : ( - Type extends Set ? Set> : ( - Type extends Map ? Map> : ( - Type extends Array ? Array> : ( - Type extends Date ? Type : ( - Type extends Record ? { - [Property in keyof Type]: ConvertArgumentType - } : Type - ) - ) - ) - ) - ); - -export type RedisCommandSignature< - Command extends RedisCommand, - Params extends Array = Parameters -> = >( - ...args: Params | [options: Options, ...rest: Params] -) => Promise< - ConvertArgumentType< - RedisCommandReply, - Options['returnBuffers'] extends true ? Buffer : string - > ->; - -export interface RedisCommands { - [command: string]: RedisCommand; -} - -export interface RedisModule { - [command: string]: RedisCommand; -} - -export interface RedisModules { - [module: string]: RedisModule; -} - -export interface RedisFunction extends RedisCommand { - NUMBER_OF_KEYS?: number; -} - -export interface RedisFunctionLibrary { - [fn: string]: RedisFunction; -} - -export interface RedisFunctions { - [library: string]: RedisFunctionLibrary; -} - -export type RedisScript = RedisScriptConfig & SHA1; - -export interface RedisScripts { - [script: string]: RedisScript; -} - -export interface RedisExtensions< - M extends RedisModules = RedisModules, - F extends RedisFunctions = RedisFunctions, - S extends RedisScripts = RedisScripts -> { - modules?: M; - functions?: F; - scripts?: S; -} - -export type ExcludeMappedString = string extends S ? never : S; +export default { + ACL_CAT, + aclCat: ACL_CAT, + ACL_DRYRUN, + aclDryRun: ACL_DRYRUN, + ACL_GENPASS, + aclGenPass: ACL_GENPASS, + ACL_GETUSER, + aclGetUser: ACL_GETUSER, + ACL_LIST, + aclList: ACL_LIST, + ACL_LOAD, + aclLoad: ACL_LOAD, + ACL_LOG_RESET, + aclLogReset: ACL_LOG_RESET, + ACL_LOG, + aclLog: ACL_LOG, + ACL_SAVE, + aclSave: ACL_SAVE, + ACL_SETUSER, + aclSetUser: ACL_SETUSER, + ACL_USERS, + aclUsers: ACL_USERS, + ACL_WHOAMI, + aclWhoAmI: ACL_WHOAMI, + APPEND, + append: APPEND, + ASKING, + asking: ASKING, + AUTH, + auth: AUTH, + BGREWRITEAOF, + bgRewriteAof: BGREWRITEAOF, + BGSAVE, + bgSave: BGSAVE, + BITCOUNT, + bitCount: BITCOUNT, + BITFIELD_RO, + bitFieldRo: BITFIELD_RO, + BITFIELD, + bitField: BITFIELD, + BITOP, + bitOp: BITOP, + BITPOS, + bitPos: BITPOS, + BLMOVE, + blMove: BLMOVE, + BLMPOP, + blmPop: BLMPOP, + BLPOP, + blPop: BLPOP, + BRPOP, + brPop: BRPOP, + BRPOPLPUSH, + brPopLPush: BRPOPLPUSH, + CLIENT_CACHING, + clientCaching: CLIENT_CACHING, + CLIENT_GETNAME, + clientGetName: CLIENT_GETNAME, + CLIENT_GETREDIR, + clientGetRedir: CLIENT_GETREDIR, + CLIENT_ID, + clientId: CLIENT_ID, + CLIENT_INFO, + clientInfo: CLIENT_INFO, + CLIENT_KILL, + clientKill: CLIENT_KILL, + CLIENT_LIST, + clientList: CLIENT_LIST, + 'CLIENT_NO-EVICT': CLIENT_NO_EVICT, + clientNoEvict: CLIENT_NO_EVICT, + CLIENT_PAUSE, + clientPause: CLIENT_PAUSE, + CLIENT_SETNAME, + clientSetName: CLIENT_SETNAME, + CLUSTER_ADDSLOTS, + clusterAddSlots: CLUSTER_ADDSLOTS, + CLUSTER_SLOTS, + clusterSlots: CLUSTER_SLOTS, + CLUSTER_MEET, + clusterMeet: CLUSTER_MEET, + CLUSTER_MYID, + clusterMyId: CLUSTER_MYID, + CLUSTER_REPLICATE, + clusterReplicate: CLUSTER_REPLICATE, + DECR, + decr: DECR, + DECRBY, + decrBy: DECRBY, + GET, + get: GET, + GETDEL, + getDel: GETDEL, + GETEX, + getEx: GETEX, + GETRANGE, + getRange: GETRANGE, + GETSET, + getSet: GETSET, + FLUSHALL, + flushAll: FLUSHALL, + HDEL, + hDel: HDEL, + HEXISTS, + hExists: HEXISTS, + HGET, + hGet: HGET, + HGETALL, + hGetAll: HGETALL, + HINCRBY, + hIncrBy: HINCRBY, + HINCRBYFLOAT, + hIncrByFloat: HINCRBYFLOAT, + HKEYS, + hKeys: HKEYS, + HLEN, + hLen: HLEN, + HMGET, + hMGet: HMGET, + HRANDFIELD_COUNT_WITHVALUES, + hRandFieldCountWithValues: HRANDFIELD_COUNT_WITHVALUES, + HRANDFIELD_COUNT, + hRandFieldCount: HRANDFIELD_COUNT, + HRANDFIELD, + hRandField: HRANDFIELD, + HSCAN, + hScan: HSCAN, + HSET, + hSet: HSET, + HSETNX, + hSetNx: HSETNX, + HSTRLEN, + hStrLen: HSTRLEN, + HVALS, + hVals: HVALS, + INCR, + incr: INCR, + INCRBY, + incrBy: INCRBY, + INCRBYFLOAT, + incrByFloat: INCRBYFLOAT, + INFO, + info: INFO, + // LCS_IDX_WITHMATCHLEN, + // LCS_IDX, + LCS_LEN, + lcsLen: LCS_LEN, + LCS, + lcs: LCS, + LINDEX, + lIndex: LINDEX, + LINSERT, + lInsert: LINSERT, + LLEN, + lLen: LLEN, + LMOVE, + lMove: LMOVE, + LMPOP, + lmPop: LMPOP, + LPOP, + lPop: LPOP, + LPOS, + lPos: LPOS, + LPUSH, + lPush: LPUSH, + LPUSHX, + lPushX: LPUSHX, + LRANGE, + lRange: LRANGE, + LREM, + lRem: LREM, + LSET, + lSet: LSET, + LTRIM, + lTrim: LTRIM, + MGET, + mGet: MGET, + MSET, + mSet: MSET, + MSETNX, + mSetNx: MSETNX, + PFADD, + pfAdd: PFADD, + PFCOUNT, + pfCount: PFCOUNT, + PFMERGE, + pfMerge: PFMERGE, + PING, + /** + * ping jsdoc + */ + ping: PING, + PSETEX, + pSetEx: PSETEX, + RPOP, + rPop: RPOP, + RPOPLPUSH, + rPopLPush: RPOPLPUSH, + RPUSH, + rPush: RPUSH, + RPUSHX, + rPushX: RPUSHX, + SCAN, + scan: SCAN, + SET, + set: SET, + SETEX, + setEx: SETEX, + SETNX, + setNx: SETNX, + SETRANGE, + setRange: SETRANGE, + SMEMBERS, + sMembers: SMEMBERS, + SSCAN, + sScan: SSCAN, + STRLEN, + strLen: STRLEN, + ZSCAN, + zScan: ZSCAN +} as const satisfies Record; diff --git a/packages/client/lib/errors.ts b/packages/client/lib/errors.ts index 3070970315..898d3833a5 100644 --- a/packages/client/lib/errors.ts +++ b/packages/client/lib/errors.ts @@ -63,3 +63,7 @@ export class ErrorReply extends Error { this.stack = undefined; } } + +export class SimpleError extends ErrorReply {} + +export class BlobError extends ErrorReply {} diff --git a/packages/client/lib/lua-script.ts b/packages/client/lib/lua-script.ts index da19417ec2..8d20735a5f 100644 --- a/packages/client/lib/lua-script.ts +++ b/packages/client/lib/lua-script.ts @@ -1,22 +1,22 @@ import { createHash } from 'crypto'; -import { RedisCommand } from './commands'; +import { Command } from './RESP/types'; -export interface RedisScriptConfig extends RedisCommand { - SCRIPT: string; - NUMBER_OF_KEYS?: number; +export type RedisScriptConfig = Command & { + SCRIPT: string | Buffer; + NUMBER_OF_KEYS?: number; } export interface SHA1 { - SHA1: string; + SHA1: string; } export function defineScript(script: S): S & SHA1 { - return { - ...script, - SHA1: scriptSha1(script.SCRIPT) - }; + return { + ...script, + SHA1: scriptSha1(script.SCRIPT) + }; } -export function scriptSha1(script: string): string { - return createHash('sha1').update(script).digest('hex'); +export function scriptSha1(script: RedisScriptConfig['SCRIPT']): string { + return createHash('sha1').update(script).digest('hex'); } diff --git a/packages/client/lib/multi-command.ts b/packages/client/lib/multi-command.ts index 08f23ffa45..2c3f7f7ee7 100644 --- a/packages/client/lib/multi-command.ts +++ b/packages/client/lib/multi-command.ts @@ -1,87 +1,60 @@ -import { fCallArguments } from './commander'; -import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisFunction, RedisScript } from './commands'; +import { Command, CommandArguments, RedisScript, TransformReply } from './RESP/types'; import { WatchError } from './errors'; export interface RedisMultiQueuedCommand { - args: RedisCommandArguments; - transformReply?: RedisCommand['transformReply']; + args: CommandArguments; + transformReply?: TransformReply; } export default class RedisMultiCommand { - static generateChainId(): symbol { - return Symbol('RedisMultiCommand Chain Id'); + static generateChainId(): symbol { + return Symbol('RedisMultiCommand Chain Id'); + } + + readonly queue: Array = []; + + readonly scriptsInUse = new Set(); + + addCommand(args: CommandArguments, transformReply?: TransformReply) { + this.queue.push({ + args, + transformReply + }); + return this; + } + + addScript(script: RedisScript, args: CommandArguments, transformReply?: TransformReply) { + const redisArgs: CommandArguments = []; + if (this.scriptsInUse.has(script.SHA1)) { + redisArgs.push('EVALSHA', script.SHA1); + } else { + this.scriptsInUse.add(script.SHA1); + redisArgs.push('EVAL', script.SCRIPT); } - readonly queue: Array = []; - - readonly scriptsInUse = new Set(); - - addCommand(args: RedisCommandArguments, transformReply?: RedisCommand['transformReply']): void { - this.queue.push({ - args, - transformReply - }); + if (script.NUMBER_OF_KEYS !== undefined) { + redisArgs.push(script.NUMBER_OF_KEYS.toString()); } - addFunction(name: string, fn: RedisFunction, args: Array): RedisCommandArguments { - const transformedArguments = fCallArguments( - name, - fn, - fn.transformArguments(...args) - ); - this.queue.push({ - args: transformedArguments, - transformReply: fn.transformReply - }); - return transformedArguments; + redisArgs.push(...args); + redisArgs.preserve = args.preserve; + + return this.addCommand(redisArgs, transformReply); + } + + handleExecReplies(rawReplies: Array): Array { + const execReply = rawReplies[rawReplies.length - 1] as (null | Array); + if (execReply === null) { + throw new WatchError(); } - 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 - ); - } + return this.transformReplies(execReply); + } - if (script.NUMBER_OF_KEYS !== undefined) { - transformedArguments.push(script.NUMBER_OF_KEYS.toString()); - } - - const scriptArguments = script.transformArguments(...args); - transformedArguments.push(...scriptArguments); - if (scriptArguments.preserve) { - transformedArguments.preserve = scriptArguments.preserve; - } - - this.addCommand( - transformedArguments, - script.transformReply - ); - - 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 { - return rawReplies.map((reply, i) => { - const { transformReply, args } = this.queue[i]; - return transformReply ? transformReply(reply, args.preserve) : reply; - }); - } + transformReplies(rawReplies: Array): Array { + return rawReplies.map((reply, i) => { + const { transformReply, args } = this.queue[i]; + return transformReply ? transformReply(reply, args.preserve) : reply; + }); + } } diff --git a/packages/client/package.json b/packages/client/package.json index c342fce26f..7cd7c758d2 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -8,7 +8,7 @@ "dist/" ], "scripts": { - "test": "nyc -r text-summary -r lcov 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/commands/PING.spec.ts' './lib/commands/APPEND.spec.ts'", "build": "tsc", "lint": "eslint ./*.ts ./lib/**/*.ts", "documentation": "typedoc" @@ -21,19 +21,19 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", + "@types/node": "^18.15.10", "@types/sinon": "^10.0.13", "@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.57.0", + "@typescript-eslint/parser": "^5.57.0", + "eslint": "^8.36.0", "nyc": "^15.1.0", - "release-it": "^15.6.0", - "sinon": "^15.0.1", + "release-it": "^15.9.3", + "sinon": "^15.0.3", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.23.28", + "typescript": "^5.0.2" }, "engines": { "node": ">=14" diff --git a/packages/client/test.mjs b/packages/client/test.mjs new file mode 100644 index 0000000000..f50279ece2 --- /dev/null +++ b/packages/client/test.mjs @@ -0,0 +1,338 @@ +import { setTimeout } from 'timers/promises'; +import { createClient, defineScript, createCluster } from './dist/index.js'; +import { TYPES } from './dist/lib/RESP/decoder.js'; + +async function client() { + console.log(`!!! CLIENT !!!`); + + const client = createClient({ + RESP, + isolationPoolOptions: { + max: 5 + }, + modules: { + /** + * module jsdoc + */ + module: { + /** + * module ping jsdoc + */ + ping: { + /** + * @param {string} [message] + */ + transformArguments(message) { + const args = ['PING']; + if (message) { + args.push(message); + } + + return args; + }, + /** + * @callback PingReply + * @returns {import('./lib/RESP/types').SimpleStringReply} + * + * @type {PingReply} + */ + transformReply: undefined + }, + /** + * module square jsdoc + */ + square: { + /** + * @param {number} number + */ + transformArguments(number) { + return ['FCALL_RO', 'square', '0', number.toString()]; + }, + /** + * @callback SquareResp2 + * @returns {import('./lib/RESP/types').BlobStringReply} + * + * @callback SquareResp3 + * @returns {import('./lib/RESP/types').DoubleReply} + * + * @type {{ 2: SquareResp2, 3: SquareResp3 }} + */ + transformReply: undefined + } + } + }, + functions: { + /** + * library jsdoc + */ + library: { + /** + * library square jsdoc + */ + square: { + IS_READ_ONLY: true, + NUMBER_OF_KEYS: 0, + /** + * @param {number} number + */ + transformArguments(number) { + return [number.toString()]; + }, + /** + * @callback SquareResp2 + * @returns {import('./lib/RESP/types').BlobStringReply} + * + * @callback SquareResp3 + * @returns {import('./lib/RESP/types').DoubleReply} + * + * @type {{ 2: SquareResp2, 3: SquareResp3 }} + */ + transformReply: undefined + } + } + }, + scripts: { + /** + * square jsdoc + */ + square: defineScript({ + SCRIPT: 'return { double = ARGV[1] * ARGV[1] };', + NUMBER_OF_KEYS: 0, + /** + * @param {number} number + */ + transformArguments(number) { + return [number.toString()]; + }, + + /** + * @callback SquareResp2 + * @returns {import('./lib/RESP/types').BlobStringReply} + * + * @callback SquareResp3 + * @returns {import('./lib/RESP/types').DoubleReply} + * + * @type {{ 2: SquareResp2, 3: SquareResp3 }} + */ + transformReply: undefined + }) + } + }); + + const multi = client.multi() + .get('a') + .set('a', 'b'); + + for (let i = 0; i< 10; i++) { + multi.incr('a'); + } + + const result = await multi.exec(); + + const bufferClient = client.withFlags({ + [TYPES.SIMPLE_STRING]: Buffer, + [TYPES.BLOB_STRING]: Buffer + }); + + client.on('error', err => console.error(err)); + + await client.connect(); + + client.ping() + + // console.log( + // 'SCAN', + // await client.scan(0), + // await bufferClient.scan(0) + // ); + + // const fn = + // `#!LUA name=math + // redis.register_function{ + // function_name = "square", + // callback = function(keys, args) return { double = args[1] * args[1] } end, + // flags = { "no-writes" } + // }`; + + // await client.sendCommand(['FLUSHALL']); + // await client.sendCommand(['FUNCTION', 'LOAD', 'REPLACE', fn]); + + // console.log( + // 'info:\n', + // await client.info(), + // 'info with flags:\n', + // await client.withFlags({ + // [TYPES.VERBATIM_STRING]: VerbatimString + // }).info(), + // ); + + // console.log( + // 'client.module.square (module):', + // await client.module.square(1), + // await client.withFlags({ + // [TYPES.DOUBLE]: String + // }).module.square(1) + // ); + + // console.log( + // 'client.library.square (function):', + // await client.library.square(2), + // await client.withFlags({ + // [TYPES.DOUBLE]: String + // }).library.square(2) + // ); + + // console.log( + // 'client.square (script):', + // await client.square(4), + // await client.withFlags({ + // [TYPES.DOUBLE]: String + // }).square(4) + // ); + + // console.log( + // 'MULTI', + // await client.multi() + // .ping() + // .module.ping() + // .library.square(2) + // .square(4) + // .exec() + // ); + + // console.log( + // 'SET key value', + // await client.set('key', 'value'), + // ); + + // console.log( + // 'GET key', + // await client.get('key'), + // ); + + // console.log( + // 'GET key (bufferClient)', + // await bufferClient.get('key'), + // ); + + // console.log( + // 'sendCommand DEL key', + // await client.sendCommand(['DEL', 'key']) + // ); + + // console.log( + // 'HSET key field value', + // await client.hSet('key', 'field', 'value') + // ); + + // console.log( + // 'HGET key field', + // await client.hGet('key', 'field') + // ); + + // console.log( + // 'HGETALL key', + // await client.hGetAll('key') + // ); + + // console.log( + // 'HGETALL key (bufferClient)', + // await bufferClient.hGetAll('key') + // ); + + // console.log( + // 'CLIENT ID', + // await client.sendCommand(['CLIENT', 'ID']), + // ); + + // await client.subscribe('channel', message => { + // console.log('channel', message); + // }); + + // let publisherClient; + // if (RESP !== 3) { + // publisherClient = client.duplicate(); + // publisherClient.on('error', err => console.error('PubSubClient error', err)); + + // await publisherClient.connect(); + // } + + // const TIMES = 3; + // console.log( + // `[PUBLISH channel ] [PING ] * ${TIMES}`, + // await Promise.all( + // Array.from({ length: 5 }).map((_, i) => + // Promise.all([ + // (publisherClient ?? client).sendCommand(['PUBLISH', 'channel', i.toString()]).catch(), + // client.ping(i.toString()), + // client.isolated().clientId(), + // client.executeIsolated(client => client.clientId()) + // ]) + // ) + // ) + // ); + + const entries = Array.from({ length: 100 }).map((_, i) => ['{a}' + i.toString(), i.toString()]) + + await client.mSet(entries); + for await (const key of client.scanIterator()) { + console.log('SCAN', key); + } + + await client.hSet('hash', entries.flat()); + for await (const entry of client.hScanIterator('hash')) { + console.log('HSCAN', entry) + } + + await Promise.all([ + // publisherClient?.disconnect(), + client.disconnect() + ]); +} + +async function cluster() { + console.log(`!!! CLUSTER !!!`); + + const cluster = createCluster({ + rootNodes: [{}], + RESP + }); + cluster.on('error', err => console.error(err)); + + await cluster.connect(); + + console.log( + 'SET key value', + await cluster.set('key', 'value') + ); + + console.log( + 'GET key', + await cluster.get('key') + ); + + await cluster.subscribe('channel', message => { + console.log('(cluster) channel', message); + }); + + const CLUSTER_TIMES = 3; + console.log( + `[PUBLISH channel ] [PING ] * ${CLUSTER_TIMES}`, + await Promise.all( + Array.from({ length: 5 }).map(async (_, i) => { + const client = await cluster.nodeClient(cluster.getRandomNode()); + return client.sendCommand(['PUBLISH', 'channel', i.toString()]); + }) + ) + ); + + // wait for messages + await setTimeout(1000); + + await cluster.disconnect(); +} + +const RESP = 3; + +await client(); +// await cluster(); diff --git a/packages/graph/package.json b/packages/graph/package.json index 40ce8b7c56..d7e0e7f368 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.14.1", + "@types/node": "^18.15.10", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.9.3", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.23.28", + "typescript": "^5.0.2" } } diff --git a/packages/json/lib/commands/GET.ts b/packages/json/lib/commands/GET.ts index 21bad09568..107f9dd975 100644 --- a/packages/json/lib/commands/GET.ts +++ b/packages/json/lib/commands/GET.ts @@ -1,4 +1,4 @@ -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; export const FIRST_KEY_INDEX = 1; @@ -17,7 +17,7 @@ export function transformArguments(key: string, options?: GetOptions): RedisComm let args: RedisCommandArguments = ['JSON.GET', key]; if (options?.path) { - args = pushVerdictArguments(args, options.path); + args = pushVariadicArguments(args, options.path); } if (options?.INDENT) { diff --git a/packages/json/package.json b/packages/json/package.json index 6f4c468bfd..738f937b73 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.14.1", + "@types/node": "^18.15.10", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.9.3", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.23.28", + "typescript": "^5.0.2" } } diff --git a/packages/old/RESP3-old/decoder.ts b/packages/old/RESP3-old/decoder.ts new file mode 100644 index 0000000000..a6b3a78248 --- /dev/null +++ b/packages/old/RESP3-old/decoder.ts @@ -0,0 +1,423 @@ +// RESP3 specification +// https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md + +import BufferComposer from '../RESP/composers/buffer'; +import { Composer } from '../RESP/composers/interface'; +import StringComposer from '../RESP/composers/string'; +import { Flags, Reply, ReplyTypes, TransformReplyType } from './types'; +import { NestedDecoderInterface } from './nested-decoders/abstract'; + +const ASCII = { + '\r': 13, + 't': 116, + '0': 48, + '-': 45, + '.': 46, + '?': 63, + 'n': 110, + 'i': 105 +}; + +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, + MAP: 37, // % + SET: 126, // ~ + // ATTRIBUTE: 124, // | + // PUSH: 62 // > +} as const; + +type RespTypes = typeof RESP_TYPES[keyof typeof RESP_TYPES]; + +interface RESP3DecoderConfig { + onReply(reply: Reply): unknown; +} + +type NestedState = NestedDecoderInterface; + +// interface NestedState< +// REMAINING extends number | undefined = number | undefined, +// REPLY extends NestedDecoderInterface = NestedDecoderInterface +// > { +// type?: RespTypes; +// remaining: REMAINING; +// reply: REPLY; +// } + +// Using TypeScript `private` and not the build-in `#` to avoid __classPrivateFieldGet and __classPrivateFieldSet + +export default class RESP3Decoder { + constructor(private config: RESP3DecoderConfig) {} + + private cursor = 0; + + private type?: RespTypes; + + write(chunk: Buffer, flags: Flags): void { + while (this.cursor < chunk.length) { + let reply = this.decode(chunk, flags); + if (reply !== undefined) { + this.config.onReply(reply); + } + } + + this.cursor -= chunk.length; + } + + private decode(chunk: Buffer, flags: Flags) { + // while (this.stack.length) { + // if (this.type !== undefined) { + // const item = this.decodeType(chunk, this.type, flags); + // if (item === undefined) return; + + // let toPush = item; + // do { + // toPush = this.stack[this.stack.length - 1].reply.push(item); + // } while (toPush); + + // } + + // if (reply !== undefined) { + // const toPush = this.stack.pop(); + // this.stack[this.stack.length].reply.push(reply); + // } + // } + if (this.type === undefined) { + const type = chunk[this.cursor] as RespTypes; + if (++this.cursor === chunk.length) { + this.type = type; + return; + } + const reply = this.decodeType(chunk, type, flags); + if (reply === undefined) { + this.type = type; + } else { + return reply; + } + } + + const reply = this.decodeType(chunk, this.type, flags); + if (reply !== undefined) { + this.type = undefined; + return reply; + } + } + + private decodeType(chunk: Buffer, type: T, flags: F) { + switch (type) { + case RESP_TYPES.NULL: + return this.decodeNull(); + + case RESP_TYPES.BOOLEAN: + return this.decodeBoolean(chunk); + + case RESP_TYPES.NUMBER: + return this.decodeNumber(chunk, flags); + + // case RESP_TYPES.BIG_NUMBER: + // return this.decodeBigNumber(chunk); + + case RESP_TYPES.DOUBLE: + return this.decodeDouble(chunk, flags); + + case RESP_TYPES.SIMPLE_STRING: + return this.decodeSimpleString(chunk, flags); + + case RESP_TYPES.MAP: + return this.decodeMap(chunk, flags); + } + } + + private decodeNull() { + this.cursor += 2; // skip CRLF + return null; + } + + private decodeBoolean(chunk: Buffer) { + const reply = chunk[this.cursor + 1] === ASCII.t; + this.cursor += 3; // skip value and CRLF + return reply; + } + + private decodeNumber(chunk: Buffer, flags: F) { + switch (flags[RESP_TYPES.NUMBER]) { + case Buffer: + return this.composeString(chunk, this.bufferComposer); + + case String: + return this.composeString(chunk, this.stringComposer); + + case Number: + default: + return this.decodeNumberAsNumber(chunk); + } + } + + private isNegativeNumber?: boolean; + + private number = 0; + + private decodeNumberAsNumber(chunk: Buffer) { + if (this.isNegativeNumber === undefined) { + this.isNegativeNumber = chunk[this.cursor] === ASCII['-']; + if (this.isNegativeNumber && ++this.cursor === chunk.length) return; + } + + do { + const byte = chunk[this.cursor]; + if (byte === ASCII['\r']) { + const number = this.isNegativeNumber ? -this.number : this.number; + this.isNegativeNumber = undefined; + this.number = 0; + this.cursor += 2; // skip CRLF + return number; + } + + this.number = this.number * 10 + byte - ASCII['0']; + } while (++this.cursor < chunk.length); + } + + private isNegativeDouble?: boolean; + + // Precalculated multipliers for decimal points to improve performance + // "A Number only keeps about 17 decimal places of precision" + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number + private static DOUBLE_DECIMAL_MULTIPLIERS = [ + 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, + 1e-7, 1e-8, 1e-9, 1e-10, 1e-11, 1e-12, + 1e-13, 1e-14, 1e-15, 1e-16, 1e-17 + ]; + + private doubleDecimalIndex?: number; + + private double = 0; + + private decodeDouble(chunk: Buffer, flags: Flags) { + switch (flags[RESP_TYPES.NUMBER]) { + case Buffer: + return this.composeString(chunk, this.bufferComposer); + + case String: + return this.composeString(chunk, this.stringComposer); + + case Number: + default: + return this.decodeDoubleAsNumber(chunk); + } + } + + private decodeDoubleAsNumber(chunk: Buffer) { + if (this.isNegativeDouble === undefined) { + this.isNegativeDouble = chunk[this.cursor] === ASCII['-']; + if (this.isNegativeDouble && ++this.cursor === chunk.length) return; + } else if (this.doubleDecimalIndex !== undefined) { + return this.decodeDecimalDouble(chunk); + } + + if (this.double === 0) { + switch (chunk[this.cursor]) { + case ASCII.i: + this.cursor += 3; // skip to CRLF + return this.returnDouble(Infinity); + + case ASCII.n: + this.cursor += 3; // skip to CRLF + return this.returnDouble(NaN); + } + } + + do { + const byte = chunk[this.cursor]; + switch (byte) { + case ASCII['\r']: + return this.returnDouble(); + + case ASCII['.']: + this.doubleDecimalIndex = 0; + if (++this.cursor === chunk.length) return; + return this.decodeDecimalDouble(chunk); + + default: + this.double = this.double * 10 + byte - ASCII['0']; + } + } while (++this.cursor < chunk.length); + } + + private returnDouble(value = this.double) { + const double = this.isNegativeDouble ? -value : value; + this.isNegativeDouble = undefined; + this.doubleDecimalIndex = undefined; + this.double = 0; + this.cursor += 2; // skip CRLF + return double; + } + + private decodeDecimalDouble(chunk: Buffer): number | undefined { + do { + const byte = chunk[this.cursor]; + if (byte === ASCII['\r']) { + return this.returnDouble(); + } + + if (RESP3Decoder.DOUBLE_DECIMAL_MULTIPLIERS.length < this.doubleDecimalIndex!) { + const multiplier = RESP3Decoder.DOUBLE_DECIMAL_MULTIPLIERS[this.doubleDecimalIndex!++]; + this.double += (byte - ASCII['0']) * multiplier; + } + } while (++this.cursor < chunk.length); + } + + private bufferComposer = new BufferComposer(); + + private stringComposer = new StringComposer(); + + private decodeSimpleString(chunk: Buffer, flags: Flags) { + switch (flags[RESP_TYPES.SIMPLE_STRING]) { + case Buffer: + return this.composeString(chunk, this.bufferComposer); + + case String: + default: + return this.composeString(chunk, this.stringComposer); + } + } + + private composeString(chunk: Buffer, composer: Composer): T | undefined { + for (let i = this.cursor; i < chunk.length; i++) { + if (chunk[i] === ASCII['\r']) { + 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 initializingNested = false; + + private nestedStack: Array<{ + type: RespTypes | undefined, + size: number, + value: Reply | undefined + }> = []; + + private decodeNested(type: RespTypes, chunk: Buffer, flags: Flags, toKeep: number) { + if (this.nestedStack.length > toKeep) { + const frame = this.nestedStack[this.nestedStack.length - 1]; + if (frame.type === undefined) { + frame.type = chunk[this.cursor++] as RespTypes; + if (this.cursor === chunk.length) return; + } + + + } + if (this.initializingNested || this.nestedStack.length === toKeep) { + // TODO: https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md#streamed-aggregated-data-types + if (chunk[this.cursor] === ASCII['?']) { + this.cursor += 3; // skip ? and CRLF + this.nestedStack.push({ + type: undefined, + size: NaN, + value: undefined + }); + } + const size = this.decodeNumberAsNumber(chunk); + if (size === undefined) { + this.initializingNested = true; + return; + } + + } + } + + // private mapKey?: Reply; + + // private decodeMap(chunk: Buffer, flags: Flags) { + // // const isNew = depth >= this.stack.length; + // // let state = this.stack[depth]; + // // if (state?.remaining === undefined) { + // // const size = this.decodeNumberAsNumber(chunk); + // // if (size === undefined) { + // // this.stack.push({ + // // type: RESP_TYPES.MAP, + // // remaining: undefined, + // // reply: undefined + // // }); + // // return; + // // } + // // if (size === 0) return {}; + // // state = { + // // type: RESP_TYPES.MAP, + // // remaining: size, + // // reply: {} + // // }; + // // } else { + // // state = this.stack[depth]; + // // } + // // // state = isNew ? this.stack[depth] : { + // // // type: RESP_TYPES.MAP, + // // // remaining: undefined, + // // // reply: undefined + // // // }; + + // switch (flags[RESP_TYPES.MAP]) { + // case Object: + // return this.decodeMapAsObject(chunk, flags, state, depth); // TODO + + // case Array: + // return; // TODO + + // case Map: + // default: + // return; // TODO + // } + // } + + // private decodeMapAsObject(chunk: Buffer, flags: Flags, state: NestedDecoderState, depth: number) { + // if (this.mapKey !== undefined) { + // const value = this. + // } + + // do { + // const key = this.decodeNewItem(chunk, flags); + // if (key === undefined) break; + // const value = this.decodeNewItem(chunk, flags); + // if (value === undefined) break; + // state.reply[`${key}`] = value; + + // if (--state.remaining! === 0) return state.reply; + // } while (this.cursor < chunk.length); + // } + + // private keepNested(chunk: Buffer, flags: Flags) { + // const reply = this.decodeType(chunk, this.stack[this.stack.length - 1], flags) + // } + + + reset() { + this.cursor = 0; + this.type = undefined; + this.isNegativeNumber = undefined; + this.number = 0; + this.isNegativeDouble = undefined; + this.doubleDecimalIndex = undefined; + this.double = 0; + this.bufferComposer.reset(); + this.stringComposer.reset(); + this.initializingNested = false; + this.nestedStack = []; + } +} \ No newline at end of file diff --git a/packages/old/RESP3-old/encoder.ts b/packages/old/RESP3-old/encoder.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/old/RESP3-old/nested-decoders/abstract.ts b/packages/old/RESP3-old/nested-decoders/abstract.ts new file mode 100644 index 0000000000..b3e5b5c304 --- /dev/null +++ b/packages/old/RESP3-old/nested-decoders/abstract.ts @@ -0,0 +1,6 @@ +import { Reply } from '../types'; + +export interface NestedDecoderInterface { + push(item: Reply): void | undefined | T; +} + diff --git a/packages/old/RESP3-old/nested-decoders/map-to-array.ts b/packages/old/RESP3-old/nested-decoders/map-to-array.ts new file mode 100644 index 0000000000..21c03567ad --- /dev/null +++ b/packages/old/RESP3-old/nested-decoders/map-to-array.ts @@ -0,0 +1,28 @@ +import { NestedDecoderInterface } from './abstract'; +import { Reply } from '../types'; + +type Type = Array<[Reply, Reply]>; + +export class MapToArray implements NestedDecoderInterface { + private array: Type; + + private remaining: number; + + constructor(size: number) { + this.array = new Array(size); + this.remaining = size; + } + + private key: Reply | undefined; + + push(item: Reply) { + if (this.key === undefined) { + this.key = item; + return; + } + + this.array[this.array.length - this.remaining--] = [this.key, item]; + this.key = undefined; + if (this.remaining === 0) return this.array; + } +} diff --git a/packages/old/RESP3-old/nested-decoders/to-array.ts b/packages/old/RESP3-old/nested-decoders/to-array.ts new file mode 100644 index 0000000000..ae4d2d7cc2 --- /dev/null +++ b/packages/old/RESP3-old/nested-decoders/to-array.ts @@ -0,0 +1,20 @@ +import { NestedDecoderInterface } from './abstract'; +import { Reply } from '../types'; + +type Type = Array; + +export class ToArray implements NestedDecoderInterface { + private array: Type; + + private remaining: number; + + constructor(size: number) { + this.array = new Array(size); + this.remaining = size; + } + + push(item: Reply) { + this.array[this.array.length - this.remaining--] = item; + if (this.remaining === 0) return this.array; + } +} diff --git a/packages/old/RESP3-old/nested-decoders/to-map.ts b/packages/old/RESP3-old/nested-decoders/to-map.ts new file mode 100644 index 0000000000..d689a04cb2 --- /dev/null +++ b/packages/old/RESP3-old/nested-decoders/to-map.ts @@ -0,0 +1,28 @@ +import { NestedDecoderInterface } from './abstract'; +import { Reply } from '../types'; + +type Type = Map; + +export class MapToArray implements NestedDecoderInterface { + private map: Type; + + private remaining: number; + + constructor(size: number) { + this.map = new Map(); + this.remaining = size; + } + + private key: Reply | undefined; + + push(item: Reply) { + if (this.key === undefined) { + this.key = item; + return; + } + + this.map.set(this.key, item); + this.key = undefined; + if (--this.remaining === 0) return this.map; + } +} diff --git a/packages/old/RESP3-old/nested-decoders/to-object.ts b/packages/old/RESP3-old/nested-decoders/to-object.ts new file mode 100644 index 0000000000..7fe4726979 --- /dev/null +++ b/packages/old/RESP3-old/nested-decoders/to-object.ts @@ -0,0 +1,28 @@ +import { NestedDecoderInterface } from './abstract'; +import { Reply } from '../types'; + +type Type = Record; + +export class MapToArray implements NestedDecoderInterface { + private object: Type; + + private remaining: number; + + constructor(size: number) { + this.object = {}; + this.remaining = size; + } + + private key: Reply | undefined; + + push(item: Reply) { + if (this.key === undefined) { + this.key = item; + return; + } + + this.object[this.key] = item; + this.key = undefined; + if (--this.remaining === 0) return this.object; + } +} diff --git a/packages/old/RESP3-old/nested-decoders/to-set.ts b/packages/old/RESP3-old/nested-decoders/to-set.ts new file mode 100644 index 0000000000..d44444c97c --- /dev/null +++ b/packages/old/RESP3-old/nested-decoders/to-set.ts @@ -0,0 +1,20 @@ +import { NestedDecoderInterface } from './abstract'; +import { Reply } from '../types'; + +type Type = Set; + +export class ToArray implements NestedDecoderInterface { + private set: Type; + + private remaining: number; + + constructor(size: number) { + this.set = new Set(); + this.remaining = size; + } + + push(item: Reply) { + this.set.add(item); + if (--this.remaining === 0) return this.set; + } +} diff --git a/packages/old/RESP3-old/types.ts b/packages/old/RESP3-old/types.ts new file mode 100644 index 0000000000..a15c851ce7 --- /dev/null +++ b/packages/old/RESP3-old/types.ts @@ -0,0 +1,101 @@ +import { RESP_TYPES } from './decoder'; + +type RESP_TYPES = { + [P in keyof typeof RESP_TYPES]: typeof RESP_TYPES[P]; +}; + +interface TypeMapping { + DEFAULT_TYPE: DEFAULT_TYPE; + TYPES: TYPES; +} + +interface TypesMapping { + [RESP_TYPES.NULL]: TypeMapping; + [RESP_TYPES.BOOLEAN]: TypeMapping; + [RESP_TYPES.NUMBER]: TypeMapping; + [RESP_TYPES.BIG_NUMBER]: TypeMapping; + [RESP_TYPES.DOUBLE]: TypeMapping; + [RESP_TYPES.SIMPLE_STRING]: TypeMapping; + [RESP_TYPES.BLOB_STRING]: TypeMapping; + [RESP_TYPES.ARRAY]: ArrayReplyTypeMapping; + [RESP_TYPES.MAP]: MapReplyTypeMapping; + [RESP_TYPES.SET]: SetReplyTypeMapping; +} + +type ArrayReplyTypeMapping = TypeMapping>; + +type ArrayReply = ReplyType>; + +type MapReplyTypeMapping = TypeMapping, { [key: string]: V; } | Array<[K, V]>>; + +type MapReply = ReplyType>; + +type SetReplyTypeMapping = TypeMapping, Array>; + +type SetReply = ReplyType>; + +type ReplyType< + RESP_TYPE extends RESP_TYPES[keyof RESP_TYPES], + TYPES extends TypeMapping +> = { + // TypeScript uses structural typing, `unique symbol` is the only way to make a type unique + // https://www.typescriptlang.org/docs/handbook/symbols.html#unique-symbol + // not using `[P in RESP_TYPE]` because it breaks inference in some cases + SYMBOL: { readonly _: unique symbol; }; + RESP_TYPE: RESP_TYPE; +} & TYPES; + +export type ReplyTypes = { + [P in keyof TypesMapping]: ReplyType; +}; + +type PrimitiveReplyTypes = ReplyTypes[Exclude]; + +type TT = TypeMapping>; + +export type Reply = string | TT; + +type Flag = (new (...args: Array) => T) | ((...args: Array) => T); + +export type Flags = { + [P in keyof TypesMapping]?: Flag; +}; + +type PickByFlag< + FLAT_TYPE, + TYPES extends TypeMapping +> = TYPES['DEFAULT_TYPE'] extends FLAT_TYPE ? TYPES['DEFAULT_TYPE'] : Extract; + +export type TransformReplyType< + FLAGS extends Flags, + REPLY +> = REPLY extends ReplyType ? ( + TransformReplyType ? PickByFlag : TYPES['DEFAULT_TYPE']> + ) : + REPLY extends Map ? Map, TransformReplyType> : + REPLY extends [infer T] ? [TransformReplyType] : + REPLY extends [infer T, ...infer REST] ? [TransformReplyType, ...TransformReplyType] : + REPLY extends Array ? Array> : + REPLY extends Record ? { + [K in keyof REPLY]: TransformReplyType + } : + REPLY extends Set ? Set> : REPLY; + +type a = TransformReplyType< + { + // [RESP_TYPES.MAP]: ArrayConstructor + [RESP_TYPES.DOUBLE]: StringConstructor, + // [RESP_TYPES.NUMBER]: BufferConstructor + }, + { + null: ReplyTypes[RESP_TYPES['NULL']], + boolean: ReplyTypes[RESP_TYPES['BOOLEAN']], + number: ReplyTypes[RESP_TYPES['NUMBER']], + bigNumber: ReplyTypes[RESP_TYPES['BIG_NUMBER']], + array: ArrayReply, + map: MapReply, + set: SetReply + } +>; + +type b = TransformReplyType ? PickByFlag> : number>; diff --git a/packages/search/lib/commands/AGGREGATE.ts b/packages/search/lib/commands/AGGREGATE.ts index c32d20b0b1..fb831f54bc 100644 --- a/packages/search/lib/commands/AGGREGATE.ts +++ b/packages/search/lib/commands/AGGREGATE.ts @@ -1,5 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArgument, transformTuplesReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArgument, transformTuplesReply } from '@redis/client/dist/lib/commands/generic-transformers'; import { Params, PropertyName, pushArgumentsWithLength, pushParamsArgs, pushSortByArguments, SortByProperty } from '.'; export enum AggregateSteps { @@ -170,7 +170,7 @@ export function pushAggregatehOptions( if (!step.properties) { args.push('0'); } else { - pushVerdictArgument(args, step.properties); + pushVariadicArgument(args, step.properties); } if (Array.isArray(step.REDUCE)) { diff --git a/packages/search/lib/commands/CREATE.ts b/packages/search/lib/commands/CREATE.ts index 21662c28d7..32440ede38 100644 --- a/packages/search/lib/commands/CREATE.ts +++ b/packages/search/lib/commands/CREATE.ts @@ -1,4 +1,4 @@ -import { pushOptionalVerdictArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushOptionalVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { RedisSearchLanguages, PropertyName, RediSearchSchema, pushSchema } from '.'; interface CreateOptions { @@ -27,7 +27,7 @@ export function transformArguments(index: string, schema: RediSearchSchema, opti args.push('ON', options.ON); } - pushOptionalVerdictArgument(args, 'PREFIX', options?.PREFIX); + pushOptionalVariadicArgument(args, 'PREFIX', options?.PREFIX); if (options?.FILTER) { args.push('FILTER', options.FILTER); @@ -81,7 +81,7 @@ export function transformArguments(index: string, schema: RediSearchSchema, opti args.push('SKIPINITIALSCAN'); } - pushOptionalVerdictArgument(args, 'STOPWORDS', options?.STOPWORDS); + pushOptionalVariadicArgument(args, 'STOPWORDS', options?.STOPWORDS); args.push('SCHEMA'); pushSchema(args, schema); diff --git a/packages/search/lib/commands/DICTADD.ts b/packages/search/lib/commands/DICTADD.ts index 60af11fd41..2a8d9c522b 100644 --- a/packages/search/lib/commands/DICTADD.ts +++ b/packages/search/lib/commands/DICTADD.ts @@ -1,8 +1,8 @@ import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export function transformArguments(dictionary: string, term: string | Array): RedisCommandArguments { - return pushVerdictArguments(['FT.DICTADD', dictionary], term); + return pushVariadicArguments(['FT.DICTADD', dictionary], term); } export declare function transformReply(): number; diff --git a/packages/search/lib/commands/DICTDEL.ts b/packages/search/lib/commands/DICTDEL.ts index a1b728f192..8a8513f559 100644 --- a/packages/search/lib/commands/DICTDEL.ts +++ b/packages/search/lib/commands/DICTDEL.ts @@ -1,8 +1,8 @@ import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export function transformArguments(dictionary: string, term: string | Array): RedisCommandArguments { - return pushVerdictArguments(['FT.DICTDEL', dictionary], term); + return pushVariadicArguments(['FT.DICTDEL', dictionary], term); } export declare function transformReply(): number; diff --git a/packages/search/lib/commands/SYNUPDATE.ts b/packages/search/lib/commands/SYNUPDATE.ts index 3384ea59d9..80582759c4 100644 --- a/packages/search/lib/commands/SYNUPDATE.ts +++ b/packages/search/lib/commands/SYNUPDATE.ts @@ -1,4 +1,4 @@ -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; interface SynUpdateOptions { @@ -17,7 +17,7 @@ export function transformArguments( args.push('SKIPINITIALSCAN'); } - return pushVerdictArguments(args, terms); + return pushVariadicArguments(args, terms); } export declare function transformReply(): 'OK'; diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index d56db7bdbb..f3fb16d702 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -32,7 +32,7 @@ import * as SYNDUMP from './SYNDUMP'; import * as SYNUPDATE from './SYNUPDATE'; import * as TAGVALS from './TAGVALS'; import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushOptionalVerdictArgument, pushVerdictArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushOptionalVariadicArgument, pushVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { SearchOptions } from './SEARCH'; export default { @@ -411,9 +411,9 @@ export function pushSearchOptions( // args.push('WITHPAYLOADS'); // } - pushOptionalVerdictArgument(args, 'INKEYS', options?.INKEYS); - pushOptionalVerdictArgument(args, 'INFIELDS', options?.INFIELDS); - pushOptionalVerdictArgument(args, 'RETURN', options?.RETURN); + pushOptionalVariadicArgument(args, 'INKEYS', options?.INKEYS); + pushOptionalVariadicArgument(args, 'INFIELDS', options?.INFIELDS); + pushOptionalVariadicArgument(args, 'RETURN', options?.RETURN); if (options?.SUMMARIZE) { args.push('SUMMARIZE'); @@ -421,7 +421,7 @@ export function pushSearchOptions( if (typeof options.SUMMARIZE === 'object') { if (options.SUMMARIZE.FIELDS) { args.push('FIELDS'); - pushVerdictArgument(args, options.SUMMARIZE.FIELDS); + pushVariadicArgument(args, options.SUMMARIZE.FIELDS); } if (options.SUMMARIZE.FRAGS) { @@ -444,7 +444,7 @@ export function pushSearchOptions( if (typeof options.HIGHLIGHT === 'object') { if (options.HIGHLIGHT.FIELDS) { args.push('FIELDS'); - pushVerdictArgument(args, options.HIGHLIGHT.FIELDS); + pushVariadicArgument(args, options.HIGHLIGHT.FIELDS); } if (options.HIGHLIGHT.TAGS) { diff --git a/packages/search/package.json b/packages/search/package.json index 684bfe1ff0..43f50ade83 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.14.1", + "@types/node": "^18.15.10", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.9.3", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.23.28", + "typescript": "^5.0.2" } } diff --git a/packages/test-utils/lib/dockers.ts b/packages/test-utils/lib/dockers.ts index a7e1c610ee..d750b7d512 100644 --- a/packages/test-utils/lib/dockers.ts +++ b/packages/test-utils/lib/dockers.ts @@ -2,259 +2,263 @@ import { createConnection } from 'net'; import { once } from 'events'; import RedisClient from '@redis/client/dist/lib/client'; import { promiseTimeout } from '@redis/client/dist/lib/utils'; -import { ClusterSlotsReply } from '@redis/client/dist/lib/commands/CLUSTER_SLOTS'; +// import { ClusterSlotsReply } from '@redis/client/dist/lib/commands/CLUSTER_SLOTS'; import * as path from 'path'; import { promisify } from 'util'; import { exec } from 'child_process'; const execAsync = promisify(exec); interface ErrorWithCode extends Error { - code: string; + code: string; } async function isPortAvailable(port: number): Promise { - try { - const socket = createConnection({ port }); - await once(socket, 'connect'); - socket.end(); - } catch (err) { - if (err instanceof Error && (err as ErrorWithCode).code === 'ECONNREFUSED') { - return true; - } + try { + const socket = createConnection({ port }); + await once(socket, 'connect'); + socket.end(); + } catch (err) { + if (err instanceof Error && (err as ErrorWithCode).code === 'ECONNREFUSED') { + return true; } + } - return false; + return false; } -const portIterator = (async function*(): AsyncIterableIterator { - for (let i = 6379; i < 65535; i++) { - if (await isPortAvailable(i)) { - yield i; - } +const portIterator = (async function* (): AsyncIterableIterator { + for (let i = 6379; i < 65535; i++) { + if (await isPortAvailable(i)) { + yield i; } + } - throw new Error('All ports are in use'); + throw new Error('All ports are in use'); })(); export interface RedisServerDockerConfig { - image: string; - version: string; + image: string; + version: string; } export interface RedisServerDocker { - port: number; - dockerId: string; + port: number; + dockerId: string; } // ".." cause it'll be in `./dist` const DOCKER_FODLER_PATH = path.join(__dirname, '../docker'); async function spawnRedisServerDocker({ image, version }: RedisServerDockerConfig, serverArguments: Array): Promise { - const port = (await portIterator.next()).value, - { stdout, stderr } = await execAsync( - 'docker run -d --network host $(' + - `docker build ${DOCKER_FODLER_PATH} -q ` + - `--build-arg IMAGE=${image}:${version} ` + - `--build-arg REDIS_ARGUMENTS="--save '' --port ${port.toString()} ${serverArguments.join(' ')}"` + - ')' - ); + const port = (await portIterator.next()).value, + { stdout, stderr } = await execAsync( + 'docker run -d --network host $(' + + `docker build ${DOCKER_FODLER_PATH} -q ` + + `--build-arg IMAGE=${image}:${version} ` + + `--build-arg REDIS_ARGUMENTS="--save '' --port ${port.toString()} ${serverArguments.join(' ')}"` + + ')' + ); - if (!stdout) { - throw new Error(`docker run error - ${stderr}`); - } + if (!stdout) { + throw new Error(`docker run error - ${stderr}`); + } - while (await isPortAvailable(port)) { - await promiseTimeout(50); - } + while (await isPortAvailable(port)) { + await promiseTimeout(50); + } - return { - port, - dockerId: stdout.trim() - }; + return { + port, + dockerId: stdout.trim() + }; } const RUNNING_SERVERS = new Map, ReturnType>(); export function spawnRedisServer(dockerConfig: RedisServerDockerConfig, serverArguments: Array): Promise { - const runningServer = RUNNING_SERVERS.get(serverArguments); - if (runningServer) { - return runningServer; - } + const runningServer = RUNNING_SERVERS.get(serverArguments); + if (runningServer) { + return runningServer; + } - const dockerPromise = spawnRedisServerDocker(dockerConfig, serverArguments); - RUNNING_SERVERS.set(serverArguments, dockerPromise); - return dockerPromise; + 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 range: Array = []; + for (let i = fromSlot; i < toSlot; i++) { + range.push(i); + } - const master = await spawnRedisClusterNodeDocker( - dockersConfig, - serverArguments - ); + const master = await spawnRedisClusterNodeDocker( + dockersConfig, + serverArguments + ); - await master.client.clusterAddSlots(range); + 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); + if (!dockersConfig.numberOfReplicas) return [master]; - while ((await replica.client.clusterSlots()).length === 0) { - await promiseTimeout(50); - } + 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); - await replica.client.clusterReplicate( - await master.client.clusterMyId() - ); + while ((await replica.client.clusterSlots()).length === 0) { + await promiseTimeout(50); + } - 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 - } - }); + const docker = await spawnRedisServerDocker(dockersConfig, [ + ...serverArguments, + '--cluster-enabled', + 'yes', + '--cluster-node-timeout', + '5000' + ]), + client = RedisClient.create({ + socket: { + port: docker.port + } + }); - await client.connect(); + await client.connect(); - return { - docker, - client - }; + 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 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( - nodes.map(async ({ client }) => { - while (totalNodes(await client.clusterSlots()) !== nodes.length) { - await promiseTimeout(50); - } - - return client.disconnect(); - }) + 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 + ) ); + } - return nodes.map(({ docker }) => docker); + 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( + nodes.map(async ({ client }) => { + while ( + totalNodes(await client.clusterSlots()) !== nodes.length || + !(await client.sendCommand(['CLUSTER', 'INFO'])).startsWith('cluster_state:ok') // TODO + ) { + await promiseTimeout(50); + } + + return client.disconnect(); + }) + ); + + return nodes.map(({ docker }) => docker); } -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 runningCluster = RUNNING_CLUSTERS.get(serverArguments); + if (runningCluster) { + return runningCluster; + } - const dockersPromise = spawnRedisClusterDockers(dockersConfig, serverArguments); - RUNNING_CLUSTERS.set(serverArguments, dockersPromise); - return dockersPromise; + 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 b9195c5717..7a783792ec 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -1,226 +1,269 @@ -import { RedisModules, RedisFunctions, RedisScripts } from '@redis/client/lib/commands'; -import RedisClient, { RedisClientOptions, RedisClientType } from '@redis/client/lib/client'; -import RedisCluster, { RedisClusterOptions, RedisClusterType } from '@redis/client/lib/cluster'; -import { RedisSocketCommonOptions } from '@redis/client/lib/client/socket'; +// import { RedisModules, RedisFunctions, RedisScripts } from '@redis/client/lib/commands'; +// import RedisClient, { RedisClientOptions, RedisClientType } from '@redis/client/lib/client'; +// import RedisCluster, { RedisClusterOptions, RedisClusterType } from '@redis/client/lib/cluster'; +// import { RedisSocketCommonOptions } from '@redis/client/lib/client/socket'; +import { + RedisModules, + RedisFunctions, + RedisScripts, + RespVersions, + createClient, + RedisClientOptions, + RedisClientType, + createCluster, + RedisClusterOptions, + RedisClusterType +} from '@redis/client'; import { RedisServerDockerConfig, spawnRedisServer, spawnRedisCluster } from './dockers'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; interface TestUtilsConfig { - dockerImageName: string; - dockerImageVersionArgument: string; - defaultDockerVersion?: string; + dockerImageName: string; + dockerImageVersionArgument: string; + defaultDockerVersion?: string; } interface CommonTestOptions { - minimumDockerVersion?: Array; + minimumDockerVersion?: Array; } interface ClientTestOptions< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions > extends CommonTestOptions { - serverArguments: Array; - clientOptions?: Partial, 'socket'> & { socket: RedisSocketCommonOptions }>; - disableClientSetup?: boolean; + serverArguments: Array; + clientOptions?: Partial>; + disableClientSetup?: boolean; } interface ClusterTestOptions< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions > extends CommonTestOptions { - serverArguments: Array; - clusterConfiguration?: Partial>; - numberOfMasters?: number; - numberOfReplicas?: number; + serverArguments: Array; + clusterConfiguration?: Partial>; + numberOfMasters?: number; + numberOfReplicas?: number; +} + +interface AllTestOptions< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions +> { + client: ClientTestOptions; + cluster: ClusterTestOptions; } interface Version { - string: string; - numbers: Array; + string: string; + numbers: Array; } export default class TestUtils { - static #parseVersionNumber(version: string): Array { - if (version === 'latest' || version === 'edge') return [Infinity]; + 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`); - } + 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; - }); - } + return value; + }); + } - 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]; - } - - 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: 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]; + } + + 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: string + }; + } + + 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; + } } - isVersionGreaterThan(minimumVersion: Array | undefined): boolean { - if (minimumVersion === undefined) return true; + return this.#VERSION_NUMBERS[lastIndex] >= minimumVersion[lastIndex]; + } - const lastIndex = Math.min(this.#VERSION_NUMBERS.length, minimumVersion.length) - 1; - for (let i = 0; i < lastIndex; i++) { - if (this.#VERSION_NUMBERS[i] > minimumVersion[i]) { - return true; - } else if (minimumVersion[i] > this.#VERSION_NUMBERS[i]) { - return false; - } + isVersionGreaterThanHook(minimumVersion: Array | undefined): void { + const isVersionGreaterThan = this.isVersionGreaterThan.bind(this); + before(function () { + if (!isVersionGreaterThan(minimumVersion)) { + return this.skip(); + } + }); + } + + testWithClient< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions + >( + title: string, + fn: (client: RedisClientType) => unknown, + options: ClientTestOptions + ): void { + let dockerPromise: ReturnType; + if (this.isVersionGreaterThan(options.minimumDockerVersion)) { + const dockerImage = this.#DOCKER_IMAGE; + before(function () { + this.timeout(30000); + + dockerPromise = spawnRedisServer(dockerImage, options.serverArguments); + return dockerPromise; + }); + } + + 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 } + }); - return this.#VERSION_NUMBERS[lastIndex] >= minimumVersion[lastIndex]; - } + if (options.disableClientSetup) { + return fn(client); + } - isVersionGreaterThanHook(minimumVersion: Array | undefined): void { - const isVersionGreaterThan = this.isVersionGreaterThan.bind(this); - before(function () { - if (!isVersionGreaterThan(minimumVersion)) { - return this.skip(); - } - }); - } + await client.connect(); - 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; - }); + try { + await client.flushAll(); + await fn(client); + } finally { + if (client.isOpen) { + await client.flushAll(); + await client.disconnect(); } + } + }); + } - 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< - 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(); - } - }) - ); - } - - 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; - }); + static async #clusterFlushAll< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions + >(cluster: RedisClusterType): Promise { + return Promise.all( + cluster.masters.map(async ({ client }) => { + if (client) { + await (await client).flushAll(); } + }) + ); + } - it(title, async function () { - if (!dockersPromise) return this.skip(); + testWithCluster< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions + >( + title: string, + fn: (cluster: RedisClusterType) => unknown, + options: ClusterTestOptions + ): void { + let dockersPromise: ReturnType; + if (this.isVersionGreaterThan(options.minimumDockerVersion)) { + const dockerImage = this.#DOCKER_IMAGE; + before(function () { + this.timeout(30000); - 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(); - } - }); + 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 = createCluster({ + rootNodes: dockers.map(({ port }) => ({ + socket: { + port + } + })), + minimizeConnections: true, + ...options.clusterConfiguration + }); + + await cluster.connect(); + + try { + await TestUtils.#clusterFlushAll(cluster); + await fn(cluster); + } finally { + await TestUtils.#clusterFlushAll(cluster); + await cluster.disconnect(); + } + }); + } + + testAll< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions + >( + title: string, + fn: (client: RedisClientType | RedisClusterType) => unknown, + options: AllTestOptions + ) { + this.testWithClient(`client.${title}`, fn, options.client); + this.testWithCluster(`cluster.${title}`, fn, options.cluster); + } } diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index c82ca36730..a6e55abe0d 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -12,13 +12,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^10.0.1", - "@types/node": "^18.14.1", - "@types/yargs": "^17.0.22", + "@types/node": "^18.15.10", + "@types/yargs": "^17.0.24", "mocha": "^10.2.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typescript": "^4.9.5", + "typescript": "^5.0.2", "yargs": "^17.7.1" } } diff --git a/packages/time-series/lib/commands/QUERYINDEX.ts b/packages/time-series/lib/commands/QUERYINDEX.ts index 46eb564704..b228578918 100644 --- a/packages/time-series/lib/commands/QUERYINDEX.ts +++ b/packages/time-series/lib/commands/QUERYINDEX.ts @@ -1,11 +1,11 @@ import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; import { Filter } from '.'; export const IS_READ_ONLY = true; export function transformArguments(filter: Filter): RedisCommandArguments { - return pushVerdictArguments(['TS.QUERYINDEX'], filter); + return pushVariadicArguments(['TS.QUERYINDEX'], filter); } export declare function transformReply(): Array; diff --git a/packages/time-series/lib/commands/index.ts b/packages/time-series/lib/commands/index.ts index 19cd075ba4..8e36e21082 100644 --- a/packages/time-series/lib/commands/index.ts +++ b/packages/time-series/lib/commands/index.ts @@ -20,7 +20,7 @@ import * as MRANGE_WITHLABELS from './MRANGE_WITHLABELS'; import * as MREVRANGE from './MREVRANGE'; import * as MREVRANGE_WITHLABELS from './MREVRANGE_WITHLABELS'; import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export default { ADD, @@ -364,7 +364,7 @@ export type Filter = string | Array; export function pushFilterArgument(args: RedisCommandArguments, filter: string | Array): RedisCommandArguments { args.push('FILTER'); - return pushVerdictArguments(args, filter); + return pushVariadicArguments(args, filter); } export interface MRangeOptions extends RangeOptions { @@ -390,7 +390,7 @@ export function pushWithLabelsArgument(args: RedisCommandArguments, selectedLabe args.push('WITHLABELS'); } else { args.push('SELECTED_LABELS'); - args = pushVerdictArguments(args, selectedLabels); + args = pushVariadicArguments(args, selectedLabels); } return args; diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 1d6a184e5f..cea7180d1e 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.14.1", + "@types/node": "^18.15.10", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.9.3", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.23.28", + "typescript": "^5.0.2" } } diff --git a/req-res-parser/a.sh b/req-res-parser/a.sh new file mode 100644 index 0000000000..684505d187 --- /dev/null +++ b/req-res-parser/a.sh @@ -0,0 +1,7 @@ +single="--single unit/type/incr --single unit/type/string --single unit/type/stream-cgroups --single unit/type/stream" + +single="--single unit/type/set" && +sudo ./runtest --log-req-res --force-resp3 --dont-clean $single && +cat ./tests/tmp/*/stdout.reqres > resp3.reqres && +sudo ./runtest --log-req-res --dont-clean $single && +cat ./tests/tmp/*/stdout.reqres > resp2.reqres \ No newline at end of file diff --git a/req-res-parser/req-res-parser.mjs b/req-res-parser/req-res-parser.mjs new file mode 100644 index 0000000000..43f8374d29 --- /dev/null +++ b/req-res-parser/req-res-parser.mjs @@ -0,0 +1,1018 @@ +import { readFile } from 'node:fs/promises'; +import { deepEqual } from 'node:assert/strict'; + +// https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md +export const TYPES = { + NULL: 95, // _ + BOOLEAN: 35, // # + NUMBER: 58, // : + BIG_NUMBER: 40, // ( + DOUBLE: 44, // , + SIMPLE_STRING: 43, // + + BLOB_STRING: 36, // $ + VERBATIM_STRING: 61, // = + SIMPLE_ERROR: 45, // - + BLOB_ERROR: 33, // ! + ARRAY: 42, // * + SET: 126, // ~ + MAP: 37, // % + ATTRIBUTE: 124, // | + PUSH: 62 // > +}; + +const ASCII = { + '\r': 13, + 't': 116, + '-': 45, + '0': 48, + '.': 46, + 'i': 105, + 'n': 110 +}; + +// this was written with performance in mind, so it's not very readable... sorry :( + +export class Decoder { + #onReply; + #onPush; + #getFlags; + + constructor({ + onReply, + onPush, + getFlags + }) { + this.#onReply = onReply; + this.#onPush = onPush; + this.#getFlags = getFlags; + } + + #cursor = 0; + + #continue; + + reset() { + this.#cursor = 0; + this.#continue = undefined; + } + + write(chunk) { + if (this.#cursor >= chunk.length || this.#continue?.(chunk)) { + this.#cursor -= chunk.length; + return; + } + + do { + const flags = this.#getFlags(), + type = chunk[this.#cursor]; + + if (++this.#cursor === chunk.length) { + this.#continue = this.#continueDecodeTypeValue.bind(this, type, flags); + break; + } + + this.#decodeTypeValue(type, flags, chunk); + } while (this.#cursor < chunk.length); + this.#cursor -= chunk.length; + } + + #continueDecodeTypeValue(type, flags, chunk) { + this.#continue = undefined; + this.#decodeTypeValue(type, flags, chunk); + return this.#cursor >= chunk.length; + } + + #decodeTypeValue(type, flags, chunk) { + if (type === TYPES.PUSH) { + this.#handleDecodedValue( + this.#onPush, + this.#decodeArray(flags, chunk) + ); + return; + } + + this.#handleDecodedValue( + this.#onReply, + this.#decodeReplyValue(type, flags, chunk) + ); + } + + #decodeReplyValue(type, flags, chunk) { + switch (type) { + case TYPES.NULL: + return this.#decodeNull(); + + case TYPES.BOOLEAN: + return this.#decodeBoolean(chunk); + + case TYPES.NUMBER: + return this.#decodeNumber(chunk); + + case TYPES.BIG_NUMBER: + return this.#decodeBigNumber(flags[TYPES.BIG_NUMBER], chunk); + + case TYPES.DOUBLE: + return this.#decodeDouble(flags[TYPES.DOUBLE], chunk); + + case TYPES.SIMPLE_STRING: + return this.#decodeSimpleString(flags[TYPES.SIMPLE_STRING], chunk); + + case TYPES.BLOB_STRING: + return this.#decodeBlobString(flags[TYPES.BLOB_STRING], chunk); + + case TYPES.VERBATIM_STRING: + return this.#decodeBlobString(flags[TYPES.BLOB_STRING], chunk); + + case TYPES.SIMPLE_ERROR: + return this.#decodeSimpleError(chunk); + + case TYPES.BLOB_ERROR: + return this.#decodeBlobError(chunk); + + case TYPES.ARRAY: + return this.#decodeArray(flags, chunk); + + case TYPES.SET: + return this.#decodeSet(flags, chunk); + + case TYPES.MAP: + return this.#decodeMap(flags, chunk); + + case TYPES.ATTRIBUTE: + throw new Error('TODO: attribute'); + } + } + + #handleDecodedValue(cb, value) { + if (typeof value === 'function') { + this.#continue = this.#continueDecodeValue.bind(this, cb, value); + return true; + } + + cb(value); + } + + #continueDecodeValue(cb, valueCb, chunk) { + this.#continue = undefined; + return this.#handleDecodedValue(cb, valueCb(chunk), chunk) || + this.#cursor >= chunk.length; + } + + #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(chunk) { + const isNegative = chunk[this.#cursor] === ASCII['-']; + if (isNegative && ++this.#cursor === chunk.length) { + return this.#continueDecodeNumber.bind( + this, + isNegative, + this.#decodeUnsingedNumber.bind(this, 0) + ); + } + + const number = this.#decodeUnsingedNumber(0, chunk); + return typeof number === 'function' ? + this.#continueDecodeNumber.bind(this, isNegative, number) : + isNegative ? -number : number; + } + + #continueDecodeNumber(isNegative, numberCb, chunk) { + const number = numberCb(chunk); + return typeof number === 'function' ? + this.#continueDecodeNumber.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(flag, chunk) { + if (flag === String) { + return this.#decodeSimpleString(String, chunk); + } + + const isNegative = chunk[this.#cursor] === ASCII['-']; + if (isNegative && ++this.#cursor === chunk.length) { + return this.#continueDecodeBigNumber.bind( + this, + isNegative, + this.#decodeUnsingedBigNumber.bind(this, 0n) + ); + } + + const bigNumber = this.#decodeUnsingedBigNumber(0n, chunk); + return typeof bigNumber === 'function' ? + this.#continueDecodeNumber.bind(this, isNegative, bigNumber) : + isNegative ? -bigNumber : bigNumber; + } + + #continueDecodeBigNumber(isNegative, bigNumberCb, chunk) { + const bigNumber = bigNumberCb(chunk); + return typeof bigNumber === 'function' ? + this.#continueDecodeBigNumber.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(flag, chunk) { + if (flag === String) { + return this.#decodeSimpleString(String, chunk); + } + + switch (chunk[this.#cursor]) { + case ASCII['n']: + this.#cursor += 5; // skip nan\r\n + return NaN; + + case ASCII['-']: + return ++this.#cursor === chunk.length ? + this.#decodeDoubleInteger.bind(this, true, 0, chunk) : + this.#decodeDoubleInteger(true, 0, chunk); + + default: + return this.#decodeDoubleInteger(false, 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; + return cursor < chunk.length ? + this.#decodeDoubleDecimal(isNegative, 0, integer, chunk) : + this.#decodeDoubleDecimal.bind(this, isNegative, 0, integer); + + case ASCII['\r']: + this.#cursor = cursor + 2; // skip \r\n + return isNegative ? -integer : integer; + + default: + integer = integer * 10 + byte - ASCII['0']; + } + } while (++cursor < chunk.length); + + this.#cursor = cursor; + return this.#continueDecodeDoubleInteger.bind(this, isNegative, integer); + } + + // Precalculated multipliers for decimal points to improve performance + // "A Number only keeps about 17 decimal places of precision" + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number + static #DOUBLE_DECIMAL_MULTIPLIERS = [ + 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, + 1e-7, 1e-8, 1e-9, 1e-10, 1e-11, 1e-12, + 1e-13, 1e-14, 1e-15, 1e-16, 1e-17 + ]; + + #decodeDoubleDecimal(isNegative, decimalIndex, double, chunk) { + let cursor = this.#cursor; + do { + const byte = chunk[cursor]; + if (byte === ASCII['\r']) { + this.#cursor = cursor + 2; // skip \r\n + return isNegative ? -double : double; + } + + if (decimalIndex < Decoder.#DOUBLE_DECIMAL_MULTIPLIERS.length) { + double += (byte - ASCII['0']) * Decoder.#DOUBLE_DECIMAL_MULTIPLIERS[decimalIndex++]; + } + } while (++cursor < chunk.length); + + this.#cursor = cursor; + return this.#decodeDoubleDecimal.bind(this, isNegative, decimalIndex, double); + } + + #findCRLF(chunk, 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(flag, chunk) { + const start = this.#cursor, + crlfIndex = this.#findCRLF(chunk, start); + if (crlfIndex === -1) { + return this.#continueDecodeSimpleString.bind( + this, + [chunk.subarray(start)], + flag + ); + } + + const slice = chunk.subarray(start, crlfIndex); + return flag === Buffer ? + slice : + slice.toString(); + } + + #continueDecodeSimpleString(chunks, flag, chunk) { + const start = this.#cursor, + crlfIndex = this.#findCRLF(chunk, start); + if (crlfIndex === -1) { + chunks.push(chunk.subarray(start)); + return this.#continueDecodeSimpleString.bind(this, chunks, flag); + } + + chunks.push(chunk.subarray(start, crlfIndex)); + return flag === Buffer ? + Buffer.concat(chunks) : + chunks.join(''); + } + + #decodeBlobString(flag, chunk) { + // RESP 2 bulk string null + // https://github.com/redis/redis-specifications/blob/master/protocol/RESP2.md#resp-bulk-strings + if (chunk[this.#cursor] === ASCII['-']) { + this.#cursor += 4; // skip -1\r\n + return null; + } + + const length = this.#decodeUnsingedNumber(0, chunk); + if (typeof length === 'function') { + return this.#continueDecodeBlobStringLength.bind(this, length, flag); + } else if (this.#cursor >= chunk.length) { + return this.#decodeBlobStringWithLength.bind(this, length, flag); + } + + return this.#decodeBlobStringWithLength(length, flag, chunk); + } + + #continueDecodeBlobStringLength(lengthCb, flag, chunk) { + const length = lengthCb(chunk); + if (typeof length === 'function') { + return this.#continueDecodeBlobStringLength.bind(this, length, flag); + } else if (this.#cursor >= chunk.length) { + return this.#decodeBlobStringWithLength.bind(this, length, flag); + } + + return this.#decodeBlobStringWithLength(length, flag, chunk); + } + + #decodeBlobStringWithLength(length, flag, chunk) { + const end = this.#cursor + length; + if (end >= chunk.length) { + const slice = chunk.subarray(this.#cursor); + this.#cursor = chunk.length; + return this.#continueDecodeBlobStringWithLength.bind( + this, + length - slice.length, + [slice], + flag + ); + } + + const slice = chunk.subarray(this.#cursor, end); + this.#cursor = end + 2; // skip ${string}\r\n + return flag === Buffer ? + slice : + slice.toString(); + } + + #continueDecodeBlobStringWithLength(length, chunks, flag, chunk) { + const end = this.#cursor + length; + if (end >= chunk.length) { + const slice = chunk.slice(this.#cursor); + chunks.push(slice); + this.#cursor = chunk.length; + return this.#continueDecodeBlobStringWithLength.bind( + this, + length - slice.length, + chunks, + flag + ); + } + + chunks.push(chunk.subarray(this.#cursor, end)); + this.#cursor = end + 2; // skip ${string}\r\n + return flag === Buffer ? + Buffer.concat(chunks) : + chunks.join(''); + } + + #decodeSimpleError(chunk) { + const string = this.#decodeSimpleString(String, chunk); + return typeof string === 'function' ? + this.#continueDecodeSimpleError.bind(this, string) : + new Error(string); // TODO use custom error + } + + #continueDecodeSimpleError(stringCb, chunk) { + const string = stringCb(chunk); + return typeof string === 'function' ? + this.#continueDecodeSimpleError.bind(this, string) : + new Error(string); // TODO use custom error + } + + #decodeBlobError(chunk) { + const string = this.#decodeBlobString(String, chunk); + return typeof string === 'function' ? + this.#continueDecodeBlobError.bind(this, string) : + new Error(string); // TODO use custom error + } + + #continueDecodeBlobError(stringCb, chunk) { + const string = stringCb(chunk); + return typeof string === 'function' ? + this.#continueDecodeBlobError.bind(this, string) : + new Error(string); // TODO use custom error + } + + #decodeNestedType(flags, chunk) { + const type = chunk[this.#cursor]; + return ++this.#cursor === chunk.length ? + this.#decodeReplyValue.bind(this, type, flags) : + this.#decodeReplyValue(type, flags, chunk); + } + + #decodeArray(flags, chunk) { + // RESP 2 null + // https://github.com/redis/redis-specifications/blob/master/protocol/RESP2.md#resp-arrays + if (chunk[this.#cursor] === ASCII['-']) { + this.#cursor += 4; // skip -1\r\n + return null; + } + + return this.#decodeArrayWithLength( + this.#decodeUnsingedNumber(0, chunk), + flags, + chunk + ); + } + + #decodeArrayWithLength(length, flags, chunk) { + return typeof length === 'function' ? + this.#continueDecodeArrayLength.bind(this, length, flags) : + this.#decodeArrayItems( + new Array(length), + 0, + flags, + chunk + ); + } + + #continueDecodeArrayLength(lengthCb, flags, chunk) { + return this.#decodeArrayWithLength( + lengthCb(chunk), + flags, + chunk + ); + } + + #decodeArrayItems(array, filled, flags, chunk) { + for (let i = filled; i < array.length; i++) { + if (this.#cursor >= chunk.length) { + return this.#decodeArrayItems.bind( + this, + array, + i, + flags + ); + } + + const item = this.#decodeNestedType(flags, chunk); + if (typeof item === 'function') { + return this.#continueDecodeArrayItems.bind( + this, + array, + i, + item, + flags + ); + } + + array[i] = item; + } + + return array; + } + + #continueDecodeArrayItems(array, filled, itemCb, flags, chunk) { + const item = itemCb(chunk); + if (typeof item === 'function') { + return this.#continueDecodeArrayItems.bind( + this, + array, + filled, + item, + flags + ); + } + + array[filled++] = item; + + return this.#decodeArrayItems(array, filled, flags, chunk); + } + + #decodeSet(flags, chunk) { + const length = this.#decodeUnsingedNumber(0, chunk); + if (typeof length === 'function') { + return this.#continueDecodeSetLength.bind(this, length, flags); + } + + return this.#decodeSetItems( + length, + flags, + chunk + ); + } + + #continueDecodeSetLength(lengthCb, flags, chunk) { + const length = lengthCb(chunk); + return typeof length === 'function' ? + this.#continueDecodeSetLength.bind(this, length, flags) : + this.#decodeSetItems(length, flags, chunk); + } + + #decodeSetItems(length, flags, chunk) { + return flags[TYPES.SET] === Set ? + this.#decodeSetAsSet( + new Set(), + length, + flags, + chunk + ) : + this.#decodeArrayItems( + new Array(length), + 0, + flags, + chunk + ); + } + + #decodeSetAsSet(set, remaining, flags, chunk) { + // using `remaining` instead of `length` & `set.size` to make it work even if the set contains duplicates + while (remaining > 0) { + if (this.#cursor >= chunk.length) { + return this.#decodeSetAsSet.bind( + this, + set, + remaining, + flags + ); + } + + const item = this.#decodeNestedType(flags, chunk); + if (typeof item === 'function') { + return this.#continueDecodeSetAsSet.bind( + this, + set, + remaining, + item, + flags + ); + } + + set.add(item); + --remaining; + } + + return set; + } + + #continueDecodeSetAsSet(set, remaining, itemCb, flags, chunk) { + const item = itemCb(chunk); + if (typeof item === 'function') { + return this.#continueDecodeSetAsSet.bind( + this, + set, + remaining, + item, + flags + ); + } + + set.add(item); + + return this.#decodeSetAsSet(set, remaining - 1, flags, chunk); + } + + #decodeMap(flags, chunk) { + const length = this.#decodeUnsingedNumber(0, chunk); + if (typeof length === 'function') { + return this.#continueDecodeMapLength.bind(this, length, flags); + } + + return this.#decodeMapItems( + length, + flags, + chunk + ); + } + + #continueDecodeMapLength(lengthCb, flags, chunk) { + const length = lengthCb(chunk); + return typeof length === 'function' ? + this.#continueDecodeMapLength.bind(this, length, flags) : + this.#decodeMapItems(length, flags, chunk); + } + + #decodeMapItems(length, flags, chunk) { + switch (flags[TYPES.MAP]) { + case Map: + return this.#decodeMapAsMap( + new Map(), + length, + flags, + chunk + ); + + case Array: + return this.#decodeArrayItems( + new Array(length * 2), + 0, + flags, + chunk + ); + + default: + return this.#decodeMapAsObject( + Object.create(null), + length, + flags, + chunk + ); + } + } + + #decodeMapAsMap(map, remaining, flags, chunk) { + // using `remaining` instead of `length` & `map.size` to make it work even if the map contains duplicate keys + while (remaining > 0) { + if (this.#cursor >= chunk.length) { + return this.#decodeMapAsMap.bind( + this, + map, + remaining, + flags + ); + } + + const key = this.#decodeMapKey(flags, chunk); + if (typeof key === 'function') { + return this.#continueDecodeMapKey.bind( + this, + map, + remaining, + key, + flags + ); + } + + if (this.#cursor >= chunk.length) { + return this.#continueDecodeMapValue.bind( + this, + map, + remaining, + key, + this.#decodeNestedType.bind(this, flags), + flags + ); + } + + const value = this.#decodeNestedType(flags, chunk); + if (typeof value === 'function') { + return this.#continueDecodeMapValue.bind( + this, + map, + remaining, + key, + value, + flags + ); + } + + map.set(key, value); + --remaining; + } + + return map; + } + + #decodeMapKey(flags, chunk) { + const type = chunk[this.#cursor]; + return ++this.#cursor === chunk.length ? + this.#decodeMapKeyValue.bind(this, type, flags) : + this.#decodeMapKeyValue(type, flags, chunk); + } + + #decodeMapKeyValue(type, flags, chunk) { + switch (type) { + // decode simple string map key as string (and not as buffer) + case TYPES.SIMPLE_STRING: + return this.#decodeSimpleString(String, chunk); + + // decode blob string map key as string (and not as buffer) + case TYPES.BLOB_STRING: + return this.#decodeBlobString(String, chunk); + + default: + return this.#decodeReplyValue(type, flags, chunk); + } + } + + #continueDecodeMapKey(map, remaining, keyCb, flags, chunk) { + const key = keyCb(chunk); + if (typeof key === 'function') { + return this.#continueDecodeMapKey.bind( + this, + map, + remaining, + key, + flags + ); + } + + if (this.#cursor >= chunk.length) { + return this.#continueDecodeMapValue.bind( + this, + map, + remaining, + key, + this.#decodeNestedType.bind(this, flags), + flags + ); + } + + const value = this.#decodeNestedType(flags, chunk); + if (typeof value === 'function') { + return this.#continueDecodeMapValue.bind( + this, + map, + remaining, + key, + value, + flags + ); + } + + map.set(key, value); + return this.#decodeMapAsMap(map, remaining - 1, flags, chunk); + } + + #continueDecodeMapValue(map, remaining, key, valueCb, flags, chunk) { + const value = valueCb(chunk); + if (typeof value === 'function') { + return this.#continueDecodeMapValue.bind( + this, + map, + remaining, + key, + value, + flags + ); + } + + map.set(key, value); + + return this.#decodeMapAsMap(map, remaining - 1, flags, chunk); + } + + #decodeMapAsObject(object, remaining, flags, chunk) { + while (remaining > 0) { + if (this.#cursor >= chunk.length) { + return this.#decodeMapAsObject.bind( + this, + object, + remaining, + flags + ); + } + + const key = this.#decodeMapKey(flags, chunk); + if (typeof key === 'function') { + return this.#continueDecodeMapAsObjectKey.bind( + this, + object, + remaining, + key, + flags + ); + } + + if (this.#cursor >= chunk.length) { + return this.#continueDecodeMapAsObjectValue.bind( + this, + object, + remaining, + key, + this.#decodeNestedType.bind(this, flags), + flags + ); + } + + const value = this.#decodeNestedType(flags, chunk); + if (typeof value === 'function') { + return this.#continueDecodeMapAsObjectValue.bind( + this, + object, + remaining, + key, + value, + flags + ); + } + + object[key] = value; + --remaining; + } + + return object; + } + + #continueDecodeMapAsObjectKey(object, remaining, keyCb, flags, chunk) { + const key = keyCb(chunk); + if (typeof key === 'function') { + return this.#continueDecodeMapAsObjectKey.bind( + this, + object, + remaining, + key, + flags + ); + } + + if (this.#cursor >= chunk.length) { + return this.#continueDecodeMapAsObjectValue.bind( + this, + object, + remaining, + key, + this.#decodeNestedType.bind(this, flags), + flags + ); + } + + const value = this.#decodeNestedType(flags, chunk); + if (typeof value === 'function') { + return this.#continueDecodeMapAsObjectValue.bind( + this, + object, + remaining, + key, + value, + flags + ); + } + + object[key] = value; + + return this.#decodeMapAsObject(object, remaining - 1, flags, chunk); + } + + #continueDecodeMapAsObjectValue(object, remaining, key, valueCb, flags, chunk) { + const value = valueCb(chunk); + if (typeof value === 'function') { + return this.#continueDecodeMapAsObjectValue.bind( + this, + object, + remaining, + key, + value, + flags + ); + } + + object[key] = value; + + return this.#decodeMapAsObject(object, remaining - 1, flags, chunk); + } +} + +async function parseFile(path) { + const log = await readFile(path); + + let i = 0; + + function readLine() { + const start = i; + while (log[i++] !== 10) {} + return log.subarray(start, i); + } + + function readArgs() { + const args = []; + while (true) { + readLine(); // skip size line + const buffer = readLine(); + const line = buffer.subarray(0, buffer.length - 2).toString(); + if (line === '__argv_end__') { + return args; + } else { + args.push(line); + } + } + } + + function readReply() { + let reply; + const decoder = new Decoder({ + onReply(r) { + reply = r; + }, + onPush(p) { + console.log('PUSH', p); + }, + getFlags() { + return {}; + } + }); + + const network = []; + while (reply === undefined) { + const toWrite = readLine(); + network.push(toWrite); + decoder.write(toWrite); + } + + return { + reply, + network: Buffer.concat(network) + }; + } + + const reqres = []; + while (i < log.length) { + const item = { + args: readArgs(), + ...readReply() + }; + reqres.push(item); + } + + return reqres; +} + +const [resp3, resp2] = await Promise.all([ + parseFile('/home/leibale/Workspace/redis/resp3.reqres'), + parseFile('/home/leibale/Workspace/redis/resp2.reqres') +]); + +console.log(resp2.length, resp3.length); +if (resp2.length !== resp3.length) throw 'invalid'; + +for (let i = 0; i < resp3.length; i++) { + try { + deepEqual( + resp3[i].reply, + resp2[i].reply + ); + } catch (err) { + console.log( + resp3[i], + '!=', + resp2[i] + ); + } +}