hypershield
Version:
Middleware suite for high-performance and resilient APIs
167 lines • 6.44 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MemoryCache = void 0;
const compressionService_1 = require("../../domains/compression/application/compressionService");
class MemoryCache {
constructor(options = {}) {
this.cache = new Map();
this.currentSize = 0;
this.compression = new compressionService_1.CompressionService();
this.options = Object.assign({ compression: true, maxItems: 1000, maxMemorySize: 100 * 1024 * 1024, pruneInterval: 60000, ttl: 3600, maxSize: 1024 * 1024 }, options);
this.startPruneInterval();
}
startPruneInterval() {
this.pruneIntervalId = setInterval(() => this.prune(), this.options.pruneInterval);
this.pruneIntervalId.unref();
}
cleanup() {
if (this.pruneIntervalId) {
clearInterval(this.pruneIntervalId);
this.pruneIntervalId = undefined;
}
}
prune() {
return __awaiter(this, void 0, void 0, function* () {
const now = Date.now();
let deletedSize = 0;
for (const [key, entry] of this.cache.entries()) {
if (this.isExpired(entry, now)) {
deletedSize += entry.size;
this.cache.delete(key);
}
}
this.currentSize -= deletedSize;
if (this.cache.size > this.options.maxItems ||
this.currentSize > this.options.maxMemorySize) {
this.pruneLRU();
}
});
}
pruneLRU() {
const entries = Array.from(this.cache.entries())
.sort((a, b) => a[1].lastAccessed - b[1].lastAccessed);
while (this.cache.size > this.options.maxItems ||
this.currentSize > this.options.maxMemorySize) {
const [key, entry] = entries.shift();
this.currentSize -= entry.size;
this.cache.delete(key);
}
}
isExpired(entry, now) {
return entry.expiresAt !== undefined && entry.expiresAt <= now;
}
calculateSize(value) {
const str = JSON.stringify(value);
return Buffer.byteLength(str, 'utf8');
}
get(key) {
return __awaiter(this, void 0, void 0, function* () {
const entry = this.cache.get(key);
if (!entry)
return null;
if (this.isExpired(entry, Date.now())) {
this.cache.delete(key);
this.currentSize -= entry.size;
return null;
}
entry.lastAccessed = Date.now();
this.cache.set(key, entry);
try {
if (this.options.compression && Buffer.isBuffer(entry.value)) {
const decompressed = yield this.compression.decompress(entry.value);
return JSON.parse(decompressed.toString());
}
return entry.value;
}
catch (error) {
console.error('Cache decompression error:', error);
return null;
}
});
}
set(key, value, ttl) {
return __awaiter(this, void 0, void 0, function* () {
const size = this.calculateSize(value);
let valueToStore = value;
if (this.options.compression) {
valueToStore = yield this.compression.compress(JSON.stringify(value));
}
const entry = {
value: valueToStore,
size,
expiresAt: ttl ? Date.now() + ttl * 1000 : Date.now() + this.options.ttl * 1000,
lastAccessed: Date.now()
};
this.cache.set(key, entry);
this.currentSize += size;
if (this.cache.size > this.options.maxItems ||
this.currentSize > this.options.maxMemorySize) {
yield this.prune();
}
});
}
delete(key) {
return __awaiter(this, void 0, void 0, function* () {
const entry = this.cache.get(key);
if (entry) {
this.currentSize -= entry.size;
this.cache.delete(key);
}
});
}
clear() {
return __awaiter(this, void 0, void 0, function* () {
this.cache.clear();
this.currentSize = 0;
});
}
mget(keys) {
return __awaiter(this, void 0, void 0, function* () {
return Promise.all(keys.map(key => this.get(key)));
});
}
exists(key) {
return __awaiter(this, void 0, void 0, function* () {
const entry = this.cache.get(key);
if (!entry)
return false;
if (this.isExpired(entry, Date.now())) {
this.cache.delete(key);
this.currentSize -= entry.size;
return false;
}
return true;
});
}
updateTTL(key, ttl) {
return __awaiter(this, void 0, void 0, function* () {
const entry = this.cache.get(key);
if (!entry)
return false;
entry.expiresAt = Date.now() + ttl * 1000;
entry.lastAccessed = Date.now();
this.cache.set(key, entry);
return true;
});
}
getTTL(key) {
return __awaiter(this, void 0, void 0, function* () {
const entry = this.cache.get(key);
if (!(entry === null || entry === void 0 ? void 0 : entry.expiresAt))
return null;
const ttl = Math.ceil((entry.expiresAt - Date.now()) / 1000);
return ttl > 0 ? ttl : null;
});
}
}
exports.MemoryCache = MemoryCache;
//# sourceMappingURL=memory.js.map