@eggjs/redis 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017-present Alibaba Group Holding Limited and other contributors.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,248 @@
1
+ # @eggjs/redis
2
+
3
+ [![NPM version][npm-image]][npm-url]
4
+ [![Node.js CI](https://github.com/eggjs/redis/actions/workflows/nodejs.yml/badge.svg)](https://github.com/eggjs/redis/actions/workflows/nodejs.yml)
5
+ [![Test coverage][codecov-image]][codecov-url]
6
+ [![Known Vulnerabilities][snyk-image]][snyk-url]
7
+ [![npm download][download-image]][download-url]
8
+ [![Node.js Version](https://img.shields.io/node/v/@eggjs/redis.svg?style=flat)](https://nodejs.org/en/download/)
9
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://makeapullrequest.com)
10
+ ![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/eggjs/redis)
11
+
12
+ [npm-image]: https://img.shields.io/npm/v/@eggjs/redis.svg?style=flat-square
13
+ [npm-url]: https://npmjs.org/package/@eggjs/redis
14
+ [codecov-image]: https://codecov.io/gh/eggjs/redis/branch/master/graph/badge.svg
15
+ [codecov-url]: https://codecov.io/gh/eggjs/redis
16
+ [snyk-image]: https://snyk.io/test/npm/@eggjs/redis/badge.svg?style=flat-square
17
+ [snyk-url]: https://snyk.io/test/npm/@eggjs/redis
18
+ [download-image]: https://img.shields.io/npm/dm/@eggjs/redis.svg?style=flat-square
19
+ [download-url]: https://npmjs.org/package/@eggjs/redis
20
+
21
+ Valkey / Redis client (support [redis protocol](https://redis.io/docs/latest/develop/reference/protocol-spec/)) based on iovalkey for egg framework
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ npm i @eggjs/redis
27
+ ```
28
+
29
+ Valkey / Redis Plugin for egg, support egg application access to Valkey / Redis Service.
30
+
31
+ This plugin based on [ioredis](https://github.com/redis/ioredis).
32
+ If you want to know specific usage, you should refer to the document of [ioredis](https://github.com/redis/ioredis).
33
+
34
+ ## Configuration
35
+
36
+ Change `${app_root}/config/plugin.js` to enable redis plugin:
37
+
38
+ ```js
39
+ exports.redis = {
40
+ enable: true,
41
+ package: '@eggjs/redis',
42
+ };
43
+ ```
44
+
45
+ Configure redis information in `${app_root}/config/config.default.js`:
46
+
47
+ **Single Client**
48
+
49
+ ```javascript
50
+ config.redis = {
51
+ client: {
52
+ port: 6379, // Redis port
53
+ host: '127.0.0.1', // Redis host
54
+ password: 'auth',
55
+ db: 0,
56
+ },
57
+ }
58
+ ```
59
+
60
+ **Multi Clients**
61
+
62
+ ```javascript
63
+ config.redis = {
64
+ clients: {
65
+ foo: { // instanceName. See below
66
+ port: 6379, // Redis port
67
+ host: '127.0.0.1', // Redis host
68
+ password: 'auth',
69
+ db: 0,
70
+ },
71
+ bar: {
72
+ port: 6379,
73
+ host: '127.0.0.1',
74
+ password: 'auth',
75
+ db: 1,
76
+ },
77
+ }
78
+ }
79
+ ```
80
+
81
+ **Sentinel**
82
+
83
+ ```javascript
84
+ config.redis = {
85
+ client: {
86
+ // Sentinel instances
87
+ sentinels: [
88
+ {
89
+ port: 26379, // Sentinel port
90
+ host: '127.0.0.1', // Sentinel host
91
+ },
92
+ // other sentinel instance config
93
+ ],
94
+ name: 'mymaster', // Master name
95
+ password: 'auth',
96
+ db: 0
97
+ },
98
+ }
99
+ ```
100
+
101
+ **No password**
102
+
103
+ Redis support no authentication access, but we are highly recommend you to use redis `requirepass` in `redis.conf`.
104
+
105
+ ```bash
106
+ $vim /etc/redis/redis.conf
107
+
108
+ requirepass xxxxxxxxxx // xxxxxxxxxx is your password
109
+ ```
110
+
111
+ Because it may be cause security risk, refer:
112
+
113
+ - <https://ruby-china.org/topics/28094>
114
+ - <https://zhuoroger.github.io/2016/07/29/redis-sec/>
115
+
116
+ If you want to access redis with no password, use `password: null`.
117
+
118
+ See [ioredis API Documentation](https://github.com/redis/ioredis#basic-usage) for all available options.
119
+
120
+ ### Customize `ioredis` version
121
+
122
+ `@eggjs/redis` using `ioredis@5` now, if you want to use other version of iovalkey or ioredis,
123
+ you can pass the instance by `config.redis.Redis`:
124
+
125
+ ```js
126
+ // config/config.default.js
127
+ config.redis = {
128
+ Redis: require('ioredis'), // customize ioredis version, only set when you needed
129
+ client: {
130
+ port: 6379, // Redis port
131
+ host: '127.0.0.1', // Redis host
132
+ password: 'auth',
133
+ db: 0,
134
+ },
135
+ }
136
+ ```
137
+
138
+ **weakDependent**
139
+
140
+ ```javascript
141
+ config.redis = {
142
+ client: {
143
+ port: 6379, // Redis port
144
+ host: '127.0.0.1', // Redis host
145
+ password: 'auth',
146
+ db: 0,
147
+ weakDependent: true, // the redis instance won't block app start
148
+ },
149
+ }
150
+ ```
151
+
152
+ ## Usage
153
+
154
+ In controller, you can use `app.redis` to get the redis instance, check [ioredis](https://github.com/redis/ioredis#basic-usage) to see how to use.
155
+
156
+ ```js
157
+ // app/controller/home.js
158
+
159
+ module.exports = app => {
160
+ return class HomeController extends app.Controller {
161
+ async index() {
162
+ const { ctx, app } = this;
163
+ // set
164
+ await app.redis.set('foo', 'bar');
165
+ // get
166
+ ctx.body = await app.redis.get('foo');
167
+ }
168
+ };
169
+ };
170
+ ```
171
+
172
+ ### Multi Clients
173
+
174
+ If your Configure with multi clients, you can use `app.redis.get(instanceName)` to get the specific redis instance and use it like above.
175
+
176
+ ```js
177
+ // app/controller/home.js
178
+
179
+ module.exports = app => {
180
+ return class HomeController extends app.Controller {
181
+ async index() {
182
+ const { ctx, app } = this;
183
+ // set
184
+ await app.redis.get('instance1').set('foo', 'bar');
185
+ // get
186
+ ctx.body = await app.redis.get('instance1').get('foo');
187
+ }
188
+ };
189
+ };
190
+ ```
191
+
192
+ ### Clients Depend on Redis Cluster
193
+
194
+ Before you start to use Redis Cluster, please checkout the [document](https://redis.io/topics/cluster-tutorial) first, especially confirm `cluster-enabled yes` in Redis Cluster configuration file.
195
+
196
+ In controller, you also can use `app.redis` to get the redis instance based on Redis Cluster.
197
+
198
+ ```js
199
+ // app/config/config.default.js
200
+ exports.redis = {
201
+ client: {
202
+ cluster: true,
203
+ nodes: [
204
+ {
205
+ host: '127.0.0.1',
206
+ port: '6379',
207
+ family: 'user',
208
+ password: 'password',
209
+ db: 'db',
210
+ },
211
+ {
212
+ host: '127.0.0.1',
213
+ port: '6380',
214
+ family: 'user',
215
+ password: 'password',
216
+ db: 'db',
217
+ },
218
+ ],
219
+ },
220
+ };
221
+
222
+ // app/controller/home.js
223
+ module.exports = app => {
224
+ return class HomeController extends app.Controller {
225
+ async index() {
226
+ const { ctx, app } = this;
227
+ // set
228
+ await app.redis.set('foo', 'bar');
229
+ // get
230
+ ctx.body = await app.redis.get('foo');
231
+ }
232
+ };
233
+ };
234
+ ```
235
+
236
+ ## Questions & Suggestions
237
+
238
+ Please open an issue [here](https://github.com/eggjs/egg/issues).
239
+
240
+ ## License
241
+
242
+ [MIT](LICENSE)
243
+
244
+ ## Contributors
245
+
246
+ [![Contributors](https://contrib.rocks/image?repo=eggjs/redis)](https://github.com/eggjs/redis/graphs/contributors)
247
+
248
+ Made with [contributors-img](https://contrib.rocks).
@@ -0,0 +1,2 @@
1
+ import { RedisBoot } from './lib/redis.js';
2
+ export default RedisBoot;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const redis_js_1 = require("./lib/redis.js");
4
+ exports.default = redis_js_1.RedisBoot;
5
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdlbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWdlbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSw2Q0FBMkM7QUFFM0Msa0JBQWUsb0JBQVMsQ0FBQyJ9
@@ -0,0 +1,2 @@
1
+ import { RedisBoot } from './lib/redis.js';
2
+ export default RedisBoot;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const redis_js_1 = require("./lib/redis.js");
4
+ exports.default = redis_js_1.RedisBoot;
5
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FwcC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLDZDQUEyQztBQUUzQyxrQkFBZSxvQkFBUyxDQUFDIn0=
@@ -0,0 +1,59 @@
1
+ import type { RedisOptions, ClusterOptions } from 'ioredis';
2
+ export interface RedisClientOptions extends RedisOptions {
3
+ /**
4
+ * Whether to enable weakDependent mode, the redis client start will not block the application start
5
+ *
6
+ * Default to `undefined`
7
+ */
8
+ weakDependent?: boolean;
9
+ }
10
+ export interface RedisClusterOptions extends ClusterOptions {
11
+ cluster: true;
12
+ nodes: RedisClientOptions[];
13
+ }
14
+ export interface RedisConfig {
15
+ /**
16
+ * Default redis client config
17
+ *
18
+ * Default to `{}`
19
+ */
20
+ default: RedisClientOptions;
21
+ /**
22
+ * Single Redis or Cluster Redis config
23
+ */
24
+ client?: RedisClientOptions | RedisClusterOptions;
25
+ /**
26
+ * Multi Redis config
27
+ */
28
+ clients?: Record<string, RedisClientOptions>;
29
+ /**
30
+ * redis client will try to use TIME command to detect client is ready or not
31
+ * if your redis server not support TIME command, please set this config to false
32
+ * see https://redis.io/commands/time
33
+ *
34
+ * Default to `true`
35
+ */
36
+ supportTimeCommand: boolean;
37
+ /**
38
+ * Whether to enable redis for `app`
39
+ *
40
+ * Default to `true`
41
+ */
42
+ app: boolean;
43
+ /**
44
+ * Whether to enable redis for `agent`
45
+ *
46
+ * Default to `false`
47
+ */
48
+ agent: boolean;
49
+ /**
50
+ * Customize iovalkey version, only set when you needed
51
+ *
52
+ * Default to `undefined`, which means using the built-in ioredis
53
+ */
54
+ Redis?: any;
55
+ }
56
+ declare const _default: {
57
+ redis: RedisConfig;
58
+ };
59
+ export default _default;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = {
4
+ redis: {
5
+ default: {},
6
+ app: true,
7
+ agent: false,
8
+ supportTimeCommand: true,
9
+ // Single Redis
10
+ // client: {
11
+ // host: 'host',
12
+ // port: 'port',
13
+ // family: 'user',
14
+ // password: 'password',
15
+ // db: 'db',
16
+ // },
17
+ //
18
+ // Cluster Redis
19
+ // client: {
20
+ // cluster: true,
21
+ // nodes: [{
22
+ // host: 'host',
23
+ // port: 'port',
24
+ // family: 'user',
25
+ // password: 'password',
26
+ // db: 'db',
27
+ // }, {
28
+ // host: 'host',
29
+ // port: 'port',
30
+ // family: 'user',
31
+ // password: 'password',
32
+ // db: 'db',
33
+ // },
34
+ // ]},
35
+ //
36
+ // Multi Redis
37
+ // clients: {
38
+ // instance1: {
39
+ // host: 'host',
40
+ // port: 'port',
41
+ // family: 'user',
42
+ // password: 'password',
43
+ // db: 'db',
44
+ // },
45
+ // instance2: {
46
+ // host: 'host',
47
+ // port: 'port',
48
+ // family: 'user',
49
+ // password: 'password',
50
+ // db: 'db',
51
+ // },
52
+ // },
53
+ },
54
+ };
55
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmRlZmF1bHQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvY29uZmlnL2NvbmZpZy5kZWZhdWx0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBMkRBLGtCQUFlO0lBQ2IsS0FBSyxFQUFFO1FBQ0wsT0FBTyxFQUFFLEVBQUU7UUFDWCxHQUFHLEVBQUUsSUFBSTtRQUNULEtBQUssRUFBRSxLQUFLO1FBQ1osa0JBQWtCLEVBQUUsSUFBSTtRQUN4QixlQUFlO1FBQ2YsWUFBWTtRQUNaLGtCQUFrQjtRQUNsQixrQkFBa0I7UUFDbEIsb0JBQW9CO1FBQ3BCLDBCQUEwQjtRQUMxQixjQUFjO1FBQ2QsS0FBSztRQUNMLEVBQUU7UUFDRixnQkFBZ0I7UUFDaEIsWUFBWTtRQUNaLG1CQUFtQjtRQUNuQixjQUFjO1FBQ2Qsb0JBQW9CO1FBQ3BCLG9CQUFvQjtRQUNwQixzQkFBc0I7UUFDdEIsNEJBQTRCO1FBQzVCLGdCQUFnQjtRQUNoQixTQUFTO1FBQ1Qsb0JBQW9CO1FBQ3BCLG9CQUFvQjtRQUNwQixzQkFBc0I7UUFDdEIsNEJBQTRCO1FBQzVCLGdCQUFnQjtRQUNoQixPQUFPO1FBQ1AsTUFBTTtRQUNOLEVBQUU7UUFDRixjQUFjO1FBQ2QsYUFBYTtRQUNiLGlCQUFpQjtRQUNqQixvQkFBb0I7UUFDcEIsb0JBQW9CO1FBQ3BCLHNCQUFzQjtRQUN0Qiw0QkFBNEI7UUFDNUIsZ0JBQWdCO1FBQ2hCLE9BQU87UUFDUCxpQkFBaUI7UUFDakIsb0JBQW9CO1FBQ3BCLG9CQUFvQjtRQUNwQixzQkFBc0I7UUFDdEIsNEJBQTRCO1FBQzVCLGdCQUFnQjtRQUNoQixPQUFPO1FBQ1AsS0FBSztLQUNTO0NBQ2pCLENBQUMifQ==
@@ -0,0 +1 @@
1
+ import './types.js';
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ require("./types.js");
4
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSxzQkFBb0IifQ==
@@ -0,0 +1,6 @@
1
+ import type { ILifecycleBoot, EggCore } from '@eggjs/core';
2
+ export declare class RedisBoot implements ILifecycleBoot {
3
+ private readonly app;
4
+ constructor(app: EggCore);
5
+ didLoad(): Promise<void>;
6
+ }
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.RedisBoot = void 0;
7
+ const node_assert_1 = __importDefault(require("node:assert"));
8
+ const node_events_1 = require("node:events");
9
+ const ioredis_1 = require("ioredis");
10
+ class RedisBoot {
11
+ app;
12
+ constructor(app) {
13
+ this.app = app;
14
+ // empty
15
+ }
16
+ async didLoad() {
17
+ const app = this.app;
18
+ if (app.type === 'application' && app.config.redis.app) {
19
+ app.addSingleton('redis', createClient);
20
+ }
21
+ else if (app.type === 'agent' && app.config.redis.agent) {
22
+ app.addSingleton('redis', createClient);
23
+ }
24
+ }
25
+ }
26
+ exports.RedisBoot = RedisBoot;
27
+ let count = 0;
28
+ function createClient(options, app) {
29
+ const RedisClass = app.config.redis.Redis ?? ioredis_1.Redis;
30
+ let client;
31
+ if ('cluster' in options && options.cluster === true) {
32
+ const config = options;
33
+ (0, node_assert_1.default)(config.nodes && config.nodes.length !== 0, '[@eggjs/redis] cluster nodes configuration is required when use cluster redis');
34
+ config.nodes.forEach(client => {
35
+ (0, node_assert_1.default)(client.host && client.port, `[@eggjs/redis] 'host: ${client.host}', 'port: ${client.port}' are required on config`);
36
+ });
37
+ app.coreLogger.info('[@eggjs/redis] cluster connecting');
38
+ client = new RedisClass.Cluster(config.nodes, config);
39
+ }
40
+ else if ('sentinels' in options && options.sentinels) {
41
+ const config = options;
42
+ (0, node_assert_1.default)(config.sentinels && config.sentinels.length !== 0, '[@eggjs/redis] sentinels configuration is required when use redis sentinel');
43
+ config.sentinels.forEach(sentinel => {
44
+ (0, node_assert_1.default)(sentinel.host && sentinel.port, `[@eggjs/redis] 'host: ${sentinel.host}', 'port: ${sentinel.port}' are required on config`);
45
+ });
46
+ const mask = config.password ? '***' : config.password;
47
+ (0, node_assert_1.default)(config.name && config.password !== undefined && config.db !== undefined, `[@eggjs/redis] 'name of master: ${config.name}', 'password: ${mask}', 'db: ${config.db}' are required on config`);
48
+ app.coreLogger.info('[@eggjs/redis] sentinel connecting start');
49
+ client = new RedisClass(config);
50
+ }
51
+ else {
52
+ const config = options;
53
+ const mask = config.password ? '***' : config.password;
54
+ (0, node_assert_1.default)((config.host && config.port && config.password !== undefined && config.db !== undefined) || config.path, `[@eggjs/redis] 'host: ${config.host}', 'port: ${config.port}', 'password: ${mask}', 'db: ${config.db}' or 'path:${config.path}' are required on config`);
55
+ if (config.host) {
56
+ app.coreLogger.info('[@eggjs/redis] server connecting redis://:***@%s:%s/%s', config.host, config.port, config.db);
57
+ }
58
+ else {
59
+ app.coreLogger.info('[@eggjs/redis] server connecting %s', config.path || config);
60
+ }
61
+ client = new RedisClass(config);
62
+ }
63
+ client.on('connect', () => {
64
+ app.coreLogger.info('[@eggjs/redis] client connect success');
65
+ });
66
+ client.on('error', err => {
67
+ app.coreLogger.error('[@eggjs/redis] client error: %s', err);
68
+ app.coreLogger.error(err);
69
+ });
70
+ const index = count++;
71
+ app.lifecycle.registerBeforeStart(async () => {
72
+ if ('weakDependent' in options && options.weakDependent) {
73
+ app.coreLogger.info(`[@eggjs/redis] instance[${index}] is weak dependent and won't block app start`);
74
+ client.once('ready', () => {
75
+ app.coreLogger.info(`[@eggjs/redis] instance[${index}] status OK`);
76
+ });
77
+ return;
78
+ }
79
+ await Promise.race([
80
+ (0, node_events_1.once)(client, 'ready'),
81
+ (0, node_events_1.once)(client, 'error'),
82
+ ]);
83
+ app.coreLogger.info(`[@eggjs/redis] instance[${index}] status OK, client ready`);
84
+ }, `[@eggjs/redis] instance[${index}] start check`);
85
+ return client;
86
+ }
87
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVkaXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL3JlZGlzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLDhEQUFpQztBQUNqQyw2Q0FBbUM7QUFDbkMscUNBQWdDO0FBSWhDLE1BQWEsU0FBUztJQUNTO0lBQTdCLFlBQTZCLEdBQVk7UUFBWixRQUFHLEdBQUgsR0FBRyxDQUFTO1FBQ3ZDLFFBQVE7SUFDVixDQUFDO0lBQ0QsS0FBSyxDQUFDLE9BQU87UUFDWCxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDO1FBQ3JCLElBQUksR0FBRyxDQUFDLElBQUksS0FBSyxhQUFhLElBQUksR0FBRyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLENBQUM7WUFDdkQsR0FBRyxDQUFDLFlBQVksQ0FBQyxPQUFPLEVBQUUsWUFBWSxDQUFDLENBQUM7UUFDMUMsQ0FBQzthQUFNLElBQUksR0FBRyxDQUFDLElBQUksS0FBSyxPQUFPLElBQUksR0FBRyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsS0FBSyxFQUFFLENBQUM7WUFDMUQsR0FBRyxDQUFDLFlBQVksQ0FBQyxPQUFPLEVBQUUsWUFBWSxDQUFDLENBQUM7UUFDMUMsQ0FBQztJQUNILENBQUM7Q0FDRjtBQVpELDhCQVlDO0FBRUQsSUFBSSxLQUFLLEdBQUcsQ0FBQyxDQUFDO0FBQ2QsU0FBUyxZQUFZLENBQUMsT0FBaUQsRUFBRSxHQUFZO0lBQ25GLE1BQU0sVUFBVSxHQUFpQixHQUFHLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxLQUFLLElBQUksZUFBSyxDQUFDO0lBQ2pFLElBQUksTUFBTSxDQUFDO0lBRVgsSUFBSSxTQUFTLElBQUksT0FBTyxJQUFJLE9BQU8sQ0FBQyxPQUFPLEtBQUssSUFBSSxFQUFFLENBQUM7UUFDckQsTUFBTSxNQUFNLEdBQUcsT0FBOEIsQ0FBQztRQUM5QyxJQUFBLHFCQUFNLEVBQUMsTUFBTSxDQUFDLEtBQUssSUFBSSxNQUFNLENBQUMsS0FBSyxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsK0VBQStFLENBQUMsQ0FBQztRQUVuSSxNQUFNLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsRUFBRTtZQUM1QixJQUFBLHFCQUFNLEVBQUMsTUFBTSxDQUFDLElBQUksSUFBSSxNQUFNLENBQUMsSUFBSSxFQUFFLHlCQUF5QixNQUFNLENBQUMsSUFBSSxhQUFhLE1BQU0sQ0FBQyxJQUFJLDBCQUEwQixDQUFDLENBQUM7UUFDN0gsQ0FBQyxDQUFDLENBQUM7UUFDSCxHQUFHLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxtQ0FBbUMsQ0FBQyxDQUFDO1FBQ3pELE1BQU0sR0FBRyxJQUFJLFVBQVUsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSxNQUFhLENBQUMsQ0FBQztJQUMvRCxDQUFDO1NBQU0sSUFBSSxXQUFXLElBQUksT0FBTyxJQUFJLE9BQU8sQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUN2RCxNQUFNLE1BQU0sR0FBRyxPQUE2QixDQUFDO1FBQzdDLElBQUEscUJBQU0sRUFBQyxNQUFNLENBQUMsU0FBUyxJQUFJLE1BQU0sQ0FBQyxTQUFTLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSw0RUFBNEUsQ0FBQyxDQUFDO1FBRXhJLE1BQU0sQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxFQUFFO1lBQ2xDLElBQUEscUJBQU0sRUFBQyxRQUFRLENBQUMsSUFBSSxJQUFJLFFBQVEsQ0FBQyxJQUFJLEVBQ25DLHlCQUF5QixRQUFRLENBQUMsSUFBSSxhQUFhLFFBQVEsQ0FBQyxJQUFJLDBCQUEwQixDQUFDLENBQUM7UUFDaEcsQ0FBQyxDQUFDLENBQUM7UUFFSCxNQUFNLElBQUksR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUM7UUFDdkQsSUFBQSxxQkFBTSxFQUFDLE1BQU0sQ0FBQyxJQUFJLElBQUksTUFBTSxDQUFDLFFBQVEsS0FBSyxTQUFTLElBQUksTUFBTSxDQUFDLEVBQUUsS0FBSyxTQUFTLEVBQzVFLG1DQUFtQyxNQUFNLENBQUMsSUFBSSxpQkFBaUIsSUFBSSxXQUFXLE1BQU0sQ0FBQyxFQUFFLDBCQUEwQixDQUFDLENBQUM7UUFFckgsR0FBRyxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsMENBQTBDLENBQUMsQ0FBQztRQUNoRSxNQUFNLEdBQUcsSUFBSSxVQUFVLENBQUMsTUFBYSxDQUFDLENBQUM7SUFDekMsQ0FBQztTQUFNLENBQUM7UUFDTixNQUFNLE1BQU0sR0FBRyxPQUE2QixDQUFDO1FBQzdDLE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQztRQUN2RCxJQUFBLHFCQUFNLEVBQUMsQ0FBQyxNQUFNLENBQUMsSUFBSSxJQUFJLE1BQU0sQ0FBQyxJQUFJLElBQUksTUFBTSxDQUFDLFFBQVEsS0FBSyxTQUFTLElBQUksTUFBTSxDQUFDLEVBQUUsS0FBSyxTQUFTLENBQUMsSUFBSSxNQUFNLENBQUMsSUFBSSxFQUM1Ryx5QkFBeUIsTUFBTSxDQUFDLElBQUksYUFBYSxNQUFNLENBQUMsSUFBSSxpQkFBaUIsSUFBSSxXQUFXLE1BQU0sQ0FBQyxFQUFFLGNBQWMsTUFBTSxDQUFDLElBQUksMEJBQTBCLENBQUMsQ0FBQztRQUM1SixJQUFJLE1BQU0sQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUNoQixHQUFHLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyx3REFBd0QsRUFDMUUsTUFBTSxDQUFDLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSSxFQUFFLE1BQU0sQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUN6QyxDQUFDO2FBQU0sQ0FBQztZQUNOLEdBQUcsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLHFDQUFxQyxFQUN2RCxNQUFNLENBQUMsSUFBSSxJQUFJLE1BQU0sQ0FBQyxDQUFDO1FBQzNCLENBQUM7UUFFRCxNQUFNLEdBQUcsSUFBSSxVQUFVLENBQUMsTUFBYSxDQUFDLENBQUM7SUFDekMsQ0FBQztJQUVELE1BQU0sQ0FBQyxFQUFFLENBQUMsU0FBUyxFQUFFLEdBQUcsRUFBRTtRQUN4QixHQUFHLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyx1Q0FBdUMsQ0FBQyxDQUFDO0lBQy9ELENBQUMsQ0FBQyxDQUFDO0lBQ0gsTUFBTSxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsR0FBRyxDQUFDLEVBQUU7UUFDdkIsR0FBRyxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsaUNBQWlDLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFDN0QsR0FBRyxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDNUIsQ0FBQyxDQUFDLENBQUM7SUFFSCxNQUFNLEtBQUssR0FBRyxLQUFLLEVBQUUsQ0FBQztJQUN0QixHQUFHLENBQUMsU0FBUyxDQUFDLG1CQUFtQixDQUFDLEtBQUssSUFBSSxFQUFFO1FBQzNDLElBQUksZUFBZSxJQUFJLE9BQU8sSUFBSSxPQUFPLENBQUMsYUFBYSxFQUFFLENBQUM7WUFDeEQsR0FBRyxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsMkJBQTJCLEtBQUssK0NBQStDLENBQUMsQ0FBQztZQUNyRyxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxHQUFHLEVBQUU7Z0JBQ3hCLEdBQUcsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLDJCQUEyQixLQUFLLGFBQWEsQ0FBQyxDQUFDO1lBQ3JFLENBQUMsQ0FBQyxDQUFDO1lBQ0gsT0FBTztRQUNULENBQUM7UUFFRCxNQUFNLE9BQU8sQ0FBQyxJQUFJLENBQUM7WUFDakIsSUFBQSxrQkFBSSxFQUFDLE1BQU0sRUFBRSxPQUFPLENBQUM7WUFDckIsSUFBQSxrQkFBSSxFQUFDLE1BQU0sRUFBRSxPQUFPLENBQUM7U0FDdEIsQ0FBQyxDQUFDO1FBQ0gsR0FBRyxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsMkJBQTJCLEtBQUssMkJBQTJCLENBQUMsQ0FBQztJQUNuRixDQUFDLEVBQUUsMkJBQTJCLEtBQUssZUFBZSxDQUFDLENBQUM7SUFFcEQsT0FBTyxNQUFNLENBQUM7QUFDaEIsQ0FBQyJ9
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -0,0 +1,11 @@
1
+ import type { Singleton } from '@eggjs/core';
2
+ import type { Redis } from 'ioredis';
3
+ import type { RedisConfig } from './config/config.default.js';
4
+ declare module '@eggjs/core' {
5
+ interface EggAppConfig {
6
+ redis: RedisConfig;
7
+ }
8
+ interface EggCore {
9
+ redis: Redis & Singleton<Redis>;
10
+ }
11
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9
@@ -0,0 +1,2 @@
1
+ import { RedisBoot } from './lib/redis.js';
2
+ export default RedisBoot;
@@ -0,0 +1,3 @@
1
+ import { RedisBoot } from './lib/redis.js';
2
+ export default RedisBoot;
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdlbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWdlbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRTNDLGVBQWUsU0FBUyxDQUFDIn0=
@@ -0,0 +1,2 @@
1
+ import { RedisBoot } from './lib/redis.js';
2
+ export default RedisBoot;
@@ -0,0 +1,3 @@
1
+ import { RedisBoot } from './lib/redis.js';
2
+ export default RedisBoot;
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FwcC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFM0MsZUFBZSxTQUFTLENBQUMifQ==
@@ -0,0 +1,59 @@
1
+ import type { RedisOptions, ClusterOptions } from 'ioredis';
2
+ export interface RedisClientOptions extends RedisOptions {
3
+ /**
4
+ * Whether to enable weakDependent mode, the redis client start will not block the application start
5
+ *
6
+ * Default to `undefined`
7
+ */
8
+ weakDependent?: boolean;
9
+ }
10
+ export interface RedisClusterOptions extends ClusterOptions {
11
+ cluster: true;
12
+ nodes: RedisClientOptions[];
13
+ }
14
+ export interface RedisConfig {
15
+ /**
16
+ * Default redis client config
17
+ *
18
+ * Default to `{}`
19
+ */
20
+ default: RedisClientOptions;
21
+ /**
22
+ * Single Redis or Cluster Redis config
23
+ */
24
+ client?: RedisClientOptions | RedisClusterOptions;
25
+ /**
26
+ * Multi Redis config
27
+ */
28
+ clients?: Record<string, RedisClientOptions>;
29
+ /**
30
+ * redis client will try to use TIME command to detect client is ready or not
31
+ * if your redis server not support TIME command, please set this config to false
32
+ * see https://redis.io/commands/time
33
+ *
34
+ * Default to `true`
35
+ */
36
+ supportTimeCommand: boolean;
37
+ /**
38
+ * Whether to enable redis for `app`
39
+ *
40
+ * Default to `true`
41
+ */
42
+ app: boolean;
43
+ /**
44
+ * Whether to enable redis for `agent`
45
+ *
46
+ * Default to `false`
47
+ */
48
+ agent: boolean;
49
+ /**
50
+ * Customize iovalkey version, only set when you needed
51
+ *
52
+ * Default to `undefined`, which means using the built-in ioredis
53
+ */
54
+ Redis?: any;
55
+ }
56
+ declare const _default: {
57
+ redis: RedisConfig;
58
+ };
59
+ export default _default;
@@ -0,0 +1,53 @@
1
+ export default {
2
+ redis: {
3
+ default: {},
4
+ app: true,
5
+ agent: false,
6
+ supportTimeCommand: true,
7
+ // Single Redis
8
+ // client: {
9
+ // host: 'host',
10
+ // port: 'port',
11
+ // family: 'user',
12
+ // password: 'password',
13
+ // db: 'db',
14
+ // },
15
+ //
16
+ // Cluster Redis
17
+ // client: {
18
+ // cluster: true,
19
+ // nodes: [{
20
+ // host: 'host',
21
+ // port: 'port',
22
+ // family: 'user',
23
+ // password: 'password',
24
+ // db: 'db',
25
+ // }, {
26
+ // host: 'host',
27
+ // port: 'port',
28
+ // family: 'user',
29
+ // password: 'password',
30
+ // db: 'db',
31
+ // },
32
+ // ]},
33
+ //
34
+ // Multi Redis
35
+ // clients: {
36
+ // instance1: {
37
+ // host: 'host',
38
+ // port: 'port',
39
+ // family: 'user',
40
+ // password: 'password',
41
+ // db: 'db',
42
+ // },
43
+ // instance2: {
44
+ // host: 'host',
45
+ // port: 'port',
46
+ // family: 'user',
47
+ // password: 'password',
48
+ // db: 'db',
49
+ // },
50
+ // },
51
+ },
52
+ };
53
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmRlZmF1bHQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvY29uZmlnL2NvbmZpZy5kZWZhdWx0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQTJEQSxlQUFlO0lBQ2IsS0FBSyxFQUFFO1FBQ0wsT0FBTyxFQUFFLEVBQUU7UUFDWCxHQUFHLEVBQUUsSUFBSTtRQUNULEtBQUssRUFBRSxLQUFLO1FBQ1osa0JBQWtCLEVBQUUsSUFBSTtRQUN4QixlQUFlO1FBQ2YsWUFBWTtRQUNaLGtCQUFrQjtRQUNsQixrQkFBa0I7UUFDbEIsb0JBQW9CO1FBQ3BCLDBCQUEwQjtRQUMxQixjQUFjO1FBQ2QsS0FBSztRQUNMLEVBQUU7UUFDRixnQkFBZ0I7UUFDaEIsWUFBWTtRQUNaLG1CQUFtQjtRQUNuQixjQUFjO1FBQ2Qsb0JBQW9CO1FBQ3BCLG9CQUFvQjtRQUNwQixzQkFBc0I7UUFDdEIsNEJBQTRCO1FBQzVCLGdCQUFnQjtRQUNoQixTQUFTO1FBQ1Qsb0JBQW9CO1FBQ3BCLG9CQUFvQjtRQUNwQixzQkFBc0I7UUFDdEIsNEJBQTRCO1FBQzVCLGdCQUFnQjtRQUNoQixPQUFPO1FBQ1AsTUFBTTtRQUNOLEVBQUU7UUFDRixjQUFjO1FBQ2QsYUFBYTtRQUNiLGlCQUFpQjtRQUNqQixvQkFBb0I7UUFDcEIsb0JBQW9CO1FBQ3BCLHNCQUFzQjtRQUN0Qiw0QkFBNEI7UUFDNUIsZ0JBQWdCO1FBQ2hCLE9BQU87UUFDUCxpQkFBaUI7UUFDakIsb0JBQW9CO1FBQ3BCLG9CQUFvQjtRQUNwQixzQkFBc0I7UUFDdEIsNEJBQTRCO1FBQzVCLGdCQUFnQjtRQUNoQixPQUFPO1FBQ1AsS0FBSztLQUNTO0NBQ2pCLENBQUMifQ==
@@ -0,0 +1 @@
1
+ import './types.js';
@@ -0,0 +1,2 @@
1
+ import './types.js';
2
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxZQUFZLENBQUMifQ==
@@ -0,0 +1,6 @@
1
+ import type { ILifecycleBoot, EggCore } from '@eggjs/core';
2
+ export declare class RedisBoot implements ILifecycleBoot {
3
+ private readonly app;
4
+ constructor(app: EggCore);
5
+ didLoad(): Promise<void>;
6
+ }
@@ -0,0 +1,80 @@
1
+ import assert from 'node:assert';
2
+ import { once } from 'node:events';
3
+ import { Redis } from 'ioredis';
4
+ export class RedisBoot {
5
+ app;
6
+ constructor(app) {
7
+ this.app = app;
8
+ // empty
9
+ }
10
+ async didLoad() {
11
+ const app = this.app;
12
+ if (app.type === 'application' && app.config.redis.app) {
13
+ app.addSingleton('redis', createClient);
14
+ }
15
+ else if (app.type === 'agent' && app.config.redis.agent) {
16
+ app.addSingleton('redis', createClient);
17
+ }
18
+ }
19
+ }
20
+ let count = 0;
21
+ function createClient(options, app) {
22
+ const RedisClass = app.config.redis.Redis ?? Redis;
23
+ let client;
24
+ if ('cluster' in options && options.cluster === true) {
25
+ const config = options;
26
+ assert(config.nodes && config.nodes.length !== 0, '[@eggjs/redis] cluster nodes configuration is required when use cluster redis');
27
+ config.nodes.forEach(client => {
28
+ assert(client.host && client.port, `[@eggjs/redis] 'host: ${client.host}', 'port: ${client.port}' are required on config`);
29
+ });
30
+ app.coreLogger.info('[@eggjs/redis] cluster connecting');
31
+ client = new RedisClass.Cluster(config.nodes, config);
32
+ }
33
+ else if ('sentinels' in options && options.sentinels) {
34
+ const config = options;
35
+ assert(config.sentinels && config.sentinels.length !== 0, '[@eggjs/redis] sentinels configuration is required when use redis sentinel');
36
+ config.sentinels.forEach(sentinel => {
37
+ assert(sentinel.host && sentinel.port, `[@eggjs/redis] 'host: ${sentinel.host}', 'port: ${sentinel.port}' are required on config`);
38
+ });
39
+ const mask = config.password ? '***' : config.password;
40
+ assert(config.name && config.password !== undefined && config.db !== undefined, `[@eggjs/redis] 'name of master: ${config.name}', 'password: ${mask}', 'db: ${config.db}' are required on config`);
41
+ app.coreLogger.info('[@eggjs/redis] sentinel connecting start');
42
+ client = new RedisClass(config);
43
+ }
44
+ else {
45
+ const config = options;
46
+ const mask = config.password ? '***' : config.password;
47
+ assert((config.host && config.port && config.password !== undefined && config.db !== undefined) || config.path, `[@eggjs/redis] 'host: ${config.host}', 'port: ${config.port}', 'password: ${mask}', 'db: ${config.db}' or 'path:${config.path}' are required on config`);
48
+ if (config.host) {
49
+ app.coreLogger.info('[@eggjs/redis] server connecting redis://:***@%s:%s/%s', config.host, config.port, config.db);
50
+ }
51
+ else {
52
+ app.coreLogger.info('[@eggjs/redis] server connecting %s', config.path || config);
53
+ }
54
+ client = new RedisClass(config);
55
+ }
56
+ client.on('connect', () => {
57
+ app.coreLogger.info('[@eggjs/redis] client connect success');
58
+ });
59
+ client.on('error', err => {
60
+ app.coreLogger.error('[@eggjs/redis] client error: %s', err);
61
+ app.coreLogger.error(err);
62
+ });
63
+ const index = count++;
64
+ app.lifecycle.registerBeforeStart(async () => {
65
+ if ('weakDependent' in options && options.weakDependent) {
66
+ app.coreLogger.info(`[@eggjs/redis] instance[${index}] is weak dependent and won't block app start`);
67
+ client.once('ready', () => {
68
+ app.coreLogger.info(`[@eggjs/redis] instance[${index}] status OK`);
69
+ });
70
+ return;
71
+ }
72
+ await Promise.race([
73
+ once(client, 'ready'),
74
+ once(client, 'error'),
75
+ ]);
76
+ app.coreLogger.info(`[@eggjs/redis] instance[${index}] status OK, client ready`);
77
+ }, `[@eggjs/redis] instance[${index}] start check`);
78
+ return client;
79
+ }
80
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVkaXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL3JlZGlzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sTUFBTSxNQUFNLGFBQWEsQ0FBQztBQUNqQyxPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ25DLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFJaEMsTUFBTSxPQUFPLFNBQVM7SUFDUztJQUE3QixZQUE2QixHQUFZO1FBQVosUUFBRyxHQUFILEdBQUcsQ0FBUztRQUN2QyxRQUFRO0lBQ1YsQ0FBQztJQUNELEtBQUssQ0FBQyxPQUFPO1FBQ1gsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQztRQUNyQixJQUFJLEdBQUcsQ0FBQyxJQUFJLEtBQUssYUFBYSxJQUFJLEdBQUcsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRSxDQUFDO1lBQ3ZELEdBQUcsQ0FBQyxZQUFZLENBQUMsT0FBTyxFQUFFLFlBQVksQ0FBQyxDQUFDO1FBQzFDLENBQUM7YUFBTSxJQUFJLEdBQUcsQ0FBQyxJQUFJLEtBQUssT0FBTyxJQUFJLEdBQUcsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQzFELEdBQUcsQ0FBQyxZQUFZLENBQUMsT0FBTyxFQUFFLFlBQVksQ0FBQyxDQUFDO1FBQzFDLENBQUM7SUFDSCxDQUFDO0NBQ0Y7QUFFRCxJQUFJLEtBQUssR0FBRyxDQUFDLENBQUM7QUFDZCxTQUFTLFlBQVksQ0FBQyxPQUFpRCxFQUFFLEdBQVk7SUFDbkYsTUFBTSxVQUFVLEdBQWlCLEdBQUcsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEtBQUssSUFBSSxLQUFLLENBQUM7SUFDakUsSUFBSSxNQUFNLENBQUM7SUFFWCxJQUFJLFNBQVMsSUFBSSxPQUFPLElBQUksT0FBTyxDQUFDLE9BQU8sS0FBSyxJQUFJLEVBQUUs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
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -0,0 +1,11 @@
1
+ import type { Singleton } from '@eggjs/core';
2
+ import type { Redis } from 'ioredis';
3
+ import type { RedisConfig } from './config/config.default.js';
4
+ declare module '@eggjs/core' {
5
+ interface EggAppConfig {
6
+ redis: RedisConfig;
7
+ }
8
+ interface EggCore {
9
+ redis: Redis & Singleton<Redis>;
10
+ }
11
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9
@@ -0,0 +1,4 @@
1
+ {
2
+ "name": "@eggjs/redis",
3
+ "version": "3.0.0"
4
+ }
package/package.json ADDED
@@ -0,0 +1,94 @@
1
+ {
2
+ "name": "@eggjs/redis",
3
+ "version": "3.0.0",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "description": "Valkey / Redis plugin for egg",
8
+ "eggPlugin": {
9
+ "name": "redis",
10
+ "exports": {
11
+ "import": "./dist/esm",
12
+ "require": "./dist/commonjs",
13
+ "typescript": "./src"
14
+ }
15
+ },
16
+ "keywords": [
17
+ "egg",
18
+ "eggPlugin",
19
+ "egg-plugin",
20
+ "redis",
21
+ "Valkey",
22
+ "database"
23
+ ],
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/eggjs/redis.git"
27
+ },
28
+ "bugs": {
29
+ "url": "https://github.com/eggjs/egg/issues"
30
+ },
31
+ "homepage": "https://github.com/eggjs/redis#readme",
32
+ "author": "jtyjty99999",
33
+ "license": "MIT",
34
+ "engines": {
35
+ "node": ">= 18.19.0"
36
+ },
37
+ "dependencies": {
38
+ "@eggjs/core": "^6.3.0",
39
+ "ioredis": "^5.4.2"
40
+ },
41
+ "devDependencies": {
42
+ "@arethetypeswrong/cli": "^0.17.1",
43
+ "@eggjs/bin": "7",
44
+ "@eggjs/mock": "^6.0.5",
45
+ "@eggjs/tsconfig": "1",
46
+ "@types/mocha": "10",
47
+ "@types/node": "22",
48
+ "egg": "^4.0.3",
49
+ "eslint": "8",
50
+ "eslint-config-egg": "14",
51
+ "rimraf": "6",
52
+ "snap-shot-it": "^7.9.10",
53
+ "tshy": "3",
54
+ "tshy-after": "1",
55
+ "typescript": "5"
56
+ },
57
+ "scripts": {
58
+ "lint": "eslint --cache src test --ext .ts",
59
+ "pretest": "npm run clean && npm run lint -- --fix",
60
+ "test": "egg-bin test",
61
+ "preci": "npm run clean && npm run lint",
62
+ "ci": "egg-bin cov",
63
+ "postci": "npm run prepublishOnly && npm run clean",
64
+ "clean": "rimraf dist",
65
+ "prepublishOnly": "tshy && tshy-after && attw --pack"
66
+ },
67
+ "type": "module",
68
+ "tshy": {
69
+ "exports": {
70
+ ".": "./src/index.ts",
71
+ "./package.json": "./package.json"
72
+ }
73
+ },
74
+ "exports": {
75
+ ".": {
76
+ "import": {
77
+ "types": "./dist/esm/index.d.ts",
78
+ "default": "./dist/esm/index.js"
79
+ },
80
+ "require": {
81
+ "types": "./dist/commonjs/index.d.ts",
82
+ "default": "./dist/commonjs/index.js"
83
+ }
84
+ },
85
+ "./package.json": "./package.json"
86
+ },
87
+ "files": [
88
+ "dist",
89
+ "src"
90
+ ],
91
+ "types": "./dist/commonjs/index.d.ts",
92
+ "main": "./dist/commonjs/index.js",
93
+ "module": "./dist/esm/index.js"
94
+ }
package/src/agent.ts ADDED
@@ -0,0 +1,3 @@
1
+ import { RedisBoot } from './lib/redis.js';
2
+
3
+ export default RedisBoot;
package/src/app.ts ADDED
@@ -0,0 +1,3 @@
1
+ import { RedisBoot } from './lib/redis.js';
2
+
3
+ export default RedisBoot;
@@ -0,0 +1,111 @@
1
+ import type { RedisOptions, ClusterOptions } from 'ioredis';
2
+
3
+ export interface RedisClientOptions extends RedisOptions {
4
+ /**
5
+ * Whether to enable weakDependent mode, the redis client start will not block the application start
6
+ *
7
+ * Default to `undefined`
8
+ */
9
+ weakDependent?: boolean;
10
+ }
11
+
12
+ export interface RedisClusterOptions extends ClusterOptions {
13
+ cluster: true;
14
+ nodes: RedisClientOptions[];
15
+ }
16
+
17
+ export interface RedisConfig {
18
+ /**
19
+ * Default redis client config
20
+ *
21
+ * Default to `{}`
22
+ */
23
+ default: RedisClientOptions;
24
+ /**
25
+ * Single Redis or Cluster Redis config
26
+ */
27
+ client?: RedisClientOptions | RedisClusterOptions;
28
+ /**
29
+ * Multi Redis config
30
+ */
31
+ clients?: Record<string, RedisClientOptions>;
32
+ /**
33
+ * redis client will try to use TIME command to detect client is ready or not
34
+ * if your redis server not support TIME command, please set this config to false
35
+ * see https://redis.io/commands/time
36
+ *
37
+ * Default to `true`
38
+ */
39
+ supportTimeCommand: boolean;
40
+ /**
41
+ * Whether to enable redis for `app`
42
+ *
43
+ * Default to `true`
44
+ */
45
+ app: boolean;
46
+ /**
47
+ * Whether to enable redis for `agent`
48
+ *
49
+ * Default to `false`
50
+ */
51
+ agent: boolean;
52
+ /**
53
+ * Customize iovalkey version, only set when you needed
54
+ *
55
+ * Default to `undefined`, which means using the built-in ioredis
56
+ */
57
+ Redis?: any;
58
+ }
59
+
60
+ export default {
61
+ redis: {
62
+ default: {},
63
+ app: true,
64
+ agent: false,
65
+ supportTimeCommand: true,
66
+ // Single Redis
67
+ // client: {
68
+ // host: 'host',
69
+ // port: 'port',
70
+ // family: 'user',
71
+ // password: 'password',
72
+ // db: 'db',
73
+ // },
74
+ //
75
+ // Cluster Redis
76
+ // client: {
77
+ // cluster: true,
78
+ // nodes: [{
79
+ // host: 'host',
80
+ // port: 'port',
81
+ // family: 'user',
82
+ // password: 'password',
83
+ // db: 'db',
84
+ // }, {
85
+ // host: 'host',
86
+ // port: 'port',
87
+ // family: 'user',
88
+ // password: 'password',
89
+ // db: 'db',
90
+ // },
91
+ // ]},
92
+ //
93
+ // Multi Redis
94
+ // clients: {
95
+ // instance1: {
96
+ // host: 'host',
97
+ // port: 'port',
98
+ // family: 'user',
99
+ // password: 'password',
100
+ // db: 'db',
101
+ // },
102
+ // instance2: {
103
+ // host: 'host',
104
+ // port: 'port',
105
+ // family: 'user',
106
+ // password: 'password',
107
+ // db: 'db',
108
+ // },
109
+ // },
110
+ } as RedisConfig,
111
+ };
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ import './types.js';
@@ -0,0 +1,92 @@
1
+ import assert from 'node:assert';
2
+ import { once } from 'node:events';
3
+ import { Redis } from 'ioredis';
4
+ import type { ILifecycleBoot, EggCore } from '@eggjs/core';
5
+ import type { RedisClusterOptions, RedisClientOptions } from '../config/config.default.js';
6
+
7
+ export class RedisBoot implements ILifecycleBoot {
8
+ constructor(private readonly app: EggCore) {
9
+ // empty
10
+ }
11
+ async didLoad() {
12
+ const app = this.app;
13
+ if (app.type === 'application' && app.config.redis.app) {
14
+ app.addSingleton('redis', createClient);
15
+ } else if (app.type === 'agent' && app.config.redis.agent) {
16
+ app.addSingleton('redis', createClient);
17
+ }
18
+ }
19
+ }
20
+
21
+ let count = 0;
22
+ function createClient(options: RedisClusterOptions | RedisClientOptions, app: EggCore) {
23
+ const RedisClass: typeof Redis = app.config.redis.Redis ?? Redis;
24
+ let client;
25
+
26
+ if ('cluster' in options && options.cluster === true) {
27
+ const config = options as RedisClusterOptions;
28
+ assert(config.nodes && config.nodes.length !== 0, '[@eggjs/redis] cluster nodes configuration is required when use cluster redis');
29
+
30
+ config.nodes.forEach(client => {
31
+ assert(client.host && client.port, `[@eggjs/redis] 'host: ${client.host}', 'port: ${client.port}' are required on config`);
32
+ });
33
+ app.coreLogger.info('[@eggjs/redis] cluster connecting');
34
+ client = new RedisClass.Cluster(config.nodes, config as any);
35
+ } else if ('sentinels' in options && options.sentinels) {
36
+ const config = options as RedisClientOptions;
37
+ assert(config.sentinels && config.sentinels.length !== 0, '[@eggjs/redis] sentinels configuration is required when use redis sentinel');
38
+
39
+ config.sentinels.forEach(sentinel => {
40
+ assert(sentinel.host && sentinel.port,
41
+ `[@eggjs/redis] 'host: ${sentinel.host}', 'port: ${sentinel.port}' are required on config`);
42
+ });
43
+
44
+ const mask = config.password ? '***' : config.password;
45
+ assert(config.name && config.password !== undefined && config.db !== undefined,
46
+ `[@eggjs/redis] 'name of master: ${config.name}', 'password: ${mask}', 'db: ${config.db}' are required on config`);
47
+
48
+ app.coreLogger.info('[@eggjs/redis] sentinel connecting start');
49
+ client = new RedisClass(config as any);
50
+ } else {
51
+ const config = options as RedisClientOptions;
52
+ const mask = config.password ? '***' : config.password;
53
+ assert((config.host && config.port && config.password !== undefined && config.db !== undefined) || config.path,
54
+ `[@eggjs/redis] 'host: ${config.host}', 'port: ${config.port}', 'password: ${mask}', 'db: ${config.db}' or 'path:${config.path}' are required on config`);
55
+ if (config.host) {
56
+ app.coreLogger.info('[@eggjs/redis] server connecting redis://:***@%s:%s/%s',
57
+ config.host, config.port, config.db);
58
+ } else {
59
+ app.coreLogger.info('[@eggjs/redis] server connecting %s',
60
+ config.path || config);
61
+ }
62
+
63
+ client = new RedisClass(config as any);
64
+ }
65
+
66
+ client.on('connect', () => {
67
+ app.coreLogger.info('[@eggjs/redis] client connect success');
68
+ });
69
+ client.on('error', err => {
70
+ app.coreLogger.error('[@eggjs/redis] client error: %s', err);
71
+ app.coreLogger.error(err);
72
+ });
73
+
74
+ const index = count++;
75
+ app.lifecycle.registerBeforeStart(async () => {
76
+ if ('weakDependent' in options && options.weakDependent) {
77
+ app.coreLogger.info(`[@eggjs/redis] instance[${index}] is weak dependent and won't block app start`);
78
+ client.once('ready', () => {
79
+ app.coreLogger.info(`[@eggjs/redis] instance[${index}] status OK`);
80
+ });
81
+ return;
82
+ }
83
+
84
+ await Promise.race([
85
+ once(client, 'ready'),
86
+ once(client, 'error'),
87
+ ]);
88
+ app.coreLogger.info(`[@eggjs/redis] instance[${index}] status OK, client ready`);
89
+ }, `[@eggjs/redis] instance[${index}] start check`);
90
+
91
+ return client;
92
+ }
package/src/types.ts ADDED
@@ -0,0 +1,14 @@
1
+ import type { Singleton } from '@eggjs/core';
2
+ import type { Redis } from 'ioredis';
3
+ import type { RedisConfig } from './config/config.default.js';
4
+
5
+ declare module '@eggjs/core' {
6
+ // add EggAppConfig overrides types
7
+ interface EggAppConfig {
8
+ redis: RedisConfig;
9
+ }
10
+
11
+ interface EggCore {
12
+ redis: Redis & Singleton<Redis>;
13
+ }
14
+ }
@@ -0,0 +1,4 @@
1
+ // make sure to import egg typings and let typescript know about it
2
+ // @see https://github.com/whxaxes/blog/issues/11
3
+ // and https://www.typescriptlang.org/docs/handbook/declaration-merging.html
4
+ import 'egg';