UNPKG

@bernierllc/generic-workflow-ui

Version:

Generic, reusable workflow UI components with linear and graph visualization

122 lines (121 loc) 4.91 kB
"use strict"; /* Copyright (c) 2025 Bernier LLC */ Object.defineProperty(exports, "__esModule", { value: true }); exports.validateWorkflowJSON = validateWorkflowJSON; /** * Validate JSON workflow definition */ function validateWorkflowJSON(json) { const errors = []; // Validate required fields if (!json.name || typeof json.name !== 'string') { errors.push({ path: 'name', message: 'Workflow name is required and must be a string', code: 'REQUIRED_FIELD' }); } if (!Array.isArray(json.nodes)) { errors.push({ path: 'nodes', message: 'Nodes must be an array', code: 'INVALID_TYPE' }); } else { // Validate nodes json.nodes.forEach((node, index) => { if (!node.id || typeof node.id !== 'string') { errors.push({ path: `nodes[${index}].id`, message: 'Node ID is required and must be a string', code: 'REQUIRED_FIELD' }); } if (!node.name || typeof node.name !== 'string') { errors.push({ path: `nodes[${index}].name`, message: 'Node name is required and must be a string', code: 'REQUIRED_FIELD' }); } if (!node.type || typeof node.type !== 'string') { errors.push({ path: `nodes[${index}].type`, message: 'Node type is required and must be a string', code: 'REQUIRED_FIELD' }); } if (!Array.isArray(node.position) || node.position.length !== 2) { errors.push({ path: `nodes[${index}].position`, message: 'Node position must be an array of [x, y] coordinates', code: 'INVALID_FORMAT' }); } }); // Check for duplicate node IDs const nodeIds = json.nodes.map((n) => n.id); const duplicates = nodeIds.filter((id, index) => nodeIds.indexOf(id) !== index); duplicates.forEach((id) => { errors.push({ path: 'nodes', message: `Duplicate node ID: ${id}`, code: 'DUPLICATE_ID' }); }); } if (!json.connections || typeof json.connections !== 'object') { errors.push({ path: 'connections', message: 'Connections must be an object', code: 'INVALID_TYPE' }); } else if (Array.isArray(json.nodes)) { // Validate connections reference valid nodes (only if nodes is a valid array) const nodeIds = new Set(json.nodes.map((n) => n.id)); Object.entries(json.connections).forEach(([nodeId, outputs]) => { if (!nodeIds.has(nodeId)) { errors.push({ path: `connections.${nodeId}`, message: `Connection references non-existent node: ${nodeId}`, code: 'INVALID_REFERENCE' }); } Object.entries(outputs).forEach(([outputType, connectionGroups]) => { connectionGroups.forEach((connections, groupIndex) => { connections.forEach((connection, connIndex) => { if (!nodeIds.has(connection.node)) { errors.push({ path: `connections.${nodeId}.${outputType}[${groupIndex}][${connIndex}].node`, message: `Connection references non-existent node: ${connection.node}`, code: 'INVALID_REFERENCE' }); } if (!connection.type || typeof connection.type !== 'string') { errors.push({ path: `connections.${nodeId}.${outputType}[${groupIndex}][${connIndex}].type`, message: 'Connection type is required and must be a string', code: 'REQUIRED_FIELD' }); } if (connection.index === undefined || typeof connection.index !== 'number') { errors.push({ path: `connections.${nodeId}.${outputType}[${groupIndex}][${connIndex}].index`, message: 'Connection index is required and must be a number', code: 'REQUIRED_FIELD' }); } }); }); }); }); } return { valid: errors.length === 0, errors }; }