cacheable 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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.
@@ -212,6 +267,7 @@ By default we use lazy expiration deletion which means on `get` and `getMany` ty
212
267
  * `clear()`: Clears the cache.
213
268
  * `size()`: The number of keys in the cache.
214
269
  * `keys()`: The keys in the cache.
270
+ * `items()`: The items in the cache as `{ key, value, expires? }`.
215
271
  * `checkExpired()`: Checks for expired keys in the cache. This is used by the `checkInterval` property.
216
272
  * `startIntervalCheck()`: Starts the interval check for expired keys if `checkInterval` is above 0 ms.
217
273
  * `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;
@@ -174,6 +243,9 @@ var CacheableMemory = class {
174
243
  get keys() {
175
244
  return this.concatStores().keys();
176
245
  }
246
+ get items() {
247
+ return this.concatStores().values();
248
+ }
177
249
  get(key) {
178
250
  const store = this.getStore(key);
179
251
  const item = store.get(key);
@@ -190,11 +262,27 @@ var CacheableMemory = class {
190
262
  }
191
263
  return this.clone(item.value);
192
264
  }
265
+ getRaw(key) {
266
+ const store = this.getStore(key);
267
+ const item = store.get(key);
268
+ if (!item) {
269
+ return void 0;
270
+ }
271
+ if (item.expires && item.expires && Date.now() > item.expires) {
272
+ store.delete(key);
273
+ return void 0;
274
+ }
275
+ this.lruMoveToFront(key);
276
+ return item;
277
+ }
193
278
  set(key, value, ttl) {
194
279
  const store = this.getStore(key);
195
280
  let expires;
196
- if (ttl !== void 0 || this._ttl !== 0) {
197
- expires = Date.now() + (ttl ?? this._ttl);
281
+ if (ttl !== void 0 || this._ttl !== void 0) {
282
+ const finalTtl = shorthandToTime(ttl ?? this._ttl);
283
+ if (finalTtl !== void 0) {
284
+ expires = finalTtl;
285
+ }
198
286
  }
199
287
  if (this._lruSize > 0) {
200
288
  if (store.has(key)) {
@@ -361,6 +449,15 @@ var CacheableMemory = class {
361
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]);
362
450
  return result;
363
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
+ }
364
461
  };
365
462
 
366
463
  // src/keyv-memory.ts
@@ -381,7 +478,6 @@ var KeyvCacheableMemory = class {
381
478
  }
382
479
  async get(key) {
383
480
  const result = this._cache.get(key);
384
- console.log("result", result);
385
481
  if (result) {
386
482
  return result;
387
483
  }
@@ -630,7 +726,7 @@ var Cacheable = class extends import_hookified.Hookified {
630
726
  this._stats.enabled = options.stats;
631
727
  }
632
728
  if (options?.ttl) {
633
- this._ttl = options.ttl;
729
+ this.setTtl(options.ttl);
634
730
  }
635
731
  }
636
732
  get stats() {
@@ -658,7 +754,7 @@ var Cacheable = class extends import_hookified.Hookified {
658
754
  return this._ttl;
659
755
  }
660
756
  set ttl(ttl) {
661
- this._ttl = ttl;
757
+ this.setTtl(ttl);
662
758
  }
663
759
  setPrimary(primary) {
664
760
  this._primary = primary instanceof import_keyv.Keyv ? primary : new import_keyv.Keyv(primary);
@@ -674,7 +770,8 @@ var Cacheable = class extends import_hookified.Hookified {
674
770
  if (!result && this._secondary) {
675
771
  result = await this._secondary.get(key);
676
772
  if (result) {
677
- await this._primary.set(key, result, this._ttl);
773
+ const finalTtl = shorthandToMilliseconds(this._ttl);
774
+ await this._primary.set(key, result, finalTtl);
678
775
  }
679
776
  }
680
777
  await this.hook("AFTER_GET" /* AFTER_GET */, { key, result });
@@ -707,7 +804,8 @@ var Cacheable = class extends import_hookified.Hookified {
707
804
  for (const [i, key] of keys.entries()) {
708
805
  if (!result[i] && secondaryResult[i]) {
709
806
  result[i] = secondaryResult[i];
710
- await this._primary.set(key, secondaryResult[i], this._ttl);
807
+ const finalTtl = shorthandToMilliseconds(this._ttl);
808
+ await this._primary.set(key, secondaryResult[i], finalTtl);
711
809
  }
712
810
  }
713
811
  }
@@ -729,7 +827,7 @@ var Cacheable = class extends import_hookified.Hookified {
729
827
  }
730
828
  async set(key, value, ttl) {
731
829
  let result = false;
732
- const finalTtl = ttl ?? this._ttl;
830
+ const finalTtl = shorthandToMilliseconds(ttl ?? this._ttl);
733
831
  try {
734
832
  const item = { key, value, ttl: finalTtl };
735
833
  await this.hook("BEFORE_SET" /* BEFORE_SET */, item);
@@ -898,7 +996,7 @@ var Cacheable = class extends import_hookified.Hookified {
898
996
  async setManyKeyv(keyv, items) {
899
997
  const promises = [];
900
998
  for (const item of items) {
901
- const finalTtl = item.ttl ?? this._ttl;
999
+ const finalTtl = shorthandToMilliseconds(item.ttl ?? this._ttl);
902
1000
  promises.push(keyv.set(item.key, item.value, finalTtl));
903
1001
  }
904
1002
  await Promise.all(promises);
@@ -911,6 +1009,15 @@ var Cacheable = class extends import_hookified.Hookified {
911
1009
  }
912
1010
  return Promise.all(promises);
913
1011
  }
1012
+ setTtl(ttl) {
1013
+ if (typeof ttl === "string" || ttl === void 0) {
1014
+ this._ttl = ttl;
1015
+ } else if (ttl > 0) {
1016
+ this._ttl = ttl;
1017
+ } else {
1018
+ this._ttl = void 0;
1019
+ }
1020
+ }
914
1021
  };
915
1022
  // Annotate the CommonJS export names for ESM import in node:
916
1023
  0 && (module.exports = {
@@ -919,5 +1026,7 @@ var Cacheable = class extends import_hookified.Hookified {
919
1026
  CacheableHooks,
920
1027
  CacheableMemory,
921
1028
  CacheableStats,
922
- KeyvCacheableMemory
1029
+ KeyvCacheableMemory,
1030
+ shorthandToMilliseconds,
1031
+ shorthandToTime
923
1032
  });
package/dist/index.d.cts CHANGED
@@ -47,11 +47,16 @@ declare class CacheableStats {
47
47
  }
48
48
 
49
49
  type CacheableMemoryOptions = {
50
- ttl?: number;
50
+ ttl?: number | string;
51
51
  useClone?: boolean;
52
52
  lruSize?: number;
53
53
  checkInterval?: number;
54
54
  };
55
+ type CacheableItem$1 = {
56
+ key: string;
57
+ value: any;
58
+ expires?: number;
59
+ };
55
60
  declare class CacheableMemory {
56
61
  private readonly _hashCache;
57
62
  private readonly _hash0;
@@ -71,8 +76,8 @@ declare class CacheableMemory {
71
76
  private _checkInterval;
72
77
  private _interval;
73
78
  constructor(options?: CacheableMemoryOptions);
74
- get ttl(): number;
75
- set ttl(value: number);
79
+ get ttl(): number | string | undefined;
80
+ set ttl(value: number | string | undefined);
76
81
  get useClone(): boolean;
77
82
  set useClone(value: boolean);
78
83
  get lruSize(): number;
@@ -81,8 +86,10 @@ declare class CacheableMemory {
81
86
  set checkInterval(value: number);
82
87
  get size(): number;
83
88
  get keys(): IterableIterator<string>;
89
+ get items(): IterableIterator<CacheableItem$1>;
84
90
  get<T>(key: string): any;
85
- set(key: string, value: any, ttl?: number): void;
91
+ getRaw(key: string): CacheableItem$1 | undefined;
92
+ set(key: string, value: any, ttl?: number | string): void;
86
93
  has(key: string): boolean;
87
94
  take<T>(key: string): any;
88
95
  delete(key: string): void;
@@ -98,6 +105,7 @@ declare class CacheableMemory {
98
105
  stopIntervalCheck(): void;
99
106
  private isPrimitive;
100
107
  private concatStores;
108
+ private setTtl;
101
109
  }
102
110
 
103
111
  declare class KeyvCacheableMemory implements KeyvStoreAdapter {
@@ -115,6 +123,9 @@ declare class KeyvCacheableMemory implements KeyvStoreAdapter {
115
123
  on(event: string, listener: (...arguments_: any[]) => void): this;
116
124
  }
117
125
 
126
+ declare const shorthandToMilliseconds: (shorthand?: string | number) => number | undefined;
127
+ declare const shorthandToTime: (shorthand?: string | number, fromDate?: Date) => number;
128
+
118
129
  declare enum CacheableHooks {
119
130
  BEFORE_SET = "BEFORE_SET",
120
131
  AFTER_SET = "AFTER_SET",
@@ -131,14 +142,14 @@ declare enum CacheableEvents {
131
142
  type CacheableItem = {
132
143
  key: string;
133
144
  value: unknown;
134
- ttl?: number;
145
+ ttl?: number | string;
135
146
  };
136
147
  type CacheableOptions = {
137
148
  primary?: Keyv | KeyvStoreAdapter;
138
149
  secondary?: Keyv | KeyvStoreAdapter;
139
150
  stats?: boolean;
140
151
  nonBlocking?: boolean;
141
- ttl?: number;
152
+ ttl?: number | string;
142
153
  };
143
154
  declare class Cacheable extends Hookified {
144
155
  private _primary;
@@ -154,8 +165,8 @@ declare class Cacheable extends Hookified {
154
165
  set secondary(secondary: Keyv | undefined);
155
166
  get nonBlocking(): boolean;
156
167
  set nonBlocking(nonBlocking: boolean);
157
- get ttl(): number | undefined;
158
- set ttl(ttl: number | undefined);
168
+ get ttl(): number | string | undefined;
169
+ set ttl(ttl: number | string | undefined);
159
170
  setPrimary(primary: Keyv | KeyvStoreAdapter): void;
160
171
  setSecondary(secondary: Keyv | KeyvStoreAdapter): void;
161
172
  get<T>(key: string): Promise<T | undefined>;
@@ -173,6 +184,7 @@ declare class Cacheable extends Hookified {
173
184
  private deleteManyKeyv;
174
185
  private setManyKeyv;
175
186
  private hasManyKeyv;
187
+ private setTtl;
176
188
  }
177
189
 
178
- export { Cacheable, CacheableEvents, CacheableHooks, type CacheableItem, CacheableMemory, type CacheableOptions, CacheableStats, KeyvCacheableMemory };
190
+ export { Cacheable, CacheableEvents, CacheableHooks, type CacheableItem, CacheableMemory, type CacheableOptions, CacheableStats, KeyvCacheableMemory, shorthandToMilliseconds, shorthandToTime };
package/dist/index.d.ts CHANGED
@@ -47,11 +47,16 @@ declare class CacheableStats {
47
47
  }
48
48
 
49
49
  type CacheableMemoryOptions = {
50
- ttl?: number;
50
+ ttl?: number | string;
51
51
  useClone?: boolean;
52
52
  lruSize?: number;
53
53
  checkInterval?: number;
54
54
  };
55
+ type CacheableItem$1 = {
56
+ key: string;
57
+ value: any;
58
+ expires?: number;
59
+ };
55
60
  declare class CacheableMemory {
56
61
  private readonly _hashCache;
57
62
  private readonly _hash0;
@@ -71,8 +76,8 @@ declare class CacheableMemory {
71
76
  private _checkInterval;
72
77
  private _interval;
73
78
  constructor(options?: CacheableMemoryOptions);
74
- get ttl(): number;
75
- set ttl(value: number);
79
+ get ttl(): number | string | undefined;
80
+ set ttl(value: number | string | undefined);
76
81
  get useClone(): boolean;
77
82
  set useClone(value: boolean);
78
83
  get lruSize(): number;
@@ -81,8 +86,10 @@ declare class CacheableMemory {
81
86
  set checkInterval(value: number);
82
87
  get size(): number;
83
88
  get keys(): IterableIterator<string>;
89
+ get items(): IterableIterator<CacheableItem$1>;
84
90
  get<T>(key: string): any;
85
- set(key: string, value: any, ttl?: number): void;
91
+ getRaw(key: string): CacheableItem$1 | undefined;
92
+ set(key: string, value: any, ttl?: number | string): void;
86
93
  has(key: string): boolean;
87
94
  take<T>(key: string): any;
88
95
  delete(key: string): void;
@@ -98,6 +105,7 @@ declare class CacheableMemory {
98
105
  stopIntervalCheck(): void;
99
106
  private isPrimitive;
100
107
  private concatStores;
108
+ private setTtl;
101
109
  }
102
110
 
103
111
  declare class KeyvCacheableMemory implements KeyvStoreAdapter {
@@ -115,6 +123,9 @@ declare class KeyvCacheableMemory implements KeyvStoreAdapter {
115
123
  on(event: string, listener: (...arguments_: any[]) => void): this;
116
124
  }
117
125
 
126
+ declare const shorthandToMilliseconds: (shorthand?: string | number) => number | undefined;
127
+ declare const shorthandToTime: (shorthand?: string | number, fromDate?: Date) => number;
128
+
118
129
  declare enum CacheableHooks {
119
130
  BEFORE_SET = "BEFORE_SET",
120
131
  AFTER_SET = "AFTER_SET",
@@ -131,14 +142,14 @@ declare enum CacheableEvents {
131
142
  type CacheableItem = {
132
143
  key: string;
133
144
  value: unknown;
134
- ttl?: number;
145
+ ttl?: number | string;
135
146
  };
136
147
  type CacheableOptions = {
137
148
  primary?: Keyv | KeyvStoreAdapter;
138
149
  secondary?: Keyv | KeyvStoreAdapter;
139
150
  stats?: boolean;
140
151
  nonBlocking?: boolean;
141
- ttl?: number;
152
+ ttl?: number | string;
142
153
  };
143
154
  declare class Cacheable extends Hookified {
144
155
  private _primary;
@@ -154,8 +165,8 @@ declare class Cacheable extends Hookified {
154
165
  set secondary(secondary: Keyv | undefined);
155
166
  get nonBlocking(): boolean;
156
167
  set nonBlocking(nonBlocking: boolean);
157
- get ttl(): number | undefined;
158
- set ttl(ttl: number | undefined);
168
+ get ttl(): number | string | undefined;
169
+ set ttl(ttl: number | string | undefined);
159
170
  setPrimary(primary: Keyv | KeyvStoreAdapter): void;
160
171
  setSecondary(secondary: Keyv | KeyvStoreAdapter): void;
161
172
  get<T>(key: string): Promise<T | undefined>;
@@ -173,6 +184,7 @@ declare class Cacheable extends Hookified {
173
184
  private deleteManyKeyv;
174
185
  private setManyKeyv;
175
186
  private hasManyKeyv;
187
+ private setTtl;
176
188
  }
177
189
 
178
- export { Cacheable, CacheableEvents, CacheableHooks, type CacheableItem, CacheableMemory, type CacheableOptions, CacheableStats, KeyvCacheableMemory };
190
+ 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;
@@ -145,6 +212,9 @@ var CacheableMemory = class {
145
212
  get keys() {
146
213
  return this.concatStores().keys();
147
214
  }
215
+ get items() {
216
+ return this.concatStores().values();
217
+ }
148
218
  get(key) {
149
219
  const store = this.getStore(key);
150
220
  const item = store.get(key);
@@ -161,11 +231,27 @@ var CacheableMemory = class {
161
231
  }
162
232
  return this.clone(item.value);
163
233
  }
234
+ getRaw(key) {
235
+ const store = this.getStore(key);
236
+ const item = store.get(key);
237
+ if (!item) {
238
+ return void 0;
239
+ }
240
+ if (item.expires && item.expires && Date.now() > item.expires) {
241
+ store.delete(key);
242
+ return void 0;
243
+ }
244
+ this.lruMoveToFront(key);
245
+ return item;
246
+ }
164
247
  set(key, value, ttl) {
165
248
  const store = this.getStore(key);
166
249
  let expires;
167
- if (ttl !== void 0 || this._ttl !== 0) {
168
- expires = Date.now() + (ttl ?? this._ttl);
250
+ if (ttl !== void 0 || this._ttl !== void 0) {
251
+ const finalTtl = shorthandToTime(ttl ?? this._ttl);
252
+ if (finalTtl !== void 0) {
253
+ expires = finalTtl;
254
+ }
169
255
  }
170
256
  if (this._lruSize > 0) {
171
257
  if (store.has(key)) {
@@ -332,6 +418,15 @@ var CacheableMemory = class {
332
418
  const result = new Map([...this._hash0, ...this._hash1, ...this._hash2, ...this._hash3, ...this._hash4, ...this._hash5, ...this._hash6, ...this._hash7, ...this._hash8, ...this._hash9]);
333
419
  return result;
334
420
  }
421
+ setTtl(ttl) {
422
+ if (typeof ttl === "string" || ttl === void 0) {
423
+ this._ttl = ttl;
424
+ } else if (ttl > 0) {
425
+ this._ttl = ttl;
426
+ } else {
427
+ this._ttl = void 0;
428
+ }
429
+ }
335
430
  };
336
431
 
337
432
  // src/keyv-memory.ts
@@ -352,7 +447,6 @@ var KeyvCacheableMemory = class {
352
447
  }
353
448
  async get(key) {
354
449
  const result = this._cache.get(key);
355
- console.log("result", result);
356
450
  if (result) {
357
451
  return result;
358
452
  }
@@ -601,7 +695,7 @@ var Cacheable = class extends Hookified {
601
695
  this._stats.enabled = options.stats;
602
696
  }
603
697
  if (options?.ttl) {
604
- this._ttl = options.ttl;
698
+ this.setTtl(options.ttl);
605
699
  }
606
700
  }
607
701
  get stats() {
@@ -629,7 +723,7 @@ var Cacheable = class extends Hookified {
629
723
  return this._ttl;
630
724
  }
631
725
  set ttl(ttl) {
632
- this._ttl = ttl;
726
+ this.setTtl(ttl);
633
727
  }
634
728
  setPrimary(primary) {
635
729
  this._primary = primary instanceof Keyv ? primary : new Keyv(primary);
@@ -645,7 +739,8 @@ var Cacheable = class extends Hookified {
645
739
  if (!result && this._secondary) {
646
740
  result = await this._secondary.get(key);
647
741
  if (result) {
648
- await this._primary.set(key, result, this._ttl);
742
+ const finalTtl = shorthandToMilliseconds(this._ttl);
743
+ await this._primary.set(key, result, finalTtl);
649
744
  }
650
745
  }
651
746
  await this.hook("AFTER_GET" /* AFTER_GET */, { key, result });
@@ -678,7 +773,8 @@ var Cacheable = class extends Hookified {
678
773
  for (const [i, key] of keys.entries()) {
679
774
  if (!result[i] && secondaryResult[i]) {
680
775
  result[i] = secondaryResult[i];
681
- await this._primary.set(key, secondaryResult[i], this._ttl);
776
+ const finalTtl = shorthandToMilliseconds(this._ttl);
777
+ await this._primary.set(key, secondaryResult[i], finalTtl);
682
778
  }
683
779
  }
684
780
  }
@@ -700,7 +796,7 @@ var Cacheable = class extends Hookified {
700
796
  }
701
797
  async set(key, value, ttl) {
702
798
  let result = false;
703
- const finalTtl = ttl ?? this._ttl;
799
+ const finalTtl = shorthandToMilliseconds(ttl ?? this._ttl);
704
800
  try {
705
801
  const item = { key, value, ttl: finalTtl };
706
802
  await this.hook("BEFORE_SET" /* BEFORE_SET */, item);
@@ -869,7 +965,7 @@ var Cacheable = class extends Hookified {
869
965
  async setManyKeyv(keyv, items) {
870
966
  const promises = [];
871
967
  for (const item of items) {
872
- const finalTtl = item.ttl ?? this._ttl;
968
+ const finalTtl = shorthandToMilliseconds(item.ttl ?? this._ttl);
873
969
  promises.push(keyv.set(item.key, item.value, finalTtl));
874
970
  }
875
971
  await Promise.all(promises);
@@ -882,6 +978,15 @@ var Cacheable = class extends Hookified {
882
978
  }
883
979
  return Promise.all(promises);
884
980
  }
981
+ setTtl(ttl) {
982
+ if (typeof ttl === "string" || ttl === void 0) {
983
+ this._ttl = ttl;
984
+ } else if (ttl > 0) {
985
+ this._ttl = ttl;
986
+ } else {
987
+ this._ttl = void 0;
988
+ }
989
+ }
885
990
  };
886
991
  export {
887
992
  Cacheable,
@@ -889,5 +994,7 @@ export {
889
994
  CacheableHooks,
890
995
  CacheableMemory,
891
996
  CacheableStats,
892
- KeyvCacheableMemory
997
+ KeyvCacheableMemory,
998
+ shorthandToMilliseconds,
999
+ shorthandToTime
893
1000
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cacheable",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "Simple Caching Engine using Keyv",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",