0.1.3•Updated a month ago
interface MemCacheEntry<T = unknown> {
expires_at: number
data: T
}
export class MemCache<T = unknown> {
readonly expiration_period_seconds: number
readonly interval_ms: number
private interval_id!: number
/**
* Creates a new non-persistent cache.
*
* @param expiration_period_seconds How long an entry should be kept in this cache.
* @param clean_interval_seconds How frequently this cache should be checked for stale entries and cleaned up.
*/
constructor(expiration_period_seconds: number = 60, clean_interval_seconds: number = 10) {
this.expiration_period_seconds = expiration_period_seconds;
this.interval_ms = clean_interval_seconds * 1000;
if(!Deno.args.includes('build')) this.queue();
}
private _entries: Map<string, MemCacheEntry<T>> = new Map<string, MemCacheEntry<T>>()
/**
* Get the value of a cached item
*/
get(key: string): T | null {
if(!this._entries.has(key)) return null;
const entry = this._entries.get(key)!;
entry.expires_at = Date.now();
return entry.data as T;
}
/**
* Checks whether a cached item exists
*/
has(key: string) {
return this._entries.has(key);
}
/**
* If a value exists, return it. Otherwise, set it and return it.
*/
async passthrough(key: string, value: PassthroughValue<T> | PassthroughFunction<T>) {
if(this.has(key)) return this.get(key);
let result;
if(typeof value === 'function') {
result = await (value as PassthroughFunction<T>)();
} else {
result = value as T | undefined | null;
}
if(result !== null && result !== undefined) {
return this.set(key, result);
}
return null;
}
/**
* Sets the cached item and returns it
*/
set(key: string, value: T) {
this._entries.set(key, { data: value, expires_at: Date.now() + this.expiration_period_seconds * 1000 });
return value;
}
/**
* Removes the cached item and returns it (if it exists)
*/
delete(key: string) {
if(!this._entries.has(key)) return null;
const value = this.get(key);
this._entries.delete(key);
return value;
}
/**
* Clears all entries from the cache
*/
clear() {
this._entries.clear();
}
private queue() {
this.interval_id = setTimeout(this.clean.bind(this), this.interval_ms);
}
private clean() {
clearTimeout(this.interval_id);
const expiration_timestamp = Date.now();
for(const [key, { expires_at }] of Object.entries(this._entries)) {
if(expires_at < expiration_timestamp) this._entries.delete(key);
}
this.queue();
}
}
type PassthroughValueBasic<T> = T | null | undefined
export type PassthroughValue<T> = PassthroughValueBasic<T> | Promise<PassthroughValueBasic<T>>
export type PassthroughFunction<T> = (...params: unknown[]) => PassthroughValue<T>;