@trifrost/core
Version:
Blazingly fast, runtime-agnostic server framework for modern edge and node environments
117 lines (116 loc) • 4.63 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Sym_TriFrostSkipCache = exports.Sym_TriFrostCached = void 0;
exports.cache = cache;
exports.cacheSkip = cacheSkip;
exports.cacheSkipped = cacheSkipped;
exports.cacheFn = cacheFn;
const string_1 = require("@valkyriestudios/utils/string");
const Als_1 = require("../../utils/Als");
exports.Sym_TriFrostCached = Symbol('trifrost.cache.cached');
exports.Sym_TriFrostSkipCache = Symbol('trifrost.cache.skip');
function hasCache(val) {
return val?.cache && typeof val.cache.get === 'function' && typeof val.cache.set === 'function';
}
/**
* Resolve a TriFrostCache instance from any combination of:
* - ALS-bound context (if available)
* - First argument (if it's a TriFrostContext)
* - `this.cache` or `this.ctx.cache` (for instance methods)
*/
function resolveCache(self, args) {
// ALS-bound context
const ctxAls = (0, Als_1.ctx)();
if (hasCache(ctxAls))
return ctxAls?.cache;
// First argument
const ctxArg = Array.isArray(args) && args.length ? args[0] : undefined;
if (hasCache(ctxArg))
return ctxArg?.cache;
// Fallback to self.cache
if (hasCache(self))
return self.cache;
// Fallback to self.ctx.cache
if (hasCache(self?.ctx))
return self.ctx.cache;
return null;
}
function cache(key, opts) {
return function (method) {
/* Prevent re-decoration */
if (Reflect.get(method, exports.Sym_TriFrostCached))
return method;
const wrapped = async function (...args) {
/* Get trifrost cache either from passed ctx, this.cache or this.ctx.cache */
const trifrost_cache = resolveCache(this, args);
/* No viable cache found */
if (!trifrost_cache)
return method.call(this, ...args);
/* Determine cache key */
const ckey = typeof key === 'function' ? key(...args.slice(0, key.length)) : (0, string_1.isNeString)(key) ? key : null;
if (!ckey)
return method.call(this, ...args);
/* Retrieve from cache, if exists -> return */
const cached = await trifrost_cache.get(ckey);
if (cached !== null && cached !== undefined)
return cached;
/* Run method */
const result = await method.call(this, ...args);
if (Object.prototype.toString.call(result) === '[object Object]' &&
Reflect.get(result, exports.Sym_TriFrostSkipCache))
return result.value;
/* Cache */
await trifrost_cache.set(ckey, result, opts);
return result;
};
/* Set to prevent re-decoration */
Reflect.set(wrapped, exports.Sym_TriFrostCached, true);
return wrapped;
};
}
/**
* Marks a value as "do not cache", will still return the value directly from the method.
*/
function cacheSkip(value) {
const v = { value };
Reflect.set(v, exports.Sym_TriFrostSkipCache, true);
return v;
}
/**
* Returns whether or not a value is a cache skip value
*/
function cacheSkipped(v) {
return (Object.prototype.toString.call(v) === '[object Object]' && Reflect.get(v, exports.Sym_TriFrostSkipCache) === true);
}
function cacheFn(key, opts) {
return function (fn) {
/* Prevent re-decoration */
if (Reflect.get(fn, exports.Sym_TriFrostCached))
return fn;
const wrapped = async function (...args) {
/* Get trifrost cache either from passed ctx, this.cache or this.ctx.cache */
const trifrost_cache = resolveCache(this, args);
/* No viable cache found */
if (!trifrost_cache)
return fn.apply(this, args);
/* Determine cache key */
const ckey = typeof key === 'function' ? key(...args.slice(0, key.length)) : (0, string_1.isNeString)(key) ? key : null;
if (!ckey)
return fn.apply(this, args);
/* Retrieve from cache, if exists -> return */
const cached = await trifrost_cache.get(ckey);
if (cached !== null && cached !== undefined)
return cached;
/* Run method */
const result = await fn.apply(this, args);
if (cacheSkipped(result))
return result.value;
/* Cache */
await trifrost_cache.set(ckey, result, opts);
return result;
};
/* Set to prevent re-decoration */
Reflect.set(wrapped, exports.Sym_TriFrostCached, true);
return wrapped;
};
}