diff --git a/src/__tests__/__snapshots__/server.caching.test.ts.snap b/src/__tests__/__snapshots__/server.caching.test.ts.snap index cd7b8e9..7dadd6f 100644 --- a/src/__tests__/__snapshots__/server.caching.test.ts.snap +++ b/src/__tests__/__snapshots__/server.caching.test.ts.snap @@ -720,6 +720,124 @@ exports[`memo should fire "onCacheRollout" callback on cache rollout, one entry ] `; +exports[`memo should memoize a function, allow custom key hashing: async 1`] = ` +[ + { + "cacheKeys": [ + "custom-hash-", + ], + "cacheLength": 2, + "errorValue": undefined, + "successValue": "[EMPTY]", + "type": "memo promise", + }, + { + "cacheKeys": [ + "custom-hash-", + ], + "cacheLength": 2, + "errorValue": undefined, + "successValue": "[EMPTY]", + "type": "memo promise", + }, + { + "cacheKeys": [ + "custom-hash-1", + ], + "cacheLength": 2, + "errorValue": undefined, + "successValue": "1", + "type": "memo promise", + }, + { + "cacheKeys": [ + "custom-hash-1", + ], + "cacheLength": 2, + "errorValue": undefined, + "successValue": "1", + "type": "memo promise", + }, + { + "cacheKeys": [ + "custom-hash-2,true", + ], + "cacheLength": 2, + "errorValue": "2", + "successValue": undefined, + "type": "memo promise", + }, + { + "cacheKeys": [ + "custom-hash-2,true", + ], + "cacheLength": 2, + "errorValue": "2", + "successValue": undefined, + "type": "memo promise", + }, +] +`; + +exports[`memo should memoize a function, allow custom key hashing: sync 1`] = ` +[ + { + "cacheKeys": [ + "custom-hash-", + ], + "cacheLength": 2, + "errorValue": undefined, + "successValue": "[EMPTY]", + "type": "memo", + }, + { + "cacheKeys": [ + "custom-hash-", + ], + "cacheLength": 2, + "errorValue": undefined, + "successValue": "[EMPTY]", + "type": "memo", + }, + { + "cacheKeys": [ + "custom-hash-1", + ], + "cacheLength": 2, + "errorValue": undefined, + "successValue": "1", + "type": "memo", + }, + { + "cacheKeys": [ + "custom-hash-1", + ], + "cacheLength": 2, + "errorValue": undefined, + "successValue": "1", + "type": "memo", + }, + { + "cacheKeys": [ + "custom-hash-2,true", + ], + "cacheLength": 2, + "errorValue": "2", + "successValue": undefined, + "type": "memo error", + }, + { + "cacheKeys": [ + "custom-hash-2,true", + ], + "cacheLength": 2, + "errorValue": "2", + "successValue": undefined, + "type": "memo error", + }, +] +`; + exports[`memo should memoize a function, cacheLimit: async 1`] = ` [ { diff --git a/src/__tests__/server.caching.test.ts b/src/__tests__/server.caching.test.ts index e4246af..22292c7 100644 --- a/src/__tests__/server.caching.test.ts +++ b/src/__tests__/server.caching.test.ts @@ -34,6 +34,13 @@ describe('memo', () => { description: 'disable memoization when cacheLimit is zero', options: { cacheLimit: 0 }, params: [[], [], [1], [1], [2, true], [2, true]] + }, + { + description: 'allow custom key hashing', + options: { + keyHash: (value: unknown) => `custom-hash-${value}` + }, + params: [[], [], [1], [1], [2, true], [2, true]] } ])('should memoize a function, $description', async ({ options, params }) => { const log: any[] = []; diff --git a/src/server.caching.ts b/src/server.caching.ts index d27f586..151755a 100644 --- a/src/server.caching.ts +++ b/src/server.caching.ts @@ -56,6 +56,7 @@ type MemoDebugHandler = (info: { type: string; value: unknown * @property [cacheLimit] Number of entries to cache before overwriting previous entries (default: 1) * @property {MemoDebugHandler} [debug] Debug callback function * @property [expire] Expandable milliseconds until cache expires + * @property [keyHash] Function to generate a predictable hash key from the provided arguments. Defaults to internal `generateHash`. * @property {OnMemoCacheHandler} [onCacheExpire] Callback when cache expires. Only fires when the `expire` option is set. * @property {OnMemoCacheHandler} [onCacheRollout] Callback when cache entries are rolled off due to cache limit. */ @@ -64,6 +65,7 @@ interface MemoOptions { cacheLimit?: number; debug?: MemoDebugHandler; expire?: number; + keyHash?: (args: unknown[]) => unknown; onCacheExpire?: OnMemoCacheHandler; onCacheRollout?: OnMemoCacheHandler; } @@ -93,6 +95,7 @@ const memo = ( cacheLimit = 1, debug = () => {}, expire, + keyHash = generateHash, onCacheExpire, onCacheRollout }: MemoOptions = {} @@ -104,6 +107,9 @@ const memo = ( const isOnCacheRolloutPromise = isPromise(onCacheRollout); const isOnCacheRollout = typeof onCacheRollout === 'function' || isOnCacheRolloutPromise; const updatedExpire = Number.parseInt(String(expire), 10) || undefined; + const setKey = function (value: unknown[]): unknown { + return keyHash.call(null, value); + }; const ized = function () { const cache: MemoCache = []; @@ -159,7 +165,7 @@ const memo = ( return bypassValue; } - const key = generateHash(args); + const key = setKey(args); // Parse, memoize and return the original value if (cache.indexOf(key) < 0) {