UNPKG

@workspace-fs/core

Version:

Multi-project workspace manager for Firesystem with support for multiple sources

168 lines (146 loc) 4.43 kB
import { IReactiveFileSystem } from "@firesystem/core"; import type { Project, WorkspaceSettings, WorkspaceStats, ProjectMetrics, OptimizationReport, } from "../types"; export class PerformanceManager { constructor( private settings: WorkspaceSettings, private getProjects: () => Project[], private getActiveProjectId: () => string | null, private disableProject: (projectId: string) => Promise<void>, ) {} /** * Get workspace statistics */ async getProjectStats(totalProjectsCount: number, disabledCount: number): Promise<WorkspaceStats> { const connections: Record<string, number> = {}; // Count connections by type for (const project of this.getProjects()) { const type = project.source.type; connections[type] = (connections[type] || 0) + 1; } // Calculate total memory usage let totalMemory = 0; for (const project of this.getProjects()) { totalMemory += project.memoryUsage || 0; } return { total: totalProjectsCount, active: this.getProjects().length, disabled: disabledCount, focused: this.getActiveProjectId(), memoryUsage: this.formatBytes(totalMemory), connections, }; } /** * Get project metrics */ async getProjectMetrics(project: Project): Promise<ProjectMetrics> { const files = await project.fs.glob("**/*"); let totalSize = 0; let fileCount = 0; let largestFile = { path: "", size: 0 }; let lastModified = new Date(0); for (const file of files) { const stat = await project.fs.stat(file); if (stat.type === "file") { fileCount++; totalSize += stat.size; if (stat.size > largestFile.size) { largestFile = { path: file, size: stat.size }; } if (stat.modified > lastModified) { lastModified = stat.modified; } } } return { fileCount, totalSize, lastModified, accessCount: project.accessCount, averageFileSize: fileCount > 0 ? Math.round(totalSize / fileCount) : 0, largestFile, }; } /** * Optimize memory usage */ async optimizeMemoryUsage(): Promise<OptimizationReport> { const report: OptimizationReport = { projectsDisabled: [], memoryFreed: 0, connectionsReleased: 0, }; // Get current memory usage const currentMemory = await this.calculateTotalMemoryUsage(); if (currentMemory < (this.settings.memoryThreshold || Infinity)) { return report; // No optimization needed } // Sort projects by last accessed (oldest first) const projectList = Array.from(this.getProjects()) .filter((p) => p.id !== this.getActiveProjectId()) // Never disable active project .sort((a, b) => a.lastAccessed.getTime() - b.lastAccessed.getTime()); // Disable projects until under threshold for (const project of projectList) { if ( currentMemory - report.memoryFreed < (this.settings.memoryThreshold || Infinity) ) { break; } const memoryBefore = project.memoryUsage || 0; await this.disableProject(project.id); report.projectsDisabled.push(project.id); report.memoryFreed += memoryBefore; report.connectionsReleased++; } return report; } /** * Estimate project memory usage */ async estimateProjectMemoryUsage( fs: IReactiveFileSystem, ): Promise<number> { try { // Basic estimation - can be improved based on FS type const size = await fs.size(); // Add overhead for metadata and structures (rough estimate) return size * 1.2; } catch { return 0; } } /** * Calculate total memory usage */ private async calculateTotalMemoryUsage(): Promise<number> { let total = 0; for (const project of this.getProjects()) { total += project.memoryUsage || 0; } return total; } /** * Format bytes to human readable */ private formatBytes(bytes: number): string { if (bytes === 0) return "0 B"; const k = 1024; const sizes = ["B", "KB", "MB", "GB", "TB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`; } /** * Update settings */ updateSettings(settings: Partial<WorkspaceSettings>): void { Object.assign(this.settings, settings); } }