UNPKG

cache-memory

Version:
263 lines 9.63 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const logger_1 = require("./logger"); const clone = require('clone'); let globalId = 0; function default_1(options) { const cacherOptions = options || {}; // @ts-ignore const self = this; self.id = options.id; if (!self.id) { globalId++; self.id = globalId; } self.id = self.id.toString(); self.ttl = options.ttl || 0; self.cachedData = {}; self.storeUndefinedObjects = (cacherOptions.storeUndefinedObjects !== undefined && cacherOptions.storeUndefinedObjects === true); self.clone = (cacherOptions.clone !== undefined && cacherOptions.clone === true); self.hits = 0; self.misses = 0; self.hitCallback = undefined; self.missCallback = undefined; self.addedCallback = undefined; self.removedCallback = undefined; self.countCallback = undefined; const isFunction = (argument) => argument && typeof argument === 'function'; if (isFunction(cacherOptions.hit)) { self.hitCallback = cacherOptions.hit; } if (isFunction(cacherOptions.miss)) { self.missCallback = cacherOptions.miss; } if (isFunction(cacherOptions.added)) { self.addedCallback = cacherOptions.added; } if (isFunction(cacherOptions.removed)) { self.removedCallback = cacherOptions.removed; } if (isFunction(cacherOptions.count)) { self.countCallback = cacherOptions.count; } const cleanKey = (key) => key.replace(/[^a-zA-Z0-9_]/g, '_'); const get = (key, options) => { const cacherOptions = options || {}; // @ts-ignore if (!cacherOptions.keyCleaned) { key = cleanKey(key); } let data = self.cachedData[key]; if (data && data.expires < new Date()) { data = undefined; // @ts-ignore remove(key, { keyCleaned: true }); raiseEvent(self.removedCallback, key); } let value = undefined; if (data) { self.hits++; raiseEvent(self.hitCallback, key); if (self.clone === true) { value = clone(data.value); } else { value = data.value; } } else { self.misses++; raiseEvent(self.missCallback, key); } (0, logger_1.log)(`get - cacher id: ${self.id}, key: ${key}`, { value }); return value; }; const getExpiry = (key) => { key = cleanKey(key); let data = self.cachedData[key]; if (data && data.expires < new Date()) { data = undefined; // @ts-ignore remove(key, { keyCleaned: true }); raiseEvent(self.removedCallback, key); } if (data) { (0, logger_1.log)(`getExpiry - cacher id: ${self.id}, key: ${key}, expiry: ${data.expires}`); } else { (0, logger_1.log)(`getExpiry - item does not exist - cacher id: ${self.id}, key: ${key}`); } return data ? data.expires : undefined; }; const getAndSet = async (key, getter, options) => { const cacherOptions = clone(options || {}); // @ts-ignore cacherOptions.keyCleaned = true; if (!isFunction(getter)) { (0, logger_1.log)(`getAndSet - no getter function is defined - cacher id: ${self.id}, key: ${key}`, { cacherOptions }); return undefined; } key = cleanKey(key); let value = get(key, cacherOptions); if (!value) { (0, logger_1.log)(`getAndSet - retrieving value from getter - cacher id: ${self.id}, key: ${key}`, { cacherOptions }); value = await getter(); setWithRefresh(key, value, getter, options); } (0, logger_1.log)(`getAndSet - cacher id: ${self.id}, key: ${key}`, { value }); return value; }; const setWithRefresh = (key, value, getter, options) => { setInternal(key, value, true, options); if (options?.refreshIntervalInMilliseconds === undefined || options?.refreshIntervalInMilliseconds === 0) return; let data = self.cachedData[key]; const timeoutFunc = (milliseconds) => { data = self.cachedData[key]; data.timeout = setTimeout(async () => { if (!data.timeout) return; (0, logger_1.log)(`getAndSet - refresh cacher id: ${self.id}, key: ${key}. Getting data to update cache'`); try { const value = await getter(); set(key, value, options); timeoutFunc(milliseconds); } catch (error) { (0, logger_1.log)(`getAndSet - refresh cacher id: ${self.id}, key: ${key}. Failed to update cache, error message was '${error.message}'`); (0, logger_1.log)(`Failed to update cache cacher id: ${self.id}, key: '${key}', error message was '${error.message}'`); timeoutFunc(options.refreshIntervalWhenRefreshFailsInMilliseconds ?? milliseconds); } }, milliseconds); }; timeoutFunc(options.refreshIntervalInMilliseconds); }; const set = (key, value, options) => { setInternal(key, value, false, options); }; const setInternal = (key, value, refresh, options) => { const cacherOptions = options || {}; if (!self.storeUndefinedObjects && (value === undefined || value === null || (typeof value.isNull === "function" && value.isNull()))) { (0, logger_1.log)(`set - not storing value as it is not defined - cacher id: ${self.id}, key: ${key}`, { value, cacherOptions }); remove(key, cacherOptions); raiseEvent(self.removedCallback, key); return; } // @ts-ignore if (!cacherOptions.keyCleaned) { key = cleanKey(key); } let timeout; if (self.cachedData[key]) { if (refresh) { timeout = self.cachedData[key].timeout; } else { if (self.cachedData[key].timeout) { clearTimeout(self.cachedData[key].timeout); delete self.cachedData[key].timeout; } } } let ttl = cacherOptions.ttl || self.ttl; let expiryDate; if (ttl === 0) { expiryDate = new Date(8640000000000000); } else { expiryDate = new Date(new Date().getTime() + (ttl * 1000)); } let valueToCache = self.clone === true ? clone(value) : value; let cacheValue = { value: valueToCache, expires: expiryDate, timeout: undefined }; if (refresh) { cacheValue.timeout = timeout; } self.cachedData[key] = cacheValue; (0, logger_1.log)(`set - stored value - cacher id: ${self.id}, key: ${key}`, { cachedData: cacheValue }); raiseEvent(self.addedCallback, key); raiseCountEvent(self.countCallback); }; const clear = () => { (0, logger_1.log)(`clear - cacher id: ${self.id}`); self.hits = 0; self.misses = 0; Object.keys(self.cachedData).forEach(key => { if (self.cachedData[key] !== undefined && self.cachedData[key].timeout) { clearTimeout(self.cachedData[key].timeout); delete self.cachedData[key].timeout; } }); self.cachedData = {}; }; const remove = (key, options) => { const cacherOptions = options || {}; // @ts-ignore if (!cacherOptions.keyCleaned) { key = cleanKey(key); } (0, logger_1.log)(`remove - key: ${key}, cacher id: ${self.id}`); if (self.cachedData[key] && self.cachedData[key].timeout) { clearTimeout(self.cachedData[key].timeout); delete self.cachedData[key].timeout; } delete self.cachedData[key]; raiseEvent(self.removedCallback, key); raiseCountEvent(self.countCallback); }; const stats = () => { let hits = self.hits; let misses = self.misses; let stats = { count: Object.keys(self.cachedData).length, hits, misses, hitRate: hits / (hits + misses) }; if (isNaN(stats.hitRate)) { stats.hitRate = 0; } (0, logger_1.log)(`stats - ${JSON.stringify(stats)}, cacher id: ${self.id}`); return stats; }; const keys = () => { let keys = Object.keys(self.cachedData); (0, logger_1.log)(`keys - cacher id: ${self.id}`, { keys }); return keys; }; const getOptions = () => { return { ttl: self.ttl, clone: self.clone, storeUndefinedObjects: self.storeUndefinedObjects }; }; const raiseEvent = (callback, key) => { if (!callback) return; callback({ id: self.id, key: key }); }; const raiseCountEvent = (callback) => { if (!callback) return; callback({ id: self.id, count: Object.keys(self.cachedData).length }); }; return { id: self.id, get, getExpiry, getAndSet, set, clear, remove, stats, keys, options: getOptions }; } exports.default = default_1; ; //# sourceMappingURL=cacher.js.map