| |
| |
| |
| |
| |
|
|
| export interface CacheEntry<V> { |
| value: V; |
| timestamp: number; |
| ttl?: number; |
| } |
|
|
| export interface CacheOptions { |
| |
| |
| |
| defaultTtl?: number; |
|
|
| |
| |
| |
| |
| deleteOnPromiseFailure?: boolean; |
|
|
| |
| |
| |
| |
| |
| storage?: 'map' | 'weakmap'; |
| } |
|
|
| |
| |
| |
| export class CacheService<K extends object | string | undefined, V> { |
| private readonly storage: |
| | Map<K, CacheEntry<V>> |
| | WeakMap<WeakKey, CacheEntry<V>>; |
| private readonly defaultTtl?: number; |
| private readonly deleteOnPromiseFailure: boolean; |
|
|
| constructor(options: CacheOptions = {}) { |
| |
| this.storage = |
| options.storage === 'weakmap' |
| ? new WeakMap<WeakKey, CacheEntry<V>>() |
| : new Map<K, CacheEntry<V>>(); |
| this.defaultTtl = options.defaultTtl; |
| this.deleteOnPromiseFailure = options.deleteOnPromiseFailure ?? true; |
| } |
|
|
| |
| |
| |
| get(key: K): V | undefined { |
| |
| |
| |
| |
| const entry = (this.storage as any).get(key) as CacheEntry<V> | undefined; |
| if (!entry) { |
| return undefined; |
| } |
|
|
| const ttl = entry.ttl ?? this.defaultTtl; |
| if (ttl !== undefined && Date.now() - entry.timestamp > ttl) { |
| this.delete(key); |
| return undefined; |
| } |
|
|
| return entry.value; |
| } |
|
|
| |
| |
| |
| set(key: K, value: V, ttl?: number): void { |
| const entry: CacheEntry<V> = { |
| value, |
| timestamp: Date.now(), |
| ttl, |
| }; |
|
|
| |
| (this.storage as any).set(key, entry); |
|
|
| if (this.deleteOnPromiseFailure && value instanceof Promise) { |
| value.catch(() => { |
| |
| |
| if ((this.storage as any).get(key) === entry) { |
| this.delete(key); |
| } |
| }); |
| } |
| } |
|
|
| |
| |
| |
| getOrCreate(key: K, creator: () => V, ttl?: number): V { |
| let value = this.get(key); |
| if (value === undefined) { |
| value = creator(); |
| this.set(key, value, ttl); |
| } |
| return value; |
| } |
|
|
| |
| |
| |
| delete(key: K): void { |
| if (this.storage instanceof Map) { |
| this.storage.delete(key); |
| } else { |
| |
| |
| |
| |
| (this.storage as any).delete(key); |
| } |
| } |
|
|
| |
| |
| |
| clear(): void { |
| if (this.storage instanceof Map) { |
| this.storage.clear(); |
| } else { |
| throw new Error('clear() is not supported on WeakMap storage'); |
| } |
| } |
| } |
|
|
| |
| |
| |
| export function createCache<K extends string | undefined, V>( |
| options: CacheOptions & { storage: 'map' }, |
| ): CacheService<K, V>; |
| export function createCache<K extends object, V>( |
| options?: CacheOptions, |
| ): CacheService<K, V>; |
| export function createCache<K extends object | string | undefined, V>( |
| options: CacheOptions = {}, |
| ): CacheService<K, V> { |
| return new CacheService<K, V>(options); |
| } |
|
|