UNPKG

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
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