@keyv/redis 5.1.0 → 5.1.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 +66 -16
- package/dist/index.cjs +195 -77
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +196 -80
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -38,6 +38,7 @@ Redis storage adapter for [Keyv](https://github.com/jaredwray/keyv).
|
|
|
38
38
|
* [Clustering](#clustering)
|
|
39
39
|
* [Sentinel](#sentinel)
|
|
40
40
|
* [TLS Support](#tls-support)
|
|
41
|
+
* [Keyv Redis Options](#keyv-redis-options)
|
|
41
42
|
* [API](#api)
|
|
42
43
|
* [Using Custom Redis Client Events](#using-custom-redis-client-events)
|
|
43
44
|
* [Migrating from v3 to v4](#migrating-from-v3-to-v4)
|
|
@@ -79,21 +80,20 @@ Here you can pass in the Redis options directly:
|
|
|
79
80
|
import Keyv from 'keyv';
|
|
80
81
|
import KeyvRedis from '@keyv/redis';
|
|
81
82
|
|
|
82
|
-
const
|
|
83
|
-
url: 'redis://localhost:6379', // The Redis server URL (use 'rediss' for TLS)
|
|
84
|
-
password: 'your_password', // Optional password if Redis has authentication enabled
|
|
83
|
+
const uri = "redis://localhost:6379";
|
|
85
84
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
85
|
+
// NOTE: please use the settings that you need to configure. Check out Keyv Redis Options section
|
|
86
|
+
const options = {
|
|
87
|
+
namespace: "test",
|
|
88
|
+
keyPrefixSeparator: "->",
|
|
89
|
+
clearBatchSize: 100,
|
|
90
|
+
useUnlink: true,
|
|
91
|
+
noNamespaceAffectsAll: true,
|
|
94
92
|
};
|
|
95
93
|
|
|
96
|
-
const
|
|
94
|
+
const keyvRedis = new KeyvRedis(uri, options);
|
|
95
|
+
|
|
96
|
+
const keyv = new Keyv(keyvRedis);
|
|
97
97
|
```
|
|
98
98
|
|
|
99
99
|
Or you can create a new Redis instance and pass it in with `KeyvOptions` such as setting the `store`:
|
|
@@ -465,6 +465,60 @@ const tlsOptions = {
|
|
|
465
465
|
const keyv = new Keyv({ store: new KeyvRedis(tlsOptions) });
|
|
466
466
|
```
|
|
467
467
|
|
|
468
|
+
# Keyv Redis Options
|
|
469
|
+
|
|
470
|
+
Here are all the options that you can set on the constructor
|
|
471
|
+
|
|
472
|
+
```ts
|
|
473
|
+
export type KeyvRedisOptions = {
|
|
474
|
+
/**
|
|
475
|
+
* Namespace for the current instance.
|
|
476
|
+
*/
|
|
477
|
+
namespace?: string;
|
|
478
|
+
/**
|
|
479
|
+
* Separator to use between namespace and key.
|
|
480
|
+
*/
|
|
481
|
+
keyPrefixSeparator?: string;
|
|
482
|
+
/**
|
|
483
|
+
* Number of keys to delete in a single batch.
|
|
484
|
+
*/
|
|
485
|
+
clearBatchSize?: number;
|
|
486
|
+
/**
|
|
487
|
+
* Enable Unlink instead of using Del for clearing keys. This is more performant but may not be supported by all Redis versions.
|
|
488
|
+
*/
|
|
489
|
+
useUnlink?: boolean;
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Whether to allow clearing all keys when no namespace is set.
|
|
493
|
+
* If set to true and no namespace is set, iterate() will return all keys.
|
|
494
|
+
* Defaults to `false`.
|
|
495
|
+
*/
|
|
496
|
+
noNamespaceAffectsAll?: boolean;
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* This is used to throw an error if the client is not connected when trying to connect. By default, this is
|
|
500
|
+
* set to true so that it throws an error when trying to connect to the Redis server fails.
|
|
501
|
+
*/
|
|
502
|
+
throwOnConnectError?: boolean;
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* This is used to throw an error if at any point there is a failure. Use this if you want to
|
|
506
|
+
* ensure that all operations are successful and you want to handle errors. By default, this is
|
|
507
|
+
* set to false so that it does not throw an error on every operation and instead emits an error event
|
|
508
|
+
* and returns no-op responses.
|
|
509
|
+
* @default false
|
|
510
|
+
*/
|
|
511
|
+
throwOnErrors?: boolean;
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Timeout in milliseconds for the connection. Default is undefined, which uses the default timeout of the Redis client.
|
|
515
|
+
* If set, it will throw an error if the connection does not succeed within the specified time.
|
|
516
|
+
* @default undefined
|
|
517
|
+
*/
|
|
518
|
+
connectionTimeout?: number;
|
|
519
|
+
};
|
|
520
|
+
```
|
|
521
|
+
|
|
468
522
|
# API
|
|
469
523
|
* **constructor([connection], [options])**
|
|
470
524
|
* **namespace** - The namespace to use for the keys.
|
|
@@ -530,14 +584,10 @@ const keyv = new Keyv({ store: redis, namespace: 'my-namespace', useKeyPrefix: f
|
|
|
530
584
|
|
|
531
585
|
This will make it so the storage adapter `@keyv/redis` will handle the namespace and not the `keyv` instance. If you leave it on it will just look duplicated like `my-namespace:my-namespace:key`.
|
|
532
586
|
|
|
533
|
-
|
|
534
|
-
|
|
535
587
|
# About Redis Sets and its Support in v4
|
|
536
588
|
|
|
537
589
|
We no longer support redis sets. This is due to the fact that it caused significant performance issues and was not a good fit for the library.
|
|
538
590
|
|
|
539
|
-
|
|
540
|
-
|
|
541
591
|
# Using with NestJS
|
|
542
592
|
|
|
543
593
|
> You can integrate `@keyv/redis` with NestJS by creating a custom `CacheModule`. This allows you to use Keyv as a cache store in your application.
|
package/dist/index.cjs
CHANGED
|
@@ -42,9 +42,9 @@ __export(index_exports, {
|
|
|
42
42
|
});
|
|
43
43
|
module.exports = __toCommonJS(index_exports);
|
|
44
44
|
var import_client = require("@redis/client");
|
|
45
|
+
var import_cluster_key_slot = __toESM(require("cluster-key-slot"), 1);
|
|
45
46
|
var import_hookified = require("hookified");
|
|
46
47
|
var import_keyv = require("keyv");
|
|
47
|
-
var import_cluster_key_slot = __toESM(require("cluster-key-slot"), 1);
|
|
48
48
|
var import_client2 = require("@redis/client");
|
|
49
49
|
var import_keyv2 = require("keyv");
|
|
50
50
|
var RedisErrorMessages = /* @__PURE__ */ ((RedisErrorMessages2) => {
|
|
@@ -79,7 +79,10 @@ var KeyvRedis = class extends import_hookified.Hookified {
|
|
|
79
79
|
};
|
|
80
80
|
if (connect) {
|
|
81
81
|
if (typeof connect === "string") {
|
|
82
|
-
this._client = (0, import_client.createClient)({
|
|
82
|
+
this._client = (0, import_client.createClient)({
|
|
83
|
+
url: connect,
|
|
84
|
+
socket
|
|
85
|
+
});
|
|
83
86
|
} else if (connect.connect !== void 0) {
|
|
84
87
|
if (this.isClientSentinel(connect)) {
|
|
85
88
|
this._client = connect;
|
|
@@ -90,9 +93,13 @@ var KeyvRedis = class extends import_hookified.Hookified {
|
|
|
90
93
|
}
|
|
91
94
|
} else if (connect instanceof Object) {
|
|
92
95
|
if (connect.sentinelRootNodes !== void 0) {
|
|
93
|
-
this._client = (0, import_client.createSentinel)(
|
|
96
|
+
this._client = (0, import_client.createSentinel)(
|
|
97
|
+
connect
|
|
98
|
+
);
|
|
94
99
|
} else if (connect.rootNodes === void 0) {
|
|
95
|
-
this._client = (0, import_client.createClient)(
|
|
100
|
+
this._client = (0, import_client.createClient)(
|
|
101
|
+
connect
|
|
102
|
+
);
|
|
96
103
|
} else {
|
|
97
104
|
this._client = (0, import_client.createCluster)(connect);
|
|
98
105
|
}
|
|
@@ -321,20 +328,50 @@ var KeyvRedis = class extends import_hookified.Hookified {
|
|
|
321
328
|
* @param {KeyvEntry[]} entries - the key value pairs to set with optional ttl
|
|
322
329
|
*/
|
|
323
330
|
async setMany(entries) {
|
|
324
|
-
const client = await this.getClient();
|
|
325
331
|
try {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
332
|
+
if (this.isCluster()) {
|
|
333
|
+
await this.getClient();
|
|
334
|
+
const slotMap = /* @__PURE__ */ new Map();
|
|
335
|
+
for (const entry of entries) {
|
|
336
|
+
const prefixedKey = this.createKeyPrefix(entry.key, this._namespace);
|
|
337
|
+
const slot = (0, import_cluster_key_slot.default)(prefixedKey);
|
|
338
|
+
const slotEntries = slotMap.get(slot) ?? [];
|
|
339
|
+
slotEntries.push(entry);
|
|
340
|
+
slotMap.set(slot, slotEntries);
|
|
333
341
|
}
|
|
342
|
+
await Promise.all(
|
|
343
|
+
Array.from(slotMap.entries(), async ([slot, slotEntries]) => {
|
|
344
|
+
const client = await this.getSlotMaster(slot);
|
|
345
|
+
const multi = client.multi();
|
|
346
|
+
for (const { key, value, ttl } of slotEntries) {
|
|
347
|
+
const prefixedKey = this.createKeyPrefix(key, this._namespace);
|
|
348
|
+
if (ttl) {
|
|
349
|
+
multi.set(prefixedKey, value, { PX: ttl });
|
|
350
|
+
} else {
|
|
351
|
+
multi.set(prefixedKey, value);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
await multi.exec();
|
|
355
|
+
})
|
|
356
|
+
);
|
|
357
|
+
} else {
|
|
358
|
+
const client = await this.getClient();
|
|
359
|
+
const multi = client.multi();
|
|
360
|
+
for (const { key, value, ttl } of entries) {
|
|
361
|
+
const prefixedKey = this.createKeyPrefix(key, this._namespace);
|
|
362
|
+
if (ttl) {
|
|
363
|
+
multi.set(prefixedKey, value, { PX: ttl });
|
|
364
|
+
} else {
|
|
365
|
+
multi.set(prefixedKey, value);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
await multi.exec();
|
|
334
369
|
}
|
|
335
|
-
await multi.exec();
|
|
336
370
|
} catch (error) {
|
|
337
371
|
this.emit("error", error);
|
|
372
|
+
if (this._throwOnConnectError && error.message === "Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true." /* RedisClientNotConnectedThrown */) {
|
|
373
|
+
throw error;
|
|
374
|
+
}
|
|
338
375
|
if (this._throwOnErrors) {
|
|
339
376
|
throw error;
|
|
340
377
|
}
|
|
@@ -365,17 +402,46 @@ var KeyvRedis = class extends import_hookified.Hookified {
|
|
|
365
402
|
* @returns {Promise<Array<boolean>>} - array of booleans for each key if it exists
|
|
366
403
|
*/
|
|
367
404
|
async hasMany(keys) {
|
|
368
|
-
const client = await this.getClient();
|
|
369
405
|
try {
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
406
|
+
const prefixedKeys = keys.map(
|
|
407
|
+
(key) => this.createKeyPrefix(key, this._namespace)
|
|
408
|
+
);
|
|
409
|
+
if (this.isCluster()) {
|
|
410
|
+
const slotMap = this.getSlotMap(prefixedKeys);
|
|
411
|
+
const resultMap = /* @__PURE__ */ new Map();
|
|
412
|
+
await Promise.all(
|
|
413
|
+
Array.from(slotMap.entries(), async ([slot, slotKeys]) => {
|
|
414
|
+
const client = await this.getSlotMaster(slot);
|
|
415
|
+
const multi = client.multi();
|
|
416
|
+
for (const key of slotKeys) {
|
|
417
|
+
multi.exists(key);
|
|
418
|
+
}
|
|
419
|
+
const results = await multi.exec();
|
|
420
|
+
for (const [index, result] of results.entries()) {
|
|
421
|
+
resultMap.set(
|
|
422
|
+
slotKeys[index],
|
|
423
|
+
typeof result === "number" && result === 1
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
})
|
|
427
|
+
);
|
|
428
|
+
return prefixedKeys.map((key) => resultMap.get(key) ?? false);
|
|
429
|
+
} else {
|
|
430
|
+
const client = await this.getClient();
|
|
431
|
+
const multi = client.multi();
|
|
432
|
+
for (const key of prefixedKeys) {
|
|
433
|
+
multi.exists(key);
|
|
434
|
+
}
|
|
435
|
+
const results = await multi.exec();
|
|
436
|
+
return results.map(
|
|
437
|
+
(result) => typeof result === "number" && result === 1
|
|
438
|
+
);
|
|
374
439
|
}
|
|
375
|
-
const results = await multi.exec();
|
|
376
|
-
return results.map((result) => typeof result === "number" && result === 1);
|
|
377
440
|
} catch (error) {
|
|
378
441
|
this.emit("error", error);
|
|
442
|
+
if (this._throwOnConnectError && error.message === "Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true." /* RedisClientNotConnectedThrown */) {
|
|
443
|
+
throw error;
|
|
444
|
+
}
|
|
379
445
|
if (this._throwOnErrors) {
|
|
380
446
|
throw error;
|
|
381
447
|
}
|
|
@@ -452,25 +518,53 @@ var KeyvRedis = class extends import_hookified.Hookified {
|
|
|
452
518
|
*/
|
|
453
519
|
async deleteMany(keys) {
|
|
454
520
|
let result = false;
|
|
455
|
-
const client = await this.getClient();
|
|
456
521
|
try {
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
522
|
+
const prefixedKeys = keys.map(
|
|
523
|
+
(key) => this.createKeyPrefix(key, this._namespace)
|
|
524
|
+
);
|
|
525
|
+
if (this.isCluster()) {
|
|
526
|
+
const slotMap = this.getSlotMap(prefixedKeys);
|
|
527
|
+
await Promise.all(
|
|
528
|
+
Array.from(slotMap.entries(), async ([slot, slotKeys]) => {
|
|
529
|
+
const client = await this.getSlotMaster(slot);
|
|
530
|
+
const multi = client.multi();
|
|
531
|
+
for (const key of slotKeys) {
|
|
532
|
+
if (this._useUnlink) {
|
|
533
|
+
multi.unlink(key);
|
|
534
|
+
} else {
|
|
535
|
+
multi.del(key);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
const results = await multi.exec();
|
|
539
|
+
for (const deleted of results) {
|
|
540
|
+
if (typeof deleted === "number" && deleted > 0) {
|
|
541
|
+
result = true;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
})
|
|
545
|
+
);
|
|
546
|
+
} else {
|
|
547
|
+
const client = await this.getClient();
|
|
548
|
+
const multi = client.multi();
|
|
549
|
+
for (const key of prefixedKeys) {
|
|
550
|
+
if (this._useUnlink) {
|
|
551
|
+
multi.unlink(key);
|
|
552
|
+
} else {
|
|
553
|
+
multi.del(key);
|
|
554
|
+
}
|
|
464
555
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
556
|
+
const results = await multi.exec();
|
|
557
|
+
for (const deleted of results) {
|
|
558
|
+
if (typeof deleted === "number" && deleted > 0) {
|
|
559
|
+
result = true;
|
|
560
|
+
}
|
|
470
561
|
}
|
|
471
562
|
}
|
|
472
563
|
} catch (error) {
|
|
473
564
|
this.emit("error", error);
|
|
565
|
+
if (this._throwOnConnectError && error.message === "Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true." /* RedisClientNotConnectedThrown */) {
|
|
566
|
+
throw error;
|
|
567
|
+
}
|
|
474
568
|
if (this._throwOnErrors) {
|
|
475
569
|
throw error;
|
|
476
570
|
}
|
|
@@ -534,7 +628,9 @@ var KeyvRedis = class extends import_hookified.Hookified {
|
|
|
534
628
|
async getMasterNodes() {
|
|
535
629
|
if (this.isCluster()) {
|
|
536
630
|
const cluster = await this.getClient();
|
|
537
|
-
const nodes = cluster.masters.map(
|
|
631
|
+
const nodes = cluster.masters.map(
|
|
632
|
+
async (main) => cluster.nodeClient(main)
|
|
633
|
+
);
|
|
538
634
|
return Promise.all(nodes);
|
|
539
635
|
}
|
|
540
636
|
return [await this.getClient()];
|
|
@@ -550,7 +646,10 @@ var KeyvRedis = class extends import_hookified.Hookified {
|
|
|
550
646
|
const match = namespace ? `${namespace}${this._keyPrefixSeparator}*` : "*";
|
|
551
647
|
let cursor = "0";
|
|
552
648
|
do {
|
|
553
|
-
const result = await client.scan(cursor, {
|
|
649
|
+
const result = await client.scan(cursor, {
|
|
650
|
+
MATCH: match,
|
|
651
|
+
TYPE: "string"
|
|
652
|
+
});
|
|
554
653
|
cursor = result.cursor.toString();
|
|
555
654
|
let { keys } = result;
|
|
556
655
|
if (!namespace && !this._noNamespaceAffectsAll) {
|
|
@@ -577,29 +676,37 @@ var KeyvRedis = class extends import_hookified.Hookified {
|
|
|
577
676
|
async clear() {
|
|
578
677
|
try {
|
|
579
678
|
const clients = await this.getMasterNodes();
|
|
580
|
-
await Promise.all(
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
let cursor = "0";
|
|
586
|
-
const batchSize = this._clearBatchSize;
|
|
587
|
-
const match = this._namespace ? `${this._namespace}${this._keyPrefixSeparator}*` : "*";
|
|
588
|
-
const deletePromises = [];
|
|
589
|
-
do {
|
|
590
|
-
const result = await client.scan(cursor, { MATCH: match, COUNT: batchSize, TYPE: "string" });
|
|
591
|
-
cursor = result.cursor.toString();
|
|
592
|
-
let { keys } = result;
|
|
593
|
-
if (keys.length === 0) {
|
|
594
|
-
continue;
|
|
595
|
-
}
|
|
596
|
-
if (!this._namespace) {
|
|
597
|
-
keys = keys.filter((key) => !key.includes(this._keyPrefixSeparator));
|
|
679
|
+
await Promise.all(
|
|
680
|
+
clients.map(async (client) => {
|
|
681
|
+
if (!this._namespace && this._noNamespaceAffectsAll) {
|
|
682
|
+
await client.flushDb();
|
|
683
|
+
return;
|
|
598
684
|
}
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
685
|
+
let cursor = "0";
|
|
686
|
+
const batchSize = this._clearBatchSize;
|
|
687
|
+
const match = this._namespace ? `${this._namespace}${this._keyPrefixSeparator}*` : "*";
|
|
688
|
+
const deletePromises = [];
|
|
689
|
+
do {
|
|
690
|
+
const result = await client.scan(cursor, {
|
|
691
|
+
MATCH: match,
|
|
692
|
+
COUNT: batchSize,
|
|
693
|
+
TYPE: "string"
|
|
694
|
+
});
|
|
695
|
+
cursor = result.cursor.toString();
|
|
696
|
+
let { keys } = result;
|
|
697
|
+
if (keys.length === 0) {
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
700
|
+
if (!this._namespace) {
|
|
701
|
+
keys = keys.filter(
|
|
702
|
+
(key) => !key.includes(this._keyPrefixSeparator)
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
deletePromises.push(this.clearWithClusterSupport(keys));
|
|
706
|
+
} while (cursor !== "0");
|
|
707
|
+
await Promise.all(deletePromises);
|
|
708
|
+
})
|
|
709
|
+
);
|
|
603
710
|
} catch (error) {
|
|
604
711
|
this.emit("error", error);
|
|
605
712
|
}
|
|
@@ -609,15 +716,25 @@ var KeyvRedis = class extends import_hookified.Hookified {
|
|
|
609
716
|
* by separating the keys by slot to solve the CROSS-SLOT restriction.
|
|
610
717
|
*/
|
|
611
718
|
async mget(keys) {
|
|
612
|
-
const slotMap = this.getSlotMap(keys);
|
|
613
719
|
const valueMap = /* @__PURE__ */ new Map();
|
|
614
|
-
|
|
615
|
-
const
|
|
616
|
-
|
|
720
|
+
if (this.isCluster()) {
|
|
721
|
+
const slotMap = this.getSlotMap(keys);
|
|
722
|
+
await Promise.all(
|
|
723
|
+
Array.from(slotMap.entries(), async ([slot, slotKeys]) => {
|
|
724
|
+
const client = await this.getSlotMaster(slot);
|
|
725
|
+
const values = await client.mGet(slotKeys);
|
|
726
|
+
for (const [index, value] of values.entries()) {
|
|
727
|
+
valueMap.set(slotKeys[index], value ?? void 0);
|
|
728
|
+
}
|
|
729
|
+
})
|
|
730
|
+
);
|
|
731
|
+
} else {
|
|
732
|
+
const client = await this.getClient();
|
|
733
|
+
const values = await client.mGet(keys);
|
|
617
734
|
for (const [index, value] of values.entries()) {
|
|
618
|
-
valueMap.set(
|
|
735
|
+
valueMap.set(keys[index], value ?? void 0);
|
|
619
736
|
}
|
|
620
|
-
}
|
|
737
|
+
}
|
|
621
738
|
return keys.map((key) => valueMap.get(key));
|
|
622
739
|
}
|
|
623
740
|
/**
|
|
@@ -627,10 +744,12 @@ var KeyvRedis = class extends import_hookified.Hookified {
|
|
|
627
744
|
async clearWithClusterSupport(keys) {
|
|
628
745
|
if (keys.length > 0) {
|
|
629
746
|
const slotMap = this.getSlotMap(keys);
|
|
630
|
-
await Promise.all(
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
747
|
+
await Promise.all(
|
|
748
|
+
Array.from(slotMap.entries(), async ([slot, keys2]) => {
|
|
749
|
+
const client = await this.getSlotMaster(slot);
|
|
750
|
+
return this._useUnlink ? client.unlink(keys2) : client.del(keys2);
|
|
751
|
+
})
|
|
752
|
+
);
|
|
634
753
|
}
|
|
635
754
|
}
|
|
636
755
|
/**
|
|
@@ -712,15 +831,11 @@ var KeyvRedis = class extends import_hookified.Hookified {
|
|
|
712
831
|
});
|
|
713
832
|
}
|
|
714
833
|
async createTimeoutPromise(timeoutMs) {
|
|
715
|
-
return new Promise(
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
},
|
|
721
|
-
timeoutMs
|
|
722
|
-
)
|
|
723
|
-
));
|
|
834
|
+
return new Promise(
|
|
835
|
+
(_, reject) => setTimeout(() => {
|
|
836
|
+
reject(new Error(`Redis timed out after ${timeoutMs}ms`));
|
|
837
|
+
}, timeoutMs)
|
|
838
|
+
);
|
|
724
839
|
}
|
|
725
840
|
};
|
|
726
841
|
function createKeyv(connect, options) {
|
|
@@ -728,7 +843,10 @@ function createKeyv(connect, options) {
|
|
|
728
843
|
const adapter = new KeyvRedis(connect, options);
|
|
729
844
|
if (options?.namespace) {
|
|
730
845
|
adapter.namespace = options.namespace;
|
|
731
|
-
const keyv2 = new import_keyv.Keyv(adapter, {
|
|
846
|
+
const keyv2 = new import_keyv.Keyv(adapter, {
|
|
847
|
+
namespace: options?.namespace,
|
|
848
|
+
useKeyPrefix: false
|
|
849
|
+
});
|
|
732
850
|
if (options?.throwOnConnectError) {
|
|
733
851
|
keyv2.throwOnErrors = true;
|
|
734
852
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -51,7 +51,7 @@ type KeyvRedisPropertyOptions = KeyvRedisOptions & {
|
|
|
51
51
|
/**
|
|
52
52
|
* Dialect used by the adapter. This is legacy so Keyv knows what is iteratable.
|
|
53
53
|
*/
|
|
54
|
-
dialect:
|
|
54
|
+
dialect: "redis";
|
|
55
55
|
/**
|
|
56
56
|
* URL used to connect to the Redis server. This is legacy so Keyv knows what is iteratable.
|
|
57
57
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -51,7 +51,7 @@ type KeyvRedisPropertyOptions = KeyvRedisOptions & {
|
|
|
51
51
|
/**
|
|
52
52
|
* Dialect used by the adapter. This is legacy so Keyv knows what is iteratable.
|
|
53
53
|
*/
|
|
54
|
-
dialect:
|
|
54
|
+
dialect: "redis";
|
|
55
55
|
/**
|
|
56
56
|
* URL used to connect to the Redis server. This is legacy so Keyv knows what is iteratable.
|
|
57
57
|
*/
|
package/dist/index.js
CHANGED
|
@@ -4,17 +4,15 @@ import {
|
|
|
4
4
|
createCluster,
|
|
5
5
|
createSentinel
|
|
6
6
|
} from "@redis/client";
|
|
7
|
+
import calculateSlot from "cluster-key-slot";
|
|
7
8
|
import { Hookified } from "hookified";
|
|
8
9
|
import { Keyv } from "keyv";
|
|
9
|
-
import calculateSlot from "cluster-key-slot";
|
|
10
10
|
import {
|
|
11
11
|
createClient as createClient2,
|
|
12
12
|
createCluster as createCluster2,
|
|
13
13
|
createSentinel as createSentinel2
|
|
14
14
|
} from "@redis/client";
|
|
15
|
-
import {
|
|
16
|
-
Keyv as Keyv2
|
|
17
|
-
} from "keyv";
|
|
15
|
+
import { Keyv as Keyv2 } from "keyv";
|
|
18
16
|
var RedisErrorMessages = /* @__PURE__ */ ((RedisErrorMessages2) => {
|
|
19
17
|
RedisErrorMessages2["RedisClientNotConnectedThrown"] = "Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true.";
|
|
20
18
|
return RedisErrorMessages2;
|
|
@@ -47,7 +45,10 @@ var KeyvRedis = class extends Hookified {
|
|
|
47
45
|
};
|
|
48
46
|
if (connect) {
|
|
49
47
|
if (typeof connect === "string") {
|
|
50
|
-
this._client = createClient({
|
|
48
|
+
this._client = createClient({
|
|
49
|
+
url: connect,
|
|
50
|
+
socket
|
|
51
|
+
});
|
|
51
52
|
} else if (connect.connect !== void 0) {
|
|
52
53
|
if (this.isClientSentinel(connect)) {
|
|
53
54
|
this._client = connect;
|
|
@@ -58,9 +59,13 @@ var KeyvRedis = class extends Hookified {
|
|
|
58
59
|
}
|
|
59
60
|
} else if (connect instanceof Object) {
|
|
60
61
|
if (connect.sentinelRootNodes !== void 0) {
|
|
61
|
-
this._client = createSentinel(
|
|
62
|
+
this._client = createSentinel(
|
|
63
|
+
connect
|
|
64
|
+
);
|
|
62
65
|
} else if (connect.rootNodes === void 0) {
|
|
63
|
-
this._client = createClient(
|
|
66
|
+
this._client = createClient(
|
|
67
|
+
connect
|
|
68
|
+
);
|
|
64
69
|
} else {
|
|
65
70
|
this._client = createCluster(connect);
|
|
66
71
|
}
|
|
@@ -289,20 +294,50 @@ var KeyvRedis = class extends Hookified {
|
|
|
289
294
|
* @param {KeyvEntry[]} entries - the key value pairs to set with optional ttl
|
|
290
295
|
*/
|
|
291
296
|
async setMany(entries) {
|
|
292
|
-
const client = await this.getClient();
|
|
293
297
|
try {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
298
|
+
if (this.isCluster()) {
|
|
299
|
+
await this.getClient();
|
|
300
|
+
const slotMap = /* @__PURE__ */ new Map();
|
|
301
|
+
for (const entry of entries) {
|
|
302
|
+
const prefixedKey = this.createKeyPrefix(entry.key, this._namespace);
|
|
303
|
+
const slot = calculateSlot(prefixedKey);
|
|
304
|
+
const slotEntries = slotMap.get(slot) ?? [];
|
|
305
|
+
slotEntries.push(entry);
|
|
306
|
+
slotMap.set(slot, slotEntries);
|
|
301
307
|
}
|
|
308
|
+
await Promise.all(
|
|
309
|
+
Array.from(slotMap.entries(), async ([slot, slotEntries]) => {
|
|
310
|
+
const client = await this.getSlotMaster(slot);
|
|
311
|
+
const multi = client.multi();
|
|
312
|
+
for (const { key, value, ttl } of slotEntries) {
|
|
313
|
+
const prefixedKey = this.createKeyPrefix(key, this._namespace);
|
|
314
|
+
if (ttl) {
|
|
315
|
+
multi.set(prefixedKey, value, { PX: ttl });
|
|
316
|
+
} else {
|
|
317
|
+
multi.set(prefixedKey, value);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
await multi.exec();
|
|
321
|
+
})
|
|
322
|
+
);
|
|
323
|
+
} else {
|
|
324
|
+
const client = await this.getClient();
|
|
325
|
+
const multi = client.multi();
|
|
326
|
+
for (const { key, value, ttl } of entries) {
|
|
327
|
+
const prefixedKey = this.createKeyPrefix(key, this._namespace);
|
|
328
|
+
if (ttl) {
|
|
329
|
+
multi.set(prefixedKey, value, { PX: ttl });
|
|
330
|
+
} else {
|
|
331
|
+
multi.set(prefixedKey, value);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
await multi.exec();
|
|
302
335
|
}
|
|
303
|
-
await multi.exec();
|
|
304
336
|
} catch (error) {
|
|
305
337
|
this.emit("error", error);
|
|
338
|
+
if (this._throwOnConnectError && error.message === "Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true." /* RedisClientNotConnectedThrown */) {
|
|
339
|
+
throw error;
|
|
340
|
+
}
|
|
306
341
|
if (this._throwOnErrors) {
|
|
307
342
|
throw error;
|
|
308
343
|
}
|
|
@@ -333,17 +368,46 @@ var KeyvRedis = class extends Hookified {
|
|
|
333
368
|
* @returns {Promise<Array<boolean>>} - array of booleans for each key if it exists
|
|
334
369
|
*/
|
|
335
370
|
async hasMany(keys) {
|
|
336
|
-
const client = await this.getClient();
|
|
337
371
|
try {
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
372
|
+
const prefixedKeys = keys.map(
|
|
373
|
+
(key) => this.createKeyPrefix(key, this._namespace)
|
|
374
|
+
);
|
|
375
|
+
if (this.isCluster()) {
|
|
376
|
+
const slotMap = this.getSlotMap(prefixedKeys);
|
|
377
|
+
const resultMap = /* @__PURE__ */ new Map();
|
|
378
|
+
await Promise.all(
|
|
379
|
+
Array.from(slotMap.entries(), async ([slot, slotKeys]) => {
|
|
380
|
+
const client = await this.getSlotMaster(slot);
|
|
381
|
+
const multi = client.multi();
|
|
382
|
+
for (const key of slotKeys) {
|
|
383
|
+
multi.exists(key);
|
|
384
|
+
}
|
|
385
|
+
const results = await multi.exec();
|
|
386
|
+
for (const [index, result] of results.entries()) {
|
|
387
|
+
resultMap.set(
|
|
388
|
+
slotKeys[index],
|
|
389
|
+
typeof result === "number" && result === 1
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
})
|
|
393
|
+
);
|
|
394
|
+
return prefixedKeys.map((key) => resultMap.get(key) ?? false);
|
|
395
|
+
} else {
|
|
396
|
+
const client = await this.getClient();
|
|
397
|
+
const multi = client.multi();
|
|
398
|
+
for (const key of prefixedKeys) {
|
|
399
|
+
multi.exists(key);
|
|
400
|
+
}
|
|
401
|
+
const results = await multi.exec();
|
|
402
|
+
return results.map(
|
|
403
|
+
(result) => typeof result === "number" && result === 1
|
|
404
|
+
);
|
|
342
405
|
}
|
|
343
|
-
const results = await multi.exec();
|
|
344
|
-
return results.map((result) => typeof result === "number" && result === 1);
|
|
345
406
|
} catch (error) {
|
|
346
407
|
this.emit("error", error);
|
|
408
|
+
if (this._throwOnConnectError && error.message === "Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true." /* RedisClientNotConnectedThrown */) {
|
|
409
|
+
throw error;
|
|
410
|
+
}
|
|
347
411
|
if (this._throwOnErrors) {
|
|
348
412
|
throw error;
|
|
349
413
|
}
|
|
@@ -420,25 +484,53 @@ var KeyvRedis = class extends Hookified {
|
|
|
420
484
|
*/
|
|
421
485
|
async deleteMany(keys) {
|
|
422
486
|
let result = false;
|
|
423
|
-
const client = await this.getClient();
|
|
424
487
|
try {
|
|
425
|
-
const
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
488
|
+
const prefixedKeys = keys.map(
|
|
489
|
+
(key) => this.createKeyPrefix(key, this._namespace)
|
|
490
|
+
);
|
|
491
|
+
if (this.isCluster()) {
|
|
492
|
+
const slotMap = this.getSlotMap(prefixedKeys);
|
|
493
|
+
await Promise.all(
|
|
494
|
+
Array.from(slotMap.entries(), async ([slot, slotKeys]) => {
|
|
495
|
+
const client = await this.getSlotMaster(slot);
|
|
496
|
+
const multi = client.multi();
|
|
497
|
+
for (const key of slotKeys) {
|
|
498
|
+
if (this._useUnlink) {
|
|
499
|
+
multi.unlink(key);
|
|
500
|
+
} else {
|
|
501
|
+
multi.del(key);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
const results = await multi.exec();
|
|
505
|
+
for (const deleted of results) {
|
|
506
|
+
if (typeof deleted === "number" && deleted > 0) {
|
|
507
|
+
result = true;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
})
|
|
511
|
+
);
|
|
512
|
+
} else {
|
|
513
|
+
const client = await this.getClient();
|
|
514
|
+
const multi = client.multi();
|
|
515
|
+
for (const key of prefixedKeys) {
|
|
516
|
+
if (this._useUnlink) {
|
|
517
|
+
multi.unlink(key);
|
|
518
|
+
} else {
|
|
519
|
+
multi.del(key);
|
|
520
|
+
}
|
|
432
521
|
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
522
|
+
const results = await multi.exec();
|
|
523
|
+
for (const deleted of results) {
|
|
524
|
+
if (typeof deleted === "number" && deleted > 0) {
|
|
525
|
+
result = true;
|
|
526
|
+
}
|
|
438
527
|
}
|
|
439
528
|
}
|
|
440
529
|
} catch (error) {
|
|
441
530
|
this.emit("error", error);
|
|
531
|
+
if (this._throwOnConnectError && error.message === "Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true." /* RedisClientNotConnectedThrown */) {
|
|
532
|
+
throw error;
|
|
533
|
+
}
|
|
442
534
|
if (this._throwOnErrors) {
|
|
443
535
|
throw error;
|
|
444
536
|
}
|
|
@@ -502,7 +594,9 @@ var KeyvRedis = class extends Hookified {
|
|
|
502
594
|
async getMasterNodes() {
|
|
503
595
|
if (this.isCluster()) {
|
|
504
596
|
const cluster = await this.getClient();
|
|
505
|
-
const nodes = cluster.masters.map(
|
|
597
|
+
const nodes = cluster.masters.map(
|
|
598
|
+
async (main) => cluster.nodeClient(main)
|
|
599
|
+
);
|
|
506
600
|
return Promise.all(nodes);
|
|
507
601
|
}
|
|
508
602
|
return [await this.getClient()];
|
|
@@ -518,7 +612,10 @@ var KeyvRedis = class extends Hookified {
|
|
|
518
612
|
const match = namespace ? `${namespace}${this._keyPrefixSeparator}*` : "*";
|
|
519
613
|
let cursor = "0";
|
|
520
614
|
do {
|
|
521
|
-
const result = await client.scan(cursor, {
|
|
615
|
+
const result = await client.scan(cursor, {
|
|
616
|
+
MATCH: match,
|
|
617
|
+
TYPE: "string"
|
|
618
|
+
});
|
|
522
619
|
cursor = result.cursor.toString();
|
|
523
620
|
let { keys } = result;
|
|
524
621
|
if (!namespace && !this._noNamespaceAffectsAll) {
|
|
@@ -545,29 +642,37 @@ var KeyvRedis = class extends Hookified {
|
|
|
545
642
|
async clear() {
|
|
546
643
|
try {
|
|
547
644
|
const clients = await this.getMasterNodes();
|
|
548
|
-
await Promise.all(
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
let cursor = "0";
|
|
554
|
-
const batchSize = this._clearBatchSize;
|
|
555
|
-
const match = this._namespace ? `${this._namespace}${this._keyPrefixSeparator}*` : "*";
|
|
556
|
-
const deletePromises = [];
|
|
557
|
-
do {
|
|
558
|
-
const result = await client.scan(cursor, { MATCH: match, COUNT: batchSize, TYPE: "string" });
|
|
559
|
-
cursor = result.cursor.toString();
|
|
560
|
-
let { keys } = result;
|
|
561
|
-
if (keys.length === 0) {
|
|
562
|
-
continue;
|
|
563
|
-
}
|
|
564
|
-
if (!this._namespace) {
|
|
565
|
-
keys = keys.filter((key) => !key.includes(this._keyPrefixSeparator));
|
|
645
|
+
await Promise.all(
|
|
646
|
+
clients.map(async (client) => {
|
|
647
|
+
if (!this._namespace && this._noNamespaceAffectsAll) {
|
|
648
|
+
await client.flushDb();
|
|
649
|
+
return;
|
|
566
650
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
651
|
+
let cursor = "0";
|
|
652
|
+
const batchSize = this._clearBatchSize;
|
|
653
|
+
const match = this._namespace ? `${this._namespace}${this._keyPrefixSeparator}*` : "*";
|
|
654
|
+
const deletePromises = [];
|
|
655
|
+
do {
|
|
656
|
+
const result = await client.scan(cursor, {
|
|
657
|
+
MATCH: match,
|
|
658
|
+
COUNT: batchSize,
|
|
659
|
+
TYPE: "string"
|
|
660
|
+
});
|
|
661
|
+
cursor = result.cursor.toString();
|
|
662
|
+
let { keys } = result;
|
|
663
|
+
if (keys.length === 0) {
|
|
664
|
+
continue;
|
|
665
|
+
}
|
|
666
|
+
if (!this._namespace) {
|
|
667
|
+
keys = keys.filter(
|
|
668
|
+
(key) => !key.includes(this._keyPrefixSeparator)
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
deletePromises.push(this.clearWithClusterSupport(keys));
|
|
672
|
+
} while (cursor !== "0");
|
|
673
|
+
await Promise.all(deletePromises);
|
|
674
|
+
})
|
|
675
|
+
);
|
|
571
676
|
} catch (error) {
|
|
572
677
|
this.emit("error", error);
|
|
573
678
|
}
|
|
@@ -577,15 +682,25 @@ var KeyvRedis = class extends Hookified {
|
|
|
577
682
|
* by separating the keys by slot to solve the CROSS-SLOT restriction.
|
|
578
683
|
*/
|
|
579
684
|
async mget(keys) {
|
|
580
|
-
const slotMap = this.getSlotMap(keys);
|
|
581
685
|
const valueMap = /* @__PURE__ */ new Map();
|
|
582
|
-
|
|
583
|
-
const
|
|
584
|
-
|
|
686
|
+
if (this.isCluster()) {
|
|
687
|
+
const slotMap = this.getSlotMap(keys);
|
|
688
|
+
await Promise.all(
|
|
689
|
+
Array.from(slotMap.entries(), async ([slot, slotKeys]) => {
|
|
690
|
+
const client = await this.getSlotMaster(slot);
|
|
691
|
+
const values = await client.mGet(slotKeys);
|
|
692
|
+
for (const [index, value] of values.entries()) {
|
|
693
|
+
valueMap.set(slotKeys[index], value ?? void 0);
|
|
694
|
+
}
|
|
695
|
+
})
|
|
696
|
+
);
|
|
697
|
+
} else {
|
|
698
|
+
const client = await this.getClient();
|
|
699
|
+
const values = await client.mGet(keys);
|
|
585
700
|
for (const [index, value] of values.entries()) {
|
|
586
|
-
valueMap.set(
|
|
701
|
+
valueMap.set(keys[index], value ?? void 0);
|
|
587
702
|
}
|
|
588
|
-
}
|
|
703
|
+
}
|
|
589
704
|
return keys.map((key) => valueMap.get(key));
|
|
590
705
|
}
|
|
591
706
|
/**
|
|
@@ -595,10 +710,12 @@ var KeyvRedis = class extends Hookified {
|
|
|
595
710
|
async clearWithClusterSupport(keys) {
|
|
596
711
|
if (keys.length > 0) {
|
|
597
712
|
const slotMap = this.getSlotMap(keys);
|
|
598
|
-
await Promise.all(
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
713
|
+
await Promise.all(
|
|
714
|
+
Array.from(slotMap.entries(), async ([slot, keys2]) => {
|
|
715
|
+
const client = await this.getSlotMaster(slot);
|
|
716
|
+
return this._useUnlink ? client.unlink(keys2) : client.del(keys2);
|
|
717
|
+
})
|
|
718
|
+
);
|
|
602
719
|
}
|
|
603
720
|
}
|
|
604
721
|
/**
|
|
@@ -680,15 +797,11 @@ var KeyvRedis = class extends Hookified {
|
|
|
680
797
|
});
|
|
681
798
|
}
|
|
682
799
|
async createTimeoutPromise(timeoutMs) {
|
|
683
|
-
return new Promise(
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
},
|
|
689
|
-
timeoutMs
|
|
690
|
-
)
|
|
691
|
-
));
|
|
800
|
+
return new Promise(
|
|
801
|
+
(_, reject) => setTimeout(() => {
|
|
802
|
+
reject(new Error(`Redis timed out after ${timeoutMs}ms`));
|
|
803
|
+
}, timeoutMs)
|
|
804
|
+
);
|
|
692
805
|
}
|
|
693
806
|
};
|
|
694
807
|
function createKeyv(connect, options) {
|
|
@@ -696,7 +809,10 @@ function createKeyv(connect, options) {
|
|
|
696
809
|
const adapter = new KeyvRedis(connect, options);
|
|
697
810
|
if (options?.namespace) {
|
|
698
811
|
adapter.namespace = options.namespace;
|
|
699
|
-
const keyv2 = new Keyv(adapter, {
|
|
812
|
+
const keyv2 = new Keyv(adapter, {
|
|
813
|
+
namespace: options?.namespace,
|
|
814
|
+
useKeyPrefix: false
|
|
815
|
+
});
|
|
700
816
|
if (options?.throwOnConnectError) {
|
|
701
817
|
keyv2.throwOnErrors = true;
|
|
702
818
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keyv/redis",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.2",
|
|
4
4
|
"description": "Redis storage adapter for Keyv",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -34,22 +34,22 @@
|
|
|
34
34
|
},
|
|
35
35
|
"homepage": "https://github.com/jaredwray/keyv",
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@redis/client": "^5.
|
|
37
|
+
"@redis/client": "^5.8.2",
|
|
38
38
|
"cluster-key-slot": "^1.1.2",
|
|
39
|
-
"hookified": "^1.
|
|
39
|
+
"hookified": "^1.12.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"keyv": "^5.5.
|
|
42
|
+
"keyv": "^5.5.1"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@
|
|
45
|
+
"@biomejs/biome": "^2.2.3",
|
|
46
|
+
"@faker-js/faker": "^10.0.0",
|
|
46
47
|
"@vitest/coverage-v8": "^3.2.4",
|
|
47
48
|
"rimraf": "^6.0.1",
|
|
48
49
|
"timekeeper": "^2.3.1",
|
|
49
|
-
"tsd": "^0.
|
|
50
|
+
"tsd": "^0.33.0",
|
|
50
51
|
"vitest": "^3.2.4",
|
|
51
|
-
"
|
|
52
|
-
"@keyv/test-suite": "^2.1.0"
|
|
52
|
+
"@keyv/test-suite": "^2.1.1"
|
|
53
53
|
},
|
|
54
54
|
"tsd": {
|
|
55
55
|
"directory": "test"
|
|
@@ -63,8 +63,8 @@
|
|
|
63
63
|
],
|
|
64
64
|
"scripts": {
|
|
65
65
|
"build": "rimraf ./dist && tsup src/index.ts --format cjs,esm --dts --clean",
|
|
66
|
-
"test": "
|
|
67
|
-
"test:ci": "
|
|
66
|
+
"test": "biome check --write && vitest run --coverage",
|
|
67
|
+
"test:ci": "biome check && vitest --run --sequence.setupFiles=list --coverage",
|
|
68
68
|
"clean": "rimraf ./node_modules ./coverage ./dist"
|
|
69
69
|
}
|
|
70
70
|
}
|