@pi-r/redis 0.6.0 → 0.6.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/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  ### @pi-r/redis
2
2
 
3
+ https://e-mc.readthedocs.io/en/latest/db/redis.html
4
+
3
5
  ### LICENSE
4
6
 
5
7
  MIT
package/client/index.js CHANGED
@@ -46,7 +46,7 @@ function removePoolProperties(options) {
46
46
  }
47
47
  return options;
48
48
  }
49
- const getPoolKey = (options) => options.url + '_' + (options.username || '') + '_' + (options.password || '') + '_' + (options.database ?? '') + '_' + (options.socket?.tls ? '1' : '0') + '_' + (options.legacyMode ? '1' : '0');
49
+ const getPoolKey = (options) => options.url && JSON.stringify(options);
50
50
  async function setCredential(item) {
51
51
  if (!REDIS_V4) {
52
52
  throw (0, types_1.errorValue)("Upgrade client to latest version" /* ERR_DB.UPGRADE */, "redis" /* STRINGS.MODULE_NAME */ + '@' + Db.getPackageVersion("redis" /* STRINGS.MODULE_NAME */));
@@ -99,18 +99,19 @@ async function setCredential(item) {
99
99
  let pool;
100
100
  username = undefined;
101
101
  password = undefined;
102
- if (typeof usePool === 'string' && (username = client.username || (0, util_1.parseConnectionString)(uri)?.username)) {
103
- [password, pool] = DbPool.validateKey(POOL_STATE, username, usePool);
104
- if (pool) {
105
- pool.add(item, password);
102
+ if ((0, types_1.isString)(usePool)) {
103
+ if (username = client.username || (0, util_1.parseConnectionString)(uri)?.username) {
104
+ [password, pool] = DbPool.validateKey(POOL_STATE, username, usePool);
105
+ if (pool) {
106
+ pool.add(item, password);
107
+ return;
108
+ }
109
+ }
110
+ if (!password) {
111
+ item.usePool = '';
106
112
  return;
107
113
  }
108
114
  }
109
- const poolKey = getPoolKey(client);
110
- if ((pool = POOL_STATE[poolKey]) && !pool.closed) {
111
- pool.add(item);
112
- return;
113
- }
114
115
  const config = this.getPoolConfig("redis" /* STRINGS.MODULE_NAME */, password);
115
116
  const poolOptions = client.isolationPoolOptions || (client.isolationPoolOptions = {});
116
117
  if (config) {
@@ -134,6 +135,11 @@ async function setCredential(item) {
134
135
  poolOptions.acquireTimeoutMillis ?? (poolOptions.acquireTimeoutMillis = timeout);
135
136
  }
136
137
  }
138
+ const poolKey = getPoolKey(client);
139
+ if ((pool = POOL_STATE[poolKey]) && !pool.closed) {
140
+ pool.add(item);
141
+ return;
142
+ }
137
143
  try {
138
144
  const target = new RedisPool(await getClient(redis, client, poolKey), poolKey, username && password ? { username, password } : undefined).add(item);
139
145
  target.parent = POOL_STATE;
@@ -229,13 +235,12 @@ async function executeBatchQuery(batch, options = '', outResult) {
229
235
  continue;
230
236
  }
231
237
  item.transactionState = 1 /* DB_TRANSACTION.ACTIVE */;
238
+ const command = (0, types_1.isPlainObject)(clientOptions.command) ? redis.commandOptions(clientOptions.command) : null;
232
239
  const uuidKey = (credential.uuidKey || (credential.uuidKey = (onceCredential ? batch[0] : item).credential?.uuidKey));
233
240
  const targetObject = typeof checkObject === 'string' ? this.hasCoerce("redis" /* STRINGS.MODULE_NAME */, 'options', uuidKey) && (0, types_1.asFunction)(checkObject) : checkObject;
234
- const renewCache = ignoreCache === 0;
235
- const cacheValue = renewCache ? { sessionKey, renewCache } : sessionKey;
236
- const command = (0, types_1.isPlainObject)(clientOptions.command) ? redis.commandOptions(clientOptions.command) : null;
241
+ const cacheValue = ignoreCache === undefined ? sessionKey : Array.isArray(ignoreCache) ? { sessionKey, exclusiveOf: ignoreCache } : { sessionKey, renewCache: ignoreCache === 0 };
237
242
  let queryString = '', rows;
238
- if (caching && ignoreCache !== true || ignoreCache === false || ignoreCache === 1 || renewCache) {
243
+ if (caching && ignoreCache !== true) {
239
244
  if ((0, types_1.isObject)(search)) {
240
245
  queryString = Db.asString(search, true);
241
246
  }
@@ -288,63 +293,128 @@ async function executeBatchQuery(batch, options = '', outResult) {
288
293
  commandType = this.commandType.UPDATE;
289
294
  const values = (!Array.isArray(update) ? [update] : update).map(async (target) => {
290
295
  return new Promise(async (success, failed) => {
291
- let { key: k, value: v, format: f = 'HASH', NX, XX, EX = 0, PX = 0, EXAT = 0, PXAT = 0, options: setOptions = {} } = target;
292
- if (v === undefined) {
296
+ let { key: k, value: v, format: f, NX, XX, options: setOptions = {} } = target;
297
+ f = ((0, types_1.isString)(f) ? f.toUpperCase() : 'HASH');
298
+ if (v === undefined && f !== 'JSON') {
293
299
  success(target);
294
300
  return;
295
301
  }
302
+ const has = (n) => typeof n === 'number' && n > 0;
296
303
  const failKey = () => failed((0, types_1.errorMessage)(f, "Invalid key" /* ERR_DB.KEY */, Db.asString(k) || "Unknown" /* ERR_MESSAGE.UNKNOWN */));
297
304
  const failValue = () => failed((0, types_1.errorMessage)(f, "Invalid value" /* ERR_DB.VALUE */, Db.asString(v) || "Unknown" /* ERR_MESSAGE.UNKNOWN */));
298
305
  const commandSet = (0, types_1.isPlainObject)(setOptions.command) && redis.commandOptions(setOptions.command);
299
- let pending;
300
- if ((f = f.toUpperCase()) === 'JSON') {
306
+ let pending, codeMin = 0, EX, PX, EXAT, PXAT, KEEPTTL;
307
+ if (f === 'JSON') {
301
308
  if (typeof k !== 'string') {
302
309
  failKey();
303
310
  return;
304
311
  }
305
- const path = target.path || '$';
306
312
  if (this.hasCoerce("redis" /* STRINGS.MODULE_NAME */, 'options', uuidKey)) {
307
313
  if ((0, types_1.isString)(v) && v.startsWith('new')) {
308
314
  ({ outV: v } = (0, types_1.coerceObject)({ outV: v }));
309
315
  }
310
316
  }
311
- const flags = NX ? { NX } : XX ? { XX } : undefined;
312
- pending = commandSet ? client.json.set(commandSet, k, path, v, flags) : client.json.set(k, path, v, flags);
317
+ let { path = '$', command: cmd, start, stop, index } = target;
318
+ const name = ((0, types_1.isString)(cmd) ? cmd.toUpperCase() : 'SET');
319
+ if (Array.isArray(v) && name !== 'ARRAPPEND' && name !== 'ARRINSERT' && name !== 'MSET') {
320
+ v = v[0];
321
+ }
322
+ switch (name) {
323
+ case 'ARRAPPEND':
324
+ pending = commandSet ? client.json.arrAppend(commandSet, k, path, ...(Array.isArray(v) ? v : [v])) : client.json.arrAppend(k, path, ...(Array.isArray(v) ? v : [v]));
325
+ break;
326
+ case 'ARRINDEX':
327
+ codeMin = -1;
328
+ pending = commandSet ? client.json.arrIndex(commandSet, k, path, v, start, stop) : client.json.arrIndex(k, path, v, start, stop);
329
+ break;
330
+ case 'ARRINSERT':
331
+ if (typeof index === 'number' || (0, types_1.isString)(index) && !isNaN(index = parseInt(index))) {
332
+ pending = commandSet ? client.json.arrInsert(commandSet, k, path, index, ...(Array.isArray(v) ? v : [v])) : client.json.arrInsert(k, path, index, ...(Array.isArray(v) ? v : [v]));
333
+ }
334
+ break;
335
+ case 'ARRPOP':
336
+ codeMin = -Infinity;
337
+ pending = commandSet ? client.json.arrPop(commandSet, k, path, index) : client.json.arrPop(k, path, index);
338
+ break;
339
+ case 'ARRTRIM':
340
+ if ((typeof start === 'number' || (0, types_1.isString)(start) && !isNaN(start = parseInt(start))) && (typeof stop === 'number' || (0, types_1.isString)(stop) && !isNaN(stop = parseInt(stop)))) {
341
+ pending = commandSet ? client.json.arrTrim(commandSet, k, path, start, stop) : client.json.arrTrim(k, path, start, stop);
342
+ }
343
+ break;
344
+ case 'DEL':
345
+ case 'FORGET':
346
+ pending = commandSet ? client.json[name === 'DEL' ? 'del' : 'forget'](commandSet, k, path) : client.json[name === 'DEL' ? 'del' : 'forget'](k, path);
347
+ break;
348
+ case 'MERGE':
349
+ pending = commandSet ? client.json.merge(commandSet, k, path, v) : client.json.merge(k, path, v);
350
+ break;
351
+ case 'MSET': {
352
+ const data = (Array.isArray(v) ? v : [v])
353
+ .filter((m) => m.value !== undefined)
354
+ .map((m) => {
355
+ m.key || (m.key = k);
356
+ m.path || (m.path = path);
357
+ return m;
358
+ });
359
+ if (data.length) {
360
+ pending = commandSet ? client.json.mSet(commandSet, data) : client.json.mSet(data);
361
+ }
362
+ break;
363
+ }
364
+ case 'NUMINCRBY':
365
+ case 'NUMMULTBY':
366
+ if (typeof v === 'number' || (0, types_1.isString)(v) && !isNaN(v = parseFloat(v))) {
367
+ pending = commandSet ? client.json[name === 'NUMINCRBY' ? 'numIncrBy' : 'numMultBy'](commandSet, k, path, v) : client.json[name === 'NUMINCRBY' ? 'numIncrBy' : 'numMultBy'](k, path, v);
368
+ }
369
+ break;
370
+ case 'STRAPPEND':
371
+ if ((0, types_1.isString)(v)) {
372
+ pending = commandSet ? client.json.strAppend(commandSet, k, path, v) : client.json.strAppend(k, path, v);
373
+ }
374
+ break;
375
+ default: {
376
+ const flags = NX ? { NX } : XX ? { XX } : undefined;
377
+ pending = commandSet ? client.json.set(commandSet, k, path, v, flags) : client.json.set(k, path, v, flags);
378
+ break;
379
+ }
380
+ }
313
381
  }
314
382
  else if (typeof k === 'string' || Buffer.isBuffer(k)) {
383
+ ({ EX, PX, EXAT, PXAT, KEEPTTL } = target);
315
384
  const field = target.field;
316
385
  if (field === undefined && field === null) {
317
386
  if (a(v)) {
318
- const flags = setOptions.set;
319
- if (flags) {
320
- if ('NX' in flags) {
321
- NX ?? (NX = !!flags.NX);
322
- delete flags.NX;
323
- }
324
- if ('XX' in flags) {
325
- XX ?? (XX = !!flags.XX);
326
- delete flags.XX;
327
- }
328
- if ('EX' in flags) {
329
- EX || (EX = flags.EX);
330
- delete flags.EX;
331
- }
332
- if ('PX' in flags) {
333
- PX || (PX = flags.PX);
334
- delete flags.PX;
335
- }
336
- if ('EXAT' in flags) {
337
- EXAT || (EXAT = flags.EXAT);
338
- delete flags.EXAT;
339
- }
340
- if ('PXAT' in flags) {
341
- PXAT || (PXAT = flags.PXAT);
342
- delete flags.PXAT;
343
- }
387
+ const flags = setOptions.set || {};
388
+ if (NX) {
389
+ flags.NX = true;
390
+ }
391
+ if (XX) {
392
+ flags.XX = true;
393
+ }
394
+ if (has(EX)) {
395
+ flags.EX = EX;
396
+ EX = 0;
397
+ }
398
+ if (has(PX)) {
399
+ flags.PX = PX;
400
+ PX = 0;
401
+ }
402
+ if (has(EXAT)) {
403
+ flags.EXAT = EXAT;
404
+ EXAT = 0;
405
+ }
406
+ if (has(PXAT)) {
407
+ flags.PXAT = PXAT;
408
+ PXAT = 0;
344
409
  }
345
- pending = (commandSet ? client.set(commandSet, k, v, setOptions.set) : client.set(k, v, setOptions.set));
410
+ if (KEEPTTL) {
411
+ flags.KEEPTTL = true;
412
+ KEEPTTL = false;
413
+ }
414
+ codeMin = NaN;
415
+ pending = commandSet ? client.set(commandSet, k, v, flags) : client.set(k, v, flags);
346
416
  }
347
- else if (typeof v === 'object') {
417
+ else if ((0, types_1.isObject)(v)) {
348
418
  let valid = true;
349
419
  if (NX || XX) {
350
420
  valid = await client.exists(k) === 1;
@@ -361,6 +431,7 @@ async function executeBatchQuery(batch, options = '', outResult) {
361
431
  }
362
432
  else if (NX) {
363
433
  if (a(field) && a(v)) {
434
+ codeMin = -1;
364
435
  pending = commandSet ? client.hSetNX(commandSet, k, b(field), b(v)) : client.hSetNX(k, b(field), b(v));
365
436
  }
366
437
  }
@@ -383,43 +454,52 @@ async function executeBatchQuery(batch, options = '', outResult) {
383
454
  return;
384
455
  }
385
456
  if (pending) {
386
- pending.then(code => {
387
- if (code === 'OK' || typeof code === 'number' || code === true) {
388
- const has = (n) => typeof n === 'number' && n !== 0;
389
- if (has(EX) || has(PX) || EXAT > 0 || PXAT > 0) {
390
- const expire = setOptions.expire;
391
- let mode;
392
- if (expire) {
393
- if (expire.NX) {
394
- mode = 'NX';
395
- }
396
- else if (expire.XX) {
397
- mode = 'XX';
398
- }
399
- else if (expire.GT) {
400
- mode = 'GT';
401
- }
402
- else if (expire.LT) {
403
- mode = 'LT';
404
- }
457
+ pending.then(async (code) => {
458
+ if (code === undefined || code === null || code !== -Infinity && (0, types_1.isString)(code) && code !== 'OK' || typeof code === 'number' && code <= codeMin || Array.isArray(code) && code.every(resp => resp === null || typeof resp === 'number' && resp <= codeMin)) {
459
+ failValue();
460
+ return;
461
+ }
462
+ let ttl = false;
463
+ if (!isNaN(codeMin) && (has(EX) || has(PX) || has(EXAT) || has(PXAT) || (ttl = !!KEEPTTL))) {
464
+ const expire = setOptions.expire;
465
+ let mode;
466
+ if (expire) {
467
+ if (expire.NX) {
468
+ mode = 'NX';
469
+ }
470
+ else if (expire.XX) {
471
+ mode = 'XX';
472
+ }
473
+ else if (expire.GT) {
474
+ mode = 'GT';
475
+ }
476
+ else if (expire.LT) {
477
+ mode = 'LT';
405
478
  }
406
- (has(EX) ? client.expire(k, EX, mode) : has(PX) ? client.pExpire(k, PX, mode) : EXAT > 0 ? client.expireAt(k, EXAT, mode) : client.pExpireAt(k, PXAT, mode))
407
- .then(valid => {
408
- if (valid) {
409
- success(target);
410
- }
411
- else {
412
- failed((0, types_1.errorMessage)(f, "Invalid value" /* ERR_DB.VALUE */, has(EX) ? 'EX: ' + EX : has(PX) ? 'PX: ' + PX : EXAT > 0 ? 'EXAT: ' + EX : 'PXAT: ' + PXAT));
413
- }
414
- })
415
- .catch(err => failed(err));
416
479
  }
417
- else {
418
- success(target);
480
+ if (ttl) {
481
+ PX = await client.pTTL(k).catch(err => {
482
+ failed(err);
483
+ return 0;
484
+ });
485
+ if (PX <= 0) {
486
+ success(target);
487
+ return;
488
+ }
419
489
  }
490
+ (has(EX) ? client.expire(k, EX, mode) : has(PX) ? client.pExpire(k, PX, mode) : has(EXAT) ? client.expireAt(k, EXAT, mode) : client.pExpireAt(k, PXAT, mode))
491
+ .then(valid => {
492
+ if (valid) {
493
+ success(target);
494
+ }
495
+ else {
496
+ failed((0, types_1.errorMessage)(f, "Invalid value" /* ERR_DB.VALUE */, has(EX) ? 'EX: ' + EX : has(PX) ? 'PX: ' + PX : has(EXAT) ? 'EXAT: ' + EX : 'PXAT: ' + PXAT));
497
+ }
498
+ })
499
+ .catch(err => failed(err));
420
500
  }
421
501
  else {
422
- failValue();
502
+ success(target);
423
503
  }
424
504
  })
425
505
  .catch(err => failed(err));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-r/redis",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "Redis client driver for E-mc.",
5
5
  "main": "client/index.js",
6
6
  "types": "client/index.d.ts",
@@ -21,8 +21,8 @@
21
21
  "license": "MIT",
22
22
  "homepage": "https://github.com/anpham6/pi-r#readme",
23
23
  "dependencies": {
24
- "@e-mc/db": "^0.8.0",
25
- "@e-mc/types": "^0.8.0",
24
+ "@e-mc/db": "^0.8.2",
25
+ "@e-mc/types": "^0.8.2",
26
26
  "redis": "^4.6.12"
27
27
  }
28
28
  }
package/types/index.d.ts CHANGED
@@ -1,15 +1,15 @@
1
1
  import type { DbDataSource } from '@e-mc/types/lib/squared';
2
2
 
3
- import type { CascadeAction, ExecuteAction, ServerAuth } from '@e-mc/types/lib/db';
3
+ import type { CascadeAction, ServerAuth } from '@e-mc/types/lib/db';
4
4
 
5
5
  import type { CommandOptions } from '@redis/client/dist/lib/command-options';
6
6
  import type { ClientCommandOptions } from '@redis/client/dist/lib/client';
7
7
  import type { RedisClientOptions } from '@redis/client';
8
8
  import type { RedisCommandArgument } from '@redis/client/dist/lib/commands';
9
9
  import type { RedisJSON } from '@redis/json/dist/commands';
10
- import type { AggregateOptions, RediSearchSchema, SearchOptions } from 'redis';
10
+ import type { AggregateOptions, RediSearchSchema, SearchOptions, SetOptions } from 'redis';
11
11
 
12
- export interface RedisDataSource extends DbDataSource<string, PlainObject, ArrayOf<RedisSetValue> | ArrayOf<RedisJSONValue>, string | RedisCredential, string>, CascadeAction, AuthValue {
12
+ export interface RedisDataSource extends DbDataSource<string, PlainObject, ArrayOf<RedisSetValue> | ArrayOf<RedisJSONValue>, string | RedisCredential, string>, AuthValue, CascadeAction {
13
13
  source: "redis";
14
14
  key?: ArrayOf<RedisCommandArgument>;
15
15
  field?: RedisCommandArgument;
@@ -29,26 +29,35 @@ export interface RedisDataSource extends DbDataSource<string, PlainObject, Array
29
29
 
30
30
  export type RedisCredential = ServerAuth;
31
31
 
32
- export interface RedisSetValue<T = "HASH" | undefined, U = RedisCommandArgument, V = RedisCommandValue | RedisHSETObject> {
32
+ export interface RedisCommand<T = "HASH" | "JSON" | undefined, U = unknown, V = unknown> {
33
33
  format: T;
34
+ command?: string;
34
35
  key?: U;
35
- field?: V;
36
36
  value?: V;
37
37
  NX?: boolean;
38
38
  XX?: boolean;
39
- EX?: number;
40
- PX?: number;
41
- EXAT?: number;
42
- PXAT?: number;
43
39
  options?: {
44
- set?: PlainObject;
40
+ set?: SetOptions;
45
41
  expire?: { [K in RedisExpireCondition]?: boolean; };
46
42
  command?: RedisCommandOptions;
47
43
  };
48
44
  }
49
45
 
50
- export interface RedisJSONValue extends Omit<RedisSetValue<"JSON", string, RedisJSON>, "field" | "EX" | "PX" | "EXAT" | "PXAT"> {
46
+ export interface RedisSetValue extends RedisCommand<"HASH", RedisCommandArgument, RedisCommandValue | RedisHSETObject> {
47
+ field?: RedisCommandValue | RedisHSETObject;
48
+ EX?: number;
49
+ PX?: number;
50
+ EXAT?: number;
51
+ PXAT?: number;
52
+ KEEPTTL?: boolean;
53
+ }
54
+
55
+ export interface RedisJSONValue extends RedisCommand<"JSON", string, ArrayOf<RedisJSON>> {
56
+ command?: RedisCommandJSON;
51
57
  path?: string;
58
+ start?: NumString;
59
+ stop?: NumString;
60
+ index?: NumString;
52
61
  }
53
62
 
54
63
  export interface RedisQuery {
@@ -58,7 +67,14 @@ export interface RedisQuery {
58
67
  options?: PlainObject;
59
68
  }
60
69
 
70
+ export interface JsonMSetItem {
71
+ key: RedisCommandArgument;
72
+ path: RedisCommandArgument;
73
+ value: RedisJSON;
74
+ }
75
+
61
76
  export type RedisFormat = "HASH" | "JSON";
77
+ export type RedisCommandJSON = "ARRAPPEND" | "ARRINDEX" | "ARRINSERT" | "ARRPOP" | "ARRTRIM" | "DEL" | "FORGET" | "MERGE" | "MSET" | "NUMINCRBY" | "NUMMULTBY" | "SET" | "STRAPPEND";
62
78
  export type RedisExpireCondition = "NX" | "XX" | "GT" | "LT";
63
79
  export type RedisCommandValue = RedisCommandArgument | number;
64
80
  export type RedisCommandOptions = CommandOptions<ClientCommandOptions>;