File size: 918 Bytes
1a8147e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
type CacheEntry<T> = {
  expiresAt: number;
  value: Promise<T>;
};

export class TtlCache<T = unknown> {
  private readonly store = new Map<string, CacheEntry<T>>();

  async getOrSet(
    key: string,
    ttlMs: number,
    loader: () => Promise<T>,
  ): Promise<T> {
    const now = Date.now();
    const cached = this.store.get(key);

    if (cached && cached.expiresAt > now) {
      return cached.value;
    }

    const value = loader().catch((error) => {
      const current = this.store.get(key);
      if (current?.value === value) {
        this.store.delete(key);
      }
      throw error;
    });

    this.store.set(key, {
      expiresAt: now + ttlMs,
      value,
    });

    return value;
  }

  clear() {
    this.store.clear();
  }

  deletePrefix(prefix: string) {
    for (const key of this.store.keys()) {
      if (key.startsWith(prefix)) {
        this.store.delete(key);
      }
    }
  }
}