flat-cache 6.0.0 → 6.1.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
@@ -1,4 +1,4 @@
1
- [<img align="center" src="https://cacheable.org/logo.svg" alt="Cacheable" />](https://github.com/jaredwray/cacheable)
1
+ [<img align="center" src="https://cacheable.org/symbol.svg" alt="Cacheable" />](https://github.com/jaredwray/cacheable)
2
2
 
3
3
  # flat-cache
4
4
  > A simple key/value storage using files to persist the data
@@ -7,16 +7,30 @@
7
7
  [![tests](https://github.com/jaredwray/cacheable/actions/workflows/tests.yml/badge.svg)](https://github.com/jaredwray/cacheable/actions/workflows/tests.yml)
8
8
  [![npm](https://img.shields.io/npm/dm/flat-cache.svg)](https://www.npmjs.com/package/flat-cache)
9
9
  [![npm](https://img.shields.io/npm/v/flat-cache)](https://www.npmjs.com/package/flat-cache)
10
- [![GitHub](https://img.shields.io/github/license/jaredwray/cacheable)](https://github.com/jaredwray/cacheable/blob/main/LICENSE)
10
+ [![license](https://img.shields.io/github/license/jaredwray/cacheable)](https://github.com/jaredwray/cacheable/blob/main/LICENSE)
11
11
 
12
12
  # Features
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
+ - 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`
17
+ - Easily Loads the data from disk and into memory with `load` or `loadFile`
17
18
  - Uses `ttl` and `lruSize` to manage the cache and persist the data
18
19
  - 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
20
+ - Uses `flatted` to parse and stringify the data by default but can be overridden using `parse` and `stringify` in options
21
+ - ESM and CommonJS support with TypeScript typings and maintained regularly!
22
+
23
+ # Table of Contents
24
+ - [Installation](#installation)
25
+ - [Getting Started](#getting-started)
26
+ - [Breaking Changes from v5 to v6](#breaking-changes-from-v5-to-v6)
27
+ - [Global Functions](#global-functions)
28
+ - [FlatCache Options (FlatCacheOptions)](#flatcache-options-flatcacheoptions)
29
+ - [API](#api)
30
+ - [Events (FlatCacheEvents)](#events-flatcacheevents)
31
+ - [Parse and Stringify for File Caching](#parse-and-stringify-for-file-caching)
32
+ - [How to Contribute](#how-to-contribute)
33
+ - [License and Copyright](#license-and-copyright)
20
34
 
21
35
  # Installation
22
36
  ```bash
@@ -43,16 +57,70 @@ const cache = new FlatCache({
43
57
  cache.setKey('key', 'value');
44
58
  ```
45
59
 
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.
60
+ 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.
61
+
62
+ here is an example doing load from already existing persisted cache
63
+
64
+ ```javascript
65
+ import { load } from 'flat-cache';
66
+ const cache = load('cache1', './cacheAltDirectory');
67
+ ```
68
+
69
+ 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.
70
+
71
+ # Breaking Changes from v5 to v6
72
+
73
+ There have been many features added and changes made to the `FlatCache` class. Here are the main changes:
74
+ - `FlatCache` is now a class and not a function which you can create instances of or using legacy method `load`, `loadFile`, or `create`
75
+ - `FlatCache` now uses `CacheableMemory` as the primary storage and then persists the data to disk
76
+ - `FlatCache` now uses `ttl` and `lruSize` to manage the cache and persist the data
77
+ - `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`
78
+ - `getKey` still exists but is now is an alias to `get` and will be removed in the future
79
+ - `setKey` still exists but is now is an alias to `set` and will be removed in the future
80
+ - `removeKey` still exists but is now is an alias to `delete` and will be removed in the future
81
+
82
+ Here is an example of the legacy method `load`:
83
+ ```javascript
84
+ const flatCache = require('flat-cache');
85
+ // loads the cache, if one does not exists for the given
86
+ // Id a new one will be prepared to be created
87
+ const cache = flatCache.load('cacheId');
88
+ ```
89
+
90
+ Now you can use the `load` method and ES6 imports:
91
+ ```javascript
92
+ import { FlatCache } from 'flat-cache';
93
+ const cache = new FlatCache();
94
+ cache.load('cacheId');
95
+ ```
96
+ 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`.
97
+
98
+ If you want to create a new cache and load from disk if it exists you can use the `create` method:
99
+ ```javascript
100
+ import { create } from 'flat-cache';
101
+ const cache = create({ cacheId: 'myCacheId', cacheDir: './mycache', ttl: 60 * 60 * 1000 });
102
+ ```
103
+
104
+ # Global Functions
105
+
106
+ In version 6 we attempted to keep as much as the functionality as possible which includes these functions:
107
+
108
+ - `create(options?: FlatCacheOptions)` - Creates a new cache and will load the data from disk if it exists
109
+ - `createFromFile(filePath, options?: FlatCacheOptions)` - Creates a new cache from a file
110
+ - `clearByCacheId(cacheId: string, cacheDir?: string)` - Clears the cache by the cacheId
111
+ - `clearAll(cacheDirectory?: string)` - Clears all the caches
47
112
 
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`
113
+
114
+ # FlatCache Options (FlatCacheOptions)
115
+ - `ttl?` - The time to live for the cache in milliseconds. Default is `0` which means no expiration
116
+ - `lruSize?` - The number of items to keep in the cache. Default is `0` which means no limit
117
+ - `useClone?` - If `true` it will clone the data before returning it. Default is `false`
118
+ - `expirationInterval?` - The interval to check for expired items in the cache. Default is `0` which means no expiration
119
+ - `persistInterval?` - The interval to save the data to disk. Default is `0` which means no persistence
120
+ - `cacheDir?` - The directory to save the cache files. Default is `./cache`
121
+ - `cacheId?` - The id of the cache. Default is `cache1`
122
+ - `serialize?` - The function to parse the data. Default is `flatted.parse`
123
+ - `deserialize?` - The function to stringify the data. Default is `flatted.stringify`
56
124
 
57
125
  # API
58
126
 
@@ -62,6 +130,7 @@ This will save the data to disk every 5 minutes and will remove any data that ha
62
130
  - `cacheFilePath` - The full path to the cache file
63
131
  - `cacheDirPath` - The full path to the cache directory
64
132
  - `persistInterval` - The interval to save the data to disk
133
+ - `changesSinceLastSave` - If there have been changes since the last save
65
134
  - `load(cacheId: string, cacheDir?: string)` - Loads the data from disk
66
135
  - `loadFile(pathToFile: string)` - Loads the data from disk
67
136
  - `all()` - Gets all the data in the cache
@@ -74,9 +143,48 @@ This will save the data to disk every 5 minutes and will remove any data that ha
74
143
  - `removeKey(key: string)` - Removes the key from the cache
75
144
  - `delete(key: string)` - Removes the key from the cache
76
145
  - `clear()` - Clears the cache
77
- - `save()` - Saves the data to disk
146
+ - `save(force? boolean)` - Saves the data to disk. If `force` is `true` it will save even if `changesSinceLastSave` is `false`
78
147
  - `destroy()` - Destroys the cache and remove files
79
148
 
149
+ # Events (FlatCacheEvents)
150
+
151
+ Events have been added since v6 to allow for more control and visibility into the cache. Here are the events that are available:
152
+
153
+ - `on(event: 'save', listener: () => void)` - Emitted when the cache is saved
154
+ - `on(event: 'load', listener: () => void)` - Emitted when the cache is loaded
155
+ - `on(event: 'delete', listener: (key: string) => void)` - Emitted when the cache is changed
156
+ - `on(event: 'clear', listener: () => void)` - Emitted when the cache is cleared
157
+ - `on(event: 'destroy', listener: () => void)` - Emitted when the cache is destroyed
158
+ - `on(event: 'error', listener: (error: Error) => void)` - Emitted when there is an error
159
+
160
+ Here is an example of how to use the `error` events:
161
+
162
+ ```javascript
163
+ import { FlatCache, FlatCacheEvents } from 'flat-cache';
164
+ const cache = new FlatCache();
165
+ cache.on(FlatCacheEvents.error, (error) => {
166
+ console.error(error);
167
+ });
168
+ ```
169
+
170
+ `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.
171
+
172
+ # Parse and Stringify for File Caching
173
+
174
+ 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:
175
+
176
+ ```javascript
177
+ import { FlatCache } from 'flat-cache';
178
+ const cache = new FlatCache({
179
+ parse: JSON.parse,
180
+ stringify: JSON.stringify,
181
+ });
182
+ ```
183
+
184
+ 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.
185
+
186
+ **NOTE: This could cause issues if you are trying to load data that was saved with a different parser or stringifier.**
187
+
80
188
  # How to Contribute
81
189
 
82
190
  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,23 +31,40 @@ 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,
37
- createFromFile: () => createFromFile
38
+ createFromFile: () => createFromFile,
39
+ default: () => FlatCacheDefault
38
40
  });
39
41
  module.exports = __toCommonJS(src_exports);
40
42
  var import_node_path = __toESM(require("path"), 1);
41
43
  var import_node_fs = __toESM(require("fs"), 1);
42
44
  var import_cacheable = require("cacheable");
43
45
  var import_flatted = require("flatted");
44
- var FlatCache = class {
46
+ var import_hookified = require("hookified");
47
+ var FlatCacheEvents = /* @__PURE__ */ ((FlatCacheEvents2) => {
48
+ FlatCacheEvents2["SAVE"] = "save";
49
+ FlatCacheEvents2["LOAD"] = "load";
50
+ FlatCacheEvents2["DELETE"] = "delete";
51
+ FlatCacheEvents2["CLEAR"] = "clear";
52
+ FlatCacheEvents2["DESTROY"] = "destroy";
53
+ FlatCacheEvents2["ERROR"] = "error";
54
+ FlatCacheEvents2["EXPIRED"] = "expired";
55
+ return FlatCacheEvents2;
56
+ })(FlatCacheEvents || {});
57
+ var FlatCache = class extends import_hookified.Hookified {
45
58
  _cache = new import_cacheable.CacheableMemory();
46
59
  _cacheDir = ".cache";
47
60
  _cacheId = "cache1";
48
61
  _persistInterval = 0;
49
62
  _persistTimer;
63
+ _changesSinceLastSave = false;
64
+ _parse = import_flatted.parse;
65
+ _stringify = import_flatted.stringify;
50
66
  constructor(options) {
67
+ super();
51
68
  if (options) {
52
69
  this._cache = new import_cacheable.CacheableMemory({
53
70
  ttl: options.ttl,
@@ -66,6 +83,12 @@ var FlatCache = class {
66
83
  this._persistInterval = options.persistInterval;
67
84
  this.startAutoPersist();
68
85
  }
86
+ if (options?.deserialize) {
87
+ this._parse = options.deserialize;
88
+ }
89
+ if (options?.serialize) {
90
+ this._stringify = options.serialize;
91
+ }
69
92
  }
70
93
  /**
71
94
  * The cache object
@@ -111,6 +134,15 @@ var FlatCache = class {
111
134
  set cacheId(value) {
112
135
  this._cacheId = value;
113
136
  }
137
+ /**
138
+ * The flag to indicate if there are changes since the last save
139
+ * @property changesSinceLastSave
140
+ * @type {Boolean}
141
+ * @default false
142
+ */
143
+ get changesSinceLastSave() {
144
+ return this._changesSinceLastSave;
145
+ }
114
146
  /**
115
147
  * The interval to persist the cache to disk. 0 means no timed persistence
116
148
  * @property persistInterval
@@ -135,13 +167,18 @@ var FlatCache = class {
135
167
  * then the cache module directory `.cacheDir` will be used instead
136
168
  *
137
169
  * @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
170
+ * @param cacheId {String} the id of the cache, would also be used as the name of the file cache
171
+ * @param cacheDir {String} directory for the cache entry
140
172
  */
141
173
  // 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);
174
+ load(cacheId, cacheDir) {
175
+ try {
176
+ const filePath = import_node_path.default.resolve(`${cacheDir ?? this._cacheDir}/${cacheId ?? this._cacheId}`);
177
+ this.loadFile(filePath);
178
+ this.emit("load" /* LOAD */);
179
+ } catch (error) {
180
+ this.emit("error" /* ERROR */, error);
181
+ }
145
182
  }
146
183
  /**
147
184
  * Load the cache from the provided file
@@ -151,10 +188,11 @@ var FlatCache = class {
151
188
  loadFile(pathToFile) {
152
189
  if (import_node_fs.default.existsSync(pathToFile)) {
153
190
  const data = import_node_fs.default.readFileSync(pathToFile, "utf8");
154
- const items = (0, import_flatted.parse)(data);
191
+ const items = this._parse(data);
155
192
  for (const key of Object.keys(items)) {
156
193
  this._cache.set(key, items[key]);
157
194
  }
195
+ this._changesSinceLastSave = true;
158
196
  }
159
197
  }
160
198
  /**
@@ -209,7 +247,7 @@ var FlatCache = class {
209
247
  * @param value {object} the value of the key. Could be any object that can be serialized with JSON.stringify
210
248
  */
211
249
  setKey(key, value, ttl) {
212
- this._cache.set(key, value, ttl);
250
+ this.set(key, value, ttl);
213
251
  }
214
252
  /**
215
253
  * Sets a key to a given value
@@ -220,6 +258,7 @@ var FlatCache = class {
220
258
  */
221
259
  set(key, value, ttl) {
222
260
  this._cache.set(key, value, ttl);
261
+ this._changesSinceLastSave = true;
223
262
  }
224
263
  /**
225
264
  * (Legacy) Remove a given key from the cache. This method will be deprecated in the future
@@ -227,7 +266,7 @@ var FlatCache = class {
227
266
  * @param key {String} the key to remove from the object
228
267
  */
229
268
  removeKey(key) {
230
- this._cache.delete(key);
269
+ this.delete(key);
231
270
  }
232
271
  /**
233
272
  * Remove a given key from the cache
@@ -236,6 +275,8 @@ var FlatCache = class {
236
275
  */
237
276
  delete(key) {
238
277
  this._cache.delete(key);
278
+ this._changesSinceLastSave = true;
279
+ this.emit("delete" /* DELETE */, key);
239
280
  }
240
281
  /**
241
282
  * (Legacy) Return the value of the provided key. This method will be deprecated in the future
@@ -256,25 +297,40 @@ var FlatCache = class {
256
297
  return this._cache.get(key);
257
298
  }
258
299
  /**
259
- * Clear the cache
300
+ * Clear the cache and save the state to disk
260
301
  * @method clear
261
302
  */
262
303
  clear() {
263
- this._cache.clear();
304
+ try {
305
+ this._cache.clear();
306
+ this._changesSinceLastSave = true;
307
+ this.save();
308
+ this.emit("clear" /* CLEAR */);
309
+ } catch (error) {
310
+ this.emit("error" /* ERROR */, error);
311
+ }
264
312
  }
265
313
  /**
266
314
  * Save the state of the cache identified by the docId to disk
267
315
  * as a JSON structure
268
316
  * @method save
269
317
  */
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 });
318
+ save(force = false) {
319
+ try {
320
+ if (this._changesSinceLastSave || force) {
321
+ const filePath = this.cacheFilePath;
322
+ const items = this.all();
323
+ const data = this._stringify(items);
324
+ if (!import_node_fs.default.existsSync(this._cacheDir)) {
325
+ import_node_fs.default.mkdirSync(this._cacheDir, { recursive: true });
326
+ }
327
+ import_node_fs.default.writeFileSync(filePath, data);
328
+ this._changesSinceLastSave = false;
329
+ this.emit("save" /* SAVE */);
330
+ }
331
+ } catch (error) {
332
+ this.emit("error" /* ERROR */, error);
276
333
  }
277
- import_node_fs.default.writeFileSync(filePath, data);
278
334
  }
279
335
  /**
280
336
  * Remove the file where the cache is persisted
@@ -282,9 +338,13 @@ var FlatCache = class {
282
338
  * @return {Boolean} true or false if the file was successfully deleted
283
339
  */
284
340
  removeCacheFile() {
285
- if (import_node_fs.default.existsSync(this.cacheFilePath)) {
286
- import_node_fs.default.rmSync(this.cacheFilePath);
287
- return true;
341
+ try {
342
+ if (import_node_fs.default.existsSync(this.cacheFilePath)) {
343
+ import_node_fs.default.rmSync(this.cacheFilePath);
344
+ return true;
345
+ }
346
+ } catch (error) {
347
+ this.emit("error" /* ERROR */, error);
288
348
  }
289
349
  return false;
290
350
  }
@@ -295,12 +355,18 @@ var FlatCache = class {
295
355
  * @return {undefined}
296
356
  */
297
357
  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 });
358
+ try {
359
+ this._cache.clear();
360
+ this.stopAutoPersist();
361
+ if (includeCacheDirectory) {
362
+ import_node_fs.default.rmSync(this.cacheDirPath, { recursive: true, force: true });
363
+ } else {
364
+ import_node_fs.default.rmSync(this.cacheFilePath, { recursive: true, force: true });
365
+ }
366
+ this._changesSinceLastSave = false;
367
+ this.emit("destroy" /* DESTROY */);
368
+ } catch (error) {
369
+ this.emit("error" /* ERROR */, error);
304
370
  }
305
371
  }
306
372
  /**
@@ -329,13 +395,15 @@ var FlatCache = class {
329
395
  }
330
396
  }
331
397
  };
332
- function create(documentId, cacheDirectory, options) {
398
+ var FlatCacheDefault = class {
399
+ static create = create;
400
+ static createFromFile = createFromFile;
401
+ static clearCacheById = clearCacheById;
402
+ static clearAll = clearAll;
403
+ };
404
+ function create(options) {
333
405
  const cache = new FlatCache(options);
334
- cache.cacheId = documentId;
335
- if (cacheDirectory) {
336
- cache.cacheDir = cacheDirectory;
337
- }
338
- cache.load(documentId, cacheDirectory);
406
+ cache.load();
339
407
  return cache;
340
408
  }
341
409
  function createFromFile(filePath, options) {
@@ -353,6 +421,7 @@ function clearAll(cacheDirectory) {
353
421
  // Annotate the CommonJS export names for ESM import in node:
354
422
  0 && (module.exports = {
355
423
  FlatCache,
424
+ FlatCacheEvents,
356
425
  clearAll,
357
426
  clearCacheById,
358
427
  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
+ deserialize?: (data: string) => any;
13
+ serialize?: (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
@@ -190,6 +212,12 @@ declare class FlatCache {
190
212
  */
191
213
  stopAutoPersist(): void;
192
214
  }
215
+ declare class FlatCacheDefault {
216
+ static create: typeof create;
217
+ static createFromFile: typeof createFromFile;
218
+ static clearCacheById: typeof clearCacheById;
219
+ static clearAll: typeof clearAll;
220
+ }
193
221
  /**
194
222
  * Load a cache identified by the given Id. If the element does not exists, then initialize an empty
195
223
  * cache storage.
@@ -200,7 +228,7 @@ declare class FlatCache {
200
228
  * @param options {FlatCacheOptions} options for the cache
201
229
  * @returns {cache} cache instance
202
230
  */
203
- declare function create(documentId: string, cacheDirectory?: string, options?: FlatCacheOptions): FlatCache;
231
+ declare function create(options?: FlatCacheOptions): FlatCache;
204
232
  /**
205
233
  * Load a cache from the provided file
206
234
  * @method createFromFile
@@ -223,4 +251,4 @@ declare function clearCacheById(cacheId: string, cacheDirectory?: string): void;
223
251
  */
224
252
  declare function clearAll(cacheDirectory?: string): void;
225
253
 
226
- export { FlatCache, type FlatCacheOptions, clearAll, clearCacheById, create, createFromFile };
254
+ export { FlatCache, FlatCacheEvents, type FlatCacheOptions, clearAll, clearCacheById, create, createFromFile, FlatCacheDefault as default };
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
+ deserialize?: (data: string) => any;
13
+ serialize?: (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
@@ -190,6 +212,12 @@ declare class FlatCache {
190
212
  */
191
213
  stopAutoPersist(): void;
192
214
  }
215
+ declare class FlatCacheDefault {
216
+ static create: typeof create;
217
+ static createFromFile: typeof createFromFile;
218
+ static clearCacheById: typeof clearCacheById;
219
+ static clearAll: typeof clearAll;
220
+ }
193
221
  /**
194
222
  * Load a cache identified by the given Id. If the element does not exists, then initialize an empty
195
223
  * cache storage.
@@ -200,7 +228,7 @@ declare class FlatCache {
200
228
  * @param options {FlatCacheOptions} options for the cache
201
229
  * @returns {cache} cache instance
202
230
  */
203
- declare function create(documentId: string, cacheDirectory?: string, options?: FlatCacheOptions): FlatCache;
231
+ declare function create(options?: FlatCacheOptions): FlatCache;
204
232
  /**
205
233
  * Load a cache from the provided file
206
234
  * @method createFromFile
@@ -223,4 +251,4 @@ declare function clearCacheById(cacheId: string, cacheDirectory?: string): void;
223
251
  */
224
252
  declare function clearAll(cacheDirectory?: string): void;
225
253
 
226
- export { FlatCache, type FlatCacheOptions, clearAll, clearCacheById, create, createFromFile };
254
+ export { FlatCache, FlatCacheEvents, type FlatCacheOptions, clearAll, clearCacheById, create, createFromFile, FlatCacheDefault as default };
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?.deserialize) {
47
+ this._parse = options.deserialize;
48
+ }
49
+ if (options?.serialize) {
50
+ this._stringify = options.serialize;
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,15 @@ var FlatCache = class {
291
355
  }
292
356
  }
293
357
  };
294
- function create(documentId, cacheDirectory, options) {
358
+ var FlatCacheDefault = class {
359
+ static create = create;
360
+ static createFromFile = createFromFile;
361
+ static clearCacheById = clearCacheById;
362
+ static clearAll = clearAll;
363
+ };
364
+ function create(options) {
295
365
  const cache = new FlatCache(options);
296
- cache.cacheId = documentId;
297
- if (cacheDirectory) {
298
- cache.cacheDir = cacheDirectory;
299
- }
300
- cache.load(documentId, cacheDirectory);
366
+ cache.load();
301
367
  return cache;
302
368
  }
303
369
  function createFromFile(filePath, options) {
@@ -314,8 +380,10 @@ function clearAll(cacheDirectory) {
314
380
  }
315
381
  export {
316
382
  FlatCache,
383
+ FlatCacheEvents,
317
384
  clearAll,
318
385
  clearCacheById,
319
386
  create,
320
- createFromFile
387
+ createFromFile,
388
+ FlatCacheDefault as default
321
389
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flat-cache",
3
- "version": "6.0.0",
3
+ "version": "6.1.1",
4
4
  "description": "A simple key/value storage using files to persist the data",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -12,32 +12,58 @@
12
12
  "import": "./dist/index.js"
13
13
  }
14
14
  },
15
- "repository": "https://github.com/jaredwray/cacheable.git",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/jaredwray/cacheable.git",
18
+ "directory": "packages/flat-cache"
19
+ },
16
20
  "author": "Jared Wray <me@jaredwray.com>",
17
21
  "license": "MIT",
18
22
  "private": false,
19
23
  "keywords": [
20
24
  "cache",
21
25
  "caching",
22
- "node",
23
- "nodejs",
24
26
  "cacheable",
25
- "cacheable-node-cache",
26
- "node-cache",
27
- "cacheable-node"
27
+ "flat-cache",
28
+ "flat",
29
+ "file",
30
+ "file-cache",
31
+ "file-caching",
32
+ "file-based-cache",
33
+ "file-persist",
34
+ "file-persistence",
35
+ "file-storage",
36
+ "file-system",
37
+ "file-management",
38
+ "filesystem-cache",
39
+ "disk-cache",
40
+ "cache-persistence",
41
+ "cache-persist",
42
+ "persistent-cache",
43
+ "persistent-storage",
44
+ "cache-to-file",
45
+ "cache-on-disk",
46
+ "cache-file",
47
+ "cache-expiration",
48
+ "cache-lifetime",
49
+ "data-persistence",
50
+ "data-storage",
51
+ "local-storage",
52
+ "file-system-cache"
28
53
  ],
29
54
  "devDependencies": {
30
- "@types/node": "^22.7.4",
31
- "@vitest/coverage-v8": "^2.1.1",
55
+ "@types/node": "^22.7.8",
56
+ "@vitest/coverage-v8": "^2.1.3",
32
57
  "rimraf": "^6.0.1",
33
58
  "tsup": "^8.3.0",
34
- "typescript": "^5.6.2",
35
- "vitest": "^2.1.1",
59
+ "typescript": "^5.6.3",
60
+ "vitest": "^2.1.3",
36
61
  "xo": "^0.59.3"
37
62
  },
38
63
  "dependencies": {
39
- "cacheable": "^1.7.1",
40
- "flatted": "^3.3.1"
64
+ "cacheable": "^1.8.1",
65
+ "flatted": "^3.3.1",
66
+ "hookified": "^1.4.0"
41
67
  },
42
68
  "files": [
43
69
  "dist",