import { EventEmitter } from '$lib/event'; import { get, writable, type Writable } from 'svelte/store'; export type CacheEvent = 'add' | 'update' | 'remove'; export class Cache { private data: Writable> = writable(new Map()); private runningCaches: Set = new Set(); private eventEmitter = new EventEmitter(); private name: string; private resolver: (data: I) => Promise; constructor(name: string, resolver: (data: I) => Promise) { this.name = name; this.resolver = resolver; } async get(key: I): Promise { 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]); }); } }