breathe-api
Version:
Model Context Protocol server for Breathe HR APIs with Swagger/OpenAPI support - also works with custom APIs
101 lines • 3.15 kB
JavaScript
import NodeCache from 'node-cache';
import PQueue from 'p-queue';
import crypto from 'crypto';
const SWAGGER_CACHE_TTL = 3600;
const API_RESPONSE_CACHE_TTL = 300;
export const swaggerCache = new NodeCache({
stdTTL: SWAGGER_CACHE_TTL,
checkperiod: 120,
useClones: false
});
export const apiResponseCache = new NodeCache({
stdTTL: API_RESPONSE_CACHE_TTL,
checkperiod: 60,
useClones: false
});
const pendingRequests = new Map();
export const requestQueue = new PQueue({
concurrency: 5,
interval: 1000,
intervalCap: 10
});
export function generateCacheKey(params) {
const normalized = {
url: params.url.toLowerCase(),
method: (params.method || 'GET').toUpperCase(),
headers: params.headers || {},
params: params.params || {},
data: params.data || null
};
const keyString = JSON.stringify(normalized, Object.keys(normalized).sort());
return crypto.createHash('sha256').update(keyString).digest('hex');
}
export function isCacheable(url, method = 'GET') {
if (method !== 'GET')
return false;
const cacheablePatterns = [
/\/swagger\.json$/i,
/\/openapi\.json$/i,
/\/api-docs/i,
/\/employees$/i,
/\/employees\/\d+$/i,
/\/departments$/i,
/\/leave-types$/i,
/\/holiday-calendars$/i,
/\/company-info$/i,
/\/shifts$/i,
/\/rosters$/i,
];
return cacheablePatterns.some(pattern => pattern.test(url));
}
export async function deduplicateRequest(key, requestFn) {
const pending = pendingRequests.get(key);
if (pending) {
return pending;
}
const requestPromise = requestFn()
.finally(() => {
pendingRequests.delete(key);
});
pendingRequests.set(key, requestPromise);
return requestPromise;
}
export async function getCachedOrFetch(cache, key, fetchFn, ttl) {
const cached = cache.get(key);
if (cached !== undefined) {
return cached;
}
const result = await deduplicateRequest(key, fetchFn);
if (ttl) {
cache.set(key, result, ttl);
}
else {
cache.set(key, result);
}
return result;
}
export function clearAllCaches() {
swaggerCache.flushAll();
apiResponseCache.flushAll();
pendingRequests.clear();
}
export function getCacheStats() {
return {
swagger: {
keys: swaggerCache.keys().length,
hits: swaggerCache.getStats().hits,
misses: swaggerCache.getStats().misses,
hitRate: swaggerCache.getStats().hits / (swaggerCache.getStats().hits + swaggerCache.getStats().misses) || 0
},
apiResponse: {
keys: apiResponseCache.keys().length,
hits: apiResponseCache.getStats().hits,
misses: apiResponseCache.getStats().misses,
hitRate: apiResponseCache.getStats().hits / (apiResponseCache.getStats().hits + apiResponseCache.getStats().misses) || 0
},
pendingRequests: pendingRequests.size,
queueSize: requestQueue.size,
queuePending: requestQueue.pending
};
}
//# sourceMappingURL=cache.js.map