validation-summary.tsβ’5.53 kB
#!/usr/bin/env node
/**
* Run validation on templates and provide a clean summary
*/
import { existsSync } from 'fs';
import path from 'path';
import { NodeRepository } from '../database/node-repository';
import { createDatabaseAdapter } from '../database/database-adapter';
import { WorkflowValidator } from '../services/workflow-validator';
import { EnhancedConfigValidator } from '../services/enhanced-config-validator';
import { TemplateRepository } from '../templates/template-repository';
import { Logger } from '../utils/logger';
const logger = new Logger({ prefix: '[validation-summary]' });
async function runValidationSummary() {
const dbPath = path.join(process.cwd(), 'data', 'nodes.db');
if (!existsSync(dbPath)) {
logger.error('Database not found. Run npm run rebuild first.');
process.exit(1);
}
const db = await createDatabaseAdapter(dbPath);
const repository = new NodeRepository(db);
const templateRepository = new TemplateRepository(db);
const validator = new WorkflowValidator(
repository,
EnhancedConfigValidator
);
try {
const templates = await templateRepository.getAllTemplates(50);
const results = {
total: templates.length,
valid: 0,
invalid: 0,
noErrors: 0,
errorCategories: {
unknownNodes: 0,
missingRequired: 0,
expressionErrors: 0,
connectionErrors: 0,
cycles: 0,
other: 0
},
commonUnknownNodes: new Map<string, number>(),
stickyNoteIssues: 0
};
for (const template of templates) {
try {
const workflow = JSON.parse(template.workflow_json || '{}');
const validationResult = await validator.validateWorkflow(workflow, {
profile: 'minimal' // Use minimal profile to focus on critical errors
});
if (validationResult.valid) {
results.valid++;
} else {
results.invalid++;
}
if (validationResult.errors.length === 0) {
results.noErrors++;
}
// Categorize errors
validationResult.errors.forEach((error: any) => {
const errorMsg = typeof error.message === 'string' ? error.message : JSON.stringify(error.message);
if (errorMsg.includes('Unknown node type')) {
results.errorCategories.unknownNodes++;
const match = errorMsg.match(/Unknown node type: (.+)/);
if (match) {
const nodeType = match[1];
results.commonUnknownNodes.set(nodeType, (results.commonUnknownNodes.get(nodeType) || 0) + 1);
}
} else if (errorMsg.includes('missing_required')) {
results.errorCategories.missingRequired++;
if (error.nodeName?.includes('Sticky Note')) {
results.stickyNoteIssues++;
}
} else if (errorMsg.includes('Expression error')) {
results.errorCategories.expressionErrors++;
} else if (errorMsg.includes('connection') || errorMsg.includes('Connection')) {
results.errorCategories.connectionErrors++;
} else if (errorMsg.includes('cycle')) {
results.errorCategories.cycles++;
} else {
results.errorCategories.other++;
}
});
} catch (error) {
results.invalid++;
}
}
// Print summary
console.log('\n' + '='.repeat(80));
console.log('WORKFLOW VALIDATION SUMMARY');
console.log('='.repeat(80));
console.log(`\nTemplates analyzed: ${results.total}`);
console.log(`Valid workflows: ${results.valid} (${((results.valid / results.total) * 100).toFixed(1)}%)`);
console.log(`Workflows without errors: ${results.noErrors} (${((results.noErrors / results.total) * 100).toFixed(1)}%)`);
console.log('\nError Categories:');
console.log(` - Unknown nodes: ${results.errorCategories.unknownNodes}`);
console.log(` - Missing required properties: ${results.errorCategories.missingRequired}`);
console.log(` (Sticky note issues: ${results.stickyNoteIssues})`);
console.log(` - Expression errors: ${results.errorCategories.expressionErrors}`);
console.log(` - Connection errors: ${results.errorCategories.connectionErrors}`);
console.log(` - Workflow cycles: ${results.errorCategories.cycles}`);
console.log(` - Other errors: ${results.errorCategories.other}`);
if (results.commonUnknownNodes.size > 0) {
console.log('\nTop Unknown Node Types:');
const sortedNodes = Array.from(results.commonUnknownNodes.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 10);
sortedNodes.forEach(([nodeType, count]) => {
console.log(` - ${nodeType} (${count} occurrences)`);
});
}
console.log('\nKey Insights:');
const stickyNotePercent = ((results.stickyNoteIssues / results.errorCategories.missingRequired) * 100).toFixed(1);
console.log(` - ${stickyNotePercent}% of missing required property errors are from Sticky Notes`);
console.log(` - Most workflows have some validation warnings (best practices)`);
console.log(` - Expression validation is working well`);
console.log(` - Node type normalization is handling most cases correctly`);
} catch (error) {
logger.error('Failed to run validation summary:', error);
process.exit(1);
} finally {
db.close();
}
}
// Run summary
runValidationSummary().catch(error => {
logger.error('Summary failed:', error);
process.exit(1);
});