restifyx.js
Version:
Advanced API endpoint handler system with automatic documentation
148 lines • 5.18 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.memoryCache = void 0;
exports.cacheMiddleware = cacheMiddleware;
const logger_1 = require("./logger");
class MemoryCache {
constructor(options = {}) {
this.cache = new Map();
this.logger = (0, logger_1.getLogger)();
this.maxSize = Number.POSITIVE_INFINITY;
this.cleanupInterval = null;
if (options.maxSize) {
this.maxSize = options.maxSize;
}
if (options.cleanupInterval) {
this.startCleanupInterval(options.cleanupInterval);
}
}
set(key, value, duration) {
const expiry = Date.now() + duration * 1000;
if (this.cache.size >= this.maxSize) {
this.evictLeastRecentlyUsed();
}
this.cache.set(key, {
data: value,
expiry,
lastAccessed: Date.now(),
});
this.logger.debug(`Cache set: ${key} (expires in ${duration}s)`);
}
get(key) {
const item = this.cache.get(key);
if (!item) {
return undefined;
}
if (Date.now() > item.expiry) {
this.logger.debug(`Cache expired: ${key}`);
this.cache.delete(key);
return undefined;
}
item.lastAccessed = Date.now();
this.cache.set(key, item);
this.logger.debug(`Cache hit: ${key}`);
return item.data;
}
delete(key) {
this.cache.delete(key);
this.logger.debug(`Cache deleted: ${key}`);
}
clear() {
this.cache.clear();
this.logger.debug("Cache cleared");
}
size() {
return this.cache.size;
}
keys() {
return Array.from(this.cache.keys());
}
has(key) {
return this.cache.has(key);
}
evictLeastRecentlyUsed() {
let oldestKey = null;
let oldestTime = Number.POSITIVE_INFINITY;
for (const [key, item] of this.cache.entries()) {
if (item.lastAccessed < oldestTime) {
oldestTime = item.lastAccessed;
oldestKey = key;
}
}
if (oldestKey) {
this.cache.delete(oldestKey);
this.logger.debug(`Cache evicted LRU item: ${oldestKey}`);
}
}
startCleanupInterval(intervalMs) {
if (this.cleanupInterval) {
clearInterval(this.cleanupInterval);
}
this.cleanupInterval = setInterval(() => {
const now = Date.now();
let expiredCount = 0;
for (const [key, item] of this.cache.entries()) {
if (now > item.expiry) {
this.cache.delete(key);
expiredCount++;
}
}
if (expiredCount > 0) {
this.logger.debug(`Cache cleanup: removed ${expiredCount} expired items`);
}
}, intervalMs);
}
stopCleanupInterval() {
if (this.cleanupInterval) {
clearInterval(this.cleanupInterval);
this.cleanupInterval = null;
}
}
}
const memoryCache = new MemoryCache({ cleanupInterval: 60000 });
exports.memoryCache = memoryCache;
function cacheMiddleware(options) {
return (req, res, next) => {
if (req.method !== "GET" && req.method !== "HEAD") {
return next();
}
const key = options.keyGenerator ? options.keyGenerator(req) : `${req.originalUrl || req.url}`;
const cachedResponse = memoryCache.get(key);
if (cachedResponse) {
return res.json(cachedResponse);
}
const originalJson = res.json;
const originalSend = res.send;
const originalEnd = res.end;
res.json = function (body) {
if ((res.statusCode >= 200 && res.statusCode < 300) || (options.cacheErrorResponses && res.statusCode >= 400)) {
if (body !== null || options.cacheNullValues) {
memoryCache.set(key, body, options.duration);
}
}
return originalJson.call(this, body);
};
res.send = function (body) {
if ((res.statusCode >= 200 && res.statusCode < 300) || (options.cacheErrorResponses && res.statusCode >= 400)) {
if (body !== null || options.cacheNullValues) {
memoryCache.set(key, body, options.duration);
}
}
return originalSend.call(this, body);
};
res.end = function (chunk, encoding, cb) {
if ((res.statusCode >= 200 && res.statusCode < 300) || (options.cacheErrorResponses && res.statusCode >= 400)) {
if ((chunk !== null && chunk !== undefined) || options.cacheNullValues) {
let data = chunk;
if (chunk && typeof chunk !== "string") {
data = chunk.toString(encoding);
}
memoryCache.set(key, data, options.duration);
}
}
return originalEnd.call(this, chunk, encoding, cb);
};
next();
};
}
//# sourceMappingURL=cache.js.map