@@ -817,6 +817,8 @@ function normalizeResponseFormat(value: any): string {
817817interface EdgeHandleValidationResult {
818818 valid : boolean
819819 error ?: string
820+ /** The normalized handle to use (e.g., simple 'if' normalized to 'condition-{uuid}') */
821+ normalizedHandle ?: string
820822}
821823
822824/**
@@ -851,13 +853,6 @@ function validateSourceHandleForBlock(
851853 }
852854
853855 case 'condition' : {
854- if ( ! sourceHandle . startsWith ( EDGE . CONDITION_PREFIX ) ) {
855- return {
856- valid : false ,
857- error : `Invalid source handle "${ sourceHandle } " for condition block. Must start with "${ EDGE . CONDITION_PREFIX } "` ,
858- }
859- }
860-
861856 const conditionsValue = sourceBlock ?. subBlocks ?. conditions ?. value
862857 if ( ! conditionsValue ) {
863858 return {
@@ -866,6 +861,8 @@ function validateSourceHandleForBlock(
866861 }
867862 }
868863
864+ // validateConditionHandle accepts simple format (if, else-if-0, else),
865+ // legacy format (condition-{blockId}-if), and internal ID format (condition-{uuid})
869866 return validateConditionHandle ( sourceHandle , sourceBlock . id , conditionsValue )
870867 }
871868
@@ -879,13 +876,6 @@ function validateSourceHandleForBlock(
879876 }
880877
881878 case 'router_v2' : {
882- if ( ! sourceHandle . startsWith ( EDGE . ROUTER_PREFIX ) ) {
883- return {
884- valid : false ,
885- error : `Invalid source handle "${ sourceHandle } " for router_v2 block. Must start with "${ EDGE . ROUTER_PREFIX } "` ,
886- }
887- }
888-
889879 const routesValue = sourceBlock ?. subBlocks ?. routes ?. value
890880 if ( ! routesValue ) {
891881 return {
@@ -894,6 +884,8 @@ function validateSourceHandleForBlock(
894884 }
895885 }
896886
887+ // validateRouterHandle accepts simple format (route-0, route-1),
888+ // legacy format (router-{blockId}-route-1), and internal ID format (router-{uuid})
897889 return validateRouterHandle ( sourceHandle , sourceBlock . id , routesValue )
898890 }
899891
@@ -910,7 +902,12 @@ function validateSourceHandleForBlock(
910902
911903/**
912904 * Validates condition handle references a valid condition in the block.
913- * Accepts both internal IDs (condition-blockId-if) and semantic keys (condition-blockId-else-if)
905+ * Accepts multiple formats:
906+ * - Simple format: "if", "else-if-0", "else-if-1", "else"
907+ * - Legacy semantic format: "condition-{blockId}-if", "condition-{blockId}-else-if"
908+ * - Internal ID format: "condition-{conditionId}"
909+ *
910+ * Returns the normalized handle (condition-{conditionId}) for storage.
914911 */
915912function validateConditionHandle (
916913 sourceHandle : string ,
@@ -943,48 +940,77 @@ function validateConditionHandle(
943940 }
944941 }
945942
946- const validHandles = new Set < string > ( )
947- const semanticPrefix = `condition-${ blockId } -`
948- let elseIfCount = 0
943+ // Build a map of all valid handle formats -> normalized handle (condition-{conditionId})
944+ const handleToNormalized = new Map < string , string > ( )
945+ const legacySemanticPrefix = `condition-${ blockId } -`
946+ let elseIfIndex = 0
949947
950948 for ( const condition of conditions ) {
951- if ( condition . id ) {
952- validHandles . add ( `condition-${ condition . id } ` )
953- }
949+ if ( ! condition . id ) continue
954950
951+ const normalizedHandle = `condition-${ condition . id } `
955952 const title = condition . title ?. toLowerCase ( )
953+
954+ // Always accept internal ID format
955+ handleToNormalized . set ( normalizedHandle , normalizedHandle )
956+
956957 if ( title === 'if' ) {
957- validHandles . add ( `${ semanticPrefix } if` )
958+ // Simple format: "if"
959+ handleToNormalized . set ( 'if' , normalizedHandle )
960+ // Legacy format: "condition-{blockId}-if"
961+ handleToNormalized . set ( `${ legacySemanticPrefix } if` , normalizedHandle )
958962 } else if ( title === 'else if' ) {
959- elseIfCount ++
960- validHandles . add (
961- elseIfCount === 1 ? `${ semanticPrefix } else-if` : `${ semanticPrefix } else-if-${ elseIfCount } `
962- )
963+ // Simple format: "else-if-0", "else-if-1", etc. (0-indexed)
964+ handleToNormalized . set ( `else-if-${ elseIfIndex } ` , normalizedHandle )
965+ // Legacy format: "condition-{blockId}-else-if" for first, "condition-{blockId}-else-if-2" for second
966+ if ( elseIfIndex === 0 ) {
967+ handleToNormalized . set ( `${ legacySemanticPrefix } else-if` , normalizedHandle )
968+ } else {
969+ handleToNormalized . set ( `${ legacySemanticPrefix } else-if-${ elseIfIndex + 1 } ` , normalizedHandle )
970+ }
971+ elseIfIndex ++
963972 } else if ( title === 'else' ) {
964- validHandles . add ( `${ semanticPrefix } else` )
973+ // Simple format: "else"
974+ handleToNormalized . set ( 'else' , normalizedHandle )
975+ // Legacy format: "condition-{blockId}-else"
976+ handleToNormalized . set ( `${ legacySemanticPrefix } else` , normalizedHandle )
965977 }
966978 }
967979
968- if ( validHandles . has ( sourceHandle ) ) {
969- return { valid : true }
980+ const normalizedHandle = handleToNormalized . get ( sourceHandle )
981+ if ( normalizedHandle ) {
982+ return { valid : true , normalizedHandle }
970983 }
971984
972- const validOptions = Array . from ( validHandles ) . slice ( 0 , 5 )
973- const moreCount = validHandles . size - validOptions . length
974- let validOptionsStr = validOptions . join ( ', ' )
975- if ( moreCount > 0 ) {
976- validOptionsStr += `, ... and ${ moreCount } more`
985+ // Build list of valid simple format options for error message
986+ const simpleOptions : string [ ] = [ ]
987+ elseIfIndex = 0
988+ for ( const condition of conditions ) {
989+ const title = condition . title ?. toLowerCase ( )
990+ if ( title === 'if' ) {
991+ simpleOptions . push ( 'if' )
992+ } else if ( title === 'else if' ) {
993+ simpleOptions . push ( `else-if-${ elseIfIndex } ` )
994+ elseIfIndex ++
995+ } else if ( title === 'else' ) {
996+ simpleOptions . push ( 'else' )
997+ }
977998 }
978999
9791000 return {
9801001 valid : false ,
981- error : `Invalid condition handle "${ sourceHandle } ". Valid handles: ${ validOptionsStr } ` ,
1002+ error : `Invalid condition handle "${ sourceHandle } ". Valid handles: ${ simpleOptions . join ( ', ' ) } ` ,
9821003 }
9831004}
9841005
9851006/**
9861007 * Validates router handle references a valid route in the block.
987- * Accepts both internal IDs (router-{routeId}) and semantic keys (router-{blockId}-route-1)
1008+ * Accepts multiple formats:
1009+ * - Simple format: "route-0", "route-1", "route-2" (0-indexed)
1010+ * - Legacy semantic format: "router-{blockId}-route-1" (1-indexed)
1011+ * - Internal ID format: "router-{routeId}"
1012+ *
1013+ * Returns the normalized handle (router-{routeId}) for storage.
9881014 */
9891015function validateRouterHandle (
9901016 sourceHandle : string ,
@@ -1017,47 +1043,48 @@ function validateRouterHandle(
10171043 }
10181044 }
10191045
1020- const validHandles = new Set < string > ( )
1021- const semanticPrefix = `router-${ blockId } -`
1046+ // Build a map of all valid handle formats -> normalized handle (router-{routeId})
1047+ const handleToNormalized = new Map < string , string > ( )
1048+ const legacySemanticPrefix = `router-${ blockId } -`
10221049
10231050 for ( let i = 0 ; i < routes . length ; i ++ ) {
10241051 const route = routes [ i ]
1052+ if ( ! route . id ) continue
10251053
1026- // Accept internal ID format: router-{uuid}
1027- if ( route . id ) {
1028- validHandles . add ( ` router-${ route . id } ` )
1029- }
1054+ const normalizedHandle = ` router-${ route . id } `
1055+
1056+ // Always accept internal ID format: router-{uuid}
1057+ handleToNormalized . set ( normalizedHandle , normalizedHandle )
10301058
1031- // Accept 1-indexed route number format: router-{blockId}-route-1, router-{blockId}-route-2, etc.
1032- validHandles . add ( `${ semanticPrefix } route-${ i + 1 } ` )
1059+ // Simple format: route-0, route-1, etc. (0-indexed)
1060+ handleToNormalized . set ( `route-${ i } ` , normalizedHandle )
1061+
1062+ // Legacy 1-indexed route number format: router-{blockId}-route-1
1063+ handleToNormalized . set ( `${ legacySemanticPrefix } route-${ i + 1 } ` , normalizedHandle )
10331064
10341065 // Accept normalized title format: router-{blockId}-{normalized-title}
1035- // Normalize: lowercase, replace spaces with dashes, remove special chars
10361066 if ( route . title && typeof route . title === 'string' ) {
10371067 const normalizedTitle = route . title
10381068 . toLowerCase ( )
10391069 . replace ( / \s + / g, '-' )
10401070 . replace ( / [ ^ a - z 0 - 9 - ] / g, '' )
10411071 if ( normalizedTitle ) {
1042- validHandles . add ( `${ semanticPrefix } ${ normalizedTitle } ` )
1072+ handleToNormalized . set ( `${ legacySemanticPrefix } ${ normalizedTitle } ` , normalizedHandle )
10431073 }
10441074 }
10451075 }
10461076
1047- if ( validHandles . has ( sourceHandle ) ) {
1048- return { valid : true }
1077+ const normalizedHandle = handleToNormalized . get ( sourceHandle )
1078+ if ( normalizedHandle ) {
1079+ return { valid : true , normalizedHandle }
10491080 }
10501081
1051- const validOptions = Array . from ( validHandles ) . slice ( 0 , 5 )
1052- const moreCount = validHandles . size - validOptions . length
1053- let validOptionsStr = validOptions . join ( ', ' )
1054- if ( moreCount > 0 ) {
1055- validOptionsStr += `, ... and ${ moreCount } more`
1056- }
1082+ // Build list of valid simple format options for error message
1083+ const simpleOptions = routes . map ( ( _ , i ) => `route-${ i } ` )
10571084
10581085 return {
10591086 valid : false ,
1060- error : `Invalid router handle "${ sourceHandle } ". Valid handles: ${ validOptionsStr } ` ,
1087+ error : `Invalid router handle "${ sourceHandle } ". Valid handles: ${ simpleOptions . join ( ', ' ) } ` ,
10611088 }
10621089}
10631090
@@ -1172,10 +1199,13 @@ function createValidatedEdge(
11721199 return false
11731200 }
11741201
1202+ // Use normalized handle if available (e.g., 'if' -> 'condition-{uuid}')
1203+ const finalSourceHandle = sourceValidation . normalizedHandle || sourceHandle
1204+
11751205 modifiedState . edges . push ( {
11761206 id : crypto . randomUUID ( ) ,
11771207 source : sourceBlockId ,
1178- sourceHandle,
1208+ sourceHandle : finalSourceHandle ,
11791209 target : targetBlockId ,
11801210 targetHandle,
11811211 type : 'default' ,
@@ -1184,7 +1214,11 @@ function createValidatedEdge(
11841214}
11851215
11861216/**
1187- * Adds connections as edges for a block
1217+ * Adds connections as edges for a block.
1218+ * Supports multiple target formats:
1219+ * - String: "target-block-id"
1220+ * - Object: { block: "target-block-id", handle?: "custom-target-handle" }
1221+ * - Array of strings or objects
11881222 */
11891223function addConnectionsAsEdges (
11901224 modifiedState : any ,
@@ -1194,19 +1228,34 @@ function addConnectionsAsEdges(
11941228 skippedItems ?: SkippedItem [ ]
11951229) : void {
11961230 Object . entries ( connections ) . forEach ( ( [ sourceHandle , targets ] ) => {
1197- const targetArray = Array . isArray ( targets ) ? targets : [ targets ]
1198- targetArray . forEach ( ( targetId : string ) => {
1231+ if ( targets === null ) return
1232+
1233+ const addEdgeForTarget = ( targetBlock : string , targetHandle ?: string ) => {
11991234 createValidatedEdge (
12001235 modifiedState ,
12011236 blockId ,
1202- targetId ,
1237+ targetBlock ,
12031238 sourceHandle ,
1204- 'target' ,
1239+ targetHandle || 'target' ,
12051240 'add_edge' ,
12061241 logger ,
12071242 skippedItems
12081243 )
1209- } )
1244+ }
1245+
1246+ if ( typeof targets === 'string' ) {
1247+ addEdgeForTarget ( targets )
1248+ } else if ( Array . isArray ( targets ) ) {
1249+ targets . forEach ( ( target : any ) => {
1250+ if ( typeof target === 'string' ) {
1251+ addEdgeForTarget ( target )
1252+ } else if ( target ?. block ) {
1253+ addEdgeForTarget ( target . block , target . handle )
1254+ }
1255+ } )
1256+ } else if ( typeof targets === 'object' && targets ?. block ) {
1257+ addEdgeForTarget ( targets . block , targets . handle )
1258+ }
12101259 } )
12111260}
12121261
0 commit comments