UNPKG

@re-shell/cli

Version:

Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja

494 lines (493 loc) 18 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.WorkspaceCacheManager = exports.WorkspaceStateManager = void 0; exports.createWorkspaceStateManager = createWorkspaceStateManager; exports.createWorkspaceCacheManager = createWorkspaceCacheManager; exports.initializeWorkspaceStorage = initializeWorkspaceStorage; const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const crypto = __importStar(require("crypto")); const error_handler_1 = require("./error-handler"); // Workspace state manager class WorkspaceStateManager { constructor(rootPath = process.cwd()) { this.isDirty = false; this.statePath = path.join(rootPath, '.re-shell', 'state.json'); this.stateData = this.createDefaultState(); } // Load state from disk async loadState() { try { if (await fs.pathExists(this.statePath)) { const data = await fs.readJson(this.statePath); this.stateData = this.validateAndMigrateState(data); } else { this.stateData = this.createDefaultState(); await this.saveState(); // Create initial state file } this.isDirty = false; return this.stateData; } catch (error) { throw new error_handler_1.ValidationError(`Failed to load workspace state: ${error.message}`); } } // Save state to disk async saveState() { try { await fs.ensureDir(path.dirname(this.statePath)); this.stateData.timestamp = new Date().toISOString(); await fs.writeJson(this.statePath, this.stateData, { spaces: 2 }); this.isDirty = false; } catch (error) { throw new error_handler_1.ValidationError(`Failed to save workspace state: ${error.message}`); } } // Get state for specific workspace getWorkspaceState(name) { return this.stateData.workspaces[name]; } // Update workspace state async updateWorkspaceState(name, updates) { const existing = this.stateData.workspaces[name] || this.createDefaultWorkspaceState(name); this.stateData.workspaces[name] = { ...existing, ...updates, lastModified: new Date().toISOString() }; this.isDirty = true; // Auto-save if significant changes if (updates.buildStatus || updates.healthScore !== undefined) { await this.saveState(); } } // Update file hashes for workspace async updateFileHashes(name, workspacePath) { try { const fileHashes = await this.calculateFileHashes(workspacePath); await this.updateWorkspaceState(name, { fileHashes }); } catch (error) { console.warn(`Failed to update file hashes for ${name}: ${error.message}`); } } // Check if workspace has changed since last update async hasWorkspaceChanged(name, workspacePath) { const state = this.getWorkspaceState(name); if (!state || !state.fileHashes) return true; try { const currentHashes = await this.calculateFileHashes(workspacePath); return !this.areHashesEqual(state.fileHashes, currentHashes); } catch (error) { return true; // Assume changed if we can't determine } } // Clear all state async clearState() { this.stateData = this.createDefaultState(); this.isDirty = true; await this.saveState(); } // Backup current state async backupState(backupName) { const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const backupFileName = backupName || `state-backup-${timestamp}.json`; const backupPath = path.join(path.dirname(this.statePath), 'backups', backupFileName); await fs.ensureDir(path.dirname(backupPath)); await fs.copy(this.statePath, backupPath); return backupPath; } // Restore state from backup async restoreState(backupPath) { if (!(await fs.pathExists(backupPath))) { throw new error_handler_1.ValidationError(`Backup file not found: ${backupPath}`); } const backupData = await fs.readJson(backupPath); this.stateData = this.validateAndMigrateState(backupData); await this.saveState(); } // Get state statistics getStateStatistics() { const workspaces = Object.values(this.stateData.workspaces); const sortedByDate = workspaces.sort((a, b) => new Date(a.lastModified).getTime() - new Date(b.lastModified).getTime()); return { workspaceCount: workspaces.length, lastModified: this.stateData.timestamp, stateFileSize: JSON.stringify(this.stateData).length, oldestWorkspace: sortedByDate[0]?.name, newestWorkspace: sortedByDate[sortedByDate.length - 1]?.name }; } // Private helper methods createDefaultState() { return { version: '1.0.0', timestamp: new Date().toISOString(), workspaces: {}, globalMetadata: {} }; } createDefaultWorkspaceState(name) { return { name, lastModified: new Date().toISOString(), fileHashes: {}, metadata: {} }; } validateAndMigrateState(data) { // Basic validation if (!data.version || !data.workspaces) { return this.createDefaultState(); } // Migration logic for future version changes if (data.version === '1.0.0') { return data; } // Default migration: recreate state return this.createDefaultState(); } async calculateFileHashes(dirPath) { const hashes = {}; try { const files = await this.getRelevantFiles(dirPath); for (const file of files) { const filePath = path.join(dirPath, file); if (await fs.pathExists(filePath)) { const content = await fs.readFile(filePath); hashes[file] = crypto.createHash('md5').update(content).digest('hex'); } } } catch (error) { // Return empty hashes if directory scanning fails } return hashes; } async getRelevantFiles(dirPath) { const files = []; const relevantExtensions = ['.ts', '.tsx', '.js', '.jsx', '.json', '.yaml', '.yml']; const ignoreDirs = ['node_modules', '.git', 'dist', 'build', '.next']; try { const scan = async (dir, basePath = '') => { const entries = await fs.readdir(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); const relativePath = path.join(basePath, entry.name); if (entry.isDirectory() && !ignoreDirs.includes(entry.name)) { await scan(fullPath, relativePath); } else if (entry.isFile()) { const ext = path.extname(entry.name); if (relevantExtensions.includes(ext)) { files.push(relativePath); } } } }; await scan(dirPath); } catch (error) { // Return empty array if scanning fails } return files.slice(0, 100); // Limit to prevent memory issues } areHashesEqual(hash1, hash2) { const keys1 = Object.keys(hash1).sort(); const keys2 = Object.keys(hash2).sort(); if (keys1.length !== keys2.length) return false; for (let i = 0; i < keys1.length; i++) { if (keys1[i] !== keys2[i] || hash1[keys1[i]] !== hash2[keys2[i]]) { return false; } } return true; } } exports.WorkspaceStateManager = WorkspaceStateManager; // Workspace cache manager class WorkspaceCacheManager { constructor(rootPath = process.cwd()) { this.memoryCache = new Map(); this.hitCount = 0; this.missCount = 0; this.cacheDir = path.join(rootPath, '.re-shell', 'cache'); this.cachePath = path.join(this.cacheDir, 'metadata.json'); this.metadata = this.createDefaultMetadata(); } // Initialize cache system async init() { await fs.ensureDir(this.cacheDir); if (await fs.pathExists(this.cachePath)) { try { this.metadata = await fs.readJson(this.cachePath); } catch (error) { this.metadata = this.createDefaultMetadata(); } } } // Get cached value async get(key) { // Check memory cache first if (this.memoryCache.has(key)) { const entry = this.memoryCache.get(key); if (this.isEntryValid(entry)) { this.hitCount++; return entry.value; } else { this.memoryCache.delete(key); } } // Check disk cache const entryPath = this.getEntryPath(key); try { if (await fs.pathExists(entryPath)) { const entry = await fs.readJson(entryPath); if (this.isEntryValid(entry)) { // Load into memory cache this.memoryCache.set(key, entry); this.hitCount++; return entry.value; } else { // Remove expired entry await fs.remove(entryPath); } } } catch (error) { // Cache read failed, treat as miss } this.missCount++; return null; } // Set cached value async set(key, value, ttl, tags) { const entry = { key, value, timestamp: new Date().toISOString(), ttl, tags, size: this.calculateSize(value) }; // Store in memory cache this.memoryCache.set(key, entry); // Store on disk const entryPath = this.getEntryPath(key); await fs.ensureDir(path.dirname(entryPath)); await fs.writeJson(entryPath, entry); // Update metadata this.metadata.totalEntries++; this.metadata.totalSize += entry.size || 0; await this.saveMetadata(); } // Invalidate specific cache entry async invalidate(key) { this.memoryCache.delete(key); const entryPath = this.getEntryPath(key); if (await fs.pathExists(entryPath)) { await fs.remove(entryPath); this.metadata.totalEntries = Math.max(0, this.metadata.totalEntries - 1); await this.saveMetadata(); } } // Invalidate entries matching pattern async invalidatePattern(pattern) { const regex = typeof pattern === 'string' ? new RegExp(pattern) : pattern; let invalidated = 0; // Clear from memory cache for (const key of this.memoryCache.keys()) { if (regex.test(key)) { this.memoryCache.delete(key); invalidated++; } } // Clear from disk cache try { const files = await fs.readdir(this.cacheDir); for (const file of files) { if (file.endsWith('.json') && file !== 'metadata.json') { const key = this.decodeKey(file.replace('.json', '')); if (regex.test(key)) { await fs.remove(path.join(this.cacheDir, file)); invalidated++; } } } } catch (error) { // Directory scan failed } this.metadata.totalEntries = Math.max(0, this.metadata.totalEntries - invalidated); await this.saveMetadata(); return invalidated; } // Clear all cache async clear() { this.memoryCache.clear(); try { await fs.emptyDir(this.cacheDir); } catch (error) { // Ignore cleanup errors } this.metadata = this.createDefaultMetadata(); await this.saveMetadata(); } // Optimize cache (remove expired entries) async optimize() { let removedEntries = 0; let freedSpace = 0; // Clean memory cache for (const [key, entry] of this.memoryCache.entries()) { if (!this.isEntryValid(entry)) { this.memoryCache.delete(key); removedEntries++; freedSpace += entry.size || 0; } } // Clean disk cache try { const files = await fs.readdir(this.cacheDir); for (const file of files) { if (file.endsWith('.json') && file !== 'metadata.json') { const entryPath = path.join(this.cacheDir, file); try { const entry = await fs.readJson(entryPath); if (!this.isEntryValid(entry)) { await fs.remove(entryPath); removedEntries++; freedSpace += entry.size || 0; } } catch (error) { // Remove corrupted cache files await fs.remove(entryPath); removedEntries++; } } } } catch (error) { // Directory scan failed } this.metadata.totalEntries = Math.max(0, this.metadata.totalEntries - removedEntries); this.metadata.totalSize = Math.max(0, this.metadata.totalSize - freedSpace); this.metadata.lastOptimized = new Date().toISOString(); await this.saveMetadata(); return { removedEntries, freedSpace }; } // Get cache statistics getCacheStatistics() { const totalRequests = this.hitCount + this.missCount; return { ...this.metadata, memoryEntries: this.memoryCache.size, hitRate: totalRequests > 0 ? this.hitCount / totalRequests : 0, missRate: totalRequests > 0 ? this.missCount / totalRequests : 0 }; } // Private helper methods createDefaultMetadata() { return { totalEntries: 0, totalSize: 0, lastOptimized: new Date().toISOString(), hitRate: 0, missRate: 0 }; } getEntryPath(key) { const encodedKey = this.encodeKey(key); return path.join(this.cacheDir, `${encodedKey}.json`); } encodeKey(key) { return Buffer.from(key).toString('base64').replace(/[/+=]/g, '_'); } decodeKey(encodedKey) { const base64 = encodedKey.replace(/_/g, '+'); return Buffer.from(base64, 'base64').toString(); } isEntryValid(entry) { if (!entry.ttl) return true; const now = Date.now(); const entryTime = new Date(entry.timestamp).getTime(); return (now - entryTime) < entry.ttl; } calculateSize(value) { try { return JSON.stringify(value).length; } catch (error) { return 0; } } async saveMetadata() { try { await fs.writeJson(this.cachePath, this.metadata, { spaces: 2 }); } catch (error) { // Ignore metadata save failures } } } exports.WorkspaceCacheManager = WorkspaceCacheManager; // Utility functions async function createWorkspaceStateManager(rootPath) { const manager = new WorkspaceStateManager(rootPath); await manager.loadState(); return manager; } async function createWorkspaceCacheManager(rootPath) { const manager = new WorkspaceCacheManager(rootPath); await manager.init(); return manager; } // Combined state and cache operations async function initializeWorkspaceStorage(rootPath) { const stateManager = await createWorkspaceStateManager(rootPath); const cacheManager = await createWorkspaceCacheManager(rootPath); return { stateManager, cacheManager }; }