97 lines
2.8 KiB
TypeScript
97 lines
2.8 KiB
TypeScript
import { EventEmitter } from '$lib/event';
|
|
import { get, writable, type Writable } from 'svelte/store';
|
|
|
|
export type CacheEvent = 'add' | 'update' | 'remove';
|
|
|
|
export class Cache<I, T> {
|
|
private data: Writable<Map<I, T>> = writable(new Map<I, T>());
|
|
private runningCaches: Set<I> = new Set();
|
|
private eventEmitter = new EventEmitter<CacheEvent, [I, T | null]>();
|
|
|
|
private name: string;
|
|
|
|
private resolver: (data: I) => Promise<T | null>;
|
|
|
|
constructor(name: string, resolver: (data: I) => Promise<T | null>) {
|
|
this.name = name;
|
|
this.resolver = resolver;
|
|
}
|
|
|
|
async get(key: I): Promise<T | null> {
|
|
const cached = get(this.data).get(key);
|
|
if (cached) {
|
|
console.log(`[Cache] Found in cache ${key}/${this.name}: `, cached);
|
|
return cached;
|
|
}
|
|
|
|
if (this.runningCaches.has(key)) {
|
|
return new Promise((resolve) => {
|
|
this.data.subscribe((data) => {
|
|
const value = data.get(key);
|
|
if (value) {
|
|
resolve(value);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
this.runningCaches.add(key);
|
|
|
|
const data = await this.resolver(key);
|
|
|
|
this.runningCaches.delete(key);
|
|
|
|
return data;
|
|
}
|
|
|
|
set(key: I, value: T) {
|
|
const data = get(this.data);
|
|
|
|
if (data.has(key)) {
|
|
console.log(`[Cache] Updated cache ${key}/${this.name}: `, value);
|
|
this.eventEmitter.emit('update', [key, value]);
|
|
}
|
|
else {
|
|
console.log(`[Cache] Added to cache ${key}/${this.name}: `, value);
|
|
this.eventEmitter.emit('add', [key, value]);
|
|
}
|
|
|
|
this.data.update((data) => data.set(key, value));
|
|
}
|
|
|
|
remove(key: I) {
|
|
console.log(`[Cache] Removed from cache ${key}/${this.name}: `, key);
|
|
|
|
this.data.update((data) => {
|
|
data.delete(key);
|
|
return data;
|
|
});
|
|
|
|
this.eventEmitter.emit('remove', [key, null]);
|
|
}
|
|
|
|
subscribe(event: CacheEvent, callback: (data: [I, T | null]) => void) {
|
|
return this.eventEmitter.on(event, callback);
|
|
}
|
|
|
|
subscribeKey(key: I, event: CacheEvent, callback: (data: T | null) => void) {
|
|
return this.subscribe(event, (data) => {
|
|
if (data[0] === key) callback(data[1]);
|
|
});
|
|
}
|
|
|
|
unsubscribe(event: CacheEvent, callback: (data: [I, T | null]) => void) {
|
|
return this.eventEmitter.off(event, callback);
|
|
}
|
|
|
|
subscribe_autoDispose(event: CacheEvent, callback: (data: [I, T | null]) => void) {
|
|
this.eventEmitter.on_autoDispose(event, callback);
|
|
}
|
|
|
|
subscribeKey_autoDispose(key: I, event: CacheEvent, callback: (data: T | null) => void) {
|
|
this.subscribe_autoDispose(event, (data) => {
|
|
if (data[0] === key) callback(data[1]);
|
|
});
|
|
}
|
|
}
|