@aaswe/codebase-ai
Version:
AI-Assisted Software Engineering (AASWE) - Rich codebase context for IDE LLMs
355 lines • 13 kB
JavaScript
"use strict";
/**
* Trigger Orchestrator
*
* Orchestrates automatic project analysis triggers from various sources
* including NPM hooks, file system changes, and manual triggers.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TriggerOrchestrator = void 0;
const events_1 = require("events");
const logger_1 = __importDefault(require("../../utils/logger"));
const NPMHookManager_1 = require("./NPMHookManager");
const InstallationDetector_1 = require("./InstallationDetector");
const ProjectAnalysisService_1 = require("../project-analysis/ProjectAnalysisService");
/**
* Trigger Orchestrator
*
* Central coordinator for all automatic analysis triggers.
* Manages trigger sources, queuing, and execution coordination.
*/
class TriggerOrchestrator extends events_1.EventEmitter {
config;
npmHookManager;
installationDetector;
projectAnalysisService;
isInitialized = false;
triggerQueue = [];
activeTriggers = new Set();
lastAnalysisTime;
triggerHistory = [];
constructor(config = {}) {
super();
this.config = {
projectRoot: process.cwd(),
enableNPMHooks: true,
enableInstallationDetection: true,
enableFileWatching: true,
analysisDelay: 2000,
maxConcurrentAnalyses: 1,
cooldownPeriod: 10000, // 10 seconds
...config
};
}
/**
* Initialize the trigger orchestrator
*/
async initialize() {
if (this.isInitialized) {
return;
}
try {
logger_1.default.info('Initializing Trigger Orchestrator', {
projectRoot: this.config.projectRoot,
enableNPMHooks: this.config.enableNPMHooks,
enableInstallationDetection: this.config.enableInstallationDetection
});
// Initialize project analysis service
this.projectAnalysisService = new ProjectAnalysisService_1.ProjectAnalysisService({
rootPath: this.config.projectRoot,
generateTTL: true,
enableWatching: this.config.enableFileWatching,
preserveBusinessContext: true
});
await this.projectAnalysisService.initialize();
// Initialize NPM hook manager
if (this.config.enableNPMHooks) {
this.npmHookManager = new NPMHookManager_1.NPMHookManager({
projectRoot: this.config.projectRoot,
analysisDelay: this.config.analysisDelay
});
await this.npmHookManager.initialize();
this.setupNPMHookListeners();
}
// Initialize installation detector
if (this.config.enableInstallationDetection) {
this.installationDetector = new InstallationDetector_1.InstallationDetector({
projectRoot: this.config.projectRoot,
debounceDelay: this.config.analysisDelay
});
await this.installationDetector.startMonitoring();
this.setupInstallationDetectorListeners();
}
// Setup project analysis service listeners
this.setupProjectAnalysisListeners();
// Start processing trigger queue
this.startTriggerProcessing();
this.isInitialized = true;
logger_1.default.info('Trigger Orchestrator initialized successfully');
}
catch (error) {
logger_1.default.error('Failed to initialize Trigger Orchestrator', { error });
throw error;
}
}
/**
* Manually trigger analysis
*/
async triggerAnalysis(reason = 'manual', priority = 'medium', metadata) {
const trigger = {
id: this.generateTriggerId(),
type: 'manual',
timestamp: new Date(),
source: 'manual',
reason,
priority,
metadata
};
logger_1.default.info('Manual analysis trigger requested', {
triggerId: trigger.id,
reason,
priority
});
this.queueTrigger(trigger);
return trigger.id;
}
/**
* Get trigger status and statistics
*/
getStatus() {
return {
isInitialized: this.isInitialized,
config: { ...this.config },
queueLength: this.triggerQueue.length,
activeTriggers: this.activeTriggers.size,
lastAnalysis: this.lastAnalysisTime,
recentTriggers: this.triggerHistory.slice(-10)
};
}
/**
* Shutdown the orchestrator
*/
async shutdown() {
logger_1.default.info('Shutting down Trigger Orchestrator');
try {
// Stop NPM hook manager
if (this.npmHookManager) {
await this.npmHookManager.removeHooks();
}
// Stop installation detector
if (this.installationDetector) {
await this.installationDetector.stopMonitoring();
}
// Shutdown project analysis service
if (this.projectAnalysisService) {
await this.projectAnalysisService.shutdown();
}
// Clear queues and active triggers
this.triggerQueue = [];
this.activeTriggers.clear();
this.isInitialized = false;
logger_1.default.info('Trigger Orchestrator shutdown completed');
}
catch (error) {
logger_1.default.error('Error during Trigger Orchestrator shutdown', { error });
throw error;
}
}
// Private methods
setupNPMHookListeners() {
if (!this.npmHookManager)
return;
this.npmHookManager.on('analysis_triggered', (event) => {
const trigger = {
id: this.generateTriggerId(),
type: 'npm_hook',
timestamp: event.timestamp,
source: `npm_${event.triggeredBy}`,
reason: `NPM ${event.type} hook triggered`,
priority: 'high',
metadata: event
};
logger_1.default.debug('NPM hook trigger received', { trigger });
this.queueTrigger(trigger);
});
}
setupInstallationDetectorListeners() {
if (!this.installationDetector)
return;
this.installationDetector.on('installation_detected', (event) => {
if (!event.triggerAnalysis) {
logger_1.default.debug('Installation detected but analysis not triggered', {
type: event.type,
changeCount: event.changes.length
});
return;
}
const trigger = {
id: this.generateTriggerId(),
type: 'installation',
timestamp: event.timestamp,
source: `${event.packageManager}_${event.type}`,
reason: `Package ${event.type}: ${event.changes.length} changes`,
priority: this.determinePriority(event),
metadata: event
};
logger_1.default.debug('Installation trigger received', { trigger });
this.queueTrigger(trigger);
});
}
setupProjectAnalysisListeners() {
if (!this.projectAnalysisService)
return;
this.projectAnalysisService.on('analysisCompleted', (result) => {
logger_1.default.info('Analysis completed', {
analysisId: result.analysisId,
duration: result.duration,
analyzedFiles: result.summary.analyzedFiles
});
this.lastAnalysisTime = result.endTime;
this.emit('analysis_completed', result);
});
this.projectAnalysisService.on('analysisFailed', (event) => {
logger_1.default.error('Analysis failed', {
analysisId: event.analysisId,
error: event.error
});
this.emit('analysis_failed', event);
});
}
queueTrigger(trigger) {
// Check cooldown period
if (this.lastAnalysisTime && this.isInCooldownPeriod()) {
logger_1.default.debug('Trigger queued due to cooldown period', {
triggerId: trigger.id,
cooldownRemaining: this.getCooldownRemaining()
});
}
// Add to queue with priority sorting
this.triggerQueue.push(trigger);
this.triggerQueue.sort((a, b) => {
const priorityOrder = { high: 3, medium: 2, low: 1 };
return priorityOrder[b.priority] - priorityOrder[a.priority];
});
this.emit('trigger_queued', trigger);
logger_1.default.debug('Trigger queued', {
triggerId: trigger.id,
queueLength: this.triggerQueue.length,
priority: trigger.priority
});
}
startTriggerProcessing() {
// Process triggers every second
setInterval(() => {
this.processTriggerQueue();
}, 1000);
}
async processTriggerQueue() {
if (this.triggerQueue.length === 0) {
return;
}
// Check if we can process more triggers
if (this.activeTriggers.size >= this.config.maxConcurrentAnalyses) {
return;
}
// Check cooldown period
if (this.isInCooldownPeriod()) {
return;
}
// Get next trigger
const trigger = this.triggerQueue.shift();
if (!trigger) {
return;
}
// Process trigger
await this.processTrigger(trigger);
}
async processTrigger(trigger) {
const startTime = new Date();
this.activeTriggers.add(trigger.id);
logger_1.default.info('Processing trigger', {
triggerId: trigger.id,
type: trigger.type,
reason: trigger.reason,
priority: trigger.priority
});
try {
this.emit('trigger_processing', trigger);
// Execute analysis
const analysisResult = await this.projectAnalysisService.analyzeProject();
const endTime = new Date();
const duration = endTime.getTime() - startTime.getTime();
const result = {
triggerId: trigger.id,
success: true,
analysisId: analysisResult.analysisId,
startTime,
endTime,
duration
};
this.triggerHistory.push(result);
this.emit('trigger_completed', { trigger, result });
logger_1.default.info('Trigger processed successfully', {
triggerId: trigger.id,
analysisId: analysisResult.analysisId,
duration
});
}
catch (error) {
const endTime = new Date();
const duration = endTime.getTime() - startTime.getTime();
const result = {
triggerId: trigger.id,
success: false,
startTime,
endTime,
duration,
error: error instanceof Error ? error.message : 'Unknown error'
};
this.triggerHistory.push(result);
this.emit('trigger_failed', { trigger, result, error });
logger_1.default.error('Trigger processing failed', {
triggerId: trigger.id,
error,
duration
});
}
finally {
this.activeTriggers.delete(trigger.id);
}
}
isInCooldownPeriod() {
if (!this.lastAnalysisTime) {
return false;
}
const timeSinceLastAnalysis = Date.now() - this.lastAnalysisTime.getTime();
return timeSinceLastAnalysis < this.config.cooldownPeriod;
}
getCooldownRemaining() {
if (!this.lastAnalysisTime) {
return 0;
}
const timeSinceLastAnalysis = Date.now() - this.lastAnalysisTime.getTime();
return Math.max(0, this.config.cooldownPeriod - timeSinceLastAnalysis);
}
determinePriority(event) {
// High priority for new package installations
if (event.type === 'package_installed') {
return 'high';
}
// Medium priority for updates
if (event.type === 'package_updated') {
return 'medium';
}
// Low priority for other changes
return 'low';
}
generateTriggerId() {
return `trigger_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
}
exports.TriggerOrchestrator = TriggerOrchestrator;
//# sourceMappingURL=TriggerOrchestrator.js.map