flat-cache 6.0.0 → 6.1.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
@@ -13,10 +13,22 @@
13
13
  - A simple key/value storage using files to persist the data
14
14
  - Uses a in-memory cache (via `CacheableMemory`) as the primary storage and then persists the data to disk
15
15
  - Automatically saves the data to disk via `persistInterval` setting. Off By Default
16
- - Easily Loads the data from disk and into memory
16
+ - Easily Loads the data from disk and into memory with `load` or `loadFile`
17
17
  - Uses `ttl` and `lruSize` to manage the cache and persist the data
18
18
  - Only saves the data to disk if the data has changed even when using `persistInterval` or calling `save()`
19
- - Uses `flatted` to parse and stringify the data by default but can be overridden
19
+ - Uses `flatted` to parse and stringify the data by default but can be overridden using `parse` and `stringify` in options
20
+
21
+ # Table of Contents
22
+ - [Installation](#installation)
23
+ - [Getting Started](#getting-started)
24
+ - [Breaking Changes from v5 to v6](#breaking-changes-from-v5-to-v6)
25
+ - [Global Functions](#global-functions)
26
+ - [FlatCache Options (FlatCacheOptions)](#flatcache-options-flatcacheoptions)
27
+ - [API](#api)
28
+ - [Events (FlatCacheEvents)](#events-flatcacheevents)
29
+ - [Parse and Stringify for File Caching](#parse-and-stringify-for-file-caching)
30
+ - [How to Contribute](#how-to-contribute)
31
+ - [License and Copyright](#license-and-copyright)
20
32
 
21
33
  # Installation
22
34
  ```bash
@@ -43,16 +55,70 @@ const cache = new FlatCache({
43
55
  cache.setKey('key', 'value');
44
56
  ```
45
57
 
46
- This will save the data to disk every 5 minutes and will remove any data that has not been accessed in 1 hour or if the cache has more than 10,000 items.
58
+ This will save the data to disk every 5 minutes and will remove any data that has not been accessed in 1 hour or if the cache has more than 10,000 items. The `expirationInterval` will check every 5 minutes for expired items and evict them. This is replacement to the `save()` method with a `prune` option as it is no longer needed due to the fact that the in-memory cache handles pruning by `ttl` expiration or `lruSize` which will keep the most recent there.
59
+
60
+ here is an example doing load from already existing persisted cache
61
+
62
+ ```javascript
63
+ import { load } from 'flat-cache';
64
+ const cache = load('cache1', './cacheAltDirectory');
65
+ ```
66
+
67
+ This will load the cache from the `./cacheAltDirectory` directory with the `cache1` id. If it doesnt exist it will not throw an error but will just return an empty cache.
68
+
69
+ # Breaking Changes from v5 to v6
70
+
71
+ There have been many features added and changes made to the `FlatCache` class. Here are the main changes:
72
+ - `FlatCache` is now a class and not a function which you can create instances of or using legacy method `load`, `loadFile`, or `create`
73
+ - `FlatCache` now uses `CacheableMemory` as the primary storage and then persists the data to disk
74
+ - `FlatCache` now uses `ttl` and `lruSize` to manage the cache and persist the data
75
+ - `FlatCache` now uses `expirationInterval` to check for expired items in the cache. If it is not set it will do a lazy check on `get` or `getKey`
76
+ - `getKey` still exists but is now is an alias to `get` and will be removed in the future
77
+ - `setKey` still exists but is now is an alias to `set` and will be removed in the future
78
+ - `removeKey` still exists but is now is an alias to `delete` and will be removed in the future
79
+
80
+ Here is an example of the legacy method `load`:
81
+ ```javascript
82
+ const flatCache = require('flat-cache');
83
+ // loads the cache, if one does not exists for the given
84
+ // Id a new one will be prepared to be created
85
+ const cache = flatCache.load('cacheId');
86
+ ```
87
+
88
+ Now you can use the `load` method and ES6 imports:
89
+ ```javascript
90
+ import { FlatCache } from 'flat-cache';
91
+ const cache = new FlatCache();
92
+ cache.load('cacheId');
93
+ ```
94
+ If you do not specify a `cacheId` it will default to what was set in `FlatCacheOptions` or the default property `cacheId` of `cache1` and default `cacheDir` of `./cache`.
95
+
96
+ If you want to create a new cache and load from disk if it exists you can use the `create` method:
97
+ ```javascript
98
+ import { create } from 'flat-cache';
99
+ const cache = create({ cacheId: 'myCacheId', cacheDir: './mycache', ttl: 60 * 60 * 1000 });
100
+ ```
101
+
102
+ # Global Functions
103
+
104
+ In version 6 we attempted to keep as much as the functionality as possible which includes these functions:
105
+
106
+ - `create(options?: FlatCacheOptions)` - Creates a new cache and will load the data from disk if it exists
107
+ - `createFromFile(filePath, options?: FlatCacheOptions)` - Creates a new cache from a file
108
+ - `clearByCacheId(cacheId: string, cacheDir?: string)` - Clears the cache by the cacheId
109
+ - `clearAll(cacheDirectory?: string)` - Clears all the caches
47
110
 
48
- # FlatCache Options
49
- - `ttl` - The time to live for the cache in milliseconds. Default is `0` which means no expiration
50
- - `lruSize` - The number of items to keep in the cache. Default is `0` which means no limit
51
- - `useClone` - If `true` it will clone the data before returning it. Default is `false`
52
- - `expirationInterval` - The interval to check for expired items in the cache. Default is `0` which means no expiration
53
- - `persistInterval` - The interval to save the data to disk. Default is `0` which means no persistence
54
- - `cacheDir` - The directory to save the cache files. Default is `./cache`
55
- - `cacheId` - The id of the cache. Default is `cache1`
111
+
112
+ # FlatCache Options (FlatCacheOptions)
113
+ - `ttl?` - The time to live for the cache in milliseconds. Default is `0` which means no expiration
114
+ - `lruSize?` - The number of items to keep in the cache. Default is `0` which means no limit
115
+ - `useClone?` - If `true` it will clone the data before returning it. Default is `false`
116
+ - `expirationInterval?` - The interval to check for expired items in the cache. Default is `0` which means no expiration
117
+ - `persistInterval?` - The interval to save the data to disk. Default is `0` which means no persistence
118
+ - `cacheDir?` - The directory to save the cache files. Default is `./cache`
119
+ - `cacheId?` - The id of the cache. Default is `cache1`
120
+ - `parse?` - The function to parse the data. Default is `flatted.parse`
121
+ - `stringify?` - The function to stringify the data. Default is `flatted.stringify`
56
122
 
57
123
  # API
58
124
 
@@ -62,6 +128,7 @@ This will save the data to disk every 5 minutes and will remove any data that ha
62
128
  - `cacheFilePath` - The full path to the cache file
63
129
  - `cacheDirPath` - The full path to the cache directory
64
130
  - `persistInterval` - The interval to save the data to disk
131
+ - `changesSinceLastSave` - If there have been changes since the last save
65
132
  - `load(cacheId: string, cacheDir?: string)` - Loads the data from disk
66
133
  - `loadFile(pathToFile: string)` - Loads the data from disk
67
134
  - `all()` - Gets all the data in the cache
@@ -74,9 +141,48 @@ This will save the data to disk every 5 minutes and will remove any data that ha
74
141
  - `removeKey(key: string)` - Removes the key from the cache
75
142
  - `delete(key: string)` - Removes the key from the cache
76
143
  - `clear()` - Clears the cache
77
- - `save()` - Saves the data to disk
144
+ - `save(force? boolean)` - Saves the data to disk. If `force` is `true` it will save even if `changesSinceLastSave` is `false`
78
145
  - `destroy()` - Destroys the cache and remove files
79
146
 
147
+ # Events (FlatCacheEvents)
148
+
149
+ Events have been added since v6 to allow for more control and visibility into the cache. Here are the events that are available:
150
+
151
+ - `on(event: 'save', listener: () => void)` - Emitted when the cache is saved
152
+ - `on(event: 'load', listener: () => void)` - Emitted when the cache is loaded
153
+ - `on(event: 'delete', listener: (key: string) => void)` - Emitted when the cache is changed
154
+ - `on(event: 'clear', listener: () => void)` - Emitted when the cache is cleared
155
+ - `on(event: 'destroy', listener: () => void)` - Emitted when the cache is destroyed
156
+ - `on(event: 'error', listener: (error: Error) => void)` - Emitted when there is an error
157
+
158
+ Here is an example of how to use the `error` events:
159
+
160
+ ```javascript
161
+ import { FlatCache, FlatCacheEvents } from 'flat-cache';
162
+ const cache = new FlatCache();
163
+ cache.on(FlatCacheEvents.error, (error) => {
164
+ console.error(error);
165
+ });
166
+ ```
167
+
168
+ `FlatCacheEvents` is an enum that contains the event names for the `on` method. You do not have to use it but makes it easier to know what events are available.
169
+
170
+ # Parse and Stringify for File Caching
171
+
172
+ By default `flat-cache` uses `flatted` to parse and stringify the data. This is to allow for more complex data structures to be saved to disk. If you want to override this you can pass in your own `parse` and `stringify` functions. Here is an example:
173
+
174
+ ```javascript
175
+ import { FlatCache } from 'flat-cache';
176
+ const cache = new FlatCache({
177
+ parse: JSON.parse,
178
+ stringify: JSON.stringify,
179
+ });
180
+ ```
181
+
182
+ This will use `JSON.parse` and `JSON.stringify` to parse and stringify the data. This is useful if you want to use a different library or have a custom way of parsing and stringifying the data.
183
+
184
+ **NOTE: This could cause issues if you are trying to load data that was saved with a different parser or stringifier.**
185
+
80
186
  # How to Contribute
81
187
 
82
188
  You can contribute by forking the repo and submitting a pull request. Please make sure to add tests and update the documentation. To learn more about how to contribute go to our main README [https://github.com/jaredwray/cacheable](https://github.com/jaredwray/cacheable). This will talk about how to `Open a Pull Request`, `Ask a Question`, or `Post an Issue`.
package/dist/index.cjs CHANGED
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
33
  FlatCache: () => FlatCache,
34
+ FlatCacheEvents: () => FlatCacheEvents,
34
35
  clearAll: () => clearAll,
35
36
  clearCacheById: () => clearCacheById,
36
37
  create: () => create,
@@ -41,13 +42,28 @@ var import_node_path = __toESM(require("path"), 1);
41
42
  var import_node_fs = __toESM(require("fs"), 1);
42
43
  var import_cacheable = require("cacheable");
43
44
  var import_flatted = require("flatted");
44
- var FlatCache = class {
45
+ var import_hookified = require("hookified");
46
+ var FlatCacheEvents = /* @__PURE__ */ ((FlatCacheEvents2) => {
47
+ FlatCacheEvents2["SAVE"] = "save";
48
+ FlatCacheEvents2["LOAD"] = "load";
49
+ FlatCacheEvents2["DELETE"] = "delete";
50
+ FlatCacheEvents2["CLEAR"] = "clear";
51
+ FlatCacheEvents2["DESTROY"] = "destroy";
52
+ FlatCacheEvents2["ERROR"] = "error";
53
+ FlatCacheEvents2["EXPIRED"] = "expired";
54
+ return FlatCacheEvents2;
55
+ })(FlatCacheEvents || {});
56
+ var FlatCache = class extends import_hookified.Hookified {
45
57
  _cache = new import_cacheable.CacheableMemory();
46
58
  _cacheDir = ".cache";
47
59
  _cacheId = "cache1";
48
60
  _persistInterval = 0;
49
61
  _persistTimer;
62
+ _changesSinceLastSave = false;
63
+ _parse = import_flatted.parse;
64
+ _stringify = import_flatted.stringify;
50
65
  constructor(options) {
66
+ super();
51
67
  if (options) {
52
68
  this._cache = new import_cacheable.CacheableMemory({
53
69
  ttl: options.ttl,
@@ -66,6 +82,12 @@ var FlatCache = class {
66
82
  this._persistInterval = options.persistInterval;
67
83
  this.startAutoPersist();
68
84
  }
85
+ if (options?.parse) {
86
+ this._parse = options.parse;
87
+ }
88
+ if (options?.stringify) {
89
+ this._stringify = options.stringify;
90
+ }
69
91
  }
70
92
  /**
71
93
  * The cache object
@@ -111,6 +133,15 @@ var FlatCache = class {
111
133
  set cacheId(value) {
112
134
  this._cacheId = value;
113
135
  }
136
+ /**
137
+ * The flag to indicate if there are changes since the last save
138
+ * @property changesSinceLastSave
139
+ * @type {Boolean}
140
+ * @default false
141
+ */
142
+ get changesSinceLastSave() {
143
+ return this._changesSinceLastSave;
144
+ }
114
145
  /**
115
146
  * The interval to persist the cache to disk. 0 means no timed persistence
116
147
  * @property persistInterval
@@ -135,13 +166,18 @@ var FlatCache = class {
135
166
  * then the cache module directory `.cacheDir` will be used instead
136
167
  *
137
168
  * @method load
138
- * @param docId {String} the id of the cache, would also be used as the name of the file cache
139
- * @param [cacheDir] {String} directory for the cache entry
169
+ * @param cacheId {String} the id of the cache, would also be used as the name of the file cache
170
+ * @param cacheDir {String} directory for the cache entry
140
171
  */
141
172
  // eslint-disable-next-line unicorn/prevent-abbreviations
142
- load(documentId, cacheDir) {
143
- const filePath = import_node_path.default.resolve(`${cacheDir ?? this._cacheDir}/${documentId}`);
144
- this.loadFile(filePath);
173
+ load(cacheId, cacheDir) {
174
+ try {
175
+ const filePath = import_node_path.default.resolve(`${cacheDir ?? this._cacheDir}/${cacheId ?? this._cacheId}`);
176
+ this.loadFile(filePath);
177
+ this.emit("load" /* LOAD */);
178
+ } catch (error) {
179
+ this.emit("error" /* ERROR */, error);
180
+ }
145
181
  }
146
182
  /**
147
183
  * Load the cache from the provided file
@@ -151,10 +187,11 @@ var FlatCache = class {
151
187
  loadFile(pathToFile) {
152
188
  if (import_node_fs.default.existsSync(pathToFile)) {
153
189
  const data = import_node_fs.default.readFileSync(pathToFile, "utf8");
154
- const items = (0, import_flatted.parse)(data);
190
+ const items = this._parse(data);
155
191
  for (const key of Object.keys(items)) {
156
192
  this._cache.set(key, items[key]);
157
193
  }
194
+ this._changesSinceLastSave = true;
158
195
  }
159
196
  }
160
197
  /**
@@ -209,7 +246,7 @@ var FlatCache = class {
209
246
  * @param value {object} the value of the key. Could be any object that can be serialized with JSON.stringify
210
247
  */
211
248
  setKey(key, value, ttl) {
212
- this._cache.set(key, value, ttl);
249
+ this.set(key, value, ttl);
213
250
  }
214
251
  /**
215
252
  * Sets a key to a given value
@@ -220,6 +257,7 @@ var FlatCache = class {
220
257
  */
221
258
  set(key, value, ttl) {
222
259
  this._cache.set(key, value, ttl);
260
+ this._changesSinceLastSave = true;
223
261
  }
224
262
  /**
225
263
  * (Legacy) Remove a given key from the cache. This method will be deprecated in the future
@@ -227,7 +265,7 @@ var FlatCache = class {
227
265
  * @param key {String} the key to remove from the object
228
266
  */
229
267
  removeKey(key) {
230
- this._cache.delete(key);
268
+ this.delete(key);
231
269
  }
232
270
  /**
233
271
  * Remove a given key from the cache
@@ -236,6 +274,8 @@ var FlatCache = class {
236
274
  */
237
275
  delete(key) {
238
276
  this._cache.delete(key);
277
+ this._changesSinceLastSave = true;
278
+ this.emit("delete" /* DELETE */, key);
239
279
  }
240
280
  /**
241
281
  * (Legacy) Return the value of the provided key. This method will be deprecated in the future
@@ -256,25 +296,40 @@ var FlatCache = class {
256
296
  return this._cache.get(key);
257
297
  }
258
298
  /**
259
- * Clear the cache
299
+ * Clear the cache and save the state to disk
260
300
  * @method clear
261
301
  */
262
302
  clear() {
263
- this._cache.clear();
303
+ try {
304
+ this._cache.clear();
305
+ this._changesSinceLastSave = true;
306
+ this.save();
307
+ this.emit("clear" /* CLEAR */);
308
+ } catch (error) {
309
+ this.emit("error" /* ERROR */, error);
310
+ }
264
311
  }
265
312
  /**
266
313
  * Save the state of the cache identified by the docId to disk
267
314
  * as a JSON structure
268
315
  * @method save
269
316
  */
270
- save() {
271
- const filePath = this.cacheFilePath;
272
- const items = this.all();
273
- const data = (0, import_flatted.stringify)(items);
274
- if (!import_node_fs.default.existsSync(this._cacheDir)) {
275
- import_node_fs.default.mkdirSync(this._cacheDir, { recursive: true });
317
+ save(force = false) {
318
+ try {
319
+ if (this._changesSinceLastSave || force) {
320
+ const filePath = this.cacheFilePath;
321
+ const items = this.all();
322
+ const data = this._stringify(items);
323
+ if (!import_node_fs.default.existsSync(this._cacheDir)) {
324
+ import_node_fs.default.mkdirSync(this._cacheDir, { recursive: true });
325
+ }
326
+ import_node_fs.default.writeFileSync(filePath, data);
327
+ this._changesSinceLastSave = false;
328
+ this.emit("save" /* SAVE */);
329
+ }
330
+ } catch (error) {
331
+ this.emit("error" /* ERROR */, error);
276
332
  }
277
- import_node_fs.default.writeFileSync(filePath, data);
278
333
  }
279
334
  /**
280
335
  * Remove the file where the cache is persisted
@@ -282,9 +337,13 @@ var FlatCache = class {
282
337
  * @return {Boolean} true or false if the file was successfully deleted
283
338
  */
284
339
  removeCacheFile() {
285
- if (import_node_fs.default.existsSync(this.cacheFilePath)) {
286
- import_node_fs.default.rmSync(this.cacheFilePath);
287
- return true;
340
+ try {
341
+ if (import_node_fs.default.existsSync(this.cacheFilePath)) {
342
+ import_node_fs.default.rmSync(this.cacheFilePath);
343
+ return true;
344
+ }
345
+ } catch (error) {
346
+ this.emit("error" /* ERROR */, error);
288
347
  }
289
348
  return false;
290
349
  }
@@ -295,12 +354,18 @@ var FlatCache = class {
295
354
  * @return {undefined}
296
355
  */
297
356
  destroy(includeCacheDirectory = false) {
298
- this._cache.clear();
299
- this.stopAutoPersist();
300
- if (includeCacheDirectory) {
301
- import_node_fs.default.rmSync(this.cacheDirPath, { recursive: true, force: true });
302
- } else {
303
- import_node_fs.default.rmSync(this.cacheFilePath, { recursive: true, force: true });
357
+ try {
358
+ this._cache.clear();
359
+ this.stopAutoPersist();
360
+ if (includeCacheDirectory) {
361
+ import_node_fs.default.rmSync(this.cacheDirPath, { recursive: true, force: true });
362
+ } else {
363
+ import_node_fs.default.rmSync(this.cacheFilePath, { recursive: true, force: true });
364
+ }
365
+ this._changesSinceLastSave = false;
366
+ this.emit("destroy" /* DESTROY */);
367
+ } catch (error) {
368
+ this.emit("error" /* ERROR */, error);
304
369
  }
305
370
  }
306
371
  /**
@@ -329,13 +394,9 @@ var FlatCache = class {
329
394
  }
330
395
  }
331
396
  };
332
- function create(documentId, cacheDirectory, options) {
397
+ function create(options) {
333
398
  const cache = new FlatCache(options);
334
- cache.cacheId = documentId;
335
- if (cacheDirectory) {
336
- cache.cacheDir = cacheDirectory;
337
- }
338
- cache.load(documentId, cacheDirectory);
399
+ cache.load();
339
400
  return cache;
340
401
  }
341
402
  function createFromFile(filePath, options) {
@@ -353,6 +414,7 @@ function clearAll(cacheDirectory) {
353
414
  // Annotate the CommonJS export names for ESM import in node:
354
415
  0 && (module.exports = {
355
416
  FlatCache,
417
+ FlatCacheEvents,
356
418
  clearAll,
357
419
  clearCacheById,
358
420
  create,
package/dist/index.d.cts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { CacheableMemory } from 'cacheable';
2
+ import { Hookified } from 'hookified';
2
3
 
3
4
  type FlatCacheOptions = {
4
5
  ttl?: number | string;
@@ -8,13 +9,27 @@ type FlatCacheOptions = {
8
9
  persistInterval?: number;
9
10
  cacheDir?: string;
10
11
  cacheId?: string;
12
+ parse?: (data: string) => any;
13
+ stringify?: (data: any) => string;
11
14
  };
12
- declare class FlatCache {
15
+ declare enum FlatCacheEvents {
16
+ SAVE = "save",
17
+ LOAD = "load",
18
+ DELETE = "delete",
19
+ CLEAR = "clear",
20
+ DESTROY = "destroy",
21
+ ERROR = "error",
22
+ EXPIRED = "expired"
23
+ }
24
+ declare class FlatCache extends Hookified {
13
25
  private readonly _cache;
14
26
  private _cacheDir;
15
27
  private _cacheId;
16
28
  private _persistInterval;
17
29
  private _persistTimer;
30
+ private _changesSinceLastSave;
31
+ private readonly _parse;
32
+ private readonly _stringify;
18
33
  constructor(options?: FlatCacheOptions);
19
34
  /**
20
35
  * The cache object
@@ -50,6 +65,13 @@ declare class FlatCache {
50
65
  * @default 'cache1'
51
66
  */
52
67
  set cacheId(value: string);
68
+ /**
69
+ * The flag to indicate if there are changes since the last save
70
+ * @property changesSinceLastSave
71
+ * @type {Boolean}
72
+ * @default false
73
+ */
74
+ get changesSinceLastSave(): boolean;
53
75
  /**
54
76
  * The interval to persist the cache to disk. 0 means no timed persistence
55
77
  * @property persistInterval
@@ -70,10 +92,10 @@ declare class FlatCache {
70
92
  * then the cache module directory `.cacheDir` will be used instead
71
93
  *
72
94
  * @method load
73
- * @param docId {String} the id of the cache, would also be used as the name of the file cache
74
- * @param [cacheDir] {String} directory for the cache entry
95
+ * @param cacheId {String} the id of the cache, would also be used as the name of the file cache
96
+ * @param cacheDir {String} directory for the cache entry
75
97
  */
76
- load(documentId: string, cacheDir?: string): void;
98
+ load(cacheId?: string, cacheDir?: string): void;
77
99
  /**
78
100
  * Load the cache from the provided file
79
101
  * @method loadFile
@@ -156,7 +178,7 @@ declare class FlatCache {
156
178
  */
157
179
  get<T>(key: string): T;
158
180
  /**
159
- * Clear the cache
181
+ * Clear the cache and save the state to disk
160
182
  * @method clear
161
183
  */
162
184
  clear(): void;
@@ -165,7 +187,7 @@ declare class FlatCache {
165
187
  * as a JSON structure
166
188
  * @method save
167
189
  */
168
- save(): void;
190
+ save(force?: boolean): void;
169
191
  /**
170
192
  * Remove the file where the cache is persisted
171
193
  * @method removeCacheFile
@@ -200,7 +222,7 @@ declare class FlatCache {
200
222
  * @param options {FlatCacheOptions} options for the cache
201
223
  * @returns {cache} cache instance
202
224
  */
203
- declare function create(documentId: string, cacheDirectory?: string, options?: FlatCacheOptions): FlatCache;
225
+ declare function create(options?: FlatCacheOptions): FlatCache;
204
226
  /**
205
227
  * Load a cache from the provided file
206
228
  * @method createFromFile
@@ -223,4 +245,4 @@ declare function clearCacheById(cacheId: string, cacheDirectory?: string): void;
223
245
  */
224
246
  declare function clearAll(cacheDirectory?: string): void;
225
247
 
226
- export { FlatCache, type FlatCacheOptions, clearAll, clearCacheById, create, createFromFile };
248
+ export { FlatCache, FlatCacheEvents, type FlatCacheOptions, clearAll, clearCacheById, create, createFromFile };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { CacheableMemory } from 'cacheable';
2
+ import { Hookified } from 'hookified';
2
3
 
3
4
  type FlatCacheOptions = {
4
5
  ttl?: number | string;
@@ -8,13 +9,27 @@ type FlatCacheOptions = {
8
9
  persistInterval?: number;
9
10
  cacheDir?: string;
10
11
  cacheId?: string;
12
+ parse?: (data: string) => any;
13
+ stringify?: (data: any) => string;
11
14
  };
12
- declare class FlatCache {
15
+ declare enum FlatCacheEvents {
16
+ SAVE = "save",
17
+ LOAD = "load",
18
+ DELETE = "delete",
19
+ CLEAR = "clear",
20
+ DESTROY = "destroy",
21
+ ERROR = "error",
22
+ EXPIRED = "expired"
23
+ }
24
+ declare class FlatCache extends Hookified {
13
25
  private readonly _cache;
14
26
  private _cacheDir;
15
27
  private _cacheId;
16
28
  private _persistInterval;
17
29
  private _persistTimer;
30
+ private _changesSinceLastSave;
31
+ private readonly _parse;
32
+ private readonly _stringify;
18
33
  constructor(options?: FlatCacheOptions);
19
34
  /**
20
35
  * The cache object
@@ -50,6 +65,13 @@ declare class FlatCache {
50
65
  * @default 'cache1'
51
66
  */
52
67
  set cacheId(value: string);
68
+ /**
69
+ * The flag to indicate if there are changes since the last save
70
+ * @property changesSinceLastSave
71
+ * @type {Boolean}
72
+ * @default false
73
+ */
74
+ get changesSinceLastSave(): boolean;
53
75
  /**
54
76
  * The interval to persist the cache to disk. 0 means no timed persistence
55
77
  * @property persistInterval
@@ -70,10 +92,10 @@ declare class FlatCache {
70
92
  * then the cache module directory `.cacheDir` will be used instead
71
93
  *
72
94
  * @method load
73
- * @param docId {String} the id of the cache, would also be used as the name of the file cache
74
- * @param [cacheDir] {String} directory for the cache entry
95
+ * @param cacheId {String} the id of the cache, would also be used as the name of the file cache
96
+ * @param cacheDir {String} directory for the cache entry
75
97
  */
76
- load(documentId: string, cacheDir?: string): void;
98
+ load(cacheId?: string, cacheDir?: string): void;
77
99
  /**
78
100
  * Load the cache from the provided file
79
101
  * @method loadFile
@@ -156,7 +178,7 @@ declare class FlatCache {
156
178
  */
157
179
  get<T>(key: string): T;
158
180
  /**
159
- * Clear the cache
181
+ * Clear the cache and save the state to disk
160
182
  * @method clear
161
183
  */
162
184
  clear(): void;
@@ -165,7 +187,7 @@ declare class FlatCache {
165
187
  * as a JSON structure
166
188
  * @method save
167
189
  */
168
- save(): void;
190
+ save(force?: boolean): void;
169
191
  /**
170
192
  * Remove the file where the cache is persisted
171
193
  * @method removeCacheFile
@@ -200,7 +222,7 @@ declare class FlatCache {
200
222
  * @param options {FlatCacheOptions} options for the cache
201
223
  * @returns {cache} cache instance
202
224
  */
203
- declare function create(documentId: string, cacheDirectory?: string, options?: FlatCacheOptions): FlatCache;
225
+ declare function create(options?: FlatCacheOptions): FlatCache;
204
226
  /**
205
227
  * Load a cache from the provided file
206
228
  * @method createFromFile
@@ -223,4 +245,4 @@ declare function clearCacheById(cacheId: string, cacheDirectory?: string): void;
223
245
  */
224
246
  declare function clearAll(cacheDirectory?: string): void;
225
247
 
226
- export { FlatCache, type FlatCacheOptions, clearAll, clearCacheById, create, createFromFile };
248
+ export { FlatCache, FlatCacheEvents, type FlatCacheOptions, clearAll, clearCacheById, create, createFromFile };
package/dist/index.js CHANGED
@@ -3,13 +3,28 @@ import path from "node:path";
3
3
  import fs from "node:fs";
4
4
  import { CacheableMemory } from "cacheable";
5
5
  import { parse, stringify } from "flatted";
6
- var FlatCache = class {
6
+ import { Hookified } from "hookified";
7
+ var FlatCacheEvents = /* @__PURE__ */ ((FlatCacheEvents2) => {
8
+ FlatCacheEvents2["SAVE"] = "save";
9
+ FlatCacheEvents2["LOAD"] = "load";
10
+ FlatCacheEvents2["DELETE"] = "delete";
11
+ FlatCacheEvents2["CLEAR"] = "clear";
12
+ FlatCacheEvents2["DESTROY"] = "destroy";
13
+ FlatCacheEvents2["ERROR"] = "error";
14
+ FlatCacheEvents2["EXPIRED"] = "expired";
15
+ return FlatCacheEvents2;
16
+ })(FlatCacheEvents || {});
17
+ var FlatCache = class extends Hookified {
7
18
  _cache = new CacheableMemory();
8
19
  _cacheDir = ".cache";
9
20
  _cacheId = "cache1";
10
21
  _persistInterval = 0;
11
22
  _persistTimer;
23
+ _changesSinceLastSave = false;
24
+ _parse = parse;
25
+ _stringify = stringify;
12
26
  constructor(options) {
27
+ super();
13
28
  if (options) {
14
29
  this._cache = new CacheableMemory({
15
30
  ttl: options.ttl,
@@ -28,6 +43,12 @@ var FlatCache = class {
28
43
  this._persistInterval = options.persistInterval;
29
44
  this.startAutoPersist();
30
45
  }
46
+ if (options?.parse) {
47
+ this._parse = options.parse;
48
+ }
49
+ if (options?.stringify) {
50
+ this._stringify = options.stringify;
51
+ }
31
52
  }
32
53
  /**
33
54
  * The cache object
@@ -73,6 +94,15 @@ var FlatCache = class {
73
94
  set cacheId(value) {
74
95
  this._cacheId = value;
75
96
  }
97
+ /**
98
+ * The flag to indicate if there are changes since the last save
99
+ * @property changesSinceLastSave
100
+ * @type {Boolean}
101
+ * @default false
102
+ */
103
+ get changesSinceLastSave() {
104
+ return this._changesSinceLastSave;
105
+ }
76
106
  /**
77
107
  * The interval to persist the cache to disk. 0 means no timed persistence
78
108
  * @property persistInterval
@@ -97,13 +127,18 @@ var FlatCache = class {
97
127
  * then the cache module directory `.cacheDir` will be used instead
98
128
  *
99
129
  * @method load
100
- * @param docId {String} the id of the cache, would also be used as the name of the file cache
101
- * @param [cacheDir] {String} directory for the cache entry
130
+ * @param cacheId {String} the id of the cache, would also be used as the name of the file cache
131
+ * @param cacheDir {String} directory for the cache entry
102
132
  */
103
133
  // eslint-disable-next-line unicorn/prevent-abbreviations
104
- load(documentId, cacheDir) {
105
- const filePath = path.resolve(`${cacheDir ?? this._cacheDir}/${documentId}`);
106
- this.loadFile(filePath);
134
+ load(cacheId, cacheDir) {
135
+ try {
136
+ const filePath = path.resolve(`${cacheDir ?? this._cacheDir}/${cacheId ?? this._cacheId}`);
137
+ this.loadFile(filePath);
138
+ this.emit("load" /* LOAD */);
139
+ } catch (error) {
140
+ this.emit("error" /* ERROR */, error);
141
+ }
107
142
  }
108
143
  /**
109
144
  * Load the cache from the provided file
@@ -113,10 +148,11 @@ var FlatCache = class {
113
148
  loadFile(pathToFile) {
114
149
  if (fs.existsSync(pathToFile)) {
115
150
  const data = fs.readFileSync(pathToFile, "utf8");
116
- const items = parse(data);
151
+ const items = this._parse(data);
117
152
  for (const key of Object.keys(items)) {
118
153
  this._cache.set(key, items[key]);
119
154
  }
155
+ this._changesSinceLastSave = true;
120
156
  }
121
157
  }
122
158
  /**
@@ -171,7 +207,7 @@ var FlatCache = class {
171
207
  * @param value {object} the value of the key. Could be any object that can be serialized with JSON.stringify
172
208
  */
173
209
  setKey(key, value, ttl) {
174
- this._cache.set(key, value, ttl);
210
+ this.set(key, value, ttl);
175
211
  }
176
212
  /**
177
213
  * Sets a key to a given value
@@ -182,6 +218,7 @@ var FlatCache = class {
182
218
  */
183
219
  set(key, value, ttl) {
184
220
  this._cache.set(key, value, ttl);
221
+ this._changesSinceLastSave = true;
185
222
  }
186
223
  /**
187
224
  * (Legacy) Remove a given key from the cache. This method will be deprecated in the future
@@ -189,7 +226,7 @@ var FlatCache = class {
189
226
  * @param key {String} the key to remove from the object
190
227
  */
191
228
  removeKey(key) {
192
- this._cache.delete(key);
229
+ this.delete(key);
193
230
  }
194
231
  /**
195
232
  * Remove a given key from the cache
@@ -198,6 +235,8 @@ var FlatCache = class {
198
235
  */
199
236
  delete(key) {
200
237
  this._cache.delete(key);
238
+ this._changesSinceLastSave = true;
239
+ this.emit("delete" /* DELETE */, key);
201
240
  }
202
241
  /**
203
242
  * (Legacy) Return the value of the provided key. This method will be deprecated in the future
@@ -218,25 +257,40 @@ var FlatCache = class {
218
257
  return this._cache.get(key);
219
258
  }
220
259
  /**
221
- * Clear the cache
260
+ * Clear the cache and save the state to disk
222
261
  * @method clear
223
262
  */
224
263
  clear() {
225
- this._cache.clear();
264
+ try {
265
+ this._cache.clear();
266
+ this._changesSinceLastSave = true;
267
+ this.save();
268
+ this.emit("clear" /* CLEAR */);
269
+ } catch (error) {
270
+ this.emit("error" /* ERROR */, error);
271
+ }
226
272
  }
227
273
  /**
228
274
  * Save the state of the cache identified by the docId to disk
229
275
  * as a JSON structure
230
276
  * @method save
231
277
  */
232
- save() {
233
- const filePath = this.cacheFilePath;
234
- const items = this.all();
235
- const data = stringify(items);
236
- if (!fs.existsSync(this._cacheDir)) {
237
- fs.mkdirSync(this._cacheDir, { recursive: true });
278
+ save(force = false) {
279
+ try {
280
+ if (this._changesSinceLastSave || force) {
281
+ const filePath = this.cacheFilePath;
282
+ const items = this.all();
283
+ const data = this._stringify(items);
284
+ if (!fs.existsSync(this._cacheDir)) {
285
+ fs.mkdirSync(this._cacheDir, { recursive: true });
286
+ }
287
+ fs.writeFileSync(filePath, data);
288
+ this._changesSinceLastSave = false;
289
+ this.emit("save" /* SAVE */);
290
+ }
291
+ } catch (error) {
292
+ this.emit("error" /* ERROR */, error);
238
293
  }
239
- fs.writeFileSync(filePath, data);
240
294
  }
241
295
  /**
242
296
  * Remove the file where the cache is persisted
@@ -244,9 +298,13 @@ var FlatCache = class {
244
298
  * @return {Boolean} true or false if the file was successfully deleted
245
299
  */
246
300
  removeCacheFile() {
247
- if (fs.existsSync(this.cacheFilePath)) {
248
- fs.rmSync(this.cacheFilePath);
249
- return true;
301
+ try {
302
+ if (fs.existsSync(this.cacheFilePath)) {
303
+ fs.rmSync(this.cacheFilePath);
304
+ return true;
305
+ }
306
+ } catch (error) {
307
+ this.emit("error" /* ERROR */, error);
250
308
  }
251
309
  return false;
252
310
  }
@@ -257,12 +315,18 @@ var FlatCache = class {
257
315
  * @return {undefined}
258
316
  */
259
317
  destroy(includeCacheDirectory = false) {
260
- this._cache.clear();
261
- this.stopAutoPersist();
262
- if (includeCacheDirectory) {
263
- fs.rmSync(this.cacheDirPath, { recursive: true, force: true });
264
- } else {
265
- fs.rmSync(this.cacheFilePath, { recursive: true, force: true });
318
+ try {
319
+ this._cache.clear();
320
+ this.stopAutoPersist();
321
+ if (includeCacheDirectory) {
322
+ fs.rmSync(this.cacheDirPath, { recursive: true, force: true });
323
+ } else {
324
+ fs.rmSync(this.cacheFilePath, { recursive: true, force: true });
325
+ }
326
+ this._changesSinceLastSave = false;
327
+ this.emit("destroy" /* DESTROY */);
328
+ } catch (error) {
329
+ this.emit("error" /* ERROR */, error);
266
330
  }
267
331
  }
268
332
  /**
@@ -291,13 +355,9 @@ var FlatCache = class {
291
355
  }
292
356
  }
293
357
  };
294
- function create(documentId, cacheDirectory, options) {
358
+ function create(options) {
295
359
  const cache = new FlatCache(options);
296
- cache.cacheId = documentId;
297
- if (cacheDirectory) {
298
- cache.cacheDir = cacheDirectory;
299
- }
300
- cache.load(documentId, cacheDirectory);
360
+ cache.load();
301
361
  return cache;
302
362
  }
303
363
  function createFromFile(filePath, options) {
@@ -314,6 +374,7 @@ function clearAll(cacheDirectory) {
314
374
  }
315
375
  export {
316
376
  FlatCache,
377
+ FlatCacheEvents,
317
378
  clearAll,
318
379
  clearCacheById,
319
380
  create,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flat-cache",
3
- "version": "6.0.0",
3
+ "version": "6.1.0",
4
4
  "description": "A simple key/value storage using files to persist the data",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -37,7 +37,8 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "cacheable": "^1.7.1",
40
- "flatted": "^3.3.1"
40
+ "flatted": "^3.3.1",
41
+ "hookified": "^1.1.0"
41
42
  },
42
43
  "files": [
43
44
  "dist",