checklist-mcp-server
Version:
An MCP server for hierarchical checklist management with HTTP streamable transport support.
157 lines (156 loc) • 5.69 kB
JavaScript
"use strict";
/**
* LRU Cache for Task Store Management
* Implements Least Recently Used cache for session-based task storage
* Manages both task data and session-to-workId mappings
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TaskStoreLRUCache = void 0;
const pino_1 = __importDefault(require("pino"));
// Configure logger for task store LRU operations
const logger = (0, pino_1.default)({ level: 'info' }, pino_1.default.destination(2));
/**
* LRU Cache implementation for task store management
* Maintains session task lists and their access order for memory management
*/
class TaskStoreLRUCache {
taskStore;
sessionToWorkIdMap;
accessOrder; // Track access order for LRU (most recent at end)
maxSize;
/**
* Creates a new TaskStoreLRUCache instance
* @param maxSize Maximum number of sessions to store (default: 100)
*/
constructor(maxSize = 100) {
this.taskStore = new Map();
this.sessionToWorkIdMap = new Map();
this.accessOrder = [];
this.maxSize = maxSize;
logger.info({ maxSize }, 'TaskStoreLRUCache initialized');
}
/**
* Sets task list for a session
* Updates access order and evicts LRU session if necessary
* @param sessionId The session identifier
* @param tasks The task list to store
*/
setTasks(sessionId, tasks) {
logger.debug({ sessionId, taskCount: tasks.length }, 'Setting tasks for session');
const exists = this.taskStore.has(sessionId);
// Always set the new value
this.taskStore.set(sessionId, tasks);
if (exists) {
// If session existed, just update its access order
this.updateAccessOrder(sessionId);
logger.debug({ sessionId }, 'Updated existing session tasks');
}
else {
// If it's a new session, check for eviction and add to access order
if (this.taskStore.size > this.maxSize) {
this.evictLRU();
}
this.accessOrder.push(sessionId);
logger.debug({ sessionId, cacheSize: this.taskStore.size }, 'Added new session');
}
}
/**
* Gets task list for a session
* Updates access order to mark as most recently used
* @param sessionId The session identifier
* @returns The task list or undefined if not found
*/
getTasks(sessionId) {
const tasks = this.taskStore.get(sessionId);
if (tasks) {
// Update access order to mark as most recently used
this.updateAccessOrder(sessionId);
logger.debug({ sessionId }, 'Retrieved tasks for session');
return tasks;
}
logger.debug({ sessionId }, 'Session not found in cache');
return undefined;
}
/**
* Checks if a session exists in the cache
* Does NOT update access order (read-only check)
* @param sessionId The session identifier
* @returns True if session exists, false otherwise
*/
hasSession(sessionId) {
return this.taskStore.has(sessionId);
}
/**
* Sets the workId mapping for a session
* @param sessionId The session identifier
* @param workId The work identifier
*/
setWorkIdMapping(sessionId, workId) {
this.sessionToWorkIdMap.set(sessionId, workId);
logger.debug({ sessionId, workId }, 'Set workId mapping for session');
}
/**
* Gets the workId mapping for a session
* @param sessionId The session identifier
* @returns The work identifier or undefined if not found
*/
getWorkIdMapping(sessionId) {
return this.sessionToWorkIdMap.get(sessionId);
}
/**
* Gets the current cache size
* @returns Number of sessions in the cache
*/
size() {
return this.taskStore.size;
}
/**
* Gets the maximum cache size
* @returns Maximum number of sessions allowed
*/
getMaxSize() {
return this.maxSize;
}
/**
* Updates the access order for a given sessionId
* Moves the sessionId to the end of the access order (most recent)
* @param sessionId The session ID to update access order for
*/
updateAccessOrder(sessionId) {
// Remove sessionId from current position
const index = this.accessOrder.indexOf(sessionId);
if (index > -1) {
this.accessOrder.splice(index, 1);
}
// Add to end (most recent)
this.accessOrder.push(sessionId);
}
/**
* Evicts the least recently used session from the cache
* Removes both task data and workId mapping
*/
evictLRU() {
if (this.accessOrder.length === 0) {
logger.warn('Attempted to evict from empty cache');
return;
}
// Get least recently used sessionId (first in access order)
const lruSessionId = this.accessOrder[0];
const evictedTasks = this.taskStore.get(lruSessionId);
const evictedWorkId = this.sessionToWorkIdMap.get(lruSessionId);
// Remove from both maps and access order
this.taskStore.delete(lruSessionId);
this.sessionToWorkIdMap.delete(lruSessionId);
this.accessOrder.shift();
logger.info({
evictedSessionId: lruSessionId,
evictedWorkId,
taskCount: evictedTasks?.length || 0,
newCacheSize: this.taskStore.size
}, 'Evicted LRU session from cache');
}
}
exports.TaskStoreLRUCache = TaskStoreLRUCache;