From 403d8726f6167816425cd92c5f8c99e6f533e5d3 Mon Sep 17 00:00:00 2001 From: Jared Wray Date: Wed, 11 Dec 2024 14:30:52 -0800 Subject: [PATCH] flat-cache - loading the expiration from file correctly (#933) --- packages/cacheable/src/memory.ts | 26 ++++++++++++++++---- packages/cacheable/test/memory.test.ts | 33 ++++++++++++++++++++++++++ packages/flat-cache/src/index.ts | 5 ++-- packages/flat-cache/test/index.test.ts | 23 ++++++++++++++++++ 4 files changed, 81 insertions(+), 6 deletions(-) diff --git a/packages/cacheable/src/memory.ts b/packages/cacheable/src/memory.ts index 4b0000f0..8b7c1380 100644 --- a/packages/cacheable/src/memory.ts +++ b/packages/cacheable/src/memory.ts @@ -21,6 +21,11 @@ export type CacheableMemoryOptions = { checkInterval?: number; }; +export type SetOptions = { + ttl?: number | string; + expire?: number | Date; +}; + export class CacheableMemory extends Hookified { private _lru = new DoublyLinkedList(); private readonly _hashCache = new Map(); @@ -239,14 +244,27 @@ export class CacheableMemory extends Hookified { * If you set undefined, it will use the default time-to-live. If both are undefined then it will not have a time-to-live. * @returns {void} */ - public set(key: string, value: any, ttl?: number | string): void { + public set(key: string, value: any, ttl?: number | string | SetOptions): void { const store = this.getStore(key); let expires; if (ttl !== undefined || this._ttl !== undefined) { - const finalTtl = shorthandToTime(ttl ?? this._ttl); + if (typeof ttl === 'object') { + if (ttl.expire) { + expires = typeof ttl.expire === 'number' ? ttl.expire : ttl.expire.getTime(); + } - if (finalTtl !== undefined) { - expires = finalTtl; + if (ttl.ttl) { + const finalTtl = shorthandToTime(ttl.ttl); + if (finalTtl !== undefined) { + expires = finalTtl; + } + } + } else { + const finalTtl = shorthandToTime(ttl ?? this._ttl); + + if (finalTtl !== undefined) { + expires = finalTtl; + } } } diff --git a/packages/cacheable/test/memory.test.ts b/packages/cacheable/test/memory.test.ts index f742e1ff..4f218a15 100644 --- a/packages/cacheable/test/memory.test.ts +++ b/packages/cacheable/test/memory.test.ts @@ -476,4 +476,37 @@ describe('cacheable wrap', async () => { const result2 = wrapped(1); expect(result).toBe(result2); // Cached }); + + test('should be able to pass in expiration time', async () => { + const cacheable = new CacheableMemory(); + const expire = Date.now() + 100; + cacheable.set('key-expire1', 'value1', {expire}); + const result = cacheable.get('key-expire1'); + expect(result).toBe('value1'); + await sleep(150); + const result2 = cacheable.get('key-expire1'); + expect(result2).toBeUndefined(); + }); + + test('should be able to pass in ttl as object', async () => { + const cacheable = new CacheableMemory(); + const ttl = '100ms'; + cacheable.set('key-expire12', 'value1', {ttl}); + const result = cacheable.get('key-expire12'); + expect(result).toBe('value1'); + await sleep(150); + const result2 = cacheable.get('key-expire12'); + expect(result2).toBeUndefined(); + }); + + test('should be able to pass in expiration time with date', async () => { + const cacheable = new CacheableMemory(); + const expire = new Date(Date.now() + 100); + cacheable.set('key-expire2', 'value2', {expire}); + const result = cacheable.get('key-expire2'); + expect(result).toBe('value2'); + await sleep(150); + const result2 = cacheable.get('key-expire2'); + expect(result2).toBeUndefined(); + }); }); diff --git a/packages/flat-cache/src/index.ts b/packages/flat-cache/src/index.ts index 66ec5f04..cfc74719 100644 --- a/packages/flat-cache/src/index.ts +++ b/packages/flat-cache/src/index.ts @@ -181,7 +181,8 @@ export class FlatCache extends Hookified { const items = this._parse(data); // eslint-disable-next-line @typescript-eslint/no-unsafe-argument for (const key of Object.keys(items)) { - this._cache.set(key, items[key]); + console.log('key', items[key]); + this._cache.set(items[key].key as string, items[key].value, {expire: items[key].expires as number}); } this._changesSinceLastSave = true; @@ -327,7 +328,7 @@ export class FlatCache extends Hookified { try { if (this._changesSinceLastSave || force) { const filePath = this.cacheFilePath; - const items = this.all(); + const items = Array.from(this._cache.items); const data = this._stringify(items); // Ensure the directory exists diff --git a/packages/flat-cache/test/index.test.ts b/packages/flat-cache/test/index.test.ts index 89455f5f..b1cb03ae 100644 --- a/packages/flat-cache/test/index.test.ts +++ b/packages/flat-cache/test/index.test.ts @@ -200,6 +200,29 @@ describe('flat-cache load from persisted cache', () => { expect(secondCache.getKey('baz')).toEqual([1, 2, 3]); firstCache.destroy(true); }); + + test('should load the cache from the file with expiration', async () => { + // eslint-disable-next-line unicorn/prevent-abbreviations + const cacheDir = '.cachefoo3'; + const cacheId = 'cache4'; + const firstCache = new FlatCache({cacheDir, cacheId}); + firstCache.setKey('foo', 'bar', 250); + firstCache.setKey('bar', {foo: 'bar'}, 500); + firstCache.setKey('baz', [1, 2, 3]); + firstCache.save(); + const secondCache = new FlatCache({cacheDir}); + secondCache.load(cacheId); + expect(secondCache.getKey('foo')).toBe('bar'); + expect(secondCache.getKey('bar')).toEqual({foo: 'bar'}); + expect(secondCache.getKey('baz')).toEqual([1, 2, 3]); + await sleep(300); + expect(secondCache.getKey('foo')).toBeUndefined(); + expect(secondCache.getKey('bar')).toEqual({foo: 'bar'}); + expect(secondCache.getKey('baz')).toEqual([1, 2, 3]); + await sleep(300); + expect(secondCache.getKey('bar')).toBeUndefined(); + firstCache.destroy(true); + }); }); describe('flat-cache exported functions', () => {