@trifrost/core
Version:
Blazingly fast, runtime-agnostic server framework for modern edge and node environments
142 lines (141 loc) • 4.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MemoryRateLimit = exports.MemoryCache = exports.MemoryStore = exports.MemoryStoreAdapter = void 0;
const array_1 = require("@valkyriestudios/utils/array");
const number_1 = require("@valkyriestudios/utils/number");
const _Cache_1 = require("../modules/Cache/_Cache");
const _RateLimit_1 = require("../modules/RateLimit/_RateLimit");
const _Storage_1 = require("./_Storage");
class MemoryStoreAdapter {
#store = new Map();
/* Garbage collection interval */
#gc = null;
/* Used for lru (least-recently-used) tracking */
#lru = new Set();
/* Set to the max amount of items allowed in our store if configured */
#lruMax = null;
constructor(opts) {
/* Configure garbage collection interval */
const filter = typeof opts?.gc_filter === 'function' ? opts.gc_filter : (_key, _v, _now, _exp) => _exp <= _now;
if ((0, number_1.isIntGt)(opts?.gc_interval, 0)) {
this.#gc = setInterval(() => {
const now = Date.now();
for (const [key, entry] of this.#store.entries()) {
if (filter(key, entry.value, now, entry.expires)) {
this.#store.delete(key);
if (this.isLRU)
this.#lru.delete(key);
}
}
}, opts.gc_interval);
}
/* Set max usage value if max entries is provided */
if ((0, number_1.isIntGt)(opts?.max_items, 0))
this.#lruMax = opts.max_items;
}
get isLRU() {
return this.#lruMax !== null;
}
async get(key) {
const val = this.#store.get(key);
if (!val)
return null;
if (this.isLRU)
this.#lru.delete(key);
if (Date.now() > val.expires) {
this.#store.delete(key);
return null;
}
else {
if (this.isLRU)
this.#lru.add(key);
return val.value;
}
}
async set(key, value, ttl) {
if (this.isLRU) {
/* Mark as most recently used in LRU */
this.#lru.delete(key);
this.#lru.add(key);
/* Evict if above size */
if (this.#lru.size > this.#lruMax) {
const to_evict = this.#lru.values().next().value;
if (to_evict) {
this.#store.delete(to_evict);
this.#lru.delete(to_evict);
}
}
}
this.#store.set(key, { value, expires: Date.now() + ttl * 1000 });
}
async del(key) {
this.#store.delete(key);
if (this.isLRU)
this.#lru.delete(key);
}
async delPrefixed(prefix) {
for (const k of this.#store.keys()) {
if (k.startsWith(prefix)) {
this.#store.delete(k);
if (this.isLRU)
this.#lru.delete(k);
}
}
}
async stop() {
if (!this.#gc)
return;
clearInterval(this.#gc);
this.#gc = null;
}
}
exports.MemoryStoreAdapter = MemoryStoreAdapter;
/**
* MARK: Store
*/
class MemoryStore extends _Storage_1.Store {
constructor(opts) {
super('MemoryStore', new MemoryStoreAdapter(opts));
}
}
exports.MemoryStore = MemoryStore;
/**
* MARK: Cache
*/
class MemoryCache extends _Cache_1.TriFrostCache {
constructor(cfg) {
super({
store: new _Storage_1.Store('MemoryCache', new MemoryStoreAdapter({
gc_interval: (0, number_1.isIntGt)(cfg?.gc_interval, 0) ? cfg?.gc_interval : 60_000,
...(cfg?.max_items !== null && { max_items: (0, number_1.isIntGt)(cfg?.max_items, 0) ? cfg.max_items : 1_000 }),
})),
});
}
}
exports.MemoryCache = MemoryCache;
/**
* MARK: RateLimit
*/
class MemoryRateLimit extends _RateLimit_1.TriFrostRateLimit {
constructor(cfg) {
const window = (0, number_1.isIntGt)(cfg?.window, 0) ? cfg.window : 60_000;
let adapter;
if (cfg?.strategy === 'sliding') {
adapter = new MemoryStoreAdapter({
gc_interval: (0, number_1.isIntGt)(cfg?.gc_interval, 0) ? cfg.gc_interval : 60_000,
gc_filter: (_, timestamps, now) => (0, array_1.isNeArray)(timestamps) && timestamps[timestamps.length - 1] < now - window,
});
}
else {
adapter = new MemoryStoreAdapter({
gc_interval: (0, number_1.isIntGt)(cfg?.gc_interval, 0) ? cfg.gc_interval : 60_000,
gc_filter: (_, value, now) => value.reset <= now,
});
}
super({
...(cfg || {}),
store: new _Storage_1.Store('MemoryRateLimit', adapter),
});
}
}
exports.MemoryRateLimit = MemoryRateLimit;