diff --git a/cypher-executor/src/actions/triplet-parser.ts b/cypher-executor/src/actions/triplet-parser.ts index 1277d19..703cf42 100644 --- a/cypher-executor/src/actions/triplet-parser.ts +++ b/cypher-executor/src/actions/triplet-parser.ts @@ -101,10 +101,23 @@ export function parseTriplets(rawTriplets: unknown[]): ParsedTriplets | null { return { edges, nodeNames, sourceNodes, sinkNodes }; } -/** 根據節點在圖中的位置決定其 type */ +/** 保留字節點名稱 — 明確宣告為 Input 或 Output 端點 */ +const INPUT_NAMES = new Set(['input', 'trigger', 'webhook', 'start']); +const OUTPUT_NAMES = new Set(['output', 'result', 'end', 'done']); + +/** 根據節點在圖中的位置決定其 type + * + * 規則: + * - 名稱在 INPUT_NAMES → Input(無論位置) + * - 名稱在 OUTPUT_NAMES → Output(無論位置) + * - sourceNode(只出現在 from)且名稱不在 INPUT_NAMES → Component(例如 cron 作為觸發源) + * - sinkNode(只出現在 to)且名稱不在 OUTPUT_NAMES → Component(最常見情況:最後一個實際零件) + * - 其他中間節點 → Component + */ export function resolveNodeRole(name: string, parsed: ParsedTriplets): NodeRole { + if (INPUT_NAMES.has(name.toLowerCase())) return 'Input'; + if (OUTPUT_NAMES.has(name.toLowerCase())) return 'Output'; if (parsed.sourceNodes.has(name)) return 'Input'; - if (parsed.sinkNodes.has(name)) return 'Output'; return 'Component'; }