@@ -11,6 +11,7 @@ interface YamlBlock {
1111 inputs ?: Record < string , any >
1212 preceding ?: string [ ]
1313 following ?: string [ ]
14+ parentId ?: string // Add parentId for nested blocks
1415}
1516
1617interface YamlWorkflow {
@@ -257,6 +258,55 @@ function calculateBlockPositions(
257258 return positions
258259}
259260
261+ /**
262+ * Sort blocks to ensure parents are processed before children
263+ * This ensures proper creation order for nested blocks
264+ */
265+ function sortBlocksByParentChildOrder ( blocks : ImportedBlock [ ] ) : ImportedBlock [ ] {
266+ const sorted : ImportedBlock [ ] = [ ]
267+ const processed = new Set < string > ( )
268+ const visiting = new Set < string > ( ) // Track blocks currently being processed to detect cycles
269+
270+ // Create a map for quick lookup
271+ const blockMap = new Map < string , ImportedBlock > ( )
272+ blocks . forEach ( block => blockMap . set ( block . id , block ) )
273+
274+ // Process blocks recursively, ensuring parents are added first
275+ function processBlock ( block : ImportedBlock ) {
276+ if ( processed . has ( block . id ) ) {
277+ return // Already processed
278+ }
279+
280+ if ( visiting . has ( block . id ) ) {
281+ // Circular dependency detected - break the cycle by processing this block without its parent
282+ logger . warn ( `Circular parent-child dependency detected for block ${ block . id } , breaking cycle` )
283+ sorted . push ( block )
284+ processed . add ( block . id )
285+ return
286+ }
287+
288+ visiting . add ( block . id )
289+
290+ // If this block has a parent, ensure the parent is processed first
291+ if ( block . parentId ) {
292+ const parentBlock = blockMap . get ( block . parentId )
293+ if ( parentBlock && ! processed . has ( block . parentId ) ) {
294+ processBlock ( parentBlock )
295+ }
296+ }
297+
298+ // Now process this block
299+ visiting . delete ( block . id )
300+ sorted . push ( block )
301+ processed . add ( block . id )
302+ }
303+
304+ // Process all blocks
305+ blocks . forEach ( block => processBlock ( block ) )
306+
307+ return sorted
308+ }
309+
260310/**
261311 * Convert YAML workflow to importable format
262312 */
@@ -296,11 +346,28 @@ export function convertYamlToWorkflow(yamlWorkflow: YamlWorkflow): ImportResult
296346
297347 // Add container-specific data
298348 if ( yamlBlock . type === 'loop' || yamlBlock . type === 'parallel' ) {
349+ // For loop/parallel blocks, map the inputs to the data field since they don't use subBlocks
299350 importedBlock . data = {
300351 width : 500 ,
301352 height : 300 ,
302353 type : yamlBlock . type === 'loop' ? 'loopNode' : 'parallelNode' ,
354+ // Map YAML inputs to data properties for loop/parallel blocks
355+ ...( yamlBlock . inputs || { } ) ,
303356 }
357+ // Clear inputs since they're now in data
358+ importedBlock . inputs = { }
359+ }
360+
361+ // Handle parent-child relationships for nested blocks
362+ if ( yamlBlock . parentId ) {
363+ importedBlock . parentId = yamlBlock . parentId
364+ importedBlock . extent = 'parent'
365+ // Also add to data for consistency with how the system works
366+ if ( ! importedBlock . data ) {
367+ importedBlock . data = { }
368+ }
369+ importedBlock . data . parentId = yamlBlock . parentId
370+ importedBlock . data . extent = 'parent'
304371 }
305372
306373 blocks . push ( importedBlock )
@@ -326,7 +393,10 @@ export function convertYamlToWorkflow(yamlWorkflow: YamlWorkflow): ImportResult
326393 }
327394 } )
328395
329- return { blocks, edges, errors, warnings }
396+ // Sort blocks to ensure parents are created before children
397+ const sortedBlocks = sortBlocksByParentChildOrder ( blocks )
398+
399+ return { blocks : sortedBlocks , edges, errors, warnings }
330400}
331401
332402/**
@@ -474,6 +544,8 @@ export async function importWorkflowFromYaml(
474544 }
475545
476546 // Create all other blocks
547+ // Note: blocks are now sorted to ensure parents come before children,
548+ // but we still need the two-phase approach because we're generating new UUIDs
477549 let blocksProcessed = 0
478550 for ( const block of blocks ) {
479551 if ( block . type === 'starter' ) {
@@ -499,10 +571,11 @@ export async function importWorkflowFromYaml(
499571 horizontalHandles : true ,
500572 isWide : false ,
501573 height : 0 ,
502- data : block . data || { } ,
574+ data : block . data || { } , // Configuration is already in block.data from convertYamlToWorkflow
503575 }
504576
505- completeSubBlockValues [ blockId ] = { ...block . inputs }
577+ // Loop/parallel blocks don't use subBlocks, their config is in data
578+ // No need to set completeSubBlockValues since they don't have subBlocks
506579 blocksProcessed ++
507580 } else if ( blockConfig ) {
508581 // Handle regular blocks
@@ -526,7 +599,7 @@ export async function importWorkflowFromYaml(
526599 horizontalHandles : true ,
527600 isWide : false ,
528601 height : 0 ,
529- data : block . data || { } ,
602+ data : block . data || { } , // This already includes parentId and extent from convertYamlToWorkflow
530603 }
531604
532605 // Set block input values
@@ -537,6 +610,25 @@ export async function importWorkflowFromYaml(
537610 }
538611 }
539612
613+ // Update parent-child relationships with mapped IDs
614+ // This two-phase approach is necessary because:
615+ // 1. We generate new UUIDs for all blocks (can't reuse YAML IDs)
616+ // 2. Parent references in YAML use the original IDs, need to map to new UUIDs
617+ // 3. All blocks must exist before we can map their parent references
618+ for ( const [ blockId , blockData ] of Object . entries ( completeBlocks ) ) {
619+ if ( blockData . data ?. parentId ) {
620+ const mappedParentId = yamlIdToActualId . get ( blockData . data . parentId )
621+ if ( mappedParentId ) {
622+ blockData . data . parentId = mappedParentId
623+ } else {
624+ logger . warn ( `Parent block not found for mapping: ${ blockData . data . parentId } ` )
625+ // Remove invalid parent reference
626+ delete blockData . data . parentId
627+ delete blockData . data . extent
628+ }
629+ }
630+ }
631+
540632 // Create complete edges using the ID mapping
541633 const completeEdges : any [ ] = [ ]
542634 for ( const edge of edges ) {
0 commit comments