UNPKG

solidworks-mcp-server

Version:

Clean Architecture SolidWorks MCP Server - Production-ready with SOLID principles

262 lines 7.76 kB
/** * State Store for SolidWorks MCP Server * Manages resource states and provides persistence */ import { logger } from '../utils/logger.js'; import * as fs from 'fs/promises'; import * as path from 'path'; export class ResourceStateStore { resources = new Map(); stateFilePath; autoSave; saveInterval = null; constructor(stateFilePath, autoSave = true) { this.stateFilePath = stateFilePath || path.join(process.cwd(), '.solidworks-mcp-state.json'); this.autoSave = autoSave; if (this.autoSave) { this.startAutoSave(); } } /** * Add or update a resource state */ async setState(resourceId, state) { this.resources.set(resourceId, state); logger.debug(`State updated for resource: ${resourceId}`, { type: state.type, status: state.status }); if (this.autoSave) { await this.save(); } } /** * Get a resource state by ID */ getState(resourceId) { return this.resources.get(resourceId); } /** * Get all resource states */ getAllStates() { return Array.from(this.resources.values()); } /** * Get states by type */ getStatesByType(type) { return this.getAllStates().filter(state => state.type === type); } /** * Get states by status */ getStatesByStatus(status) { return this.getAllStates().filter(state => state.status === status); } /** * Remove a resource state */ async removeState(resourceId) { const deleted = this.resources.delete(resourceId); if (deleted) { logger.debug(`State removed for resource: ${resourceId}`); if (this.autoSave) { await this.save(); } } return deleted; } /** * Clear all states */ async clear() { this.resources.clear(); logger.info('All resource states cleared'); if (this.autoSave) { await this.save(); } } /** * Save state to file */ async save() { try { const snapshot = this.createSnapshot(); const json = JSON.stringify(snapshot, null, 2); await fs.writeFile(this.stateFilePath, json, 'utf-8'); logger.debug(`State saved to ${this.stateFilePath}`); } catch (error) { logger.error('Failed to save state', error); throw error; } } /** * Load state from file */ async load() { try { const data = await fs.readFile(this.stateFilePath, 'utf-8'); const snapshot = JSON.parse(data); this.resources.clear(); // Convert array back to Map if (Array.isArray(snapshot.resources)) { for (const state of snapshot.resources) { this.resources.set(state.id, state); } } else if (snapshot.resources) { // Handle Map serialized as object for (const [id, state] of Object.entries(snapshot.resources)) { this.resources.set(id, state); } } logger.info(`State loaded from ${this.stateFilePath}`, { totalResources: this.resources.size }); } catch (error) { if (error.code === 'ENOENT') { logger.debug('No state file found, starting with empty state'); } else { logger.error('Failed to load state', error); throw error; } } } /** * Create a state snapshot */ createSnapshot() { const byType = {}; const byStatus = {}; for (const state of this.resources.values()) { byType[state.type] = (byType[state.type] || 0) + 1; byStatus[state.status] = (byStatus[state.status] || 0) + 1; } return { version: '1.0.0', timestamp: new Date().toISOString(), resources: this.resources, metadata: { totalResources: this.resources.size, byType, byStatus } }; } /** * Start auto-save interval */ startAutoSave() { // Save every 30 seconds this.saveInterval = setInterval(async () => { if (this.resources.size > 0) { await this.save(); } }, 30000); } /** * Stop auto-save interval */ stopAutoSave() { if (this.saveInterval) { clearInterval(this.saveInterval); this.saveInterval = null; } } /** * Get statistics about stored states */ getStatistics() { const snapshot = this.createSnapshot(); return { totalResources: snapshot.metadata.totalResources, byType: snapshot.metadata.byType, byStatus: snapshot.metadata.byStatus, oldestResource: this.getOldestResource(), newestResource: this.getNewestResource() }; } /** * Get the oldest resource */ getOldestResource() { let oldest; for (const state of this.resources.values()) { if (!oldest || state.metadata.createdAt < oldest.metadata.createdAt) { oldest = state; } } return oldest; } /** * Get the newest resource */ getNewestResource() { let newest; for (const state of this.resources.values()) { if (!newest || state.metadata.createdAt > newest.metadata.createdAt) { newest = state; } } return newest; } /** * Query states with filters */ queryStates(filters) { let results = this.getAllStates(); if (filters.type) { results = results.filter(s => s.type === filters.type); } if (filters.status) { results = results.filter(s => s.status === filters.status); } if (filters.tags) { results = results.filter(s => { for (const [key, value] of Object.entries(filters.tags)) { if (s.metadata.tags[key] !== value) { return false; } } return true; }); } if (filters.createdAfter) { results = results.filter(s => s.metadata.createdAt >= filters.createdAfter); } if (filters.createdBefore) { results = results.filter(s => s.metadata.createdAt <= filters.createdBefore); } return results; } /** * Export states to JSON */ async exportToJSON(filePath) { const snapshot = this.createSnapshot(); const json = JSON.stringify(snapshot, null, 2); await fs.writeFile(filePath, json, 'utf-8'); logger.info(`States exported to ${filePath}`); } /** * Import states from JSON */ async importFromJSON(filePath) { const data = await fs.readFile(filePath, 'utf-8'); const snapshot = JSON.parse(data); // Merge with existing states if (Array.isArray(snapshot.resources)) { for (const state of snapshot.resources) { this.resources.set(state.id, state); } } logger.info(`States imported from ${filePath}`, { imported: snapshot.resources.length || 0 }); if (this.autoSave) { await this.save(); } } } //# sourceMappingURL=store.js.map