Files
sofarr/server/utils/cache.js
Gronod 8c4cc20551
All checks were successful
Build and Push Docker Image / build (push) Successful in 48s
Licence Check / Licence compatibility and copyright header verification (push) Successful in 1m21s
CI / Security audit (push) Successful in 1m47s
CI / Tests & coverage (push) Successful in 2m1s
Add MIT copyright headers to all source files
2026-05-19 09:07:42 +01:00

76 lines
1.9 KiB
JavaScript

// Copyright (c) 2026 Gordon Bolton. MIT License.
const { logToFile } = require('./logger');
class MemoryCache {
constructor() {
this.store = new Map();
}
get(key) {
const entry = this.store.get(key);
if (!entry) return null;
if (Date.now() > entry.expiresAt) {
this.store.delete(key);
return null;
}
return entry.value;
}
set(key, value, ttlMs) {
this.store.set(key, {
value,
expiresAt: Date.now() + ttlMs
});
}
invalidate(key) {
this.store.delete(key);
}
clear() {
this.store.clear();
}
getStats() {
const now = Date.now();
const entries = [];
let totalSize = 0;
for (const [key, entry] of this.store.entries()) {
// Maps must be converted before JSON.stringify (which renders them as "{}")
const serializable = entry.value instanceof Map ? Object.fromEntries(entry.value) : entry.value;
const json = JSON.stringify(serializable);
const sizeBytes = Buffer.byteLength(json, 'utf8');
totalSize += sizeBytes;
const ttlRemaining = Math.max(0, entry.expiresAt - now);
const expired = now > entry.expiresAt;
let itemCount = null;
if (entry.value instanceof Map) {
itemCount = entry.value.size;
} else if (Array.isArray(entry.value)) {
itemCount = entry.value.length;
} else if (entry.value && typeof entry.value === 'object') {
if (Array.isArray(entry.value.records)) itemCount = entry.value.records.length;
else if (Array.isArray(entry.value.slots)) itemCount = entry.value.slots.length;
}
entries.push({
key,
sizeBytes,
itemCount,
ttlRemainingMs: ttlRemaining,
expired
});
}
return {
entryCount: this.store.size,
totalSizeBytes: totalSize,
entries
};
}
}
const cache = new MemoryCache();
module.exports = cache;