@naturalcycles/redis-lib 4.0.1 → 4.0.2
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/dist/redisClient.d.ts +1 -1
- package/dist/redisClient.js +79 -31
- package/package.json +1 -1
- package/src/redisClient.ts +83 -32
package/dist/redisClient.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export declare class RedisClient implements CommonClient {
|
|
|
29
29
|
cfg: Required<RedisClientCfg>;
|
|
30
30
|
connected: boolean;
|
|
31
31
|
private _redis?;
|
|
32
|
-
redis(): Redis
|
|
32
|
+
redis(): Promise<Redis>;
|
|
33
33
|
connect(): Promise<void>;
|
|
34
34
|
disconnect(): Promise<void>;
|
|
35
35
|
[Symbol.asyncDispose](): Promise<void>;
|
package/dist/redisClient.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Transform } from 'node:stream';
|
|
1
2
|
import { _stringMapEntries } from '@naturalcycles/js-lib';
|
|
2
3
|
/**
|
|
3
4
|
Wraps the redis sdk with unified interface.
|
|
@@ -22,11 +23,11 @@ export class RedisClient {
|
|
|
22
23
|
cfg;
|
|
23
24
|
connected = false;
|
|
24
25
|
_redis;
|
|
25
|
-
redis() {
|
|
26
|
+
async redis() {
|
|
26
27
|
if (this._redis)
|
|
27
28
|
return this._redis;
|
|
28
29
|
// lazy-load the library
|
|
29
|
-
const redisLib =
|
|
30
|
+
const { default: redisLib } = await import('ioredis');
|
|
30
31
|
const redis = new redisLib.Redis(this.cfg.redisOptions);
|
|
31
32
|
const { logger } = this.cfg;
|
|
32
33
|
const redisEvents = ['connect', 'close', 'reconnecting', 'end'];
|
|
@@ -41,62 +42,78 @@ export class RedisClient {
|
|
|
41
42
|
}
|
|
42
43
|
async connect() {
|
|
43
44
|
if (!this.connected) {
|
|
44
|
-
await this.redis()
|
|
45
|
+
const redis = await this.redis();
|
|
46
|
+
await redis.connect();
|
|
45
47
|
this.connected = true;
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
50
|
async disconnect() {
|
|
51
|
+
const redis = await this.redis();
|
|
49
52
|
this.log('redis: quit...');
|
|
50
|
-
this.log(`redis: quit`, await
|
|
53
|
+
this.log(`redis: quit`, await redis.quit());
|
|
51
54
|
this.connected = false;
|
|
52
55
|
}
|
|
53
56
|
async [Symbol.asyncDispose]() {
|
|
54
57
|
await this.disconnect();
|
|
55
58
|
}
|
|
56
59
|
async ping() {
|
|
57
|
-
await this.redis()
|
|
60
|
+
const redis = await this.redis();
|
|
61
|
+
await redis.ping();
|
|
58
62
|
}
|
|
59
63
|
async del(keys) {
|
|
60
|
-
|
|
64
|
+
const redis = await this.redis();
|
|
65
|
+
return await redis.del(keys);
|
|
61
66
|
}
|
|
62
67
|
async get(key) {
|
|
63
|
-
|
|
68
|
+
const redis = await this.redis();
|
|
69
|
+
return await redis.get(key);
|
|
64
70
|
}
|
|
65
71
|
async getBuffer(key) {
|
|
66
|
-
|
|
72
|
+
const redis = await this.redis();
|
|
73
|
+
return await redis.getBuffer(key);
|
|
67
74
|
}
|
|
68
75
|
async mget(keys) {
|
|
69
|
-
|
|
76
|
+
const redis = await this.redis();
|
|
77
|
+
return await redis.mget(keys);
|
|
70
78
|
}
|
|
71
79
|
async mgetBuffer(keys) {
|
|
72
|
-
|
|
80
|
+
const redis = await this.redis();
|
|
81
|
+
return await redis.mgetBuffer(keys);
|
|
73
82
|
}
|
|
74
83
|
async set(key, value) {
|
|
75
|
-
await this.redis()
|
|
84
|
+
const redis = await this.redis();
|
|
85
|
+
await redis.set(key, value);
|
|
76
86
|
}
|
|
77
87
|
async hgetall(key) {
|
|
78
|
-
const
|
|
88
|
+
const redis = await this.redis();
|
|
89
|
+
const result = await redis.hgetall(key);
|
|
79
90
|
if (Object.keys(result).length === 0)
|
|
80
91
|
return null;
|
|
81
92
|
return result;
|
|
82
93
|
}
|
|
83
94
|
async hget(key, field) {
|
|
84
|
-
|
|
95
|
+
const redis = await this.redis();
|
|
96
|
+
return await redis.hget(key, field);
|
|
85
97
|
}
|
|
86
98
|
async hset(key, value) {
|
|
87
|
-
await this.redis()
|
|
99
|
+
const redis = await this.redis();
|
|
100
|
+
await redis.hset(key, value);
|
|
88
101
|
}
|
|
89
102
|
async hdel(key, fields) {
|
|
90
|
-
await this.redis()
|
|
103
|
+
const redis = await this.redis();
|
|
104
|
+
await redis.hdel(key, ...fields);
|
|
91
105
|
}
|
|
92
106
|
async hmget(key, fields) {
|
|
93
|
-
|
|
107
|
+
const redis = await this.redis();
|
|
108
|
+
return await redis.hmget(key, ...fields);
|
|
94
109
|
}
|
|
95
110
|
async hmgetBuffer(key, fields) {
|
|
96
|
-
|
|
111
|
+
const redis = await this.redis();
|
|
112
|
+
return await redis.hmgetBuffer(key, ...fields);
|
|
97
113
|
}
|
|
98
114
|
async hincr(key, field, increment = 1) {
|
|
99
|
-
|
|
115
|
+
const redis = await this.redis();
|
|
116
|
+
return await redis.hincrby(key, field, increment);
|
|
100
117
|
}
|
|
101
118
|
async hincrBatch(key, incrementTuples) {
|
|
102
119
|
const results = {};
|
|
@@ -111,7 +128,8 @@ export class RedisClient {
|
|
|
111
128
|
return validResults;
|
|
112
129
|
}
|
|
113
130
|
async setWithTTL(key, value, expireAt) {
|
|
114
|
-
await this.redis()
|
|
131
|
+
const redis = await this.redis();
|
|
132
|
+
await redis.set(key, value, 'EXAT', expireAt);
|
|
115
133
|
}
|
|
116
134
|
async hsetWithTTL(_key, _value, _expireAt) {
|
|
117
135
|
throw new Error('Not supported until Redis 7.4.0');
|
|
@@ -120,17 +138,20 @@ export class RedisClient {
|
|
|
120
138
|
// const keyList = valueKeys.join(' ')
|
|
121
139
|
// const commandString = `HEXPIREAT ${key} ${expireAt} FIELDS ${numberOfKeys} ${keyList}`
|
|
122
140
|
// const [command, ...args] = commandString.split(' ')
|
|
123
|
-
// await
|
|
124
|
-
// await
|
|
141
|
+
// await redis.hset(key, value)
|
|
142
|
+
// await redis.call(command!, args)
|
|
125
143
|
}
|
|
126
144
|
async mset(obj) {
|
|
127
|
-
await this.redis()
|
|
145
|
+
const redis = await this.redis();
|
|
146
|
+
await redis.mset(obj);
|
|
128
147
|
}
|
|
129
148
|
async msetBuffer(obj) {
|
|
130
|
-
await this.redis()
|
|
149
|
+
const redis = await this.redis();
|
|
150
|
+
await redis.mset(obj);
|
|
131
151
|
}
|
|
132
152
|
async incr(key, by = 1) {
|
|
133
|
-
|
|
153
|
+
const redis = await this.redis();
|
|
154
|
+
return await redis.incrby(key, by);
|
|
134
155
|
}
|
|
135
156
|
async incrBatch(incrementTuples) {
|
|
136
157
|
const results = {};
|
|
@@ -145,7 +166,8 @@ export class RedisClient {
|
|
|
145
166
|
return validResults;
|
|
146
167
|
}
|
|
147
168
|
async ttl(key) {
|
|
148
|
-
|
|
169
|
+
const redis = await this.redis();
|
|
170
|
+
return await redis.ttl(key);
|
|
149
171
|
}
|
|
150
172
|
async dropTable(table) {
|
|
151
173
|
let count = 0;
|
|
@@ -177,36 +199,45 @@ export class RedisClient {
|
|
|
177
199
|
Returns BATCHES of keys in each iteration (as-is).
|
|
178
200
|
*/
|
|
179
201
|
scanStream(opt) {
|
|
180
|
-
return
|
|
202
|
+
return createReadableFromAsync(async () => {
|
|
203
|
+
const redis = await this.redis();
|
|
204
|
+
return redis.scanStream(opt);
|
|
205
|
+
});
|
|
181
206
|
}
|
|
182
207
|
/**
|
|
183
208
|
* Like scanStream, but flattens the stream of keys.
|
|
184
209
|
*/
|
|
185
210
|
scanStreamFlat(opt) {
|
|
186
211
|
// biome-ignore lint/correctness/noFlatMapIdentity: ok
|
|
187
|
-
return this.
|
|
212
|
+
return this.scanStream(opt).flatMap(keys => keys);
|
|
188
213
|
}
|
|
189
214
|
async scanCount(opt) {
|
|
215
|
+
const redis = await this.redis();
|
|
190
216
|
// todo: implement more efficiently, e.g via LUA?
|
|
191
217
|
let count = 0;
|
|
192
|
-
await
|
|
218
|
+
await redis.scanStream(opt).forEach(keys => {
|
|
193
219
|
count += keys.length;
|
|
194
220
|
});
|
|
195
221
|
return count;
|
|
196
222
|
}
|
|
197
223
|
hscanStream(key, opt) {
|
|
198
|
-
return
|
|
224
|
+
return createReadableFromAsync(async () => {
|
|
225
|
+
const redis = await this.redis();
|
|
226
|
+
return redis.hscanStream(key, opt);
|
|
227
|
+
});
|
|
199
228
|
}
|
|
200
229
|
async hscanCount(key, opt) {
|
|
201
230
|
let count = 0;
|
|
202
|
-
const
|
|
231
|
+
const redis = await this.redis();
|
|
232
|
+
const stream = redis.hscanStream(key, opt);
|
|
203
233
|
await stream.forEach((keyValueList) => {
|
|
204
234
|
count += keyValueList.length / 2;
|
|
205
235
|
});
|
|
206
236
|
return count;
|
|
207
237
|
}
|
|
208
238
|
async withPipeline(fn) {
|
|
209
|
-
const
|
|
239
|
+
const redis = await this.redis();
|
|
240
|
+
const pipeline = redis.pipeline();
|
|
210
241
|
await fn(pipeline);
|
|
211
242
|
await pipeline.exec();
|
|
212
243
|
}
|
|
@@ -214,3 +245,20 @@ export class RedisClient {
|
|
|
214
245
|
this.cfg.logger.log(...args);
|
|
215
246
|
}
|
|
216
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* Turn async function into Readable.
|
|
250
|
+
*/
|
|
251
|
+
function createReadableFromAsync(fn) {
|
|
252
|
+
const transform = new Transform({
|
|
253
|
+
objectMode: true,
|
|
254
|
+
transform: (chunk, _encoding, cb) => {
|
|
255
|
+
cb(null, chunk);
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
void fn()
|
|
259
|
+
.then(readable => {
|
|
260
|
+
readable.on('error', err => transform.emit('error', err)).pipe(transform);
|
|
261
|
+
})
|
|
262
|
+
.catch(err => transform.emit('error', err));
|
|
263
|
+
return transform;
|
|
264
|
+
}
|
package/package.json
CHANGED
package/src/redisClient.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { Transform } from 'node:stream'
|
|
1
2
|
import type {
|
|
2
3
|
AnyObject,
|
|
4
|
+
AsyncFunction,
|
|
3
5
|
CommonLogger,
|
|
4
6
|
NullableBuffer,
|
|
5
7
|
NullableString,
|
|
@@ -10,7 +12,6 @@ import type {
|
|
|
10
12
|
import { _stringMapEntries } from '@naturalcycles/js-lib'
|
|
11
13
|
import type { ReadableTyped } from '@naturalcycles/nodejs-lib'
|
|
12
14
|
import type { Redis, RedisOptions } from 'ioredis'
|
|
13
|
-
import type * as RedisLib from 'ioredis'
|
|
14
15
|
import type { ScanStreamOptions } from 'ioredis/built/types.js'
|
|
15
16
|
import type { ChainableCommander } from 'ioredis/built/utils/RedisCommander.js'
|
|
16
17
|
|
|
@@ -57,11 +58,11 @@ export class RedisClient implements CommonClient {
|
|
|
57
58
|
|
|
58
59
|
private _redis?: Redis
|
|
59
60
|
|
|
60
|
-
redis(): Redis {
|
|
61
|
+
async redis(): Promise<Redis> {
|
|
61
62
|
if (this._redis) return this._redis
|
|
62
63
|
|
|
63
64
|
// lazy-load the library
|
|
64
|
-
const redisLib =
|
|
65
|
+
const { default: redisLib } = await import('ioredis')
|
|
65
66
|
const redis = new redisLib.Redis(this.cfg.redisOptions)
|
|
66
67
|
|
|
67
68
|
const { logger } = this.cfg
|
|
@@ -82,14 +83,16 @@ export class RedisClient implements CommonClient {
|
|
|
82
83
|
|
|
83
84
|
async connect(): Promise<void> {
|
|
84
85
|
if (!this.connected) {
|
|
85
|
-
await this.redis()
|
|
86
|
+
const redis = await this.redis()
|
|
87
|
+
await redis.connect()
|
|
86
88
|
this.connected = true
|
|
87
89
|
}
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
async disconnect(): Promise<void> {
|
|
93
|
+
const redis = await this.redis()
|
|
91
94
|
this.log('redis: quit...')
|
|
92
|
-
this.log(`redis: quit`, await
|
|
95
|
+
this.log(`redis: quit`, await redis.quit())
|
|
93
96
|
this.connected = false
|
|
94
97
|
}
|
|
95
98
|
|
|
@@ -98,63 +101,77 @@ export class RedisClient implements CommonClient {
|
|
|
98
101
|
}
|
|
99
102
|
|
|
100
103
|
async ping(): Promise<void> {
|
|
101
|
-
await this.redis()
|
|
104
|
+
const redis = await this.redis()
|
|
105
|
+
await redis.ping()
|
|
102
106
|
}
|
|
103
107
|
|
|
104
108
|
async del(keys: string[]): Promise<number> {
|
|
105
|
-
|
|
109
|
+
const redis = await this.redis()
|
|
110
|
+
return await redis.del(keys)
|
|
106
111
|
}
|
|
107
112
|
|
|
108
113
|
async get(key: string): Promise<NullableString> {
|
|
109
|
-
|
|
114
|
+
const redis = await this.redis()
|
|
115
|
+
return await redis.get(key)
|
|
110
116
|
}
|
|
111
117
|
|
|
112
118
|
async getBuffer(key: string): Promise<NullableBuffer> {
|
|
113
|
-
|
|
119
|
+
const redis = await this.redis()
|
|
120
|
+
return await redis.getBuffer(key)
|
|
114
121
|
}
|
|
115
122
|
|
|
116
123
|
async mget(keys: string[]): Promise<NullableString[]> {
|
|
117
|
-
|
|
124
|
+
const redis = await this.redis()
|
|
125
|
+
return await redis.mget(keys)
|
|
118
126
|
}
|
|
119
127
|
|
|
120
128
|
async mgetBuffer(keys: string[]): Promise<NullableBuffer[]> {
|
|
121
|
-
|
|
129
|
+
const redis = await this.redis()
|
|
130
|
+
return await redis.mgetBuffer(keys)
|
|
122
131
|
}
|
|
123
132
|
|
|
124
133
|
async set(key: string, value: string | number | Buffer): Promise<void> {
|
|
125
|
-
await this.redis()
|
|
134
|
+
const redis = await this.redis()
|
|
135
|
+
await redis.set(key, value)
|
|
126
136
|
}
|
|
127
137
|
|
|
128
138
|
async hgetall<T extends Record<string, string> = Record<string, string>>(
|
|
129
139
|
key: string,
|
|
130
140
|
): Promise<T | null> {
|
|
131
|
-
const
|
|
141
|
+
const redis = await this.redis()
|
|
142
|
+
const result = await redis.hgetall(key)
|
|
132
143
|
if (Object.keys(result).length === 0) return null
|
|
133
144
|
return result as T
|
|
134
145
|
}
|
|
135
146
|
|
|
136
147
|
async hget(key: string, field: string): Promise<NullableString> {
|
|
137
|
-
|
|
148
|
+
const redis = await this.redis()
|
|
149
|
+
return await redis.hget(key, field)
|
|
138
150
|
}
|
|
139
151
|
|
|
140
152
|
async hset(key: string, value: AnyObject): Promise<void> {
|
|
141
|
-
await this.redis()
|
|
153
|
+
const redis = await this.redis()
|
|
154
|
+
await redis.hset(key, value)
|
|
142
155
|
}
|
|
143
156
|
|
|
144
157
|
async hdel(key: string, fields: string[]): Promise<void> {
|
|
145
|
-
await this.redis()
|
|
158
|
+
const redis = await this.redis()
|
|
159
|
+
await redis.hdel(key, ...fields)
|
|
146
160
|
}
|
|
147
161
|
|
|
148
162
|
async hmget(key: string, fields: string[]): Promise<NullableString[]> {
|
|
149
|
-
|
|
163
|
+
const redis = await this.redis()
|
|
164
|
+
return await redis.hmget(key, ...fields)
|
|
150
165
|
}
|
|
151
166
|
|
|
152
167
|
async hmgetBuffer(key: string, fields: string[]): Promise<NullableBuffer[]> {
|
|
153
|
-
|
|
168
|
+
const redis = await this.redis()
|
|
169
|
+
return await redis.hmgetBuffer(key, ...fields)
|
|
154
170
|
}
|
|
155
171
|
|
|
156
172
|
async hincr(key: string, field: string, increment = 1): Promise<number> {
|
|
157
|
-
|
|
173
|
+
const redis = await this.redis()
|
|
174
|
+
return await redis.hincrby(key, field, increment)
|
|
158
175
|
}
|
|
159
176
|
|
|
160
177
|
async hincrBatch(key: string, incrementTuples: [string, number][]): Promise<[string, number][]> {
|
|
@@ -181,7 +198,8 @@ export class RedisClient implements CommonClient {
|
|
|
181
198
|
value: string | number | Buffer,
|
|
182
199
|
expireAt: UnixTimestamp,
|
|
183
200
|
): Promise<void> {
|
|
184
|
-
await this.redis()
|
|
201
|
+
const redis = await this.redis()
|
|
202
|
+
await redis.set(key, value, 'EXAT', expireAt)
|
|
185
203
|
}
|
|
186
204
|
|
|
187
205
|
async hsetWithTTL(_key: string, _value: AnyObject, _expireAt: UnixTimestamp): Promise<void> {
|
|
@@ -191,20 +209,23 @@ export class RedisClient implements CommonClient {
|
|
|
191
209
|
// const keyList = valueKeys.join(' ')
|
|
192
210
|
// const commandString = `HEXPIREAT ${key} ${expireAt} FIELDS ${numberOfKeys} ${keyList}`
|
|
193
211
|
// const [command, ...args] = commandString.split(' ')
|
|
194
|
-
// await
|
|
195
|
-
// await
|
|
212
|
+
// await redis.hset(key, value)
|
|
213
|
+
// await redis.call(command!, args)
|
|
196
214
|
}
|
|
197
215
|
|
|
198
216
|
async mset(obj: Record<string, string | number>): Promise<void> {
|
|
199
|
-
await this.redis()
|
|
217
|
+
const redis = await this.redis()
|
|
218
|
+
await redis.mset(obj)
|
|
200
219
|
}
|
|
201
220
|
|
|
202
221
|
async msetBuffer(obj: Record<string, Buffer>): Promise<void> {
|
|
203
|
-
await this.redis()
|
|
222
|
+
const redis = await this.redis()
|
|
223
|
+
await redis.mset(obj)
|
|
204
224
|
}
|
|
205
225
|
|
|
206
226
|
async incr(key: string, by = 1): Promise<number> {
|
|
207
|
-
|
|
227
|
+
const redis = await this.redis()
|
|
228
|
+
return await redis.incrby(key, by)
|
|
208
229
|
}
|
|
209
230
|
|
|
210
231
|
async incrBatch(incrementTuples: [string, number][]): Promise<[string, number][]> {
|
|
@@ -227,7 +248,8 @@ export class RedisClient implements CommonClient {
|
|
|
227
248
|
}
|
|
228
249
|
|
|
229
250
|
async ttl(key: string): Promise<number> {
|
|
230
|
-
|
|
251
|
+
const redis = await this.redis()
|
|
252
|
+
return await redis.ttl(key)
|
|
231
253
|
}
|
|
232
254
|
|
|
233
255
|
async dropTable(table: string): Promise<void> {
|
|
@@ -266,7 +288,10 @@ export class RedisClient implements CommonClient {
|
|
|
266
288
|
Returns BATCHES of keys in each iteration (as-is).
|
|
267
289
|
*/
|
|
268
290
|
scanStream(opt?: ScanStreamOptions): ReadableTyped<string[]> {
|
|
269
|
-
return
|
|
291
|
+
return createReadableFromAsync(async () => {
|
|
292
|
+
const redis = await this.redis()
|
|
293
|
+
return redis.scanStream(opt)
|
|
294
|
+
})
|
|
270
295
|
}
|
|
271
296
|
|
|
272
297
|
/**
|
|
@@ -274,14 +299,15 @@ export class RedisClient implements CommonClient {
|
|
|
274
299
|
*/
|
|
275
300
|
scanStreamFlat(opt: ScanStreamOptions): ReadableTyped<string> {
|
|
276
301
|
// biome-ignore lint/correctness/noFlatMapIdentity: ok
|
|
277
|
-
return
|
|
302
|
+
return this.scanStream(opt).flatMap(keys => keys)
|
|
278
303
|
}
|
|
279
304
|
|
|
280
305
|
async scanCount(opt: ScanStreamOptions): Promise<number> {
|
|
306
|
+
const redis = await this.redis()
|
|
281
307
|
// todo: implement more efficiently, e.g via LUA?
|
|
282
308
|
let count = 0
|
|
283
309
|
|
|
284
|
-
await (
|
|
310
|
+
await (redis.scanStream(opt) as ReadableTyped<string[]>).forEach(keys => {
|
|
285
311
|
count += keys.length
|
|
286
312
|
})
|
|
287
313
|
|
|
@@ -289,13 +315,17 @@ export class RedisClient implements CommonClient {
|
|
|
289
315
|
}
|
|
290
316
|
|
|
291
317
|
hscanStream(key: string, opt?: ScanStreamOptions): ReadableTyped<string[]> {
|
|
292
|
-
return
|
|
318
|
+
return createReadableFromAsync(async () => {
|
|
319
|
+
const redis = await this.redis()
|
|
320
|
+
return redis.hscanStream(key, opt)
|
|
321
|
+
})
|
|
293
322
|
}
|
|
294
323
|
|
|
295
324
|
async hscanCount(key: string, opt?: ScanStreamOptions): Promise<number> {
|
|
296
325
|
let count = 0
|
|
297
326
|
|
|
298
|
-
const
|
|
327
|
+
const redis = await this.redis()
|
|
328
|
+
const stream = redis.hscanStream(key, opt)
|
|
299
329
|
|
|
300
330
|
await stream.forEach((keyValueList: string[]) => {
|
|
301
331
|
count += keyValueList.length / 2
|
|
@@ -305,7 +335,8 @@ export class RedisClient implements CommonClient {
|
|
|
305
335
|
}
|
|
306
336
|
|
|
307
337
|
async withPipeline(fn: (pipeline: ChainableCommander) => Promisable<void>): Promise<void> {
|
|
308
|
-
const
|
|
338
|
+
const redis = await this.redis()
|
|
339
|
+
const pipeline = redis.pipeline()
|
|
309
340
|
await fn(pipeline)
|
|
310
341
|
await pipeline.exec()
|
|
311
342
|
}
|
|
@@ -314,3 +345,23 @@ export class RedisClient implements CommonClient {
|
|
|
314
345
|
this.cfg.logger.log(...args)
|
|
315
346
|
}
|
|
316
347
|
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Turn async function into Readable.
|
|
351
|
+
*/
|
|
352
|
+
function createReadableFromAsync<T>(fn: AsyncFunction<ReadableTyped<T>>): ReadableTyped<T> {
|
|
353
|
+
const transform = new Transform({
|
|
354
|
+
objectMode: true,
|
|
355
|
+
transform: (chunk, _encoding, cb) => {
|
|
356
|
+
cb(null, chunk)
|
|
357
|
+
},
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
void fn()
|
|
361
|
+
.then(readable => {
|
|
362
|
+
readable.on('error', err => transform.emit('error', err)).pipe(transform)
|
|
363
|
+
})
|
|
364
|
+
.catch(err => transform.emit('error', err))
|
|
365
|
+
|
|
366
|
+
return transform
|
|
367
|
+
}
|