(
graph: WorkflowGraph,
httpConnections: HttpConnection[],
log?: (msg: string) => void
)
| 352 | * Uses fuzzy path matching to connect full paths to relative node IDs. |
| 353 | */ |
| 354 | export function addHttpConnectionEdges( |
| 355 | graph: WorkflowGraph, |
| 356 | httpConnections: HttpConnection[], |
| 357 | log?: (msg: string) => void |
| 358 | ): { graph: WorkflowGraph; addedEdges: number; addedNodes: number } { |
| 359 | const _log = log || console.log; |
| 360 | |
| 361 | if (!httpConnections || httpConnections.length === 0) { |
| 362 | return { graph, addedEdges: 0, addedNodes: 0 }; |
| 363 | } |
| 364 | |
| 365 | // Build lookup for fuzzy matching (handles path suffix matching) |
| 366 | const lookup = buildNodeLookup(graph.nodes); |
| 367 | const newEdges: WorkflowEdge[] = []; |
| 368 | const newNodes: WorkflowNode[] = []; |
| 369 | |
| 370 | for (const conn of httpConnections) { |
| 371 | // Convert full paths to relative paths for node ID matching |
| 372 | const clientRelPath = toRelativePath(conn.client.file); |
| 373 | const handlerRelPath = toRelativePath(conn.handler.file); |
| 374 | |
| 375 | // Build candidate node IDs (relative paths) |
| 376 | const clientCandidateId = `${clientRelPath}::${conn.client.function}`; |
| 377 | const handlerCandidateId = `${handlerRelPath}::${conn.handler.function}`; |
| 378 | |
| 379 | // Try fuzzy matching to find existing nodes |
| 380 | let matchedClientId = findMatchingNodeId(clientCandidateId, lookup); |
| 381 | let matchedHandlerId = findMatchingNodeId(handlerCandidateId, lookup); |
| 382 | |
| 383 | // Create stub node for client if it doesn't exist (e.g., Go services calling Python APIs) |
| 384 | if (!matchedClientId) { |
| 385 | const stubId = clientCandidateId; |
| 386 | const func = conn.client.function; |
| 387 | const stubNode: WorkflowNode = { |
| 388 | id: stubId, |
| 389 | label: `${func}()`, // Add () to indicate it's a function |
| 390 | type: 'step', |
| 391 | source: { file: clientRelPath, line: conn.client.line, function: func } |
| 392 | }; |
| 393 | newNodes.push(stubNode); |
| 394 | lookup.exact.add(stubId); |
| 395 | lookup.exact.add(stubId.toLowerCase()); |
| 396 | matchedClientId = stubId; |
| 397 | } |
| 398 | |
| 399 | // Create stub node for handler endpoints that don't exist as cached nodes. |
| 400 | // These are API endpoints in files with no LLM workflows (e.g., vendor-api). |
| 401 | if (!matchedHandlerId) { |
| 402 | const stubId = handlerCandidateId; |
| 403 | const func = conn.handler.function; |
| 404 | const stubNode: WorkflowNode = { |
| 405 | id: stubId, |
| 406 | label: `${func}()`, // Add () to indicate it's a function |
| 407 | type: 'step', |
| 408 | source: { file: handlerRelPath, line: conn.handler.line, function: func } |
| 409 | }; |
| 410 | newNodes.push(stubNode); |
| 411 | lookup.exact.add(stubId); |
no test coverage detected