@rt_lite/api
Version:
An unoficial rotten tomatoes REST api
113 lines (83 loc) • 2.48 kB
text/typescript
import { CacheAlgorithm } from '~/models/cache-algorithm.model';
import { CacheRecord } from '~/models/cache-record.model';
import type { ICacheOptions } from '~/models/cache-options.model';
import { time } from '~/utilities/time.utility';
export type ICache <T> = {
has: (key: string) => boolean;
get: (key: string) => T | null;
set: (key: string, value: T) => T;
delete: (key: string) => T | null;
clear: () => void;
};
export class Cache <T> implements ICache<T> {
static readonly DEFAULT_OPTIONS: ICacheOptions = {
algorithm: CacheAlgorithm.LRU,
maxSize: Infinity,
ttl: time(1).days
};
readonly #algorithm: CacheAlgorithm;
readonly #cache = new Map<string, CacheRecord<T>>();
readonly #maxSize: number;
readonly #ttl: number;
constructor ({
algorithm = Cache.DEFAULT_OPTIONS.algorithm,
maxSize = Cache.DEFAULT_OPTIONS.maxSize,
ttl = Cache.DEFAULT_OPTIONS.ttl
}: Readonly<Partial<ICacheOptions>> = { ...Cache.DEFAULT_OPTIONS }) {
this.#algorithm = algorithm;
this.#maxSize = maxSize;
this.#ttl = ttl;
}
has (key: string): boolean {
return this.#cache.has(key);
}
get (key: string): T | null {
const record = this.#cache.get(key);
if (!record) return null;
const now = Date.now();
if (now - record.modified.getTime() > this.#ttl) {
this.#cache.delete(key);
return null;
}
return record.contents;
}
set (key: string, value: Readonly<T>): T {
const existingRecord = this.#cache.get(key);
if (existingRecord) {
existingRecord.contents = value;
this.#cache.set(key, existingRecord);
return value;
}
const record = new CacheRecord(value);
this.#cache.set(key, record);
this.#enforceMaxSize();
return value;
}
delete (key: string): T | null {
const record = this.#cache.get(key);
if (!record) return null;
this.#cache.delete(key);
return record.contents;
}
clear (): void {
this.#cache.clear();
}
#enforceMaxSize (): void {
if (this.#cache.size <= this.#maxSize) return;
const purgeLRU = (): void => {
const oldestKey = [...this.#cache.entries()]
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
.sort(([, { read: readA }], [, { read: readB }]) => {
if (readA > readB) return 1;
if (readA < readB) return -1;
return 0;
})[0][0];
this.delete(oldestKey);
};
switch (this.#algorithm) {
case CacheAlgorithm.LRU:
default:
while (this.#cache.size > this.#maxSize) purgeLRU();
}
}
}