63 lines
1.7 KiB
JavaScript
63 lines
1.7 KiB
JavaScript
export class APICache {
|
|
constructor({ maxSize = 250, ttl = 1000 * 60 * 20 } = {}) {
|
|
this.maxSize = maxSize;
|
|
this.ttl = ttl;
|
|
this.store = new Map();
|
|
}
|
|
|
|
makeKey(namespace, key) {
|
|
return `${namespace}:${String(key)}`;
|
|
}
|
|
|
|
isExpired(entry) {
|
|
return !entry || Date.now() - entry.ts > entry.ttl;
|
|
}
|
|
|
|
pruneToSize() {
|
|
if (this.store.size <= this.maxSize) return;
|
|
const entries = [...this.store.entries()].sort((a, b) => a[1].ts - b[1].ts);
|
|
const overflow = this.store.size - this.maxSize;
|
|
for (let i = 0; i < overflow; i += 1) {
|
|
this.store.delete(entries[i][0]);
|
|
}
|
|
}
|
|
|
|
async get(namespace, key) {
|
|
const cacheKey = this.makeKey(namespace, key);
|
|
const entry = this.store.get(cacheKey);
|
|
if (!entry) return null;
|
|
if (this.isExpired(entry)) {
|
|
this.store.delete(cacheKey);
|
|
return null;
|
|
}
|
|
return entry.value;
|
|
}
|
|
|
|
async set(namespace, key, value, ttl = this.ttl) {
|
|
const cacheKey = this.makeKey(namespace, key);
|
|
this.store.set(cacheKey, { value, ts: Date.now(), ttl });
|
|
this.pruneToSize();
|
|
}
|
|
|
|
clearExpired() {
|
|
for (const [key, entry] of this.store.entries()) {
|
|
if (this.isExpired(entry)) this.store.delete(key);
|
|
}
|
|
}
|
|
|
|
async clear() {
|
|
this.store.clear();
|
|
}
|
|
|
|
getCacheStats() {
|
|
const namespaces = new Set();
|
|
for (const key of this.store.keys()) namespaces.add(key.split(':', 1)[0]);
|
|
return {
|
|
size: this.store.size,
|
|
namespaces: [...namespaces],
|
|
maxSize: this.maxSize,
|
|
ttlMs: this.ttl,
|
|
};
|
|
}
|
|
}
|