import type { Edge, Node } from "@xyflow/react"; import { NODE_KINDS } from "../studio/nodeCatalog"; // Task DSL —— React Flow 画布的可序列化表示,提交给 Gateway 解析组装。 export interface TaskDsl { version: "1"; nodes: Array<{ id: string; kind: string; label?: string; config: unknown }>; edges: Array<{ source: string; target: string }>; } // exportDsl 把画布的节点/连线导出为类型化 JSON DSL。 export function exportDsl(nodes: Node[], edges: Edge[]): TaskDsl { return { version: "1", nodes: nodes.map((n) => { const d = n.data as { kind: string; label?: string; config?: unknown }; return { id: n.id, kind: d.kind, label: d.label, config: d.config ?? {} }; }), edges: edges.map((e) => ({ source: e.source, target: e.target })), }; } export interface Issue { level: "error" | "warn"; msg: string; } // validate 轻量校验(前端先行;真实 schema 校验应由后端兜底)。 export function validate(nodes: Node[], edges: Edge[]): Issue[] { const issues: Issue[] = []; if (nodes.length === 0) { issues.push({ level: "error", msg: "画布为空:至少添加一个节点" }); return issues; } const connected = new Set(); edges.forEach((e) => { connected.add(e.source); connected.add(e.target); }); for (const n of nodes) { const d = n.data as { kind: string; label?: string; config?: Record }; const k = NODE_KINDS[d.kind]; if (nodes.length > 1 && !connected.has(n.id)) { issues.push({ level: "warn", msg: `节点「${d.label || k?.label}」未连线(孤立)` }); } k?.fields .filter((f) => f.required) .forEach((f) => { const v = d.config?.[f.key]; if (v === undefined || v === "" || String(v).startsWith("(未")) { issues.push({ level: "warn", msg: `节点「${d.label || k.label}」缺必填项:${f.label}` }); } }); } return issues; }