import type { ValidateWorkflowGraphOptions, WorkflowEdge, WorkflowGraph, WorkflowNode } from './workflows.ts' import { assertWorkflowGraphValid } from './workflows.ts' import type { Connection, NodeRef } from './workflow-builder-types.ts' import { resolveNodeId, resolveSourcePort, resolveTargetPort } from './workflow-builder-ports.ts' export interface BuildGraphOptions extends ValidateWorkflowGraphOptions { validate?: boolean } function getNode(nodeMap: Map, ref: NodeRef): WorkflowNode { const id = resolveNodeId(ref) const node = nodeMap.get(id) if (!node) throw new Error(`buildGraph: node '${id}' not found`) return node } function createUniqueEdgeId(baseId: string, usedIds: Set): string { if (!usedIds.has(baseId)) { usedIds.add(baseId) return baseId } let suffix = 2 let candidateId = `${baseId}_${suffix}` while (usedIds.has(candidateId)) { suffix += 1 candidateId = `${baseId}_${suffix}` } usedIds.add(candidateId) return candidateId } export function buildGraph( nodes: WorkflowNode[], connections: Connection[], options: BuildGraphOptions = {}, ): WorkflowGraph { const nodeMap = new Map(nodes.map(node => [node.id, node])) const edgeIds = new Set() const edges: WorkflowEdge[] = connections.map(([from, to, sourcePort]) => { const sourceNode = getNode(nodeMap, from) const targetNode = getNode(nodeMap, to) const sourcePortId = resolveSourcePort(sourceNode, sourcePort) const targetPortId = resolveTargetPort(targetNode) const baseEdgeId = `from_${sourceNode.id}_${sourcePortId}_to_${targetNode.id}_${targetPortId}` const edgeId = createUniqueEdgeId(baseEdgeId, edgeIds) return { id: edgeId, source: { id: sourceNode.id, portId: sourcePortId }, target: { id: targetNode.id, portId: targetPortId }, } }) const graph = { nodes, edges } if (options.validate ?? true) { assertWorkflowGraphValid(graph, { version: options.version ?? 'v3', strictUiCompatibility: options.strictUiCompatibility, strictReferenceCompatibility: options.strictReferenceCompatibility, strictAuthoring: options.strictAuthoring, }) } return graph }