From c0cf85de7073de84ba6799d5293943ad74ff482e Mon Sep 17 00:00:00 2001 From: Thijs Louisse Date: Wed, 8 May 2024 14:27:34 +0200 Subject: [PATCH] feat: improve memoize cache api and types --- .../src/program/utils/memoize.js | 77 ++++++++++++------- 1 file changed, 50 insertions(+), 27 deletions(-) diff --git a/packages-node/providence-analytics/src/program/utils/memoize.js b/packages-node/providence-analytics/src/program/utils/memoize.js index 93400223d..1fa51e526 100644 --- a/packages-node/providence-analytics/src/program/utils/memoize.js +++ b/packages-node/providence-analytics/src/program/utils/memoize.js @@ -1,6 +1,7 @@ -export const memoizeConfig = { - isCacheDisabled: false, -}; +/** + * For testing purposes, it is possible to disable caching. + */ +let shouldCache = true; /** * @param {object|any[]|string} arg @@ -13,37 +14,59 @@ function isObject(arg) { * @param {object|any[]|string} arg */ function createCachableArg(arg) { - if (isObject(arg)) { - try { - return JSON.stringify(arg); - } catch { - return arg; - } + if (!isObject(arg)) return arg; + try { + return JSON.stringify(arg); + } catch { + return arg; } - return arg; } /** + * @template T * @type {(functionToMemoize:T, opts?:{ storage?:object; serializeObjects?: boolean }) => T} */ export function memoize(functionToMemoize, { storage = {}, serializeObjects = false } = {}) { - // @ts-ignore + // @ts-expect-erro // eslint-disable-next-line func-names - return function () { - // eslint-disable-next-line prefer-rest-params - const args = [...arguments]; - const cachableArgs = !serializeObjects ? args : args.map(createCachableArg); - // Allow disabling of cache for testing purposes - // @ts-ignore - if (!memoizeConfig.isCacheDisabled && cachableArgs in storage) { - // @ts-ignore - return storage[cachableArgs]; + return /** @type {* & T} */ ( + function memoizedFn() { + // eslint-disable-next-line prefer-rest-params + const args = [...arguments]; + const cachableArgs = !serializeObjects ? args : args.map(createCachableArg); + // Allow disabling of cache for testing purposes + // @ts-expect-error + if (shouldCache && cachableArgs in storage) { + // @ts-expect-error + return storage[cachableArgs]; + } + // @ts-expect-error + const outcome = functionToMemoize.apply(this, args); + // @ts-expect-error + // eslint-disable-next-line no-param-reassign + storage[cachableArgs] = outcome; + return outcome; } - // @ts-ignore - const outcome = functionToMemoize.apply(this, args); - // @ts-ignore - // eslint-disable-next-line no-param-reassign - storage[cachableArgs] = outcome; - return outcome; - }; + ); } + +/** + * For testing purposes, it is possible to disable caching. + */ +memoize.disableCaching = () => { + shouldCache = false; +}; +/** + * Once testing is done, it is possible to restore caching. + */ +memoize.restoreCaching = initialValue => { + shouldCache = initialValue || true; +}; + +Object.defineProperty(memoize, 'isCacheEnabled', { + // writable: false, + // enumerable: true, + get() { + return shouldCache; + }, +});