44 INodeType ,
55 INodeTypeDescription ,
66 NodeOperationError ,
7+ NodeConnectionType ,
78} from 'n8n-workflow' ;
8- import { WorkflowExecuteAdditionalData , WorkflowExecute , WorkflowExecuteMode } from 'n8n-core' ;
9+ import { WorkflowExecute } from 'n8n-core' ;
910
1011export class DynamicNode implements INodeType {
1112 description : INodeTypeDescription = {
@@ -14,73 +15,57 @@ export class DynamicNode implements INodeType {
1415 icon : 'file:dynamicNode.svg' ,
1516 group : [ 'transform' ] ,
1617 version : 1 ,
17- description : 'Wraps a full node JSON and executes it dynamically' ,
1818 defaults : {
1919 name : 'Dynamic Node' ,
2020 color : '#00BB00' ,
2121 } ,
22- inputs : [ 'main' ] ,
23- outputs : [ 'main' ] ,
22+ inputs : [ 'main' ] as NodeConnectionType [ ] ,
23+ outputs : [ 'main' ] as NodeConnectionType [ ] ,
2424 properties : [
2525 {
2626 displayName : 'Node JSON' ,
2727 name : 'nodeJson' ,
2828 type : 'json' ,
2929 default : { } ,
30- description : 'Full node definition (as in workflow export) to execute' ,
30+ description : 'Full node definition (from a workflow export) to execute' ,
3131 } ,
3232 ] ,
3333 } ;
3434
3535 async execute ( this : IExecuteFunctions ) : Promise < INodeExecutionData [ ] [ ] > {
36- // Retrieve the JSON for the node to run
36+ // 1) Grab and validate the JSON the user pasted in
3737 const nodeJson = this . getNodeParameter ( 'nodeJson' , 0 ) as any ;
38- if ( ! nodeJson || typeof nodeJson !== 'object' ) {
39- throw new NodeOperationError ( this . getNode ( ) , 'nodeJson must be a valid JSON object' ) ;
38+ if ( typeof nodeJson !== 'object' ) {
39+ throw new NodeOperationError ( this . getNode ( ) , 'The Node JSON must be a valid object' ) ;
4040 }
4141
42- // Helper function to recursively evaluate expressions in the JSON
43- const evaluateExpressions = ( obj : any ) : any => {
44- if ( typeof obj === 'string' && obj . includes ( '{{' ) ) {
45- return this . evaluateExpression ( obj , 0 ) ;
46- } else if ( Array . isArray ( obj ) ) {
47- return obj . map ( evaluateExpressions ) ;
48- } else if ( typeof obj === 'object' && obj !== null ) {
49- return Object . fromEntries (
50- Object . entries ( obj ) . map ( ( [ key , value ] ) => [ key , evaluateExpressions ( value ) ] )
51- ) ;
52- }
53- return obj ;
54- } ;
55-
56- // Evaluate all expressions in the node JSON
57- const evaluatedNodeJson = evaluateExpressions ( nodeJson ) ;
58-
59- // Build a minimal workflow around the single node
42+ // 2) Build a one-node workflow around it
6043 const workflowData = {
6144 name : 'DynamicWorkflow' ,
6245 nodes : [
6346 {
64- ...evaluatedNodeJson ,
65- // Override position so it doesn't overlap
47+ ...nodeJson ,
6648 position : [ 0 , 0 ] ,
6749 } ,
6850 ] ,
6951 connections : { } ,
7052 } ;
7153
72- // Prepare the core execution engine
73- const additionalData : WorkflowExecuteAdditionalData = await ( this . getWorkflow ( ) as any ) . getActiveWorkflow ( ) ;
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 ( ) ;
7457 const workflow = new WorkflowExecute ( additionalData , workflowData ) ;
7558
76- // Execute only the dynamic node
77- const runResult = await workflow . run ( {
78- runData : { } ,
79- destinationNode : evaluatedNodeJson . name ,
80- mode : 'manual' as WorkflowExecuteMode ,
81- } ) ;
59+ // 4) Run it in “manual” mode
60+ const runResult = await workflow . run ( 'manual' , { } as any ) ;
61+
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 [ ] [ ] ;
8267
83- // Return whatever the wrapped node returns
84- return runResult ; // INodeExecutionData[][]
68+ // 6) Return that so n8n pipes it downstream
69+ return runData || [ [ ] ] ;
8570 }
8671}
0 commit comments