checklist-mcp-server
Version:
An MCP server for hierarchical checklist management with HTTP streamable transport support.
160 lines (159 loc) • 5.81 kB
JavaScript
"use strict";
/**
* LRU Cache for Work Information Storage
* Implements Least Recently Used cache with Map-based storage and access order tracking
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WorkInfoLRUCache = void 0;
const pino_1 = __importDefault(require("pino"));
// Configure logger for LRU cache operations
const logger = (0, pino_1.default)({ level: 'info' }, pino_1.default.destination(2));
/**
* LRU Cache implementation for work information storage
* Uses Map for O(1) access and maintains access order for LRU eviction
*/
class WorkInfoLRUCache {
cache;
accessOrder; // Track access order for LRU (most recent at end)
maxSize;
/**
* Creates a new WorkInfoLRUCache instance
* @param maxSize Maximum number of entries to store (default: 10)
*/
constructor(maxSize = 10) {
this.cache = new Map();
this.accessOrder = [];
this.maxSize = maxSize;
logger.info({ maxSize }, 'WorkInfoLRUCache initialized');
}
/**
* Stores work information in the cache
* If workId already exists, updates the entry and moves to most recent
* If cache is at capacity, evicts least recently used entry
* @param workInfo The work information to store
*/
set(workInfo) {
const { workId } = workInfo;
logger.debug({ workId, description: workInfo.work_description }, 'Setting work info in cache');
const exists = this.cache.has(workId);
// Always set the new value, whether it's an update or a new entry
this.cache.set(workId, workInfo);
if (exists) {
// If it existed, just update its access order
this.updateAccessOrder(workId);
logger.debug({ workId }, 'Updated existing work info entry');
}
else {
// If it's a new entry, check for eviction and add to access order
if (this.cache.size > this.maxSize) {
this.evictLRU();
}
this.accessOrder.push(workId);
logger.debug({ workId, cacheSize: this.cache.size }, 'Added new work info entry');
}
}
/**
* Retrieves work information by workId
* Updates access order to mark as most recently used
* @param workId The work ID to retrieve
* @returns The work information or null if not found
*/
get(workId) {
const workInfo = this.cache.get(workId);
if (workInfo) {
// Update access order to mark as most recently used
this.updateAccessOrder(workId);
logger.debug({ workId }, 'Retrieved work info from cache');
// Return a deep copy to prevent modification of the cached object
return JSON.parse(JSON.stringify(workInfo));
}
logger.debug({ workId }, 'Work info not found in cache');
return null;
}
/**
* Returns a list of recent work information summaries
* Ordered by most recently used first
* @returns Array of work info summaries
*/
getRecentList() {
// Return in reverse order of accessOrder (most recent first)
const recentWorkIds = [...this.accessOrder].reverse();
const recentWorks = recentWorkIds
.map(workId => {
const workInfo = this.cache.get(workId);
if (workInfo) {
return {
workId: workInfo.workId,
work_timestamp: workInfo.work_timestamp,
work_description: workInfo.work_description
};
}
return null;
})
.filter((work) => work !== null);
logger.debug({ count: recentWorks.length }, 'Retrieved recent works list');
return recentWorks;
}
/**
* Gets the current cache size
* @returns Number of entries in the cache
*/
size() {
return this.cache.size;
}
/**
* Checks if the cache is empty
* @returns True if cache is empty, false otherwise
*/
isEmpty() {
return this.cache.size === 0;
}
/**
* Clears all entries from the cache
*/
clear() {
const previousSize = this.cache.size;
this.cache.clear();
this.accessOrder = [];
logger.info({ previousSize }, 'Cache cleared');
}
/**
* Updates the access order for a given workId
* Moves the workId to the end of the access order (most recent)
* @param workId The work ID to update access order for
*/
updateAccessOrder(workId) {
// Remove workId from current position
const index = this.accessOrder.indexOf(workId);
if (index > -1) {
this.accessOrder.splice(index, 1);
}
// Add to end (most recent)
this.accessOrder.push(workId);
}
/**
* Evicts the least recently used entry from the cache
* Removes the entry at the beginning of the access order
*/
evictLRU() {
if (this.accessOrder.length === 0) {
logger.warn('Attempted to evict from empty cache');
return;
}
// Get least recently used workId (first in access order)
const lruWorkId = this.accessOrder[0];
const evictedWork = this.cache.get(lruWorkId);
// Remove from cache and access order
this.cache.delete(lruWorkId);
this.accessOrder.shift();
logger.info({
evictedWorkId: lruWorkId,
evictedDescription: evictedWork?.work_description,
newCacheSize: this.cache.size
}, 'Evicted LRU entry from cache');
}
}
exports.WorkInfoLRUCache = WorkInfoLRUCache;