@@ -4,9 +4,10 @@ import {
44 INodeType ,
55 INodeTypeDescription ,
66 NodeOperationError ,
7- NodeConnectionType ,
87} from 'n8n-workflow' ;
9- import { WorkflowExecute } from 'n8n-core' ;
8+
9+ // JSON import requires "resolveJsonModule" in tsconfig
10+ import subWorkflowTemplate from './subWorkflowTemplate.json' ;
1011
1112export class DynamicNode implements INodeType {
1213 description : INodeTypeDescription = {
@@ -15,57 +16,57 @@ export class DynamicNode implements INodeType {
1516 icon : 'file:dynamicNode.svg' ,
1617 group : [ 'transform' ] ,
1718 version : 1 ,
18- defaults : {
19- name : 'Dynamic Node' ,
20- color : '#00BB00' ,
21- } ,
22- inputs : [ 'main' ] as NodeConnectionType [ ] ,
23- outputs : [ 'main' ] as NodeConnectionType [ ] ,
19+ defaults : { name : 'Dynamic Node' , color : '#00BB00' } ,
20+ inputs : [ 'main' ] ,
21+ outputs : [ 'main' ] ,
2422 properties : [
2523 {
2624 displayName : 'Node JSON' ,
2725 name : 'nodeJson' ,
2826 type : 'json' ,
2927 default : { } ,
30- description : 'Full node definition (from a workflow export) to execute ' ,
28+ description : 'Paste in your exported node JSON here ' ,
3129 } ,
3230 ] ,
3331 } ;
3432
3533 async execute ( this : IExecuteFunctions ) : Promise < INodeExecutionData [ ] [ ] > {
36- // 1) Grab and validate the JSON the user pasted in
37- const nodeJson = this . getNodeParameter ( 'nodeJson' , 0 ) as any ;
38- if ( typeof nodeJson !== 'object' ) {
39- throw new NodeOperationError ( this . getNode ( ) , 'The Node JSON must be a valid object' ) ;
34+ // 1) Pull in the incoming items
35+ const items = this . getInputData ( ) ;
36+
37+ // 2) Get the user-provided node JSON
38+ const raw = this . getNodeParameter ( 'nodeJson' , 0 ) as any ;
39+ if ( typeof raw !== 'object' ) {
40+ throw new NodeOperationError ( this . getNode ( ) , 'Node JSON must be an object' ) ;
4041 }
4142
42- // 2) Build a one-node workflow around it
43- const workflowData = {
44- name : 'DynamicWorkflow' ,
45- nodes : [
46- {
47- ...nodeJson ,
48- position : [ 0 , 0 ] ,
49- } ,
50- ] ,
51- connections : { } ,
52- } ;
43+ // 3) Clone the sub-workflow template
44+ const template = JSON . parse ( JSON . stringify ( subWorkflowTemplate ) ) as any ;
5345
54- // 3) Pull in whatever “additionalData” n8n-core needs
55- // (this.getWorkflow() is your current running workflow instance)
56- const additionalData = await ( this . getWorkflow ( ) as any ) . getActiveWorkflow ( ) ;
57- const workflow = new WorkflowExecute ( additionalData , workflowData ) ;
46+ // 4) Make sure the JSON includes a name
47+ if ( ! raw . name ) {
48+ throw new NodeOperationError ( this . getNode ( ) , 'Your JSON must include a `name` field' ) ;
49+ }
50+ // 5) Inject the node definition
51+ template . nodes . push ( raw ) ;
52+ // 6) Wire Start -> your node
53+ template . connections . Start . main [ 0 ] [ 0 ] . node = raw . name ;
5854
59- // 4) Run it in “manual” mode
60- const runResult = await workflow . run ( 'manual' , { } as any ) ;
55+ // 7) Execute the mini-workflow
56+ const executionResult = await this . executeWorkflow (
57+ { code : template } ,
58+ items ,
59+ ) ;
6160
62- // 5) Extract just the data for our node by name
63- const nodeName = nodeJson . name ;
64- const runData = ( runResult as any ) . runData
65- ?. resultData
66- ?. runData [ nodeName ] as INodeExecutionData [ ] [ ] ;
61+ // 8) Normalize the result: either it's an array or { data }
62+ let outputData : INodeExecutionData [ ] [ ] ;
63+ if ( Array . isArray ( executionResult ) ) {
64+ outputData = executionResult ;
65+ } else {
66+ outputData = ( executionResult as any ) . data ;
67+ }
6768
68- // 6) Return that so n8n pipes it downstream
69- return runData || [ [ ] ] ;
69+ // 9) Prepare and return
70+ return this . prepareOutputData ( outputData ) ;
7071 }
71- }
72+ }
0 commit comments