cacheable 1.6.1 → 1.7.1

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
@@ -19,10 +19,26 @@
19
19
  * Resilient to failures with try/catch and offline
20
20
  * Hooks and Events to extend functionality
21
21
  * Comprehensive testing and code coverage
22
+ * Shorthand for ttl in milliseconds `(1m = 60000) (1h = 3600000) (1d = 86400000)`
22
23
  * Distributed Caching Sync via Pub/Sub (coming soon)
23
24
  * ESM and CommonJS support with TypeScript
24
25
  * Maintained and supported regularly
25
26
 
27
+ ## Table of Contents
28
+ * [Getting Started](#getting-started)
29
+ * [Basic Usage](#basic-usage)
30
+ * [Hooks and Events](#hooks-and-events)
31
+ * [Storage Tiering and Caching](#storage-tiering-and-caching)
32
+ * [Shorthand for Time to Live (ttl)](#shorthand-for-time-to-live-ttl)
33
+ * [Non-Blocking Operations](#non-blocking-operations)
34
+ * [CacheSync - Distributed Updates](#cachesync---distributed-updates)
35
+ * [Cacheable Options](#cacheable-options)
36
+ * [Cacheable Statistics (Instance Only)](#cacheable-statistics-instance-only)
37
+ * [API](#api)
38
+ * [CacheableMemory - In-Memory Cache](#cacheablememory---in-memory-cache)
39
+ * [How to Contribute](#how-to-contribute)
40
+ * [License and Copyright](#license-and-copyright)
41
+
26
42
  ## Getting Started
27
43
 
28
44
  `cacheable` is primarily used as an extension to you caching engine with a robust storage backend [Keyv](https://keyv.org), Memonization (Wrap), Hooks, Events, and Statistics.
@@ -99,6 +115,44 @@ cacheable.onHook(CacheableHooks.BEFORE_SET, (data) => {
99
115
  * `Deleting Data`: Deletes the value from the primary store and secondary store at the same time waiting for both to respond.
100
116
  * `Clearing Data`: Clears the primary store and secondary store at the same time waiting for both to respond.
101
117
 
118
+ # Shorthand for Time to Live (ttl)
119
+
120
+ By default `Cacheable` and `CacheableMemory` the `ttl` is in milliseconds but you can use shorthand for the time to live. Here are the following shorthand values:
121
+
122
+ * `ms`: Milliseconds such as (1ms = 1)
123
+ * `s`: Seconds such as (1s = 1000)
124
+ * `m`: Minutes such as (1m = 60000)
125
+ * `h` or `hr`: Hours such as (1h = 3600000)
126
+ * `d`: Days such as (1d = 86400000)
127
+
128
+ Here is an example of how to use the shorthand for the `ttl`:
129
+
130
+ ```javascript
131
+ import { Cacheable } from 'cacheable';
132
+ const cache = new Cacheable({ ttl: '15m' }); //sets the default ttl to 15 minutes (900000 ms)
133
+ cache.set('key', 'value', '1h'); //sets the ttl to 1 hour (3600000 ms) and overrides the default
134
+ ```
135
+
136
+ if you want to disable the `ttl` you can set it to `0` or `undefined`:
137
+
138
+ ```javascript
139
+ import { Cacheable } from 'cacheable';
140
+ const cache = new Cacheable({ ttl: 0 }); //sets the default ttl to 0 which is disabled
141
+ cache.set('key', 'value', 0); //sets the ttl to 0 which is disabled
142
+ ```
143
+
144
+ If you set the ttl to anything below `0` or `undefined` it will disable the ttl for the cache and the value that returns will be `undefined`. With no ttl set the value will be stored `indefinitely`.
145
+
146
+ ```javascript
147
+ import { Cacheable } from 'cacheable';
148
+ const cache = new Cacheable({ ttl: 0 }); //sets the default ttl to 0 which is disabled
149
+ console.log(cache.ttl); // undefined
150
+ cache.ttl = '1h'; // sets the default ttl to 1 hour (3600000 ms)
151
+ console.log(cache.ttl); // '1h'
152
+ cache.ttl = -1; // sets the default ttl to 0 which is disabled
153
+ console.log(cache.ttl); // undefined
154
+ ```
155
+
102
156
  ## Non-Blocking Operations
103
157
 
104
158
  If you want your layer 2 (secondary) store to be non-blocking you can set the `nonBlocking` property to `true` in the options. This will make the secondary store non-blocking and will not wait for the secondary store to respond on `setting data`, `deleting data`, or `clearing data`. This is useful if you want to have a faster response time and not wait for the secondary store to respond.
@@ -131,6 +185,7 @@ The following options are available for you to configure `cacheable`:
131
185
  * `secondary`: The secondary store for the cache (layer 2) usually a persistent cache by Keyv.
132
186
  * `nonBlocking`: If the secondary store is non-blocking. Default is `false`.
133
187
  * `stats`: To enable statistics for this instance. Default is `false`.
188
+ * `ttl`: The default time to live for the cache in milliseconds. Default is `undefined` which is disabled.
134
189
 
135
190
  ## Cacheable Statistics (Instance Only)
136
191
 
@@ -181,7 +236,7 @@ _This does not enable statistics for your layer 2 cache as that is a distributed
181
236
  ```javascript
182
237
  import { CacheableMemory } from 'cacheable';
183
238
  const options = {
184
- ttl: 60 * 60 * 1000, // 1 hour
239
+ ttl: '1h', // 1 hour
185
240
  useClones: true, // use clones for the values (default is true)
186
241
  lruSize: 1000, // the size of the LRU cache (default is 0 which is unlimited)
187
242
  }
@@ -198,7 +253,7 @@ By default we use lazy expiration deletion which means on `get` and `getMany` ty
198
253
 
199
254
  ### CacheableMemory Options
200
255
 
201
- * `ttl`: The time to live for the cache in milliseconds.
256
+ * `ttl`: The time to live for the cache in milliseconds. Default is `undefined` which is means indefinitely.
202
257
  * `useClones`: If the cache should use clones for the values. Default is `true`.
203
258
  * `lruSize`: The size of the LRU cache. Default is `0` which is unlimited.
204
259
  * `checkInterval`: The interval to check for expired keys in milliseconds. Default is `0` which is disabled.
@@ -206,13 +261,20 @@ By default we use lazy expiration deletion which means on `get` and `getMany` ty
206
261
  ### CacheableMemory API
207
262
 
208
263
  * `set(key, value, ttl?)`: Sets a value in the cache.
264
+ * `setMany([{key, value, ttl?}])`: Sets multiple values in the cache from `CachableItem`.
209
265
  * `get(key)`: Gets a value from the cache.
266
+ * `getMany([keys])`: Gets multiple values from the cache.
267
+ * `getRaw(key)`: Gets a value from the cache as `CacheableStoreItem`.
268
+ * `getManyRaw([keys])`: Gets multiple values from the cache as `CacheableStoreItem`.
210
269
  * `has(key)`: Checks if a value exists in the cache.
211
270
  * `delete(key)`: Deletes a value from the cache.
271
+ * `deleteMany([keys])`: Deletes multiple values from the cache.
272
+ * `take(key)`: Takes a value from the cache and deletes it.
273
+ * `takeMany([keys])`: Takes multiple values from the cache and deletes them.
212
274
  * `clear()`: Clears the cache.
213
275
  * `size()`: The number of keys in the cache.
214
276
  * `keys()`: The keys in the cache.
215
- * `items()`: The items in the cache as `{ key, value, expiration }`.
277
+ * `items()`: The items in the cache as `CacheableStoreItem` example `{ key, value, expires? }`.
216
278
  * `checkExpired()`: Checks for expired keys in the cache. This is used by the `checkInterval` property.
217
279
  * `startIntervalCheck()`: Starts the interval check for expired keys if `checkInterval` is above 0 ms.
218
280
  * `stopIntervalCheck()`: Stops the interval check for expired keys.
package/dist/index.cjs CHANGED
@@ -25,12 +25,81 @@ __export(src_exports, {
25
25
  CacheableHooks: () => CacheableHooks,
26
26
  CacheableMemory: () => CacheableMemory,
27
27
  CacheableStats: () => CacheableStats,
28
- KeyvCacheableMemory: () => KeyvCacheableMemory
28
+ KeyvCacheableMemory: () => KeyvCacheableMemory,
29
+ shorthandToMilliseconds: () => shorthandToMilliseconds,
30
+ shorthandToTime: () => shorthandToTime
29
31
  });
30
32
  module.exports = __toCommonJS(src_exports);
31
33
  var import_keyv = require("keyv");
32
34
  var import_hookified = require("hookified");
33
35
 
36
+ // src/shorthand-time.ts
37
+ var shorthandToMilliseconds = (shorthand) => {
38
+ let milliseconds;
39
+ if (shorthand === void 0) {
40
+ return void 0;
41
+ }
42
+ if (typeof shorthand === "number") {
43
+ milliseconds = shorthand;
44
+ } else if (typeof shorthand === "string") {
45
+ shorthand = shorthand.trim();
46
+ if (Number.isNaN(Number(shorthand))) {
47
+ const match = /^([\d.]+)\s*(ms|s|m|h|hr|d)$/i.exec(shorthand);
48
+ if (!match) {
49
+ throw new Error(
50
+ `Unsupported time format: "${shorthand}". Use 'ms', 's', 'm', 'h', 'hr', or 'd'.`
51
+ );
52
+ }
53
+ const [, value, unit] = match;
54
+ const numericValue = Number.parseFloat(value);
55
+ const unitLower = unit.toLowerCase();
56
+ switch (unitLower) {
57
+ case "ms": {
58
+ milliseconds = numericValue;
59
+ break;
60
+ }
61
+ case "s": {
62
+ milliseconds = numericValue * 1e3;
63
+ break;
64
+ }
65
+ case "m": {
66
+ milliseconds = numericValue * 1e3 * 60;
67
+ break;
68
+ }
69
+ case "h": {
70
+ milliseconds = numericValue * 1e3 * 60 * 60;
71
+ break;
72
+ }
73
+ case "hr": {
74
+ milliseconds = numericValue * 1e3 * 60 * 60;
75
+ break;
76
+ }
77
+ case "d": {
78
+ milliseconds = numericValue * 1e3 * 60 * 60 * 24;
79
+ break;
80
+ }
81
+ /* c8 ignore next 3 */
82
+ default: {
83
+ milliseconds = Number(shorthand);
84
+ }
85
+ }
86
+ } else {
87
+ milliseconds = Number(shorthand);
88
+ }
89
+ } else {
90
+ throw new TypeError("Time must be a string or a number.");
91
+ }
92
+ return milliseconds;
93
+ };
94
+ var shorthandToTime = (shorthand, fromDate) => {
95
+ fromDate ||= /* @__PURE__ */ new Date();
96
+ const milliseconds = shorthandToMilliseconds(shorthand);
97
+ if (milliseconds === void 0) {
98
+ return fromDate.getTime();
99
+ }
100
+ return fromDate.getTime() + milliseconds;
101
+ };
102
+
34
103
  // src/memory-lru.ts
35
104
  var ListNode = class {
36
105
  // eslint-disable-next-line @typescript-eslint/parameter-properties
@@ -118,7 +187,7 @@ var CacheableMemory = class {
118
187
  _hash8 = /* @__PURE__ */ new Map();
119
188
  _hash9 = /* @__PURE__ */ new Map();
120
189
  _lru = new DoublyLinkedList();
121
- _ttl = 0;
190
+ _ttl;
122
191
  // Turned off by default
123
192
  _useClone = true;
124
193
  // Turned on by default
@@ -130,7 +199,7 @@ var CacheableMemory = class {
130
199
  // Turned off by default
131
200
  constructor(options) {
132
201
  if (options?.ttl) {
133
- this._ttl = options.ttl;
202
+ this.setTtl(options.ttl);
134
203
  }
135
204
  if (options?.useClone !== void 0) {
136
205
  this._useClone = options.useClone;
@@ -147,7 +216,7 @@ var CacheableMemory = class {
147
216
  return this._ttl;
148
217
  }
149
218
  set ttl(value) {
150
- this._ttl = value;
219
+ this.setTtl(value);
151
220
  }
152
221
  get useClone() {
153
222
  return this._useClone;
@@ -193,11 +262,41 @@ var CacheableMemory = class {
193
262
  }
194
263
  return this.clone(item.value);
195
264
  }
265
+ getMany(keys) {
266
+ const result = new Array();
267
+ for (const key of keys) {
268
+ result.push(this.get(key));
269
+ }
270
+ return result;
271
+ }
272
+ getRaw(key) {
273
+ const store = this.getStore(key);
274
+ const item = store.get(key);
275
+ if (!item) {
276
+ return void 0;
277
+ }
278
+ if (item.expires && item.expires && Date.now() > item.expires) {
279
+ store.delete(key);
280
+ return void 0;
281
+ }
282
+ this.lruMoveToFront(key);
283
+ return item;
284
+ }
285
+ getManyRaw(keys) {
286
+ const result = new Array();
287
+ for (const key of keys) {
288
+ result.push(this.getRaw(key));
289
+ }
290
+ return result;
291
+ }
196
292
  set(key, value, ttl) {
197
293
  const store = this.getStore(key);
198
294
  let expires;
199
- if (ttl !== void 0 || this._ttl !== 0) {
200
- expires = Date.now() + (ttl ?? this._ttl);
295
+ if (ttl !== void 0 || this._ttl !== void 0) {
296
+ const finalTtl = shorthandToTime(ttl ?? this._ttl);
297
+ if (finalTtl !== void 0) {
298
+ expires = finalTtl;
299
+ }
201
300
  }
202
301
  if (this._lruSize > 0) {
203
302
  if (store.has(key)) {
@@ -220,6 +319,11 @@ var CacheableMemory = class {
220
319
  expires
221
320
  });
222
321
  }
322
+ setMany(items) {
323
+ for (const item of items) {
324
+ this.set(item.key, item.value, item.ttl);
325
+ }
326
+ }
223
327
  has(key) {
224
328
  const item = this.get(key);
225
329
  return Boolean(item);
@@ -232,10 +336,22 @@ var CacheableMemory = class {
232
336
  this.delete(key);
233
337
  return item;
234
338
  }
339
+ takeMany(keys) {
340
+ const result = new Array();
341
+ for (const key of keys) {
342
+ result.push(this.take(key));
343
+ }
344
+ return result;
345
+ }
235
346
  delete(key) {
236
347
  const store = this.getStore(key);
237
348
  store.delete(key);
238
349
  }
350
+ deleteMany(keys) {
351
+ for (const key of keys) {
352
+ this.delete(key);
353
+ }
354
+ }
239
355
  clear() {
240
356
  this._hash0.clear();
241
357
  this._hash1.clear();
@@ -364,6 +480,15 @@ var CacheableMemory = class {
364
480
  const result = new Map([...this._hash0, ...this._hash1, ...this._hash2, ...this._hash3, ...this._hash4, ...this._hash5, ...this._hash6, ...this._hash7, ...this._hash8, ...this._hash9]);
365
481
  return result;
366
482
  }
483
+ setTtl(ttl) {
484
+ if (typeof ttl === "string" || ttl === void 0) {
485
+ this._ttl = ttl;
486
+ } else if (ttl > 0) {
487
+ this._ttl = ttl;
488
+ } else {
489
+ this._ttl = void 0;
490
+ }
491
+ }
367
492
  };
368
493
 
369
494
  // src/keyv-memory.ts
@@ -384,38 +509,35 @@ var KeyvCacheableMemory = class {
384
509
  }
385
510
  async get(key) {
386
511
  const result = this._cache.get(key);
387
- console.log("result", result);
388
512
  if (result) {
389
513
  return result;
390
514
  }
391
515
  return void 0;
392
516
  }
393
- set(key, value, ttl) {
517
+ async getMany(keys) {
518
+ const result = this._cache.getMany(keys);
519
+ return result;
520
+ }
521
+ async set(key, value, ttl) {
394
522
  this._cache.set(key, value, ttl);
395
523
  }
524
+ async setMany(values) {
525
+ this._cache.setMany(values);
526
+ }
396
527
  async delete(key) {
397
528
  this._cache.delete(key);
398
529
  return true;
399
530
  }
531
+ async deleteMany(key) {
532
+ this._cache.deleteMany(key);
533
+ return true;
534
+ }
400
535
  async clear() {
401
536
  this._cache.clear();
402
537
  }
403
538
  async has(key) {
404
539
  return this._cache.has(key);
405
540
  }
406
- async getMany(keys) {
407
- const result = [];
408
- for (const key of keys) {
409
- result.push(this._cache.get(key));
410
- }
411
- return result;
412
- }
413
- async deleteMany(key) {
414
- for (const k of key) {
415
- this._cache.delete(k);
416
- }
417
- return true;
418
- }
419
541
  on(event, listener) {
420
542
  return this;
421
543
  }
@@ -633,7 +755,7 @@ var Cacheable = class extends import_hookified.Hookified {
633
755
  this._stats.enabled = options.stats;
634
756
  }
635
757
  if (options?.ttl) {
636
- this._ttl = options.ttl;
758
+ this.setTtl(options.ttl);
637
759
  }
638
760
  }
639
761
  get stats() {
@@ -661,7 +783,7 @@ var Cacheable = class extends import_hookified.Hookified {
661
783
  return this._ttl;
662
784
  }
663
785
  set ttl(ttl) {
664
- this._ttl = ttl;
786
+ this.setTtl(ttl);
665
787
  }
666
788
  setPrimary(primary) {
667
789
  this._primary = primary instanceof import_keyv.Keyv ? primary : new import_keyv.Keyv(primary);
@@ -677,7 +799,8 @@ var Cacheable = class extends import_hookified.Hookified {
677
799
  if (!result && this._secondary) {
678
800
  result = await this._secondary.get(key);
679
801
  if (result) {
680
- await this._primary.set(key, result, this._ttl);
802
+ const finalTtl = shorthandToMilliseconds(this._ttl);
803
+ await this._primary.set(key, result, finalTtl);
681
804
  }
682
805
  }
683
806
  await this.hook("AFTER_GET" /* AFTER_GET */, { key, result });
@@ -710,7 +833,8 @@ var Cacheable = class extends import_hookified.Hookified {
710
833
  for (const [i, key] of keys.entries()) {
711
834
  if (!result[i] && secondaryResult[i]) {
712
835
  result[i] = secondaryResult[i];
713
- await this._primary.set(key, secondaryResult[i], this._ttl);
836
+ const finalTtl = shorthandToMilliseconds(this._ttl);
837
+ await this._primary.set(key, secondaryResult[i], finalTtl);
714
838
  }
715
839
  }
716
840
  }
@@ -732,7 +856,7 @@ var Cacheable = class extends import_hookified.Hookified {
732
856
  }
733
857
  async set(key, value, ttl) {
734
858
  let result = false;
735
- const finalTtl = ttl ?? this._ttl;
859
+ const finalTtl = shorthandToMilliseconds(ttl ?? this._ttl);
736
860
  try {
737
861
  const item = { key, value, ttl: finalTtl };
738
862
  await this.hook("BEFORE_SET" /* BEFORE_SET */, item);
@@ -901,7 +1025,7 @@ var Cacheable = class extends import_hookified.Hookified {
901
1025
  async setManyKeyv(keyv, items) {
902
1026
  const promises = [];
903
1027
  for (const item of items) {
904
- const finalTtl = item.ttl ?? this._ttl;
1028
+ const finalTtl = shorthandToMilliseconds(item.ttl ?? this._ttl);
905
1029
  promises.push(keyv.set(item.key, item.value, finalTtl));
906
1030
  }
907
1031
  await Promise.all(promises);
@@ -914,6 +1038,15 @@ var Cacheable = class extends import_hookified.Hookified {
914
1038
  }
915
1039
  return Promise.all(promises);
916
1040
  }
1041
+ setTtl(ttl) {
1042
+ if (typeof ttl === "string" || ttl === void 0) {
1043
+ this._ttl = ttl;
1044
+ } else if (ttl > 0) {
1045
+ this._ttl = ttl;
1046
+ } else {
1047
+ this._ttl = void 0;
1048
+ }
1049
+ }
917
1050
  };
918
1051
  // Annotate the CommonJS export names for ESM import in node:
919
1052
  0 && (module.exports = {
@@ -922,5 +1055,7 @@ var Cacheable = class extends import_hookified.Hookified {
922
1055
  CacheableHooks,
923
1056
  CacheableMemory,
924
1057
  CacheableStats,
925
- KeyvCacheableMemory
1058
+ KeyvCacheableMemory,
1059
+ shorthandToMilliseconds,
1060
+ shorthandToTime
926
1061
  });
package/dist/index.d.cts CHANGED
@@ -46,17 +46,23 @@ declare class CacheableStats {
46
46
  resetStoreValues(): void;
47
47
  }
48
48
 
49
- type CacheableMemoryOptions = {
50
- ttl?: number;
51
- useClone?: boolean;
52
- lruSize?: number;
53
- checkInterval?: number;
49
+ type CacheableItem = {
50
+ key: string;
51
+ value: any;
52
+ ttl?: number | string;
54
53
  };
55
- type CacheableItem$1 = {
54
+ type CacheableStoreItem = {
56
55
  key: string;
57
56
  value: any;
58
57
  expires?: number;
59
58
  };
59
+
60
+ type CacheableMemoryOptions = {
61
+ ttl?: number | string;
62
+ useClone?: boolean;
63
+ lruSize?: number;
64
+ checkInterval?: number;
65
+ };
60
66
  declare class CacheableMemory {
61
67
  private readonly _hashCache;
62
68
  private readonly _hash0;
@@ -76,8 +82,8 @@ declare class CacheableMemory {
76
82
  private _checkInterval;
77
83
  private _interval;
78
84
  constructor(options?: CacheableMemoryOptions);
79
- get ttl(): number;
80
- set ttl(value: number);
85
+ get ttl(): number | string | undefined;
86
+ set ttl(value: number | string | undefined);
81
87
  get useClone(): boolean;
82
88
  set useClone(value: boolean);
83
89
  get lruSize(): number;
@@ -86,12 +92,18 @@ declare class CacheableMemory {
86
92
  set checkInterval(value: number);
87
93
  get size(): number;
88
94
  get keys(): IterableIterator<string>;
89
- get items(): IterableIterator<CacheableItem$1>;
95
+ get items(): IterableIterator<CacheableStoreItem>;
90
96
  get<T>(key: string): any;
91
- set(key: string, value: any, ttl?: number): void;
97
+ getMany<T>(keys: string[]): any[];
98
+ getRaw(key: string): CacheableStoreItem | undefined;
99
+ getManyRaw(keys: string[]): Array<CacheableStoreItem | undefined>;
100
+ set(key: string, value: any, ttl?: number | string): void;
101
+ setMany(items: CacheableItem[]): void;
92
102
  has(key: string): boolean;
93
103
  take<T>(key: string): any;
104
+ takeMany<T>(keys: string[]): any[];
94
105
  delete(key: string): void;
106
+ deleteMany(keys: string[]): void;
95
107
  clear(): void;
96
108
  hashKey(key: string): number;
97
109
  getStore(key: string): Map<string, any>;
@@ -104,6 +116,7 @@ declare class CacheableMemory {
104
116
  stopIntervalCheck(): void;
105
117
  private isPrimitive;
106
118
  private concatStores;
119
+ private setTtl;
107
120
  }
108
121
 
109
122
  declare class KeyvCacheableMemory implements KeyvStoreAdapter {
@@ -112,15 +125,23 @@ declare class KeyvCacheableMemory implements KeyvStoreAdapter {
112
125
  private readonly _cache;
113
126
  constructor(options?: CacheableMemoryOptions);
114
127
  get<Value>(key: string): Promise<StoredData<Value> | undefined>;
115
- set(key: string, value: any, ttl?: number): void;
128
+ getMany<Value>(keys: string[]): Promise<Array<StoredData<Value | undefined>>>;
129
+ set(key: string, value: any, ttl?: number): Promise<void>;
130
+ setMany(values: Array<{
131
+ key: string;
132
+ value: any;
133
+ ttl?: number;
134
+ }>): Promise<void>;
116
135
  delete(key: string): Promise<boolean>;
136
+ deleteMany?(key: string[]): Promise<boolean>;
117
137
  clear(): Promise<void>;
118
138
  has?(key: string): Promise<boolean>;
119
- getMany?<Value>(keys: string[]): Promise<Array<StoredData<Value | undefined>>>;
120
- deleteMany?(key: string[]): Promise<boolean>;
121
139
  on(event: string, listener: (...arguments_: any[]) => void): this;
122
140
  }
123
141
 
142
+ declare const shorthandToMilliseconds: (shorthand?: string | number) => number | undefined;
143
+ declare const shorthandToTime: (shorthand?: string | number, fromDate?: Date) => number;
144
+
124
145
  declare enum CacheableHooks {
125
146
  BEFORE_SET = "BEFORE_SET",
126
147
  AFTER_SET = "AFTER_SET",
@@ -134,17 +155,12 @@ declare enum CacheableHooks {
134
155
  declare enum CacheableEvents {
135
156
  ERROR = "error"
136
157
  }
137
- type CacheableItem = {
138
- key: string;
139
- value: unknown;
140
- ttl?: number;
141
- };
142
158
  type CacheableOptions = {
143
159
  primary?: Keyv | KeyvStoreAdapter;
144
160
  secondary?: Keyv | KeyvStoreAdapter;
145
161
  stats?: boolean;
146
162
  nonBlocking?: boolean;
147
- ttl?: number;
163
+ ttl?: number | string;
148
164
  };
149
165
  declare class Cacheable extends Hookified {
150
166
  private _primary;
@@ -160,8 +176,8 @@ declare class Cacheable extends Hookified {
160
176
  set secondary(secondary: Keyv | undefined);
161
177
  get nonBlocking(): boolean;
162
178
  set nonBlocking(nonBlocking: boolean);
163
- get ttl(): number | undefined;
164
- set ttl(ttl: number | undefined);
179
+ get ttl(): number | string | undefined;
180
+ set ttl(ttl: number | string | undefined);
165
181
  setPrimary(primary: Keyv | KeyvStoreAdapter): void;
166
182
  setSecondary(secondary: Keyv | KeyvStoreAdapter): void;
167
183
  get<T>(key: string): Promise<T | undefined>;
@@ -179,6 +195,7 @@ declare class Cacheable extends Hookified {
179
195
  private deleteManyKeyv;
180
196
  private setManyKeyv;
181
197
  private hasManyKeyv;
198
+ private setTtl;
182
199
  }
183
200
 
184
- export { Cacheable, CacheableEvents, CacheableHooks, type CacheableItem, CacheableMemory, type CacheableOptions, CacheableStats, KeyvCacheableMemory };
201
+ export { Cacheable, CacheableEvents, CacheableHooks, type CacheableItem, CacheableMemory, type CacheableOptions, CacheableStats, KeyvCacheableMemory, shorthandToMilliseconds, shorthandToTime };
package/dist/index.d.ts CHANGED
@@ -46,17 +46,23 @@ declare class CacheableStats {
46
46
  resetStoreValues(): void;
47
47
  }
48
48
 
49
- type CacheableMemoryOptions = {
50
- ttl?: number;
51
- useClone?: boolean;
52
- lruSize?: number;
53
- checkInterval?: number;
49
+ type CacheableItem = {
50
+ key: string;
51
+ value: any;
52
+ ttl?: number | string;
54
53
  };
55
- type CacheableItem$1 = {
54
+ type CacheableStoreItem = {
56
55
  key: string;
57
56
  value: any;
58
57
  expires?: number;
59
58
  };
59
+
60
+ type CacheableMemoryOptions = {
61
+ ttl?: number | string;
62
+ useClone?: boolean;
63
+ lruSize?: number;
64
+ checkInterval?: number;
65
+ };
60
66
  declare class CacheableMemory {
61
67
  private readonly _hashCache;
62
68
  private readonly _hash0;
@@ -76,8 +82,8 @@ declare class CacheableMemory {
76
82
  private _checkInterval;
77
83
  private _interval;
78
84
  constructor(options?: CacheableMemoryOptions);
79
- get ttl(): number;
80
- set ttl(value: number);
85
+ get ttl(): number | string | undefined;
86
+ set ttl(value: number | string | undefined);
81
87
  get useClone(): boolean;
82
88
  set useClone(value: boolean);
83
89
  get lruSize(): number;
@@ -86,12 +92,18 @@ declare class CacheableMemory {
86
92
  set checkInterval(value: number);
87
93
  get size(): number;
88
94
  get keys(): IterableIterator<string>;
89
- get items(): IterableIterator<CacheableItem$1>;
95
+ get items(): IterableIterator<CacheableStoreItem>;
90
96
  get<T>(key: string): any;
91
- set(key: string, value: any, ttl?: number): void;
97
+ getMany<T>(keys: string[]): any[];
98
+ getRaw(key: string): CacheableStoreItem | undefined;
99
+ getManyRaw(keys: string[]): Array<CacheableStoreItem | undefined>;
100
+ set(key: string, value: any, ttl?: number | string): void;
101
+ setMany(items: CacheableItem[]): void;
92
102
  has(key: string): boolean;
93
103
  take<T>(key: string): any;
104
+ takeMany<T>(keys: string[]): any[];
94
105
  delete(key: string): void;
106
+ deleteMany(keys: string[]): void;
95
107
  clear(): void;
96
108
  hashKey(key: string): number;
97
109
  getStore(key: string): Map<string, any>;
@@ -104,6 +116,7 @@ declare class CacheableMemory {
104
116
  stopIntervalCheck(): void;
105
117
  private isPrimitive;
106
118
  private concatStores;
119
+ private setTtl;
107
120
  }
108
121
 
109
122
  declare class KeyvCacheableMemory implements KeyvStoreAdapter {
@@ -112,15 +125,23 @@ declare class KeyvCacheableMemory implements KeyvStoreAdapter {
112
125
  private readonly _cache;
113
126
  constructor(options?: CacheableMemoryOptions);
114
127
  get<Value>(key: string): Promise<StoredData<Value> | undefined>;
115
- set(key: string, value: any, ttl?: number): void;
128
+ getMany<Value>(keys: string[]): Promise<Array<StoredData<Value | undefined>>>;
129
+ set(key: string, value: any, ttl?: number): Promise<void>;
130
+ setMany(values: Array<{
131
+ key: string;
132
+ value: any;
133
+ ttl?: number;
134
+ }>): Promise<void>;
116
135
  delete(key: string): Promise<boolean>;
136
+ deleteMany?(key: string[]): Promise<boolean>;
117
137
  clear(): Promise<void>;
118
138
  has?(key: string): Promise<boolean>;
119
- getMany?<Value>(keys: string[]): Promise<Array<StoredData<Value | undefined>>>;
120
- deleteMany?(key: string[]): Promise<boolean>;
121
139
  on(event: string, listener: (...arguments_: any[]) => void): this;
122
140
  }
123
141
 
142
+ declare const shorthandToMilliseconds: (shorthand?: string | number) => number | undefined;
143
+ declare const shorthandToTime: (shorthand?: string | number, fromDate?: Date) => number;
144
+
124
145
  declare enum CacheableHooks {
125
146
  BEFORE_SET = "BEFORE_SET",
126
147
  AFTER_SET = "AFTER_SET",
@@ -134,17 +155,12 @@ declare enum CacheableHooks {
134
155
  declare enum CacheableEvents {
135
156
  ERROR = "error"
136
157
  }
137
- type CacheableItem = {
138
- key: string;
139
- value: unknown;
140
- ttl?: number;
141
- };
142
158
  type CacheableOptions = {
143
159
  primary?: Keyv | KeyvStoreAdapter;
144
160
  secondary?: Keyv | KeyvStoreAdapter;
145
161
  stats?: boolean;
146
162
  nonBlocking?: boolean;
147
- ttl?: number;
163
+ ttl?: number | string;
148
164
  };
149
165
  declare class Cacheable extends Hookified {
150
166
  private _primary;
@@ -160,8 +176,8 @@ declare class Cacheable extends Hookified {
160
176
  set secondary(secondary: Keyv | undefined);
161
177
  get nonBlocking(): boolean;
162
178
  set nonBlocking(nonBlocking: boolean);
163
- get ttl(): number | undefined;
164
- set ttl(ttl: number | undefined);
179
+ get ttl(): number | string | undefined;
180
+ set ttl(ttl: number | string | undefined);
165
181
  setPrimary(primary: Keyv | KeyvStoreAdapter): void;
166
182
  setSecondary(secondary: Keyv | KeyvStoreAdapter): void;
167
183
  get<T>(key: string): Promise<T | undefined>;
@@ -179,6 +195,7 @@ declare class Cacheable extends Hookified {
179
195
  private deleteManyKeyv;
180
196
  private setManyKeyv;
181
197
  private hasManyKeyv;
198
+ private setTtl;
182
199
  }
183
200
 
184
- export { Cacheable, CacheableEvents, CacheableHooks, type CacheableItem, CacheableMemory, type CacheableOptions, CacheableStats, KeyvCacheableMemory };
201
+ export { Cacheable, CacheableEvents, CacheableHooks, type CacheableItem, CacheableMemory, type CacheableOptions, CacheableStats, KeyvCacheableMemory, shorthandToMilliseconds, shorthandToTime };
package/dist/index.js CHANGED
@@ -2,6 +2,73 @@
2
2
  import { Keyv } from "keyv";
3
3
  import { Hookified } from "hookified";
4
4
 
5
+ // src/shorthand-time.ts
6
+ var shorthandToMilliseconds = (shorthand) => {
7
+ let milliseconds;
8
+ if (shorthand === void 0) {
9
+ return void 0;
10
+ }
11
+ if (typeof shorthand === "number") {
12
+ milliseconds = shorthand;
13
+ } else if (typeof shorthand === "string") {
14
+ shorthand = shorthand.trim();
15
+ if (Number.isNaN(Number(shorthand))) {
16
+ const match = /^([\d.]+)\s*(ms|s|m|h|hr|d)$/i.exec(shorthand);
17
+ if (!match) {
18
+ throw new Error(
19
+ `Unsupported time format: "${shorthand}". Use 'ms', 's', 'm', 'h', 'hr', or 'd'.`
20
+ );
21
+ }
22
+ const [, value, unit] = match;
23
+ const numericValue = Number.parseFloat(value);
24
+ const unitLower = unit.toLowerCase();
25
+ switch (unitLower) {
26
+ case "ms": {
27
+ milliseconds = numericValue;
28
+ break;
29
+ }
30
+ case "s": {
31
+ milliseconds = numericValue * 1e3;
32
+ break;
33
+ }
34
+ case "m": {
35
+ milliseconds = numericValue * 1e3 * 60;
36
+ break;
37
+ }
38
+ case "h": {
39
+ milliseconds = numericValue * 1e3 * 60 * 60;
40
+ break;
41
+ }
42
+ case "hr": {
43
+ milliseconds = numericValue * 1e3 * 60 * 60;
44
+ break;
45
+ }
46
+ case "d": {
47
+ milliseconds = numericValue * 1e3 * 60 * 60 * 24;
48
+ break;
49
+ }
50
+ /* c8 ignore next 3 */
51
+ default: {
52
+ milliseconds = Number(shorthand);
53
+ }
54
+ }
55
+ } else {
56
+ milliseconds = Number(shorthand);
57
+ }
58
+ } else {
59
+ throw new TypeError("Time must be a string or a number.");
60
+ }
61
+ return milliseconds;
62
+ };
63
+ var shorthandToTime = (shorthand, fromDate) => {
64
+ fromDate ||= /* @__PURE__ */ new Date();
65
+ const milliseconds = shorthandToMilliseconds(shorthand);
66
+ if (milliseconds === void 0) {
67
+ return fromDate.getTime();
68
+ }
69
+ return fromDate.getTime() + milliseconds;
70
+ };
71
+
5
72
  // src/memory-lru.ts
6
73
  var ListNode = class {
7
74
  // eslint-disable-next-line @typescript-eslint/parameter-properties
@@ -89,7 +156,7 @@ var CacheableMemory = class {
89
156
  _hash8 = /* @__PURE__ */ new Map();
90
157
  _hash9 = /* @__PURE__ */ new Map();
91
158
  _lru = new DoublyLinkedList();
92
- _ttl = 0;
159
+ _ttl;
93
160
  // Turned off by default
94
161
  _useClone = true;
95
162
  // Turned on by default
@@ -101,7 +168,7 @@ var CacheableMemory = class {
101
168
  // Turned off by default
102
169
  constructor(options) {
103
170
  if (options?.ttl) {
104
- this._ttl = options.ttl;
171
+ this.setTtl(options.ttl);
105
172
  }
106
173
  if (options?.useClone !== void 0) {
107
174
  this._useClone = options.useClone;
@@ -118,7 +185,7 @@ var CacheableMemory = class {
118
185
  return this._ttl;
119
186
  }
120
187
  set ttl(value) {
121
- this._ttl = value;
188
+ this.setTtl(value);
122
189
  }
123
190
  get useClone() {
124
191
  return this._useClone;
@@ -164,11 +231,41 @@ var CacheableMemory = class {
164
231
  }
165
232
  return this.clone(item.value);
166
233
  }
234
+ getMany(keys) {
235
+ const result = new Array();
236
+ for (const key of keys) {
237
+ result.push(this.get(key));
238
+ }
239
+ return result;
240
+ }
241
+ getRaw(key) {
242
+ const store = this.getStore(key);
243
+ const item = store.get(key);
244
+ if (!item) {
245
+ return void 0;
246
+ }
247
+ if (item.expires && item.expires && Date.now() > item.expires) {
248
+ store.delete(key);
249
+ return void 0;
250
+ }
251
+ this.lruMoveToFront(key);
252
+ return item;
253
+ }
254
+ getManyRaw(keys) {
255
+ const result = new Array();
256
+ for (const key of keys) {
257
+ result.push(this.getRaw(key));
258
+ }
259
+ return result;
260
+ }
167
261
  set(key, value, ttl) {
168
262
  const store = this.getStore(key);
169
263
  let expires;
170
- if (ttl !== void 0 || this._ttl !== 0) {
171
- expires = Date.now() + (ttl ?? this._ttl);
264
+ if (ttl !== void 0 || this._ttl !== void 0) {
265
+ const finalTtl = shorthandToTime(ttl ?? this._ttl);
266
+ if (finalTtl !== void 0) {
267
+ expires = finalTtl;
268
+ }
172
269
  }
173
270
  if (this._lruSize > 0) {
174
271
  if (store.has(key)) {
@@ -191,6 +288,11 @@ var CacheableMemory = class {
191
288
  expires
192
289
  });
193
290
  }
291
+ setMany(items) {
292
+ for (const item of items) {
293
+ this.set(item.key, item.value, item.ttl);
294
+ }
295
+ }
194
296
  has(key) {
195
297
  const item = this.get(key);
196
298
  return Boolean(item);
@@ -203,10 +305,22 @@ var CacheableMemory = class {
203
305
  this.delete(key);
204
306
  return item;
205
307
  }
308
+ takeMany(keys) {
309
+ const result = new Array();
310
+ for (const key of keys) {
311
+ result.push(this.take(key));
312
+ }
313
+ return result;
314
+ }
206
315
  delete(key) {
207
316
  const store = this.getStore(key);
208
317
  store.delete(key);
209
318
  }
319
+ deleteMany(keys) {
320
+ for (const key of keys) {
321
+ this.delete(key);
322
+ }
323
+ }
210
324
  clear() {
211
325
  this._hash0.clear();
212
326
  this._hash1.clear();
@@ -335,6 +449,15 @@ var CacheableMemory = class {
335
449
  const result = new Map([...this._hash0, ...this._hash1, ...this._hash2, ...this._hash3, ...this._hash4, ...this._hash5, ...this._hash6, ...this._hash7, ...this._hash8, ...this._hash9]);
336
450
  return result;
337
451
  }
452
+ setTtl(ttl) {
453
+ if (typeof ttl === "string" || ttl === void 0) {
454
+ this._ttl = ttl;
455
+ } else if (ttl > 0) {
456
+ this._ttl = ttl;
457
+ } else {
458
+ this._ttl = void 0;
459
+ }
460
+ }
338
461
  };
339
462
 
340
463
  // src/keyv-memory.ts
@@ -355,38 +478,35 @@ var KeyvCacheableMemory = class {
355
478
  }
356
479
  async get(key) {
357
480
  const result = this._cache.get(key);
358
- console.log("result", result);
359
481
  if (result) {
360
482
  return result;
361
483
  }
362
484
  return void 0;
363
485
  }
364
- set(key, value, ttl) {
486
+ async getMany(keys) {
487
+ const result = this._cache.getMany(keys);
488
+ return result;
489
+ }
490
+ async set(key, value, ttl) {
365
491
  this._cache.set(key, value, ttl);
366
492
  }
493
+ async setMany(values) {
494
+ this._cache.setMany(values);
495
+ }
367
496
  async delete(key) {
368
497
  this._cache.delete(key);
369
498
  return true;
370
499
  }
500
+ async deleteMany(key) {
501
+ this._cache.deleteMany(key);
502
+ return true;
503
+ }
371
504
  async clear() {
372
505
  this._cache.clear();
373
506
  }
374
507
  async has(key) {
375
508
  return this._cache.has(key);
376
509
  }
377
- async getMany(keys) {
378
- const result = [];
379
- for (const key of keys) {
380
- result.push(this._cache.get(key));
381
- }
382
- return result;
383
- }
384
- async deleteMany(key) {
385
- for (const k of key) {
386
- this._cache.delete(k);
387
- }
388
- return true;
389
- }
390
510
  on(event, listener) {
391
511
  return this;
392
512
  }
@@ -604,7 +724,7 @@ var Cacheable = class extends Hookified {
604
724
  this._stats.enabled = options.stats;
605
725
  }
606
726
  if (options?.ttl) {
607
- this._ttl = options.ttl;
727
+ this.setTtl(options.ttl);
608
728
  }
609
729
  }
610
730
  get stats() {
@@ -632,7 +752,7 @@ var Cacheable = class extends Hookified {
632
752
  return this._ttl;
633
753
  }
634
754
  set ttl(ttl) {
635
- this._ttl = ttl;
755
+ this.setTtl(ttl);
636
756
  }
637
757
  setPrimary(primary) {
638
758
  this._primary = primary instanceof Keyv ? primary : new Keyv(primary);
@@ -648,7 +768,8 @@ var Cacheable = class extends Hookified {
648
768
  if (!result && this._secondary) {
649
769
  result = await this._secondary.get(key);
650
770
  if (result) {
651
- await this._primary.set(key, result, this._ttl);
771
+ const finalTtl = shorthandToMilliseconds(this._ttl);
772
+ await this._primary.set(key, result, finalTtl);
652
773
  }
653
774
  }
654
775
  await this.hook("AFTER_GET" /* AFTER_GET */, { key, result });
@@ -681,7 +802,8 @@ var Cacheable = class extends Hookified {
681
802
  for (const [i, key] of keys.entries()) {
682
803
  if (!result[i] && secondaryResult[i]) {
683
804
  result[i] = secondaryResult[i];
684
- await this._primary.set(key, secondaryResult[i], this._ttl);
805
+ const finalTtl = shorthandToMilliseconds(this._ttl);
806
+ await this._primary.set(key, secondaryResult[i], finalTtl);
685
807
  }
686
808
  }
687
809
  }
@@ -703,7 +825,7 @@ var Cacheable = class extends Hookified {
703
825
  }
704
826
  async set(key, value, ttl) {
705
827
  let result = false;
706
- const finalTtl = ttl ?? this._ttl;
828
+ const finalTtl = shorthandToMilliseconds(ttl ?? this._ttl);
707
829
  try {
708
830
  const item = { key, value, ttl: finalTtl };
709
831
  await this.hook("BEFORE_SET" /* BEFORE_SET */, item);
@@ -872,7 +994,7 @@ var Cacheable = class extends Hookified {
872
994
  async setManyKeyv(keyv, items) {
873
995
  const promises = [];
874
996
  for (const item of items) {
875
- const finalTtl = item.ttl ?? this._ttl;
997
+ const finalTtl = shorthandToMilliseconds(item.ttl ?? this._ttl);
876
998
  promises.push(keyv.set(item.key, item.value, finalTtl));
877
999
  }
878
1000
  await Promise.all(promises);
@@ -885,6 +1007,15 @@ var Cacheable = class extends Hookified {
885
1007
  }
886
1008
  return Promise.all(promises);
887
1009
  }
1010
+ setTtl(ttl) {
1011
+ if (typeof ttl === "string" || ttl === void 0) {
1012
+ this._ttl = ttl;
1013
+ } else if (ttl > 0) {
1014
+ this._ttl = ttl;
1015
+ } else {
1016
+ this._ttl = void 0;
1017
+ }
1018
+ }
888
1019
  };
889
1020
  export {
890
1021
  Cacheable,
@@ -892,5 +1023,7 @@ export {
892
1023
  CacheableHooks,
893
1024
  CacheableMemory,
894
1025
  CacheableStats,
895
- KeyvCacheableMemory
1026
+ KeyvCacheableMemory,
1027
+ shorthandToMilliseconds,
1028
+ shorthandToTime
896
1029
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cacheable",
3
- "version": "1.6.1",
3
+ "version": "1.7.1",
4
4
  "description": "Simple Caching Engine using Keyv",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",