UNPKG

ultimate-mcp-server

Version:

The definitive all-in-one Model Context Protocol server for AI-assisted coding across 30+ platforms

442 lines 14.8 kB
/** * In-Memory Content Storage * Simple implementation for development and testing */ import { BaseContentStorage } from './base.js'; import { Logger } from '../../utils/logger.js'; const logger = new Logger('MemoryContentStorage'); export class MemoryContentStorage extends BaseContentStorage { name = 'memory'; spaces = new Map(); environments = new Map(); contentTypes = new Map(); entries = new Map(); assets = new Map(); comments = new Map(); history = new Map(); // Space management async createSpace(space) { const newSpace = { ...space, id: this.generateId(), createdAt: new Date(), updatedAt: new Date(), environments: [] }; this.spaces.set(newSpace.id, newSpace); // Create default environment const defaultEnv = await this.createEnvironment(newSpace.id, { name: 'master', isActive: true }); newSpace.environments = [defaultEnv]; logger.info(`Created space: ${newSpace.name}`); return newSpace; } async getSpace(spaceId) { return this.spaces.get(spaceId) || null; } async updateSpace(spaceId, updates) { const space = this.spaces.get(spaceId); if (!space) { throw new Error(`Space not found: ${spaceId}`); } const updated = { ...space, ...updates, id: space.id, createdAt: space.createdAt, updatedAt: new Date() }; this.spaces.set(spaceId, updated); return updated; } async deleteSpace(spaceId) { // Delete all related data for (const [envId, env] of this.environments) { if (env.spaceId === spaceId) { this.environments.delete(envId); } } for (const [entryId, entry] of this.entries) { // Check if entry belongs to this space this.entries.delete(entryId); } for (const [assetId, asset] of this.assets) { // Check if asset belongs to this space this.assets.delete(assetId); } this.spaces.delete(spaceId); logger.info(`Deleted space: ${spaceId}`); } async listSpaces() { return Array.from(this.spaces.values()); } // Environment management async createEnvironment(spaceId, environment) { const newEnv = { ...environment, id: this.generateId(), spaceId, createdAt: new Date(), updatedAt: new Date() }; this.environments.set(newEnv.id, newEnv); return newEnv; } async getEnvironment(environmentId) { return this.environments.get(environmentId) || null; } async listEnvironments(spaceId) { return Array.from(this.environments.values()) .filter(env => env.spaceId === spaceId); } async deleteEnvironment(environmentId) { this.environments.delete(environmentId); } // Content type management async createContentType(spaceId, contentType) { const newType = { ...contentType, id: this.generateId() }; this.contentTypes.set(newType.id, newType); logger.info(`Created content type: ${newType.name}`); return newType; } async getContentType(typeId) { return this.contentTypes.get(typeId) || null; } async updateContentType(typeId, updates) { const type = this.contentTypes.get(typeId); if (!type) { throw new Error(`Content type not found: ${typeId}`); } const updated = { ...type, ...updates, id: type.id }; this.contentTypes.set(typeId, updated); return updated; } async deleteContentType(typeId) { this.contentTypes.delete(typeId); } async listContentTypes(spaceId) { // In a real implementation, content types would be associated with spaces return Array.from(this.contentTypes.values()); } // Entry management async createEntry(spaceId, entry) { const newEntry = { ...entry, id: this.generateId(), createdAt: new Date(), updatedAt: new Date(), version: 1 }; this.entries.set(newEntry.id, newEntry); // Add to history this.addToHistory(newEntry.id, { version: 1, author: entry.author || 'system', changes: Object.keys(entry.fields).map(field => ({ field, oldValue: undefined, newValue: entry.fields[field], action: 'added' })), timestamp: new Date() }); logger.info(`Created entry: ${newEntry.id}`); return newEntry; } async getEntry(entryId) { return this.entries.get(entryId) || null; } async updateEntry(entryId, updates) { const entry = this.entries.get(entryId); if (!entry) { throw new Error(`Entry not found: ${entryId}`); } const changes = []; if (updates.fields) { for (const [field, newValue] of Object.entries(updates.fields)) { const oldValue = entry.fields[field]; if (oldValue !== newValue) { changes.push({ field, oldValue, newValue, action: (oldValue === undefined ? 'added' : 'modified') }); } } } const updated = { ...entry, ...updates, id: entry.id, createdAt: entry.createdAt, updatedAt: new Date(), version: entry.version + 1 }; this.entries.set(entryId, updated); // Add to history if (changes.length > 0) { this.addToHistory(entryId, { version: updated.version, author: updates.author || entry.author || 'system', changes, timestamp: new Date() }); } return updated; } async deleteEntry(entryId) { // Delete associated comments for (const [commentId, comment] of this.comments) { if (comment.entryId === entryId) { this.comments.delete(commentId); } } // Delete history this.history.delete(entryId); this.entries.delete(entryId); logger.info(`Deleted entry: ${entryId}`); } async searchEntries(options) { let entries = Array.from(this.entries.values()); // Apply filters entries = this.applyFilter(entries, options); // Apply sorting if (options.sort) { entries.sort((a, b) => { const aVal = a.fields[options.sort.field] || ''; const bVal = b.fields[options.sort.field] || ''; const compare = aVal < bVal ? -1 : aVal > bVal ? 1 : 0; return options.sort.order === 'asc' ? compare : -compare; }); } // Apply pagination const result = this.paginate(entries, options.pagination); return { entries: result.items, pagination: result.pagination }; } // Asset management async createAsset(spaceId, asset) { const newAsset = { ...asset, id: this.generateId(), createdAt: new Date(), updatedAt: new Date(), version: 1 }; this.assets.set(newAsset.id, newAsset); logger.info(`Created asset: ${newAsset.title}`); return newAsset; } async getAsset(assetId) { return this.assets.get(assetId) || null; } async updateAsset(assetId, updates) { const asset = this.assets.get(assetId); if (!asset) { throw new Error(`Asset not found: ${assetId}`); } const updated = { ...asset, ...updates, id: asset.id, createdAt: asset.createdAt, updatedAt: new Date(), version: asset.version + 1 }; this.assets.set(assetId, updated); return updated; } async deleteAsset(assetId) { this.assets.delete(assetId); logger.info(`Deleted asset: ${assetId}`); } async searchAssets(options) { let assets = Array.from(this.assets.values()); // Apply filters assets = this.applyFilter(assets, options); // Apply sorting if (options.sort) { assets.sort((a, b) => { const aVal = a[options.sort.field] || ''; const bVal = b[options.sort.field] || ''; const compare = aVal < bVal ? -1 : aVal > bVal ? 1 : 0; return options.sort.order === 'asc' ? compare : -compare; }); } // Apply pagination const result = this.paginate(assets, options.pagination); return { assets: result.items, pagination: result.pagination }; } // Comment management async createComment(comment) { const newComment = { ...comment, id: this.generateId(), createdAt: new Date(), updatedAt: new Date(), replies: [] }; this.comments.set(newComment.id, newComment); // If it's a reply, add to parent's replies if (comment.parentId) { const parent = this.comments.get(comment.parentId); if (parent) { parent.replies = parent.replies || []; parent.replies.push(newComment); } } logger.info(`Created comment on entry ${newComment.entryId}`); return newComment; } async getComment(commentId) { return this.comments.get(commentId) || null; } async updateComment(commentId, updates) { const comment = this.comments.get(commentId); if (!comment) { throw new Error(`Comment not found: ${commentId}`); } const updated = { ...comment, ...updates, id: comment.id, createdAt: comment.createdAt, updatedAt: new Date() }; this.comments.set(commentId, updated); return updated; } async deleteComment(commentId) { const comment = this.comments.get(commentId); if (!comment) return; // Remove from parent's replies if it's a reply if (comment.parentId) { const parent = this.comments.get(comment.parentId); if (parent && parent.replies) { parent.replies = parent.replies.filter(r => r.id !== commentId); } } // Delete all replies if (comment.replies) { for (const reply of comment.replies) { await this.deleteComment(reply.id); } } this.comments.delete(commentId); } async getEntryComments(entryId) { return Array.from(this.comments.values()) .filter(comment => comment.entryId === entryId && !comment.parentId); } async getCommentThread(commentId) { const comment = this.comments.get(commentId); if (!comment) return []; const thread = [comment]; // Get all replies recursively const addReplies = (parent) => { if (parent.replies) { for (const reply of parent.replies) { thread.push(reply); addReplies(reply); } } }; addReplies(comment); return thread; } // History management async getEntryHistory(entryId) { const versions = this.history.get(entryId) || []; return { entryId, versions: versions.sort((a, b) => b.version - a.version) }; } async restoreVersion(entryId, version) { const entry = this.entries.get(entryId); if (!entry) { throw new Error(`Entry not found: ${entryId}`); } const history = await this.getEntryHistory(entryId); const targetVersion = history.versions.find(v => v.version === version); if (!targetVersion) { throw new Error(`Version ${version} not found for entry ${entryId}`); } // Reconstruct fields from changes const restoredFields = { ...entry.fields }; for (const change of targetVersion.changes) { if (change.action === 'removed') { delete restoredFields[change.field]; } else { restoredFields[change.field] = change.newValue; } } return this.updateEntry(entryId, { fields: restoredFields, author: 'system-restore' }); } // Statistics async getStats(spaceId) { const entries = Array.from(this.entries.values()); const assets = Array.from(this.assets.values()); const comments = Array.from(this.comments.values()); const entryTypes = {}; const statusBreakdown = { draft: 0, published: 0, archived: 0, deleted: 0 }; for (const entry of entries) { entryTypes[entry.type] = (entryTypes[entry.type] || 0) + 1; statusBreakdown[entry.status]++; } // Calculate storage (rough estimate) const entriesSize = entries.reduce((acc, e) => acc + JSON.stringify(e).length, 0); const assetsSize = assets.reduce((acc, a) => acc + (a.file.size || 0), 0); // Find last activity const allDates = [ ...entries.map(e => e.updatedAt), ...assets.map(a => a.updatedAt), ...comments.map(c => c.updatedAt) ]; const lastActivity = allDates.length > 0 ? new Date(Math.max(...allDates.map(d => d.getTime()))) : new Date(); return { totalEntries: entries.length, totalAssets: assets.length, totalComments: comments.length, entryTypes, statusBreakdown, storageUsed: entriesSize + assetsSize, lastActivity }; } // Private helper methods addToHistory(entryId, version) { const versions = this.history.get(entryId) || []; versions.push(version); this.history.set(entryId, versions); } } //# sourceMappingURL=memory.js.map