UNPKG

flowengine-n8n-workflow-builder

Version:

Build n8n workflows from text using AI. Connect to Claude, Cursor, or any LLM to generate and validate n8n workflows with expert knowledge and intelligent auto-fixing. Built by FlowEngine. Now with real node parameter schemas from n8n packages!

101 lines 3.73 kB
/** * Workflow Tester - Dry-run and Simulation * * Test workflows without executing them, with mock data injection * and validation of execution paths. */ /** * Perform dry-run test of workflow */ export function testWorkflow(workflow, mockData) { const errors = []; const warnings = []; const executionPath = []; // Find trigger node const trigger = workflow.nodes.find(n => n.type.includes('Trigger') || n.type.includes('trigger')); if (!trigger) { errors.push('No trigger node found - workflow cannot start'); return { success: false, errors, warnings, executionPath }; } executionPath.push(trigger.name); // Trace execution path let currentNode = trigger.name; const visited = new Set(); let iterations = 0; const maxIterations = 100; // Prevent infinite loops while (currentNode && iterations < maxIterations) { if (visited.has(currentNode)) { warnings.push(`Potential infinite loop detected at ${currentNode}`); break; } visited.add(currentNode); iterations++; // Find next nodes const connections = workflow.connections[currentNode]; if (!connections || !connections.main || connections.main.length === 0) { break; // End of workflow } // Take first path (main output 0) const nextConnections = connections.main[0]; if (!nextConnections || nextConnections.length === 0) { break; } currentNode = nextConnections[0].node; executionPath.push(currentNode); } // Check for orphaned nodes (not in execution path) const orphaned = workflow.nodes.filter(n => !n.type.includes('Trigger') && !n.type.includes('trigger') && !executionPath.includes(n.name)); if (orphaned.length > 0) { warnings.push(`${orphaned.length} node(s) not in execution path: ${orphaned.map(n => n.name).join(', ')}`); } // Validate required credentials workflow.nodes.forEach(node => { if (node.credentials && Object.keys(node.credentials).length > 0) { Object.keys(node.credentials).forEach(credType => { const cred = node.credentials?.[credType]; if (cred?.id?.includes('placeholder')) { errors.push(`Node "${node.name}" requires ${credType} credentials`); } }); } }); // Estimate duration (rough calculation) const estimatedDuration = executionPath.length * 1000; // 1 second per node return { success: errors.length === 0, errors, warnings, executionPath, estimatedDuration, }; } /** * Validate workflow can execute end-to-end */ export function validateExecutionPath(workflow) { const issues = []; // Check all connections point to valid nodes Object.entries(workflow.connections).forEach(([sourceName, connections]) => { const sourceNode = workflow.nodes.find(n => n.name === sourceName); if (!sourceNode) { issues.push(`Connection source "${sourceName}" not found in nodes`); } Object.values(connections).forEach((connArray) => { connArray?.forEach?.((conns) => { conns?.forEach?.((conn) => { const targetNode = workflow.nodes.find(n => n.name === conn?.node); if (!targetNode && conn?.node) { issues.push(`Connection target "${conn.node}" not found in nodes`); } }); }); }); }); return { valid: issues.length === 0, issues, }; } //# sourceMappingURL=tester.js.map