UNPKG

datapilot-cli

Version:

Enterprise-grade streaming multi-format data analysis with comprehensive statistical insights and intelligent relationship detection - supports CSV, JSON, Excel, TSV, Parquet - memory-efficient, cross-platform

358 lines 14.3 kB
"use strict"; /** * Enhanced Dependency Injection System for CLI Analyzers * Manages complex dependencies between analysis sections with caching and validation */ Object.defineProperty(exports, "__esModule", { value: true }); exports.DependencyChainValidator = exports.AnalyzerDependencyResolver = void 0; exports.createDependencyResolver = createDependencyResolver; const logger_1 = require("../utils/logger"); const types_1 = require("../core/types"); /** * Concrete implementation of dependency resolver with caching and validation */ class AnalyzerDependencyResolver { dependencies = new Map(); resolutionOrder = []; resolverFunctions = new Map(); context; constructor(context = {}) { this.context = context; this.setupResolutionOrder(); } /** * Define the canonical dependency resolution order */ setupResolutionOrder() { this.resolutionOrder = [ 'section1', // Overview - no dependencies 'section2', // Quality - depends on section1 (optional) 'section3', // EDA - depends on section1 (optional) 'section4', // Visualization - depends on section1, section3 'section5', // Engineering - depends on section1, section2, section3 'section6', // Modeling - depends on section1, section2, section3, section5 ]; } /** * Register resolver function for a section */ registerResolver(sectionName, resolver) { this.resolverFunctions.set(sectionName, resolver); logger_1.logger.debug(`Registered resolver for ${sectionName}`, this.context); } /** * Resolve a specific section dependency with error handling and retries */ async resolve(sectionName) { const startTime = Date.now(); const sectionContext = { ...this.context, section: sectionName, operation: 'dependency_resolution', }; try { // Check if already cached if (this.dependencies.has(sectionName)) { const cached = this.dependencies.get(sectionName); logger_1.logger.debug(`Using cached result for ${sectionName} (cached at ${cached.timestamp.toISOString()})`, sectionContext); return cached.result; } // Validate resolver exists const resolver = this.resolverFunctions.get(sectionName); if (!resolver) { throw types_1.DataPilotError.analysis(`No resolver registered for section: ${sectionName}`, 'MISSING_RESOLVER', sectionContext, [ { action: 'Register resolver', description: `Call registerResolver('${sectionName}', resolver) before resolving`, severity: types_1.ErrorSeverity.HIGH, }, ]); } logger_1.logger.info(`Resolving dependency: ${sectionName}`, sectionContext); // Execute resolver with timeout const result = await this.executeWithTimeout(resolver, 300000); // 5 minute timeout // Validate result this.validateSectionResult(sectionName, result); // Cache the result this.cache(sectionName, result); const duration = Date.now() - startTime; logger_1.logger.info(`Successfully resolved ${sectionName} in ${duration}ms`, sectionContext); return result; } catch (error) { const duration = Date.now() - startTime; logger_1.logger.error(`Failed to resolve ${sectionName} after ${duration}ms`, sectionContext, error); if (error instanceof types_1.DataPilotError) { throw error; } throw types_1.DataPilotError.analysis(`Failed to resolve dependency ${sectionName}: ${error instanceof Error ? error.message : 'Unknown error'}`, 'DEPENDENCY_RESOLUTION_FAILED', sectionContext, [ { action: 'Check analyzer implementation', description: 'Verify the analyzer for this section is working correctly', severity: types_1.ErrorSeverity.HIGH, }, { action: 'Check input data', description: 'Ensure the input data is valid and accessible', severity: types_1.ErrorSeverity.MEDIUM, }, ]); } } /** * Execute resolver with timeout protection */ async executeWithTimeout(resolver, timeoutMs) { return Promise.race([ resolver(), new Promise((_, reject) => setTimeout(() => reject(new Error('Resolver timeout')), timeoutMs)), ]); } /** * Validate that a section result has the expected structure */ validateSectionResult(sectionName, result) { if (!result) { throw types_1.DataPilotError.validation(`${sectionName} resolver returned null or undefined`, 'NULL_RESULT', { section: sectionName }); } // Check for required properties based on section type const validationRules = this.getSectionValidationRules(sectionName); for (const rule of validationRules) { if (!this.hasProperty(result, rule.property)) { throw types_1.DataPilotError.validation(`${sectionName} result missing required property: ${rule.property}`, 'MISSING_PROPERTY', { section: sectionName }, [ { action: 'Check analyzer implementation', description: `Ensure ${sectionName} analyzer returns an object with ${rule.property}`, severity: types_1.ErrorSeverity.HIGH, }, ]); } } // Check for warnings array if (result.warnings && !Array.isArray(result.warnings)) { throw types_1.DataPilotError.validation(`${sectionName} warnings must be an array`, 'INVALID_WARNINGS', { section: sectionName }); } } /** * Get validation rules for a specific section */ getSectionValidationRules(sectionName) { const rules = { section1: [ { property: 'overview', required: true }, { property: 'warnings', required: true }, { property: 'performanceMetrics', required: false }, ], section2: [ { property: 'qualityAudit', required: true }, { property: 'warnings', required: true }, { property: 'performanceMetrics', required: false }, ], section3: [ { property: 'edaAnalysis', required: true }, { property: 'warnings', required: true }, { property: 'performanceMetrics', required: false }, ], section4: [ { property: 'visualizationAnalysis', required: true }, { property: 'warnings', required: true }, { property: 'performanceMetrics', required: false }, ], section5: [ { property: 'engineeringAnalysis', required: true }, { property: 'warnings', required: true }, { property: 'performanceMetrics', required: false }, ], section6: [ { property: 'modelingAnalysis', required: true }, { property: 'warnings', required: true }, { property: 'performanceMetrics', required: false }, ], }; return rules[sectionName] || []; } /** * Check if object has a property using safe property access */ hasProperty(obj, property) { return obj && typeof obj === 'object' && property in obj; } /** * Cache a section result with metadata */ cache(sectionName, result) { const dependency = { name: sectionName, result, timestamp: new Date(), cached: true, }; this.dependencies.set(sectionName, dependency); logger_1.logger.debug(`Cached result for ${sectionName}`, { ...this.context, section: sectionName, cacheSize: this.dependencies.size, }); } /** * Clear all cached dependencies */ clear() { const cachedCount = this.dependencies.size; this.dependencies.clear(); this.resolverFunctions.clear(); logger_1.logger.debug(`Cleared ${cachedCount} cached dependencies`, this.context); } /** * Check if a section is already cached */ has(sectionName) { return this.dependencies.has(sectionName); } /** * Get all cached section names */ getCachedSections() { return Array.from(this.dependencies.keys()); } /** * Get dependency resolution statistics */ getStats() { const dependencies = Array.from(this.dependencies.values()); const timestamps = dependencies.map((dep) => dep.timestamp); return { totalCached: this.dependencies.size, cachedSections: Array.from(this.dependencies.keys()), oldestCache: timestamps.length > 0 ? new Date(Math.min(...timestamps.map((t) => t.getTime()))) : undefined, newestCache: timestamps.length > 0 ? new Date(Math.max(...timestamps.map((t) => t.getTime()))) : undefined, }; } /** * Resolve multiple dependencies in order */ async resolveMultiple(sectionNames) { const results = {}; // Sort by resolution order to ensure dependencies are resolved first const orderedSections = sectionNames.sort((a, b) => { const indexA = this.resolutionOrder.indexOf(a); const indexB = this.resolutionOrder.indexOf(b); return indexA - indexB; }); for (const sectionName of orderedSections) { results[sectionName] = await this.resolve(sectionName); } return results; } /** * Get the recommended resolution order for given sections */ getResolutionOrder(sectionNames) { return this.resolutionOrder.filter((section) => sectionNames.includes(section)); } /** * Check if all dependencies for a section are available */ canResolve(sectionName) { const dependencies = this.getSectionDependencies(sectionName); const missingDependencies = dependencies.filter((dep) => !this.has(dep)); return { canResolve: missingDependencies.length === 0, missingDependencies, }; } /** * Get the dependencies for a specific section */ getSectionDependencies(sectionName) { const dependencyMap = { section1: [], section2: [], // Can optionally use section1 but not required section3: [], // Can optionally use section1 but not required section4: ['section1', 'section3'], section5: ['section1', 'section2', 'section3'], section6: ['section1', 'section2', 'section3'], // section5 is resolved internally }; return dependencyMap[sectionName] || []; } } exports.AnalyzerDependencyResolver = AnalyzerDependencyResolver; /** * Factory function to create a configured dependency resolver */ function createDependencyResolver(filePath, options, context = {}) { const resolver = new AnalyzerDependencyResolver({ ...context, filePath, operation: 'dependency_management', }); // The actual resolvers will be registered by the CLI orchestrator // based on the sections to be executed return resolver; } /** * Dependency chain validator for complex analysis pipelines */ class DependencyChainValidator { dependencies = new Map(); constructor() { this.setupDependencyMap(); } setupDependencyMap() { this.dependencies.set('section1', []); this.dependencies.set('section2', []); this.dependencies.set('section3', []); this.dependencies.set('section4', ['section1', 'section3']); this.dependencies.set('section5', ['section1', 'section2', 'section3']); this.dependencies.set('section6', ['section1', 'section2', 'section3']); } /** * Validate that all dependencies can be resolved for the given sections */ validateChain(requestedSections) { const errors = []; const warnings = []; for (const section of requestedSections) { const deps = this.dependencies.get(section); if (!deps) { errors.push(`Unknown section: ${section}`); continue; } for (const dep of deps) { if (!requestedSections.includes(dep)) { errors.push(`Section ${section} requires ${dep}, but ${dep} is not in the execution plan`); } } } // Check for potential performance issues if (requestedSections.includes('section6') && requestedSections.length === 1) { warnings.push('Running section6 alone will require executing sections 1, 2, 3, and 5 as dependencies'); } return { isValid: errors.length === 0, errors, warnings, }; } /** * Get the optimal execution order for the given sections */ getExecutionOrder(requestedSections) { const allRequired = new Set(); // Add all requested sections and their dependencies for (const section of requestedSections) { allRequired.add(section); const deps = this.dependencies.get(section) || []; deps.forEach((dep) => allRequired.add(dep)); } // Sort by dependency order const ordered = ['section1', 'section2', 'section3', 'section4', 'section5', 'section6']; return ordered.filter((section) => allRequired.has(section)); } } exports.DependencyChainValidator = DependencyChainValidator; //# sourceMappingURL=dependency-resolver.js.map