handy-redis
Advanced tools
Comparing version 1.8.3 to 2.0.0
export declare const flattenDeep: (array: any[]) => any[]; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.flattenDeep = void 0; | ||
exports.flattenDeep = (array) => { | ||
const flat = []; | ||
for (const item of array) { | ||
const values = Array.isArray(item) ? exports.flattenDeep(item) : [item]; | ||
for (const val of values) { | ||
flat.push(val); | ||
if (Array.isArray(item)) { | ||
for (const val of exports.flattenDeep(item)) { | ||
flat.push(val); | ||
} | ||
} | ||
else { | ||
flat.push(item); | ||
} | ||
} | ||
return flat; | ||
}; | ||
//# sourceMappingURL=flatten.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
//# sourceMappingURL=interface.js.map |
@@ -1,10 +0,1 @@ | ||
import { ClientOpts, RedisClient } from "redis"; | ||
import { IHandyRedis } from "./generated/interface"; | ||
export { IHandyRedis } from "./generated/interface"; | ||
export interface ICreateHandyClient { | ||
(port_arg: number, host_arg?: string, options?: ClientOpts): IHandyRedis; | ||
(unix_socket: string, options?: ClientOpts): IHandyRedis; | ||
(options?: ClientOpts): IHandyRedis; | ||
(redisClient: RedisClient): IHandyRedis; | ||
} | ||
export declare const createHandyClient: ICreateHandyClient; | ||
export { addNodeRedisCommand, createNodeRedisClient, WrappedNodeRedisClient, CreateNodeRedisClient, createNodeRedisClient as createHandyClient, WrappedNodeRedisClient as IHandyRedis, } from "./node_redis"; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const redis_1 = require("redis"); | ||
const flatten_1 = require("./flatten"); | ||
const overrides_1 = require("./overrides"); | ||
exports.createHandyClient = (...clientArgs) => { | ||
const nodeRedis = (typeof clientArgs[0] === "object" && typeof clientArgs[0].scan === "function") | ||
? clientArgs[0] | ||
: redis_1.createClient.apply(null, clientArgs); | ||
const handyClient = { redis: nodeRedis }; | ||
Object.keys(nodeRedis.__proto__).forEach((key) => { | ||
const func = nodeRedis[key]; | ||
if (overrides_1.useUnderlyingImpl.has(key)) { | ||
handyClient[key] = func.bind(nodeRedis); | ||
} | ||
else { | ||
const wrapped = (...args) => new Promise((resolve, reject) => { | ||
const flattened = flatten_1.flattenDeep(args); | ||
func.apply(nodeRedis, flattened.concat([(err, data) => err ? reject(err) : resolve(data)])); | ||
}); | ||
handyClient[key] = wrapped; | ||
} | ||
}); | ||
Object.assign(handyClient, overrides_1.additionalFunctions); | ||
return handyClient; | ||
}; | ||
exports.createHandyClient = exports.createNodeRedisClient = exports.addNodeRedisCommand = void 0; | ||
var node_redis_1 = require("./node_redis"); | ||
Object.defineProperty(exports, "addNodeRedisCommand", { enumerable: true, get: function () { return node_redis_1.addNodeRedisCommand; } }); | ||
Object.defineProperty(exports, "createNodeRedisClient", { enumerable: true, get: function () { return node_redis_1.createNodeRedisClient; } }); | ||
// for backwards-compatibility | ||
Object.defineProperty(exports, "createHandyClient", { enumerable: true, get: function () { return node_redis_1.createNodeRedisClient; } }); | ||
//# sourceMappingURL=index.js.map |
127
package.json
{ | ||
"name": "handy-redis", | ||
"version": "1.8.3", | ||
"description": "A wrapper around node_redis with Promise and TypeScript support.", | ||
"version": "2.0.0", | ||
"description": "A redis client with first-class Promise and TypeScript support, and extensive documentation.", | ||
"keywords": [ | ||
"redis", | ||
"typescript", | ||
"promise" | ||
], | ||
"homepage": "https://github.com/mmkal/handy-redis#readme", | ||
"bugs": { | ||
"url": "https://github.com/mmkal/handy-redis/issues" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/mmkal/handy-redis.git" | ||
}, | ||
"license": "ISC", | ||
"author": "mmkal", | ||
"main": "dist/index.js", | ||
"types": "dist/index.d.ts", | ||
"files": [ | ||
"dist" | ||
], | ||
"engines": { | ||
"node": ">= 8.0.0" | ||
}, | ||
"types": "dist/index.d.ts", | ||
"scripts": { | ||
"_": "echo \"these pass-through-to-docker scripts are mostly just as a hint for when I come back to this in a few weeks and forget what I'm supposed to do.\"", | ||
"prebuild": "yarn codegen", | ||
"build": "yarn typecheck && yarn compile", | ||
"check-clean": "check-clean", | ||
"ci": "run-s clean build coverage lint check-clean", | ||
"preclean": "del-cli temp/backup-test-generated && mkdir -p temp && cp -r test/generated temp/backup-test-generated", | ||
"clean": "del-cli 'dist' 'src/generated' 'test/generated'", | ||
"codegen": "ts-node codegen/generate-schema && ts-node codegen/generate-client && ts-node codegen/generate-tests", | ||
"compile": "tsc -p tsconfig.lib.json", | ||
"coverage": "yarn test --coverage", | ||
"coveralls": "yarn coverage --coverageReporters=text-lcov | coveralls", | ||
"lint": "eslint --max-warnings 0 --ext .ts,.js,.md .", | ||
"redis-cli": "docker-compose exec redis redis-cli", | ||
"redis:down": "docker-compose down", | ||
"redis:up": "docker-compose up", | ||
"redis:down": "docker-compose down", | ||
"redis-cli": "docker-compose exec redis redis-cli", | ||
"clean": "del-cli 'dist' 'src/generated' 'test/generated/**/*.ts' '**/*.js' '**/*.js.map' '!node_modules/**/*'", | ||
"compile": "tsc -p .", | ||
"prebuild": "ts-node --transpile-only scripts/generate-types && ts-node scripts/generate-tests", | ||
"build": "tsc -p .", | ||
"prerelease": "yarn compile", | ||
"release": "semantic-release", | ||
"test": "jest --runInBand --forceExit --detectOpenHandles", | ||
"lint": "tslint **/*.ts", | ||
"check-clean": "check-clean", | ||
"ci": "run-s clean build test lint check-clean", | ||
"prerelease": "tsc -p src", | ||
"release": "semantic-release", | ||
"coverage": "yarn test --coverage", | ||
"coveralls": "yarn coverage --coverageReporters=text-lcov | coveralls" | ||
"tsn": "ts-node --transpile-only", | ||
"typecheck": "tsc -p ." | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/mmkal/handy-redis.git" | ||
}, | ||
"keywords": [ | ||
"redis", | ||
"typescript" | ||
], | ||
"author": "mmkal", | ||
"license": "ISC", | ||
"bugs": { | ||
"url": "https://github.com/mmkal/handy-redis/issues" | ||
}, | ||
"homepage": "https://github.com/mmkal/handy-redis#readme", | ||
"jest": { | ||
"globals": { | ||
"ts-jest": { | ||
"diagnostics": false | ||
} | ||
}, | ||
"transform": { | ||
"^.+\\.tsx?$": "ts-jest" | ||
}, | ||
"testRegex": ".*.test.ts$", | ||
"testURL": "http://localhost", | ||
"moduleFileExtensions": [ | ||
"ts", | ||
"tsx", | ||
"js", | ||
"jsx", | ||
"json" | ||
] | ||
}, | ||
"prettier": { | ||
"tabWidth": 4 | ||
}, | ||
"dependencies": { | ||
"@types/redis": "^2.8.14", | ||
"redis": "^3.0.2" | ||
"@types/redis": "^2.8.27" | ||
}, | ||
"devDependencies": { | ||
"@types/cross-spawn": "^6.0.0", | ||
"@types/eslint": "^7.2.0", | ||
"@types/jest": "^24.0.18", | ||
@@ -77,25 +57,30 @@ "@types/lodash": "^4.14.120", | ||
"@types/redis-mock": "^0.17.0", | ||
"@types/shelljs": "^0.8.5", | ||
"@types/yamljs": "^0.2.30", | ||
"@typescript-eslint/eslint-plugin": "^4.4.0", | ||
"@typescript-eslint/parser": "^4.4.0", | ||
"check-clean": "^0.1.0", | ||
"coveralls": "^3.0.2", | ||
"cross-spawn": "^7.0.1", | ||
"del-cli": "^3.0.0", | ||
"dotenv-extended": "^2.3.0", | ||
"eslint": "^7.2.0", | ||
"eslint-config-xo": "^0.31.0", | ||
"eslint-config-xo-typescript": "^0.31.0", | ||
"eslint-plugin-codegen": "^0.9.0", | ||
"eslint-plugin-import": "^2.21.2", | ||
"eslint-plugin-jest": "^23.13.2", | ||
"eslint-plugin-prettier": "^3.1.3", | ||
"eslint-plugin-unicorn": "^20.1.0", | ||
"expect-type": "^0.7.4", | ||
"jest": "^25.1.0", | ||
"jest": "^26.0.1", | ||
"lodash": "^4.17.14", | ||
"npm-run-all": "^4.1.5", | ||
"prettier": "^1.14.2", | ||
"redis-commands": "^1.5.0", | ||
"prettier": "^2.0.5", | ||
"redis": "^3.0.2", | ||
"redis-mock": "^0.49.0", | ||
"semantic-release": "^15.13.16", | ||
"shelljs": "^0.8.3", | ||
"shx": "^0.3.2", | ||
"ts-jest": "^25.0.0", | ||
"string-argv": "^0.3.1", | ||
"ts-jest": "^26.4.1", | ||
"ts-node": "^8.3.0", | ||
"tslint": "^6.0.0", | ||
"typescript": "^3.8.3", | ||
"yamljs": "^0.3.0" | ||
"typescript": "^4.0.3" | ||
}, | ||
"engines": { | ||
"node": ">= 10.0.0" | ||
} | ||
} |
116
README.md
# handy-redis | ||
A wrapper around [node_redis](https://npmjs.com/package/redis) with Promise and TypeScript support. | ||
[![Build Status](https://travis-ci.org/mmkal/handy-redis.svg?branch=master)](https://travis-ci.org/mmkal/handy-redis) | ||
[![Coverage Status](https://coveralls.io/repos/github/mmkal/handy-redis/badge.svg)](https://coveralls.io/github/mmkal/handy-redis?branch=master) | ||
[![Node CI](https://github.com/mmkal/handy-redis/workflows/CI/badge.svg)](https://github.com/mmkal/handy-redis/actions?query=workflow%3ACI) | ||
[![codecov](https://codecov.io/gh/mmkal/handy-redis/branch/master/graph/badge.svg)](https://codecov.io/gh/mmkal/handy-redis) | ||
[![npm version](https://badge.fury.io/js/handy-redis.svg)](https://www.npmjs.com/package/handy-redis) | ||
@@ -22,8 +22,8 @@ | ||
```JavaScript | ||
import { createHandyClient } from 'handy-redis'; | ||
import { createNodeRedisClient } from 'handy-redis'; | ||
(async function() { | ||
const client = createHandyClient(); | ||
// or, call createHandyClient(opts) using opts for https://www.npmjs.com/package/redis#rediscreateclient | ||
// or, call createHandyClient(oldClient) where oldClient is an existing node_redis client. | ||
const client = createNodeRedisClient(); | ||
// or, call createNodeRedisClient(opts) using opts for https://www.npmjs.com/package/redis#rediscreateclient | ||
// or, call createNodeRedisClient(oldClient) where oldClient is an existing node_redis client. | ||
@@ -36,7 +36,8 @@ await client.set('foo', 'bar'); | ||
Vanilla JS: | ||
Vanilla JS, no async/await: | ||
```JavaScript | ||
const handyRedis = require('handy-redis'); | ||
const { createNodeRedisClient } = require('handy-redis'); | ||
const client = handyRedis.createHandyClient(); | ||
const client = createNodeRedisClient(); | ||
@@ -52,22 +53,52 @@ client | ||
Note: the [redis](https://npmjs.com/package/redis) package is listed as a peer dependency, so should be installed separately. If you need to use recent redis commands (e.g. `xadd` (recent at time of writing, at least)), you can run `npm install redis-commands` to tell the `redis` package to use more up-to-date commands than [redis](https://npmjs.com/package/redis) pulls in by default. | ||
### Adding new commands | ||
Note that the [`redis`](https://npmjs.com/package/redis) package should be installed separately. If you need to use recent redis commands (e.g. `lpos` (recent at time of writing, at least)), which is not included in the [`redis`](https://npmjs.com/package/redis) package by default, you can use `addNodeRedisCommand`: | ||
```js | ||
import { addNodeRedisCommand, createNodeRedisClient } from 'handy-redis' | ||
addNodeRedisCommand('lpos') | ||
const client = createNodeRedisClient(...) | ||
``` | ||
If there's a command without a type, a new version of this library will need to be released - [raise an issue](https://github.com/mmkal/handy-redis/issues) if you come across one. | ||
### Accessing the underlying client | ||
Some features of node_redis are not duplicated in this library, such as `watch`, pubsub and events generally. To use them, get the underlying client via `.nodeRedis`: | ||
```js | ||
import { createNodeRedisClient } from 'handy-redis' | ||
const client = createNodeRedisClient(...) | ||
client.nodeRedis.on('error', err => console.error(err)) | ||
client.nodeRedis.publish('a_channel', 'a message') | ||
``` | ||
### v1.x compatibility | ||
Some aliases exist for backwards-compatibility with v1.x: | ||
- `createNodeRedisClient` (preferred) is aliased to `createHandyClient` | ||
- `WrappedNodeRedisClient` (preferred) is aliased to `IHandyRedis` | ||
- `client.nodeRedis` (preferred) is aliased to `client.redis` | ||
### Examples | ||
See the [snapshot tests](https://github.com/mmkal/handy-redis/blob/master/test/generated/commands/__snapshots__) for tons of usage examples. | ||
See the [snapshot tests](./test/generated/commands) for tons of usage examples. | ||
### Multi | ||
Most members of node_redis's `multi` type don't need to be promisified, because they execute synchronously. Only `exec` is async. For a promisified version of that, use `execMulti`: | ||
Most members of node_redis's `multi` type don't need to be promisified, because they execute synchronously. Only `exec` is async. Usage example: | ||
```JavaScript | ||
import { createHandyClient } from 'handy-redis'; | ||
import { createNodeRedisClient } from 'handy-redis'; | ||
(async function() { | ||
const client = createHandyClient(); | ||
const client = createNodeRedisClient(); | ||
const multi = client.multi().set("z:foo", "987").keys("z:*").get("z:foo"); | ||
const result = await client.multi().set("z:foo", "987").keys("z:*").get("z:foo").exec(); | ||
const result = await client.execMulti(multi); | ||
console.log(result); // ["OK", ["z:foo"], "987"] | ||
@@ -77,4 +108,14 @@ })(); | ||
`execMulti` is generic, so in TypeScript you can use something like `const strings = await client.execMulti<string>(multi)` if you know all results will be strings. Otherwise the type will default to `{}`. | ||
The resolved value returned by `exec` is a tuple type, which keeps track of the commands that have been queued. In the above example, the type will be `[string, string[], string]`. | ||
Note: `multi` results are strongly-typed only when using typescript 4.0 and above - for lower typescript versions they will gracefully fall back to a union type (for the example above, it'll be `Array<string | string[]>`). | ||
`client.batch()` also works, with the same API. See [node_redis docs](https://www.npmjs.com/package/redis#clientbatchcommands) for details. | ||
#### Migrating from v1.x | ||
The client no longer has an `execMulti` function. Use the `.exec()` method on the multi instance. | ||
___ | ||
## Development | ||
@@ -84,10 +125,33 @@ | ||
<details> | ||
<summary>How it works</summary> | ||
The client is generated from the [redis-doc](https://github.com/redis/redis-doc) repo. | ||
- `yarn codegen` generates code: | ||
- `generate-schema`: | ||
- [commands.json](./docs/redis-doc/commands.json) is used to output a commands file with json-schema arguments and return types. | ||
- Argument lists are modeled as arrays, which are flattened when sent to the underlying client. e.g. `SET` might have args `['foo', 'bar', ['EX', 60]]` corresponding to the CLI command `SET foo bar EX 60` | ||
- the markdown documentation for each command is parsed for the return type | ||
- `generate-client`: | ||
- the json-schema from the previous step is parsed and used to generate a [typescript interface of commands](./src/generated/interface.ts) | ||
- `generate-tests`: | ||
- the markdown docs for each command are parsed and transformed into typescript calls. e.g. `SET FOO BAR EX 60` is decoded into `client.set('foo', 'bar', ['EX', 60])` | ||
- these typescript calls are put into jest tests and their outputs are snapshotted | ||
- these tests are internal only and are not included in the published package | ||
At each stage, there are some [patches](./codegen/patches) to plug gaps and inconsistencies in redis-doc and node_redis. | ||
From all the code-generation only the [interface file](./src/generated/interface.ts) is exported. When a client is created, each command on the node_redis client prototype is added as a method on handy-redis's client, a wrapped and promisified version of the equivalent node_redis method. | ||
</details> | ||
```cli | ||
git clone https://github.com/mmkal/handy-redis --recursive | ||
git clone https://github.com/mmkal/handy-redis | ||
cd handy-redis | ||
npm install | ||
yarn | ||
``` | ||
Then in a separate terminal, make sure you have docker installed and `docker-compose` is on your path, and start up a redis server in the background with `yarn redis:up`. | ||
Make sure you have docker installed and `docker-compose` is on your path, and start up a redis server in the background with `yarn redis:up -d`. | ||
To fully test the package as it is on your machine, the same way travis does: | ||
To fully test the package as it is on your machine, the same way CI does: | ||
@@ -104,8 +168,12 @@ ```cli | ||
If you cloned without `--recursive` you'll need to run `git submodule update --init` to get the redis-doc repo locally. | ||
Redis doc was added via `git subtree add --prefix docs/redis-doc https://github.com/redis/redis-doc master --squash` following [this guide](https://www.atlassian.com/git/tutorials/git-subtree). Here's how they say it can be updated: | ||
``` | ||
git subtree pull --prefix docs/redis-doc https://github.com/redis/redis-doc master --squash | ||
``` | ||
### Testing | ||
If a snapshot test fails, it's possible it just needs to be updated. Make sure your git status is clean and run `npm test -- -u`. | ||
If a snapshot test fails, it's possible it just needs to be updated. Make sure your git status is clean and run `yarn test -u`. | ||
Types are tested using [expect-type](https://npmjs.com/package/expect-type). |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
1
18
174
328294
30
5596
2
- Removedredis@^3.0.2
- Removeddenque@1.5.1(transitive)
- Removedredis@3.1.2(transitive)
- Removedredis-commands@1.7.0(transitive)
- Removedredis-errors@1.2.0(transitive)
- Removedredis-parser@3.0.0(transitive)
Updated@types/redis@^2.8.27