( compiledDefinition: WorkflowDefinition, actionPorts: Map<string, ActionPortSnapshot>, errors: ValidationError[], )
| 324 | } |
| 325 | |
| 326 | function validateEdgeCompatibility( |
| 327 | compiledDefinition: WorkflowDefinition, |
| 328 | actionPorts: Map<string, ActionPortSnapshot>, |
| 329 | errors: ValidationError[], |
| 330 | ) { |
| 331 | const actions = new Map(compiledDefinition.actions.map((action) => [action.ref, action])); |
| 332 | |
| 333 | for (const edge of compiledDefinition.edges) { |
| 334 | const sourceAction = actions.get(edge.sourceRef); |
| 335 | const targetAction = actions.get(edge.targetRef); |
| 336 | |
| 337 | if (!sourceAction || !targetAction) { |
| 338 | errors.push({ |
| 339 | node: edge.targetRef, |
| 340 | field: 'inputMappings', |
| 341 | message: `Edge references unknown action(s): ${edge.sourceRef} -> ${edge.targetRef}`, |
| 342 | severity: 'error', |
| 343 | suggestion: 'Ensure each edge points to a valid node', |
| 344 | }); |
| 345 | continue; |
| 346 | } |
| 347 | |
| 348 | const sourcePorts = actionPorts.get(sourceAction.ref); |
| 349 | const targetPorts = actionPorts.get(targetAction.ref); |
| 350 | |
| 351 | if (!sourcePorts || !targetPorts) { |
| 352 | errors.push({ |
| 353 | node: targetAction.ref, |
| 354 | field: 'inputMappings', |
| 355 | message: `Missing port metadata for ${edge.sourceRef} -> ${edge.targetRef}`, |
| 356 | severity: 'error', |
| 357 | suggestion: 'Verify component metadata exports both inputs and outputs', |
| 358 | }); |
| 359 | continue; |
| 360 | } |
| 361 | |
| 362 | const sourceHandle = edge.sourceHandle; |
| 363 | const targetHandle = edge.targetHandle; |
| 364 | |
| 365 | // Check for malformed data edges: if one handle is present but not the other |
| 366 | const hasSourceHandle = !!sourceHandle; |
| 367 | const hasTargetHandle = !!targetHandle; |
| 368 | |
| 369 | if (hasSourceHandle && !hasTargetHandle) { |
| 370 | errors.push({ |
| 371 | node: targetAction.ref, |
| 372 | field: 'inputMappings', |
| 373 | message: `Edge has sourceHandle "${sourceHandle}" but missing targetHandle. Data edges must specify both source and target handles.`, |
| 374 | severity: 'error', |
| 375 | suggestion: |
| 376 | 'Add targetHandle to specify which input port on the target node should receive the data', |
| 377 | }); |
| 378 | continue; |
| 379 | } |
| 380 | |
| 381 | if (!hasSourceHandle && hasTargetHandle) { |
| 382 | errors.push({ |
| 383 | node: targetAction.ref, |
no test coverage detected