66 NodeOperationError ,
77 NodeConnectionType ,
88} from 'n8n-workflow' ;
9+ import { v4 as uuidv4 } from 'uuid' ;
910
1011// JSON import requires "resolveJsonModule" in tsconfig
1112import subWorkflowTemplate from './subWorkflowTemplate.json' ;
@@ -36,7 +37,7 @@ export class DynamicNode implements INodeType {
3637 // 1) Pull in the incoming items
3738 const items = this . getInputData ( ) ;
3839
39- // 2) Get the user- provided node JSON (string or object)
40+ // 2) Get the user‐ provided node JSON (string or object)
4041 const rawParam = this . getNodeParameter ( 'nodeJson' , 0 ) as any ;
4142 let raw : any ;
4243 if ( typeof rawParam === 'string' ) {
@@ -55,43 +56,55 @@ export class DynamicNode implements INodeType {
5556 throw new NodeOperationError ( this . getNode ( ) , 'Node JSON must be an object' ) ;
5657 }
5758
58- // 3) Clone the sub-workflow template
59- const template = JSON . parse ( JSON . stringify ( subWorkflowTemplate ) ) as any ;
60-
61- // 4) Ensure the JSON includes a name
62- if ( ! raw . name ) {
59+ // —— UNWRAP & CLEANUP ——
60+ // 3) If it’s a full export, pull out the first node
61+ let nodeJson : any ;
62+ if ( Array . isArray ( raw . nodes ) && raw . nodes . length > 0 ) {
63+ nodeJson = raw . nodes [ 0 ] ;
64+ } else {
65+ nodeJson = raw ;
66+ }
67+ // 4) Remove export-only keys
68+ delete nodeJson . connections ;
69+ delete nodeJson . pinData ;
70+ delete nodeJson . meta ;
71+ // 5) Validate it still has a name
72+ if ( ! nodeJson . name ) {
6373 throw new NodeOperationError ( this . getNode ( ) , 'Your JSON must include a `name` field' ) ;
6474 }
6575
66- // 5) Inject the node definition
67- template . nodes . push ( raw ) ;
76+ // —— AVOID COLLISIONS ——
77+ // 6) Suffix the name and assign a fresh ID
78+ nodeJson . name = `${ nodeJson . name } - Dynamic Node` ;
79+ nodeJson . id = `dynamic-${ uuidv4 ( ) } ` ;
80+
81+ // 7) Clone the sub-workflow template
82+ const template = JSON . parse ( JSON . stringify ( subWorkflowTemplate ) ) as any ;
6883
69- // 6) Wire Start → your node
70- template . connections . Start . main [ 0 ] [ 0 ] . node = raw . name ;
84+ // 8) Inject & wire
85+ template . nodes . push ( nodeJson ) ;
86+ template . connections . Start . main [ 0 ] [ 0 ] . node = nodeJson . name ;
7187
72- // 7 ) Execute the mini‐ workflow, waiting for every page…
88+ // 9 ) Execute the mini- workflow to completion
7389 const workflowProxy = this . getWorkflowDataProxy ( 0 ) ;
7490 const executionResult : any = await this . executeWorkflow (
75- { code : template } , // your one‐node sub‐workflow
76- items , // incoming items
77- { } , // an empty object where run data will live
91+ { code : template } ,
92+ items ,
93+ { } ,
7894 {
7995 parentExecution : {
8096 executionId : workflowProxy . $execution . id ,
8197 workflowId : workflowProxy . $workflow . id ,
8298 } ,
83- doNotWaitToFinish : false , // ← crucial: wait for pagination to finish
99+ doNotWaitToFinish : false ,
84100 } ,
85101 ) ;
86102
87- // 8) executionResult.data is already your sub-workflow’s output
88- // as INodeExecutionData[][] keyed by output ports
103+ // 10) Grab and return the data
89104 const returnedData = Array . isArray ( executionResult )
90105 ? executionResult
91106 : ( executionResult as any ) . data as INodeExecutionData [ ] [ ] ;
92107
93- // 9) Return the raw multi-port output directly
94- // (one port in our template, so n8n will pick that up)
95108 return returnedData ;
96109 }
97110}
0 commit comments