@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
JavaScript
"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 };
}