UNPKG

@midwayjs/cache-manager

Version:
197 lines (196 loc) 7.67 kB
"use strict"; /** * fork from https://github.com/node-cache-manager/node-cache-manager/tree/master * reason: Support node.js v18 version below and add some features * for example: add methodWrap */ Object.defineProperty(exports, "__esModule", { value: true }); exports.multiCaching = exports.createCache = exports.caching = void 0; const prmoiseCoalesce_1 = require("./prmoiseCoalesce"); const store_1 = require("./store"); /** * Generic caching interface that wraps any caching library with a compatible interface. */ async function caching(factory, args) { if (factory === 'memory') { const store = (0, store_1.memoryStore)(args); return createCache(store, args); } if (typeof factory === 'function') { const store = await factory(args); return createCache(store, args); } return createCache(factory, args); } exports.caching = caching; /** * Create cache instance by store (non-async). */ function createCache(store, args) { return { /** * Wraps a function in cache. I.e., the first time the function is run, * its results are stored in cache so subsequent calls retrieve from cache * instead of calling the function. * @example * const result = await cache.wrap('key', () => Promise.resolve(1)); * */ wrap: async (key, fn, ttl) => { return (0, prmoiseCoalesce_1.coalesceAsync)(key, async () => { const value = await store.get(key); if (value === undefined) { const result = await fn(); const cacheTTL = typeof ttl === 'function' ? ttl(result) : ttl; await store.set(key, result, cacheTTL); return result; } else if (args === null || args === void 0 ? void 0 : args.refreshThreshold) { const cacheTTL = typeof ttl === 'function' ? ttl(value) : ttl; const remainingTtl = await store.ttl(key); if (remainingTtl !== -1 && remainingTtl < args.refreshThreshold) { (0, prmoiseCoalesce_1.coalesceAsync)(`+++${key}`, fn).then(result => store.set(key, result, cacheTTL)); } } return value; }); }, store: store, del: (key) => store.del(key), get: (key) => store.get(key), set: (key, value, ttl) => store.set(key, value, ttl), reset: () => store.reset(), /** * cache function with args */ methodWrap: async (key, fn, fnArgs, ttl) => { return (0, prmoiseCoalesce_1.coalesceAsync)(key, async () => { const value = await store.get(key); if (value === undefined) { const result = await fn(...fnArgs); const cacheTTL = typeof ttl === 'function' ? ttl(result) : ttl; await store.set(key, result, cacheTTL); return result; } else if (args === null || args === void 0 ? void 0 : args.refreshThreshold) { const cacheTTL = typeof ttl === 'function' ? ttl(value) : ttl; const remainingTtl = await store.ttl(key); if (remainingTtl !== -1 && remainingTtl < args.refreshThreshold) { // fn(...fnArgs).then(result => store.set(key, result, cacheTTL)); (0, prmoiseCoalesce_1.coalesceAsync)(`+++${key}`, () => fn(...fnArgs)).then(result => store.set(key, result, cacheTTL)); } } return value; }); }, }; } exports.createCache = createCache; /** * Module that lets you specify a hierarchy of caches. */ function multiCaching(caches) { const get = async (key) => { for (const cache of caches) { try { const val = await cache.get(key); if (val !== undefined) return val; } catch (e) { // ignore } } }; const set = async (key, data, ttl) => { await Promise.all(caches.map(cache => cache.set(key, data, ttl))); }; return { get, set, del: async (key) => { await Promise.all(caches.map(cache => cache.del(key))); }, async wrap(key, fn, ttl) { let value; let i = 0; for (; i < caches.length; i++) { try { value = await caches[i].get(key); if (value !== undefined) break; } catch (e) { // ignore } } if (value === undefined) { const result = await fn(); const cacheTTL = typeof ttl === 'function' ? ttl(result) : ttl; await set(key, result, cacheTTL); return result; } else { const cacheTTL = typeof ttl === 'function' ? ttl(value) : ttl; Promise.all(caches.slice(0, i).map(cache => cache.set(key, value, cacheTTL))).then(); caches[i].wrap(key, fn, ttl).then(); // call wrap for store for internal refreshThreshold logic, see: src/caching.ts caching.wrap } return value; }, reset: async () => { await Promise.all(caches.map(x => x.reset())); }, mget: async (...keys) => { const values = new Array(keys.length).fill(undefined); for (const cache of caches) { if (values.every(x => x !== undefined)) break; try { const val = await cache.store.mget(...keys); val.forEach((v, i) => { if (values[i] === undefined && v !== undefined) values[i] = v; }); } catch (e) { // ignore } } return values; }, mset: async (args, ttl) => { await Promise.all(caches.map(cache => cache.store.mset(args, ttl))); }, mdel: async (...keys) => { await Promise.all(caches.map(cache => cache.store.mdel(...keys))); }, methodWrap: async (key, fn, fnArgs, ttl) => { let value; let i = 0; for (; i < caches.length; i++) { try { value = await caches[i].get(key); if (value !== undefined) break; } catch (e) { // ignore } } if (value === undefined) { const result = await fn(...fnArgs); const cacheTTL = typeof ttl === 'function' ? ttl(result) : ttl; await set(key, result, cacheTTL); return result; } else { const cacheTTL = typeof ttl === 'function' ? ttl(value) : ttl; Promise.all(caches.slice(0, i).map(cache => cache.set(key, value, cacheTTL))).then(); caches[i].methodWrap(key, fn, fnArgs, ttl).then(); // call wrap for store for internal refreshThreshold logic, see: src/caching.ts caching.wrap } return value; }, }; } exports.multiCaching = multiCaching; //# sourceMappingURL=cacheManager.js.map