You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
React Flow allows any node to connect to any other node with no type checking. This issue implements a fully typed connection validation system — enforcing per-block source/target rules, handle-level port typing, max-edge constraints, and real-time visual rejection feedback — integrated with the existing graph validation pipeline.
Why This Is Hard
React Flow's isValidConnection callback fires synchronously on every connection attempt. It must check rules against the current live node/edge state, which means it needs access to the full nodes and edges arrays inside a stable callback — requiring careful use of useCallback with the right dependency array to avoid stale closure bugs.
Condition blocks have two distinct output ports: true and false branches. The validation must be handle-aware: the sourceHandle field on the connection identifies which port is being connected, and the system must enforce that each handle can only be used once (one edge per output handle).
Visual rejection UX: React Flow has no built-in rejection animation. When a connection is rejected, a red ghost edge must be rendered briefly using onConnectEnd and temporary edge state, then removed after 600 ms.
Dynamic extensibility: as new block types are added (RBACCheck, CrossContractCall, Loop), the connection rule table must be extensible without touching the validation callbacks.
Connection Rule Table
Source Block
Valid Target Types
Max out-edges
Handle-level rule
default (Start)
Any
1
Single output handle
Auth
Transfer, Storage, Event, Condition
1
Single output handle
Transfer
Storage, Event, Condition
1
Single output handle
Storage
Transfer, Event, Condition
1
Single output handle
Condition
Any except default
2
sourceHandle: "true" and sourceHandle: "false" each used at most once
Event
None (terminal)
0
No outputs allowed
Rule Architecture (new file src/lib/compile/connectionRules.ts)
All three consumers import and call validateConnection — no duplication of logic.
validateGraphStructure extension
After the existing reachability check, iterate all edges and call validateConnection for each. Collect all violations and return them as details in the CompileError with code INVALID_CONNECTION.
On connection end when the attempt was invalid (tracked via a ref set on connection start): briefly add a temporary red dashed edge to the canvas for 600 ms using setEdges, then remove it. The red edge renders with a custom invalidEdge edge type showing a tooltip with the rejection reason.
Handle styling
Valid connection targets highlight their input handle green during a drag.
Invalid targets dim their handle to 30% opacity.
Implemented via useStore from React Flow to detect active connection state.
Acceptance Criteria
CONNECTION_RULES is defined in connectionRules.ts for all current block types.
validateConnection is the single source of truth used by React Flow, validateGraphStructure, and the type inference engine.
isValidConnection in BlockEditor prevents invalid edges from being created.
Attempting to connect a second output from a Start node is rejected.
Connecting to an Event block output is rejected (terminal block).
A Condition block allows exactly two outgoing edges (true/false handles); a third is rejected.
validateGraphStructure returns an INVALID_CONNECTION error for rule-violating edges in loaded graphs.
A red ghost edge appears briefly (>= 400 ms) when a connection attempt is rejected.
Valid target handles highlight green during a drag; invalid targets dim.
Unit tests in connectionRules.test.ts cover every rule in the table, including handle constraints.
A Playwright E2E test: attempts to connect an Event block output, asserts the edge is not created and the red ghost appears.
Summary
React Flow allows any node to connect to any other node with no type checking. This issue implements a fully typed connection validation system — enforcing per-block source/target rules, handle-level port typing, max-edge constraints, and real-time visual rejection feedback — integrated with the existing graph validation pipeline.
Why This Is Hard
isValidConnectioncallback fires synchronously on every connection attempt. It must check rules against the current live node/edge state, which means it needs access to the fullnodesandedgesarrays inside a stable callback — requiring careful use ofuseCallbackwith the right dependency array to avoid stale closure bugs.trueandfalsebranches. The validation must be handle-aware: thesourceHandlefield on the connection identifies which port is being connected, and the system must enforce that each handle can only be used once (one edge per output handle).isValidConnectionReact Flow callback (prevents invalid connections), (2)validateGraphStructureinvalidate.ts(catches invalid connections in JSON-loaded graphs), and (3) the type inference engine (issue feat(compiler): implement graph-level type inference engine — propagate and validate Rust types across block connections at edit time #125). The rules must be defined once and consumed in all three places — no duplication.onConnectEndand temporary edge state, then removed after 600 ms.Connection Rule Table
default(Start)AuthTransfer,Storage,Event,ConditionTransferStorage,Event,ConditionStorageTransfer,Event,ConditionConditiondefaultsourceHandle: "true"andsourceHandle: "false"each used at most onceEventRule Architecture (new file
src/lib/compile/connectionRules.ts)All three consumers import and call
validateConnection— no duplication of logic.validateGraphStructureextensionAfter the existing reachability check, iterate all edges and call
validateConnectionfor each. Collect all violations and return them asdetailsin theCompileErrorwith codeINVALID_CONNECTION.React Flow integration
Visual rejection feedback
On connection end when the attempt was invalid (tracked via a ref set on connection start): briefly add a temporary red dashed edge to the canvas for 600 ms using
setEdges, then remove it. The red edge renders with a custominvalidEdgeedge type showing a tooltip with the rejection reason.Handle styling
useStorefrom React Flow to detect active connection state.Acceptance Criteria
CONNECTION_RULESis defined inconnectionRules.tsfor all current block types.validateConnectionis the single source of truth used by React Flow,validateGraphStructure, and the type inference engine.isValidConnectioninBlockEditorprevents invalid edges from being created.Eventblock output is rejected (terminal block).Conditionblock allows exactly two outgoing edges (true/false handles); a third is rejected.validateGraphStructurereturns anINVALID_CONNECTIONerror for rule-violating edges in loaded graphs.connectionRules.test.tscover every rule in the table, including handle constraints.Eventblock output, asserts the edge is not created and the red ghost appears.