Vue Flow / React Flow compiler
compilers/vueFlow is the shipped implementation of the general compiler contract, for graphs shaped like a Vue Flow export (a list of nodes plus a list of edges connecting them).
compilers/vueFlow is the shipped implementation of the general compiler contract, for graphs shaped like a Vue Flow export (a list of nodes plus a list of edges connecting them).
It works for React Flow too
Vue Flow's data model intentionally mirrors React Flow's: both describe a graph as { nodes, edges }, where each node carries id/type/data/position and each edge carries id/source/target/sourceHandle/targetHandle. In practice a graph exported from React Flow decodes into the same VueFlowNode/Edges structs below unchanged — the package name reflects which frontend this was first built against, not a hard dependency on Vue Flow specifically. If you're on React Flow, you can very likely use this compiler as-is; only reach for writing a new compiler if your export genuinely diverges (custom edge shape, non-handle-based routing, etc).
The moving parts
type VueFlow struct {
Nodes []VueFlowNode
Edges []Edges
}
type VueFlowNode struct {
ID string
Type string // your frontend's node type string, e.g. "code", "contract", "pluginNative"
Data any // your frontend's arbitrary per-node form data
// ...position/dimension fields, irrelevant to compilation
}
type Edges struct {
ID string
Source string
Target string
SourceHandle string // which output handle on the source node this edge left from
TargetHandle string
Data EdgePayload // Tags []string, EdgeType string
Label string
}
You provide a hook, func(VueFlowNode) (*models.Node, error), that inspects VueFlowNode.Data (a map[string]any in practice, since it comes from decoded JSON) and returns the corresponding models.Node — everything except Next, which the compiler fills in for you from the edges.
cmpr := compiler.NewVueFlowCompiler(compiler.WithEachNodeFunc(myNodeBuilder))
nodeMap, errsByNodeId := cmpr.Compile(startNodeId, vueFlowGraph)
What Compile actually does
Starting from startNodeId, it walks the graph depth-first following outgoing edges:
- Call your hook on the current
VueFlowNodeto get a*models.Node. - For every edge whose
Sourceis this node, append amodels.Nextto the node'sNextlist:NodeId← edgeTargetTags← edgeData.TagsMeta←{"edgeId": ..., "label": ..., "edgeHandle": edge.SourceHandle}
- Recurse into each
Next.NodeIdnot already visited. - Return the accumulated
map[nodeId]*models.Node, plus any per-node errors your hook returned (compilation stops walking further from a node once its hook errors).
This is why a conditional/branching frontend node (multiple output handles: success, error, else, ...) doesn't need special handling in the compiler itself — each handle is just an edge with its own SourceHandle/tags, and it's the Contract node's own rule logic (see nodes.md) that decides which tagged Next entries actually fire at runtime.
For the deeper mechanics of how a specific handle id ends up meaning something at runtime (and a worked traversal example), see compilers/vueFlow/vuewFlow.md — that file predates some of the field names above, so treat models/flow.go as the source of truth for the current Node/Next shape.
Writing your own hook
A hook typically switches on VueFlowNode.Type and constructs the matching nodes.* builder from nodes.md, pulling whatever fields your frontend form stored in Data:
func myNodeBuilder(vfn compiler.VueFlowNode) (*inflowModels.Node, error) {
data := vfn.Data.(map[string]any)
node := inflowModels.Node{ID: vfn.ID, Title: data["title"].(string)}
switch vfn.Type {
case "code":
node.Type = inflowModels.CodeNodeType
n := inflowNodes.NewJsNode(data["logic_rule"].(string))
node.Code = &n.CodeRule
case "contract":
node.Type = inflowModels.RuleNodeType
n := inflowNodes.NewJsRuleLogicNode(inflowNodes.WithContractLogicCode(data["logic_rule"].(string)))
node.Contract = &n.ContractRule
// ... your other node types
}
return &node, nil
}
If your frontend needs to reference a backend-registered extrinsic service by a logical name rather than hardcoding its NATS subject, resolve it through svcHandler.GetSvc(name) inside the hook (see dev-backend/inflow/compiler.go's NODE_MY_A case for a full example, including filling subject placeholders with SvcTopic.MakeReqSubjectWithParams).
Debugging a graph
GetOutboundsEdges(nodeId) / GetInboundsEdges(nodeId) on *VueFlowCompiler let you inspect a node's edges directly if you need to debug why a walk produced (or omitted) a particular transition.
Next
- README.md — the general compiler contract, and how to add one for a different graph library
- ../nodes.md — what to build inside your hook for each node type
- ../infra.md — how the compiled node map is actually served to an engine instance