@openfinance/cache
Version:
A simple, in-memory cache for javascript projects
148 lines • 5.64 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class Cache {
constructor(config, _log) {
this._log = _log;
this._cache = {};
this._groomingCache = false;
config = config || {};
config.maxLength = typeof config.maxLength !== "undefined" ? config.maxLength : 1000;
config.ttlSec = config.ttlSec || 0;
this.config = config;
}
/**
* Public method to clear the cache
*
* This may be used in an administrative endpoint when, for example, the database is manually
* manipulated, or it may be used internally to clear a specific key on change.
*/
clear(key) {
if (!key) {
this.log("notice", `Clearing all cache keys`);
this._cache = {};
}
else {
this.log("notice", `Clearing cache key ${key.toString()}`);
if (typeof key === "string") {
if (this._cache.hasOwnProperty(key)) {
this.log("info", "String key found. Deleting value.");
delete this._cache[key];
}
else {
this.log("info", "String key not found in cache. Not deleting anything.");
}
}
else {
this.log("info", "RegExp key. Matching against all cache keys.");
for (let x in this._cache) {
if (key.test(x)) {
this.log("debug", `Key matched: ${x}. Deleting value.`);
delete this._cache[x];
}
}
}
}
}
get(key, q, ttlSec) {
if (!q) {
const val = this._cache.hasOwnProperty(key) ? this._cache[key].v : undefined;
this.log("debug", `Returning value for cache key ${key}: ${JSON.stringify(val)}`);
return val;
}
if (!this._cache.hasOwnProperty(key)) {
this.log("info", `Cache not set for '${key}'. Getting result and caching.`);
const t = Date.now();
let ttl = ((typeof ttlSec !== "undefined" && ttlSec !== null)
? ttlSec
: (typeof this.config.ttlSec !== "undefined" && this.config.ttlSec !== null)
? this.config.ttlSec
: 0) * 1000;
const timeout = ttl > 0
? setTimeout(() => {
if (this._cache.hasOwnProperty(key)) {
delete this._cache[key];
}
}, ttl)
: null;
const val = q();
// If q returns a promise, then we have to await that
if (isPromise(val)) {
this.log("info", `Function returned promise. Awaiting....`);
return new Promise((res, rej) => {
val
.then((v) => {
this.log("info", `Got response. Setting cache and returning.`);
this.log("debug", `Returning value for cache key ${key}: ${JSON.stringify(v)}`);
this._cache[key] = { t, v, ttl: timeout };
this.groomCache();
res(v);
})
.catch((e) => {
rej(e);
});
});
}
else {
this.log("info", `Function returned value. Returning immediately.`);
this.log("debug", `Returning value for cache key ${key}: ${JSON.stringify(val)}`);
this._cache[key] = { t, v: val, ttl: timeout };
this.groomCache();
return Promise.resolve(val);
}
}
else {
const val = this._cache[key].v;
this._cache[key].t = Date.now();
this.log("debug", `Using cached value for '${key}'`);
this.log("debug", `Returning value for cache key ${key}: ${JSON.stringify(val)}`);
return Promise.resolve(val);
}
}
/**
* Remove old values if cache is getting too long
*/
groomCache() {
return new Promise((res, rej) => {
if (this._groomingCache) {
res();
}
// Set state to "grooming"
this._groomingCache = true;
// Groom if necessary
const cacheLength = Object.keys(this._cache).length;
if (cacheLength > this.config.maxLength) {
this.log("notice", `Current cache is ${cacheLength} objects. Grooming.`);
let kill = null;
let oldest = Date.now();
for (let k in this._cache) {
const t = this._cache[k].t;
if (t < oldest) {
oldest = t;
kill = k;
}
}
if (kill !== null) {
this.log("info", `Destroying item at ${kill}, which was last used at ${new Date(oldest).toString()}`);
delete this._cache[kill];
}
}
// Set state back to idle
this._groomingCache = false;
// Resolve the promise
res();
});
}
/**
* Log at a given log level, if logger is available
*/
log(level, msg) {
if (this._log) {
this._log[level](msg);
}
}
}
exports.Cache = Cache;
function isPromise(obj) {
return typeof obj.then !== "undefined";
}
//# sourceMappingURL=index.js.map