UNPKG

aios-core

Version:

Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework

527 lines (448 loc) 15.9 kB
const fs = require('fs').promises; const path = require('path'); const semver = require('semver'); const chalk = require('chalk'); /** * Framework version tracking utility for AIOS-FULLSTACK * Manages framework versions, migration history, and compatibility tracking */ class VersionTracker { constructor(options = {}) { this.rootPath = options.rootPath || process.cwd(); this.versionFile = path.join(this.rootPath, '.aios', 'version-info.json'); this.migrationDir = path.join(this.rootPath, '.aios', 'migrations'); this.currentVersion = null; this.versionInfo = null; } /** * Initialize version tracking system */ async initialize() { try { // Ensure directories exist await fs.mkdir(path.dirname(this.versionFile), { recursive: true }); await fs.mkdir(this.migrationDir, { recursive: true }); // Check if version info exists try { await fs.access(this.versionFile); this.versionInfo = await this.loadVersionInfo(); } catch (_error) { // Create initial version info this.versionInfo = this.createInitialVersionInfo(); await this.saveVersionInfo(); console.log(chalk.green('✅ Version tracking initialized')); } this.currentVersion = this.versionInfo.current_version; return this.versionInfo; } catch (_error) { console.error(chalk.red(`Version tracking initialization failed: ${error.message}`)); throw error; } } /** * Get current framework version */ async getCurrentVersion() { if (!this.versionInfo) { await this.initialize(); } return this.currentVersion; } /** * Record a new framework version */ async recordVersion(versionData) { try { const { version, description, changes = [], migration_required = false, breaking_changes = [], compatibility_notes = '', release_notes = '' } = versionData; // Validate version format if (!semver.valid(version)) { throw new Error(`Invalid version format: ${version}`); } // Check if version already exists if (this.versionInfo.versions.some(v => v.version === version)) { throw new Error(`Version ${version} already exists`); } // Validate version increment if (this.currentVersion && !semver.gt(version, this.currentVersion)) { throw new Error(`New version ${version} must be greater than current ${this.currentVersion}`); } const versionEntry = { version, description, timestamp: new Date().toISOString(), changes, migration_required, breaking_changes, compatibility_notes, release_notes, previous_version: this.currentVersion, status: 'active', components_modified: await this.detectModifiedComponents(), api_changes: await this.detectApiChanges(), deprecations: [], migration_path: migration_required ? `${this.currentVersion}-to-${version}` : null }; // Add to version history this.versionInfo.versions.unshift(versionEntry); this.versionInfo.current_version = version; this.versionInfo.previous_version = this.currentVersion; this.versionInfo.last_updated = new Date().toISOString(); this.versionInfo.total_versions++; // Update current version this.currentVersion = version; // Save updated info await this.saveVersionInfo(); console.log(chalk.green(`✅ Recorded new framework version: ${version}`)); if (migration_required) { console.log(chalk.yellow(`⚠️ Migration required from ${this.versionInfo.previous_version} to ${version}`)); } return versionEntry; } catch (_error) { console.error(chalk.red(`Failed to record version: ${error.message}`)); throw error; } } /** * Get version history with optional filtering */ async getVersionHistory(options = {}) { const { limit = 10, include_migrations = true, include_breaking = false, since_version = null, status_filter = null } = options; if (!this.versionInfo) { await this.initialize(); } let versions = [...this.versionInfo.versions]; // Apply filters if (since_version) { const sinceIndex = versions.findIndex(v => v.version === since_version); if (sinceIndex !== -1) { versions = versions.slice(0, sinceIndex); } } if (status_filter) { versions = versions.filter(v => v.status === status_filter); } if (include_breaking) { versions = versions.filter(v => v.breaking_changes?.length > 0); } if (!include_migrations) { versions = versions.filter(v => !v.migration_required); } // Apply limit versions = versions.slice(0, limit); return { current_version: this.currentVersion, total_versions: this.versionInfo.total_versions, filtered_count: versions.length, versions: versions.map(v => ({ version: v.version, description: v.description, timestamp: v.timestamp, migration_required: v.migration_required, breaking_changes: v.breaking_changes?.length || 0, status: v.status, components_modified: v.components_modified?.length || 0 })) }; } /** * Check compatibility between versions */ async checkCompatibility(fromVersion, toVersion) { const compatibility = { compatible: false, migration_required: false, breaking_changes: [], warnings: [], migration_path: null, estimated_effort: 'unknown', risk_level: 'low' }; try { // Validate versions if (!semver.valid(fromVersion) || !semver.valid(toVersion)) { throw new Error('Invalid version format provided'); } const fromVersionInfo = this.versionInfo.versions.find(v => v.version === fromVersion); const toVersionInfo = this.versionInfo.versions.find(v => v.version === toVersion); if (!fromVersionInfo) { compatibility.warnings.push(`Source version ${fromVersion} not found in history`); } if (!toVersionInfo) { compatibility.warnings.push(`Target version ${toVersion} not found in history`); } // Check semantic version compatibility const versionDiff = semver.diff(fromVersion, toVersion); switch (versionDiff) { case 'patch': compatibility.compatible = true; compatibility.risk_level = 'low'; compatibility.estimated_effort = 'minimal'; break; case 'minor': compatibility.compatible = true; compatibility.migration_required = toVersionInfo?.migration_required || false; compatibility.risk_level = 'low'; compatibility.estimated_effort = 'low'; break; case 'major': compatibility.migration_required = true; compatibility.risk_level = 'high'; compatibility.estimated_effort = 'high'; compatibility.compatible = false; break; case 'premajor': case 'preminor': case 'prepatch': compatibility.migration_required = true; compatibility.risk_level = 'medium'; compatibility.estimated_effort = 'medium'; compatibility.compatible = false; break; } // Collect breaking changes in the path const versionsInPath = this.getVersionsInPath(fromVersion, toVersion); for (const version of versionsInPath) { if (version.breaking_changes?.length > 0) { compatibility.breaking_changes.push(...version.breaking_changes); compatibility.migration_required = true; } } // Set migration path if needed if (compatibility.migration_required) { compatibility.migration_path = `${fromVersion}-to-${toVersion}`; } return compatibility; } catch (_error) { compatibility.warnings.push(`Compatibility check failed: ${error.message}`); return compatibility; } } /** * Mark a version as deprecated */ async deprecateVersion(version, deprecationInfo = {}) { try { const versionEntry = this.versionInfo.versions.find(v => v.version === version); if (!versionEntry) { throw new Error(`Version ${version} not found`); } if (versionEntry.status === 'deprecated') { throw new Error(`Version ${version} is already deprecated`); } const deprecation = { deprecated_at: new Date().toISOString(), reason: deprecationInfo.reason || 'Version deprecated', migration_target: deprecationInfo.migration_target || this.currentVersion, removal_timeline: deprecationInfo.removal_timeline || 'TBD', deprecation_notice: deprecationInfo.notice || `Version ${version} is deprecated. Please upgrade to ${this.currentVersion}.` }; versionEntry.status = 'deprecated'; versionEntry.deprecation_info = deprecation; await this.saveVersionInfo(); console.log(chalk.yellow(`⚠️ Version ${version} marked as deprecated`)); return deprecation; } catch (_error) { console.error(chalk.red(`Failed to deprecate version: ${error.message}`)); throw error; } } /** * Get migration path between versions */ async getMigrationPath(fromVersion, toVersion) { const path = { from_version: fromVersion, to_version: toVersion, steps: [], total_migrations: 0, estimated_duration: 0, risk_assessment: 'low', requirements: [], rollback_available: true }; try { const versionsInPath = this.getVersionsInPath(fromVersion, toVersion); for (const version of versionsInPath) { if (version.migration_required) { const step = { from: version.previous_version, to: version.version, migration_id: version.migration_path, breaking_changes: version.breaking_changes || [], estimated_time: this.estimateMigrationTime(version), risk_level: this.assessMigrationRisk(version), prerequisites: version.migration_prerequisites || [], rollback_supported: true }; path.steps.push(step); path.total_migrations++; path.estimated_duration += step.estimated_time; if (step.risk_level === 'high') { path.risk_assessment = 'high'; } else if (step.risk_level === 'medium' && path.risk_assessment === 'low') { path.risk_assessment = 'medium'; } } } // Collect unique requirements path.requirements = [...new Set( path.steps.flatMap(step => step.prerequisites) )]; return path; } catch (_error) { console.error(chalk.red(`Failed to get migration path: ${error.message}`)); throw error; } } /** * Generate version report */ async generateVersionReport() { if (!this.versionInfo) { await this.initialize(); } const report = { timestamp: new Date().toISOString(), current_version: this.currentVersion, total_versions: this.versionInfo.total_versions, version_distribution: { active: 0, deprecated: 0, archived: 0 }, migration_summary: { total_migrations: 0, pending_migrations: 0, failed_migrations: 0 }, breaking_changes_summary: { total_breaking_versions: 0, recent_breaking_changes: [] }, compatibility_matrix: {}, recommendations: [] }; // Analyze version distribution this.versionInfo.versions.forEach(version => { report.version_distribution[version.status]++; if (version.migration_required) { report.migration_summary.total_migrations++; } if (version.breaking_changes?.length > 0) { report.breaking_changes_summary.total_breaking_versions++; if (semver.gte(version.version, semver.major(this.currentVersion) + '.0.0')) { report.breaking_changes_summary.recent_breaking_changes.push({ version: version.version, changes: version.breaking_changes.slice(0, 3) }); } } }); // Generate recommendations if (report.version_distribution.deprecated > 5) { report.recommendations.push({ type: 'cleanup', priority: 'medium', message: `${report.version_distribution.deprecated} deprecated versions should be archived` }); } if (report.breaking_changes_summary.total_breaking_versions > 10) { report.recommendations.push({ type: 'stability', priority: 'high', message: 'High number of breaking changes detected. Consider stability improvements.' }); } return report; } // Private helper methods createInitialVersionInfo() { return { schema_version: '1.0.0', current_version: '1.0.0', previous_version: null, created_at: new Date().toISOString(), last_updated: new Date().toISOString(), total_versions: 1, versions: [{ version: '1.0.0', description: 'Initial framework version', timestamp: new Date().toISOString(), changes: ['Initial framework setup'], migration_required: false, breaking_changes: [], compatibility_notes: 'Initial version', release_notes: 'AIOS-FULLSTACK framework v1.0.0', previous_version: null, status: 'active', components_modified: [], api_changes: [], deprecations: [], migration_path: null }], migration_history: [], compatibility_matrix: {} }; } async loadVersionInfo() { try { const content = await fs.readFile(this.versionFile, 'utf-8'); return JSON.parse(content); } catch (_error) { throw new Error(`Failed to load version info: ${error.message}`); } } async saveVersionInfo() { try { const content = JSON.stringify(this.versionInfo, null, 2); await fs.writeFile(this.versionFile, content); } catch (_error) { throw new Error(`Failed to save version info: ${error.message}`); } } async detectModifiedComponents() { // This would analyze git changes or compare with previous state // For now, return empty array return []; } async detectApiChanges() { // This would analyze API changes between versions // For now, return empty array return []; } getVersionsInPath(fromVersion, toVersion) { const versions = this.versionInfo.versions.filter(v => { return semver.gt(v.version, fromVersion) && semver.lte(v.version, toVersion); }); return versions.sort((a, b) => semver.compare(a.version, b.version)); } estimateMigrationTime(version) { // Simple heuristic based on breaking changes and components modified const baseTime = 30; // minutes const breakingChangeMultiplier = version.breaking_changes?.length || 0; const componentMultiplier = version.components_modified?.length || 0; return baseTime + (breakingChangeMultiplier * 15) + (componentMultiplier * 5); } assessMigrationRisk(version) { const breakingChanges = version.breaking_changes?.length || 0; const componentsModified = version.components_modified?.length || 0; if (breakingChanges > 5 || componentsModified > 20) return 'high'; if (breakingChanges > 2 || componentsModified > 10) return 'medium'; return 'low'; } } module.exports = VersionTracker;