diff --git a/examples/README.md b/examples/README.md index 62b7efe38d..1080f424da 100644 --- a/examples/README.md +++ b/examples/README.md @@ -3,7 +3,7 @@ This folder contains example scripts showing how to use Node Redis in different scenarios. | File Name | Description | -| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +|------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------| | `blocking-list-pop.js` | Block until an element is pushed to a list. | | `bloom-filter.js` | Space efficient set membership checks with a [Bloom Filter](https://en.wikipedia.org/wiki/Bloom_filter) using [RedisBloom](https://redisbloom.io). | | `check-connection-status.js` | Check the client's connection status. | @@ -12,6 +12,7 @@ This folder contains example scripts showing how to use Node Redis in different | `connect-to-cluster.js` | Connect to a Redis cluster. | | `count-min-sketch.js` | Estimate the frequency of a given event using the [RedisBloom](https://redisbloom.io) Count-Min Sketch. | | `cuckoo-filter.js` | Space efficient set membership checks with a [Cuckoo Filter](https://en.wikipedia.org/wiki/Cuckoo_filter) using [RedisBloom](https://redisbloom.io). | +| `dump-and-restore.js` | Demonstrates the use of the [`DUMP`](https://redis.io/commands/dump/) and [`RESTORE`](https://redis.io/commands/restore/) commands | | `get-server-time.js` | Get the time from the Redis server. | | `hyperloglog.js` | Showing use of Hyperloglog commands [PFADD, PFCOUNT and PFMERGE](https://redis.io/commands/?group=hyperloglog). | | `lua-multi-incr.js` | Define a custom lua script that allows you to perform INCRBY on multiple keys. | diff --git a/examples/dump-and-restore.js b/examples/dump-and-restore.js new file mode 100644 index 0000000000..081e44f9f9 --- /dev/null +++ b/examples/dump-and-restore.js @@ -0,0 +1,22 @@ +// This example demonstrates the use of the DUMP and RESTORE commands + +import { commandOptions, createClient } from 'redis'; + +const client = createClient(); +await client.connect(); + +// DUMP a specific key into a local variable +const dump = await client.dump( + commandOptions({ returnBuffers: true }), + 'source' +); + +// RESTORE into a new key +await client.restore('destination', 0, dump); + +// RESTORE and REPLACE an existing key +await client.restore('destination', 0, dump, { + REPLACE: true +}); + +await client.quit(); diff --git a/package-lock.json b/package-lock.json index 93a1539a19..9fb5849aba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -131,9 +131,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", - "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz", + "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==", "dev": true, "engines": { "node": ">=6.9.0" @@ -219,9 +219,9 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" @@ -354,7 +354,7 @@ "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, @@ -922,9 +922,9 @@ "dev": true }, "node_modules/@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", + "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", "dev": true }, "node_modules/@types/mocha": { @@ -3734,6 +3734,20 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, + "node_modules/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3774,9 +3788,9 @@ "dev": true }, "node_modules/sinon": { - "version": "15.2.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.2.0.tgz", - "integrity": "sha512-nPS85arNqwBXaIsFCkolHjGIkFo+Oxu9vbgmBJizLAhqe6P2o3Qmj3KCUoRkfhHtvgDhZdWD3risLHAUJ8npjw==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-16.0.0.tgz", + "integrity": "sha512-B8AaZZm9CT5pqe4l4uWJztfD/mOTa7dL8Qo0W4+s+t74xECOgSZDDQCBjNgIK3+n4kyxQrSTv2V5ul8K25qkiQ==", "dev": true, "dependencies": { "@sinonjs/commons": "^3.0.0", @@ -3995,9 +4009,9 @@ } }, "node_modules/ts-api-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.2.tgz", - "integrity": "sha512-Cbu4nIqnEdd+THNEsBdkolnOXhg0I8XteoHaEKgvsxpsbWda4IsUut2c187HxywQCvveojow0Dgw/amxtSKVkQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", "dev": true, "engines": { "node": ">=16.13.0" @@ -4447,7 +4461,7 @@ }, "packages/json": { "name": "@redis/json", - "version": "1.0.4", + "version": "1.0.5", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -4488,7 +4502,7 @@ }, "packages/search": { "name": "@redis/search", - "version": "1.1.3", + "version": "1.1.4", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" diff --git a/packages/client/lib/commands/CLIENT_NO-TOUCH.spec.ts b/packages/client/lib/commands/CLIENT_NO-TOUCH.spec.ts new file mode 100644 index 0000000000..80ee0ada1f --- /dev/null +++ b/packages/client/lib/commands/CLIENT_NO-TOUCH.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CLIENT_NO-TOUCH'; + +describe('CLIENT NO-TOUCH', () => { + testUtils.isVersionGreaterThanHook([7, 2]); + + describe('transformArguments', () => { + it('true', () => { + assert.deepEqual( + transformArguments(true), + ['CLIENT', 'NO-TOUCH', 'ON'] + ); + }); + + it('false', () => { + assert.deepEqual( + transformArguments(false), + ['CLIENT', 'NO-TOUCH', 'OFF'] + ); + }); + }); + + testUtils.testWithClient('client.clientNoTouch', async client => { + assert.equal( + await client.clientNoTouch(true), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/CLIENT_NO-TOUCH.ts b/packages/client/lib/commands/CLIENT_NO-TOUCH.ts new file mode 100644 index 0000000000..d11f693dba --- /dev/null +++ b/packages/client/lib/commands/CLIENT_NO-TOUCH.ts @@ -0,0 +1,11 @@ +import { RedisCommandArguments } from '.'; + +export function transformArguments(value: boolean): RedisCommandArguments { + return [ + 'CLIENT', + 'NO-TOUCH', + value ? 'ON' : 'OFF' + ]; +} + +export declare function transformReply(): 'OK' | Buffer; diff --git a/packages/client/lib/commands/CLUSTER_MYSHARDID.spec.ts b/packages/client/lib/commands/CLUSTER_MYSHARDID.spec.ts new file mode 100644 index 0000000000..180289870c --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_MYSHARDID.spec.ts @@ -0,0 +1,22 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CLUSTER_MYSHARDID'; + +describe('CLUSTER MYSHARDID', () => { + testUtils.isVersionGreaterThanHook([7, 2]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['CLUSTER', 'MYSHARDID'] + ); + }); + + testUtils.testWithCluster('clusterNode.clusterMyShardId', async cluster => { + const client = await cluster.nodeClient(cluster.masters[0]); + assert.equal( + typeof await client.clusterMyShardId(), + 'string' + ); + }, GLOBAL.CLUSTERS.OPEN); +}); diff --git a/packages/client/lib/commands/CLUSTER_MYSHARDID.ts b/packages/client/lib/commands/CLUSTER_MYSHARDID.ts new file mode 100644 index 0000000000..1c4f8b82f5 --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_MYSHARDID.ts @@ -0,0 +1,7 @@ +export const IS_READ_ONLY = true; + +export function transformArguments() { + return ['CLUSTER', 'MYSHARDID']; +} + +export declare function transformReply(): string | Buffer; diff --git a/packages/client/lib/commands/LATENCY_HISTORY.spec.ts b/packages/client/lib/commands/LATENCY_HISTORY.spec.ts new file mode 100644 index 0000000000..e79e969b26 --- /dev/null +++ b/packages/client/lib/commands/LATENCY_HISTORY.spec.ts @@ -0,0 +1,26 @@ +import {strict as assert} from 'assert'; +import testUtils, {GLOBAL} from '../test-utils'; +import { transformArguments } from './LATENCY_HISTORY'; + +describe('LATENCY HISTORY', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('command'), + ['LATENCY', 'HISTORY', 'command'] + ); + }); + + testUtils.testWithClient('client.latencyHistory', async client => { + await Promise.all([ + client.configSet('latency-monitor-threshold', '100'), + client.sendCommand(['DEBUG', 'SLEEP', '1']) + ]); + + const latencyHisRes = await client.latencyHistory('command'); + assert.ok(Array.isArray(latencyHisRes)); + for (const [timestamp, latency] of latencyHisRes) { + assert.equal(typeof timestamp, 'number'); + assert.equal(typeof latency, 'number'); + } + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/LATENCY_HISTORY.ts b/packages/client/lib/commands/LATENCY_HISTORY.ts new file mode 100644 index 0000000000..c0b1964553 --- /dev/null +++ b/packages/client/lib/commands/LATENCY_HISTORY.ts @@ -0,0 +1,27 @@ +export type EventType = ( + 'active-defrag-cycle' | + 'aof-fsync-always' | + 'aof-stat' | + 'aof-rewrite-diff-write' | + 'aof-rename' | + 'aof-write' | + 'aof-write-active-child' | + 'aof-write-alone' | + 'aof-write-pending-fsync' | + 'command' | + 'expire-cycle' | + 'eviction-cycle' | + 'eviction-del' | + 'fast-command' | + 'fork' | + 'rdb-unlink-temp-file' +); + +export function transformArguments(event: EventType) { + return ['LATENCY', 'HISTORY', event]; +} + +export declare function transformReply(): Array<[ + timestamp: number, + latency: number, +]>; diff --git a/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.spec.ts b/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.spec.ts new file mode 100644 index 0000000000..fea1373b55 --- /dev/null +++ b/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.spec.ts @@ -0,0 +1,48 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './PUBSUB_SHARDNUMSUB'; + +describe('PUBSUB SHARDNUMSUB', () => { + testUtils.isVersionGreaterThanHook([7]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(), + ['PUBSUB', 'SHARDNUMSUB'] + ); + }); + + it('string', () => { + assert.deepEqual( + transformArguments('channel'), + ['PUBSUB', 'SHARDNUMSUB', 'channel'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments(['1', '2']), + ['PUBSUB', 'SHARDNUMSUB', '1', '2'] + ); + }); + }); + + testUtils.testWithClient('client.pubSubShardNumSub', async client => { + assert.deepEqual( + await client.pubSubShardNumSub(['foo', 'bar']), + Object.create(null, { + foo: { + value: 0, + configurable: true, + enumerable: true + }, + bar: { + value: 0, + configurable: true, + enumerable: true + } + }) + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.ts b/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.ts new file mode 100644 index 0000000000..4d7f4d8a71 --- /dev/null +++ b/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.ts @@ -0,0 +1,24 @@ +import { pushVerdictArguments } from './generic-transformers'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export const IS_READ_ONLY = true; + +export function transformArguments( + channels?: Array | RedisCommandArgument +): RedisCommandArguments { + const args = ['PUBSUB', 'SHARDNUMSUB']; + + if (channels) return pushVerdictArguments(args, channels); + + return args; +} + +export function transformReply(rawReply: Array): Record { + const transformedReply = Object.create(null); + + for (let i = 0; i < rawReply.length; i += 2) { + transformedReply[rawReply[i]] = rawReply[i + 1]; + } + + return transformedReply; +} diff --git a/packages/client/lib/commands/RESTORE.spec.ts b/packages/client/lib/commands/RESTORE.spec.ts new file mode 100644 index 0000000000..89d42f3d4d --- /dev/null +++ b/packages/client/lib/commands/RESTORE.spec.ts @@ -0,0 +1,74 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './RESTORE'; + +describe('RESTORE', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key', 0, 'value'), + ['RESTORE', 'key', '0', 'value'] + ); + }); + + it('with REPLACE', () => { + assert.deepEqual( + transformArguments('key', 0, 'value', { + REPLACE: true + }), + ['RESTORE', 'key', '0', 'value', 'REPLACE'] + ); + }); + + it('with ABSTTL', () => { + assert.deepEqual( + transformArguments('key', 0, 'value', { + ABSTTL: true + }), + ['RESTORE', 'key', '0', 'value', 'ABSTTL'] + ); + }); + + it('with IDLETIME', () => { + assert.deepEqual( + transformArguments('key', 0, 'value', { + IDLETIME: 1 + }), + ['RESTORE', 'key', '0', 'value', 'IDLETIME', '1'] + ); + }); + + it('with FREQ', () => { + assert.deepEqual( + transformArguments('key', 0, 'value', { + FREQ: 1 + }), + ['RESTORE', 'key', '0', 'value', 'FREQ', '1'] + ); + }); + + it('with REPLACE, ABSTTL, IDLETIME and FREQ', () => { + assert.deepEqual( + transformArguments('key', 0, 'value', { + REPLACE: true, + ABSTTL: true, + IDLETIME: 1, + FREQ: 2 + }), + ['RESTORE', 'key', '0', 'value', 'REPLACE', 'ABSTTL', 'IDLETIME', '1', 'FREQ', '2'] + ); + }); + }); + + testUtils.testWithClient('client.restore', async client => { + const [, dump] = await Promise.all([ + client.set('source', 'value'), + client.dump(client.commandOptions({ returnBuffers: true }), 'source') + ]); + + assert.equal( + await client.restore('destination', 0, dump), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/RESTORE.ts b/packages/client/lib/commands/RESTORE.ts new file mode 100644 index 0000000000..d9ac11c424 --- /dev/null +++ b/packages/client/lib/commands/RESTORE.ts @@ -0,0 +1,39 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export const FIRST_KEY_INDEX = 1; + +interface RestoreOptions { + REPLACE?: true; + ABSTTL?: true; + IDLETIME?: number; + FREQ?: number; +} + +export function transformArguments( + key: RedisCommandArgument, + ttl: number, + serializedValue: RedisCommandArgument, + options?: RestoreOptions +): RedisCommandArguments { + const args = ['RESTORE', key, ttl.toString(), serializedValue]; + + if (options?.REPLACE) { + args.push('REPLACE'); + } + + if (options?.ABSTTL) { + args.push('ABSTTL'); + } + + if (options?.IDLETIME) { + args.push('IDLETIME', options.IDLETIME.toString()); + } + + if (options?.FREQ) { + args.push('FREQ', options.FREQ.toString()); + } + + return args; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/graph/package.json b/packages/graph/package.json index 9f819a0f79..207386f2e0 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -28,4 +28,5 @@ "keywords": [ "redis", "RedisGraph" - ]} + ] +} diff --git a/packages/json/package.json b/packages/json/package.json index c1fd188e1a..4f1d197c00 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@redis/json", - "version": "1.0.4", + "version": "1.0.5", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts index ef6c0d5c2d..ff7ab7e201 100644 --- a/packages/search/lib/commands/SEARCH.ts +++ b/packages/search/lib/commands/SEARCH.ts @@ -6,7 +6,6 @@ export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; export interface SearchOptions { - // NOCONTENT?: true; TODO VERBATIM?: true; NOSTOPWORDS?: true; // WITHSCORES?: true; diff --git a/packages/search/lib/commands/SEARCH_NOCONTENT.spec.ts b/packages/search/lib/commands/SEARCH_NOCONTENT.spec.ts new file mode 100644 index 0000000000..da5a6feaba --- /dev/null +++ b/packages/search/lib/commands/SEARCH_NOCONTENT.spec.ts @@ -0,0 +1,45 @@ +import { strict as assert } from 'assert'; +import { SchemaFieldTypes } from '.'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments, transformReply } from './SEARCH_NOCONTENT'; + +describe('SEARCH_NOCONTENT', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('index', 'query'), + ['FT.SEARCH', 'index', 'query', 'NOCONTENT'] + ); + }); + }); + + describe('transformReply', () => { + it('returns total and keys', () => { + assert.deepEqual(transformReply([3, '1', '2', '3']), { + total: 3, + documents: ['1', '2', '3'] + }) + }); + }); + + describe('client.ft.searchNoContent', () => { + testUtils.testWithClient('returns total and keys', async client => { + await Promise.all([ + client.ft.create('index', { + field: SchemaFieldTypes.TEXT + }), + client.hSet('1', 'field', 'field1'), + client.hSet('2', 'field', 'field2'), + client.hSet('3', 'field', 'field3') + ]); + + assert.deepEqual( + await client.ft.searchNoContent('index', '*'), + { + total: 3, + documents: ['1','2','3'] + } + ); + }, GLOBAL.SERVERS.OPEN); + }); +}); diff --git a/packages/search/lib/commands/SEARCH_NOCONTENT.ts b/packages/search/lib/commands/SEARCH_NOCONTENT.ts new file mode 100644 index 0000000000..ab50ae2b9f --- /dev/null +++ b/packages/search/lib/commands/SEARCH_NOCONTENT.ts @@ -0,0 +1,30 @@ +import { RedisCommandArguments } from "@redis/client/dist/lib/commands"; +import { pushSearchOptions } from "."; +import { SearchOptions, SearchRawReply } from "./SEARCH"; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments( + index: string, + query: string, + options?: SearchOptions +): RedisCommandArguments { + return pushSearchOptions( + ['FT.SEARCH', index, query, 'NOCONTENT'], + options + ); +} + +export interface SearchNoContentReply { + total: number; + documents: Array; +}; + +export function transformReply(reply: SearchRawReply): SearchNoContentReply { + return { + total: reply[0], + documents: reply.slice(1) + }; +} diff --git a/packages/search/package.json b/packages/search/package.json index 18a2fc8b48..3135ea5fe5 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "1.1.3", + "version": "1.1.4", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 0d6a55f820..d340080266 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -28,4 +28,5 @@ "keywords": [ "redis", "RedisTimeSeries" - ]} + ] +}