UNPKG

with-simple-caching

Version:

A wrapper that makes it simple to add caching to any function

104 lines 6.58 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.withSimpleCachingAsync = exports.getOutputCacheOptionFromCacheInput = void 0; const simple_in_memory_cache_1 = require("simple-in-memory-cache"); const type_fns_1 = require("type-fns"); const getCacheFromCacheOption_1 = require("../options/getCacheFromCacheOption"); const defaults_1 = require("../serde/defaults"); const withExtendableCaching_1 = require("./withExtendableCaching"); /** * method to get the output cache option chosen by the user from the cache input */ const getOutputCacheOptionFromCacheInput = (cacheInput) => 'output' in cacheInput ? cacheInput.output : cacheInput; exports.getOutputCacheOptionFromCacheInput = getOutputCacheOptionFromCacheInput; /** * method to get the output cache option chosen by the user from the cache input */ const getDeduplicationCacheOptionFromCacheInput = (cacheInput) => 'deduplication' in cacheInput ? cacheInput.deduplication : (0, simple_in_memory_cache_1.createCache)({ expiration: { minutes: 15 }, // support deduplicating requests that take up to 15 minutes to resolve, by default (note: we remove the promise as soon as it resolves through "serialize" method below) }); /** * a wrapper which adds asynchronous caching to asynchronous logic * * note * - utilizes an additional in-memory synchronous cache under the hood to prevent duplicate requests (otherwise, while async cache is resolving, a duplicate parallel request may have be made) * - can be given a synchronous cache, since what you can do on an asynchronous cache you can do on a synchronous cache, but not the other way around */ const withSimpleCachingAsync = (logic, { cache: cacheOption, serialize: { key: serializeKey = defaults_1.defaultKeySerializationMethod, // default serialize key to JSON.stringify value: serializeValue = defaults_1.defaultValueSerializationMethod, // default serialize value to noOp } = {}, deserialize: { value: deserializeValue = defaults_1.noOp } = {}, expiration, bypass = { get: defaults_1.defaultShouldBypassGetMethod, set: defaults_1.defaultShouldBypassSetMethod, }, }) => { // add async caching to the logic const logicWithAsyncCaching = ((...args) => __awaiter(void 0, void 0, void 0, function* () { var _a, _b; // define key based on args the function was invoked with const key = serializeKey({ forInput: args }); // define cache based on options const cache = (0, getCacheFromCacheOption_1.getCacheFromCacheOption)({ forInput: args, cacheOption: (0, exports.getOutputCacheOptionFromCacheInput)(cacheOption), }); // see if its already cached const cachedValue = ((_a = bypass.get) === null || _a === void 0 ? void 0 : _a.call(bypass, args)) ? undefined : yield cache.get(key); if ((0, type_fns_1.isNotUndefined)(cachedValue)) return deserializeValue(cachedValue); // if already cached, return it immediately // if its not, grab the output from the logic const output = yield logic(...args); // if was asked to bypass cache.set, we can return the output now if ((_b = bypass.set) === null || _b === void 0 ? void 0 : _b.call(bypass, args)) return output; // set the output to the cache const serializedOutput = serializeValue(output); yield cache.set(key, serializedOutput, { expiration }); // if the output was undefined, we can just return here - no deserialization needed if (output === undefined) return output; // and now re-get from the cache, to ensure that output on first response === output on second response const cachedValueNow = yield cache.get(key); if ((0, type_fns_1.isNotUndefined)(cachedValueNow)) return deserializeValue(cachedValueNow); // otherwise, somehow, get-after-set returned undefined. warn about this and return output // eslint-disable-next-line no-console console.warn( // warn about this because it should never occur 'withSimpleCachingAsync encountered a situation which should not occur: cache.get returned undefined immediately after having been set. returning the output directly to prevent irrecoverable failure.', { key }); return output; })); // wrap the logic with extended sync caching, to ensure that duplicate requests resolve the same promise from in-memory (rather than each getting a promise to check the async cache + operate separately) const { execute, invalidate } = (0, withExtendableCaching_1.withExtendableCaching)(logicWithAsyncCaching, { cache: getDeduplicationCacheOptionFromCacheInput(cacheOption), serialize: { key: serializeKey, }, }); // define a function which the user will run which kicks off the result + invalidates the in-memory cache promise as soon as it finishes const logicWithAsyncCachingAndInMemoryRequestDeduplication = (...args) => __awaiter(void 0, void 0, void 0, function* () { // start executing the request w/ async caching + sync caching const promiseResult = execute(...args); // ensure that after the promise resolves, we remove it from the cache (so that unique subsequent requests can still be made) const promiseResultAfterInvalidation = promiseResult .finally(() => invalidate({ forInput: args })) .then(() => promiseResult); // return the result after invalidation return promiseResultAfterInvalidation; }); // return the function w/ async caching and sync-in-memory-request-deduplication return logicWithAsyncCachingAndInMemoryRequestDeduplication; }; exports.withSimpleCachingAsync = withSimpleCachingAsync; //# sourceMappingURL=withSimpleCachingAsync.js.map