scripts/src/lib/cache.ts

66 lines
1.5 KiB
TypeScript

import addSeconds from 'date-fns/addSeconds';
import isAfter from 'date-fns/isAfter';
export interface Storage {
getItem(key: string): string | null;
setItem(key: string, value: string): void;
removeItem(key: string): void;
}
export class InMemoryStorage {
private readonly data = new Map<string, string>();
getItem(key: string): string | null {
const d = this.data.get(key);
return d ? d : null;
}
setItem(key: string, value: string) {
this.data.set(key, value);
}
removeItem(key: string) {
this.data.delete(key);
}
}
export class Cache {
constructor(private readonly storage: Storage) {}
// load first tries to load data from storage, if there is no data or it is expired then it executes the given function and caches the result
async load<T>(key: string, ttl: number, func: () => Promise<T>): Promise<T> {
const data = this.get<T>(key);
if (data) {
return data;
}
const res = await func();
this.storage.setItem(
key,
JSON.stringify({
data: res,
exp: addSeconds(new Date(), ttl),
})
);
return res;
}
get<T>(key: string): T | null {
try {
const fromStorage = JSON.parse(this.storage.getItem(key) ?? '');
if (!fromStorage.exp || !fromStorage.data) {
return null;
}
if (isAfter(new Date(), new Date(fromStorage.exp))) {
this.storage.removeItem(key);
return null;
}
return fromStorage.data;
} catch (err) {
return null;
}
}
}