import { BinaryDataManager , UserSettings , WorkflowExecute } from 'n8n-core' ; import { IDataObject , IExecuteData , IExecuteWorkflowInfo , INode , INodeExecutionData , INodeParameters , IRun , IRunExecutionData , ITaskData , IWorkflowCredentials , IWorkflowExecuteAdditionalData , IWorkflowExecuteHooks , IWorkflowHooksOptionalParameters , LoggerProxy as Logger , Workflow , WorkflowExecuteMode , WorkflowHooks , } from 'n8n-workflow' ; import { LessThanOrEqual } from 'typeorm' ; import { DateUtils } from 'typeorm/util/DateUtils' ; import * as config from '../config' ; import { ActiveExecutions , CredentialsHelper , Db , ExternalHooks , IExecutionDb , IExecutionFlattedDb , IExecutionResponse , InternalHooksManager , IPushDataExecutionFinished , IWorkflowBase , IWorkflowExecuteProcess , IWorkflowExecutionDataProcess , NodeTypes , Push , ResponseHelper , WebhookHelpers , WorkflowCredentials , WorkflowHelpers , } from '.' ; const ERROR_TRIGGER_TYPE = config . get ( 'nodes.errorTriggerType' ) as string ; param { IWorkflowBase } workflowData param { IRun } fullRunData param { WorkflowExecuteMode } mode param { string } executionId function executeErrorWorkflow ( workflowData , fullRunData , mode , executionId ? , retryOf ? ) { let pastExecutionUrl ; if ( executionId !== undefined ) { pastExecutionUrl = `${ WebhookHelpers . getWebhookBaseUrl ( ) }execution/${ executionId }` ; } if ( fullRunData . data . resultData . error !== undefined ) { const workflowErrorData = { execution : { id : executionId , url : pastExecutionUrl , error : fullRunData . data . resultData . error , lastNodeExecuted : fullRunData . data . resultData . lastNodeExecuted ! , mode , retryOf , } , workflow : { id : workflowData . id !== undefined ? workflowData . id . toString ( ) : undefined , name : workflowData . name , } , } ; if ( workflowData . settings !== undefined && workflowData . settings . errorWorkflow && ! ( mode === 'error' && workflowData . id && workflowData . settings . errorWorkflow . toString ( ) === workflowData . id . toString ( ) ) ) { Logger . verbose ( `Start external error workflow` , { executionId , errorWorkflowId : workflowData . settings . errorWorkflow . toString ( ) , workflowId : workflowData . id , } ) ; WorkflowHelpers . executeErrorWorkflow ( workflowData . settings . errorWorkflow as string , workflowErrorData ) ; } else if ( mode !== 'error' && workflowData . id !== undefined && workflowData . nodes . some ( ( node ) => node . type === ERROR_TRIGGER_TYPE ) ) { Logger . verbose ( `Start internal error workflow` , { executionId , workflowId : workflowData . id } ) ; WorkflowHelpers . executeErrorWorkflow ( workflowData . id . toString ( ) , workflowErrorData ) ; } } } /**
 * Prunes Saved Execution which are older than configured.
 * Throttled to be executed just once in configured timeframe.
 *
 */ let throttling = false ; function pruneExecutionData ( this ) { if ( ! throttling ) { Logger . verbose ( 'Pruning execution data from database' ) ; throttling = true ; const timeout = config . get ( 'executions.pruneDataTimeout' ) as number ; const maxAge = config . get ( 'executions.pruneDataMaxAge' ) as number ; const date = new Date ( ) ; date . setHours ( date . getHours ( ) - maxAge ) ; const utcDate = DateUtils . mixedDateToUtcDatetimeString ( date ) ; Db . collections . Execution ! . delete ( { stoppedAt : LessThanOrEqual ( utcDate ) } ) . then ( ( data ) => setTimeout ( ( ) => { throttling = false ; } , timeout * 1000 ) ) . catch ( ( error ) => { throttling = false ; Logger . error ( `Failed pruning execution data from database for execution ID ${ this . executionId } (hookFunctionsSave)` , { ... error , executionId : this . executionId , sessionId : this . sessionId , workflowId : this . workflowData . id , } ) ; } ) ; } } returns { IWorkflowExecuteHooks } function hookFunctionsPush ( ) { return { nodeExecuteBefore : [ async function ( this , nodeName ) : Promise < void > { if ( this . sessionId === undefined ) { return ; } Logger . debug ( `Executing hook on node "${ nodeName }" (hookFunctionsPush)` , { executionId : this . executionId , sessionId : this . sessionId , workflowId : this . workflowData . id , } ) ; const pushInstance = Push . getInstance ( ) ; pushInstance . send ( 'nodeExecuteBefore' , { executionId : this . executionId , nodeName , } , this . sessionId ) ; } , ] , nodeExecuteAfter : [ async function ( this , nodeName , data ) : Promise < void > { if ( this . sessionId === undefined ) { return ; } Logger . debug ( `Executing hook on node "${ nodeName }" (hookFunctionsPush)` , { executionId : this . executionId , sessionId : this . sessionId , workflowId : this . workflowData . id , } ) ; const pushInstance = Push . getInstance ( ) ; pushInstance . send ( 'nodeExecuteAfter' , { executionId : this . executionId , nodeName , data , } , this . sessionId ) ; } , ] , workflowExecuteBefore : [ async function ( this ) : Promise < void > { Logger . debug ( `Executing hook (hookFunctionsPush)` , { executionId : this . executionId , sessionId : this . sessionId , workflowId : this . workflowData . id , } ) ; if ( this . sessionId === undefined ) { return ; } const pushInstance = Push . getInstance ( ) ; pushInstance . send ( 'executionStarted' , { executionId : this . executionId , mode : this . mode , startedAt : new Date ( ) , retryOf : this . retryOf , workflowId : this . workflowData . id , sessionId : this . sessionId , workflowName : this . workflowData . name , } , this . sessionId ) ; } , ] , workflowExecuteAfter : [ async function ( this , fullRunData , newStaticData ) : Promise < void > { Logger . debug ( `Executing hook (hookFunctionsPush)` , { executionId : this . executionId , sessionId : this . sessionId , workflowId : this . workflowData . id , } ) ; if ( this . sessionId === undefined ) { return ; } const pushRunData = { ... fullRunData , data : { ... fullRunData . data , resultData : { ... fullRunData . data . resultData , runData : { } , } , } , } ; Logger . debug ( `Save execution progress to database for execution ID ${ this . executionId } ` , { executionId : this . executionId , workflowId : this . workflowData . id , } ) ; const sendData = { executionId : this . executionId , data : pushRunData , retryOf : this . retryOf , } ; const pushInstance = Push . getInstance ( ) ; pushInstance . send ( 'executionFinished' , sendData , this . sessionId ) ; } , ] , } ; } export function hookFunctionsPreExecute ( parentProcessMode ? ) { const externalHooks = ExternalHooks ( ) ; return { workflowExecuteBefore : [ async function ( this , workflow ) : Promise < void > { await externalHooks . run ( 'workflow.preExecute' , [ workflow , this . mode ] ) ; } , ] , nodeExecuteAfter : [ async function ( nodeName , data , executionData ) : Promise < void > { if ( this . workflowData . settings !== undefined ) { if ( this . workflowData . settings . saveExecutionProgress === false ) { return ; } if ( this . workflowData . settings . saveExecutionProgress !== true && ! config . get ( 'executions.saveExecutionProgress' ) ) { return ; } } else if ( ! config . get ( 'executions.saveExecutionProgress' ) ) { return ; } try { Logger . debug ( `Save execution progress to database for execution ID ${ this . executionId } ` , { executionId : this . executionId , nodeName } ) ; const execution = await Db . collections . Execution ! . findOne ( this . executionId ) ; if ( execution === undefined ) { return ; } const fullExecutionData = ResponseHelper . unflattenExecutionData ( execution ) ; if ( fullExecutionData . finished ) { return ; } if ( fullExecutionData . data === undefined ) { fullExecutionData . data = { startData : { } , resultData : { runData : { } , } , executionData : { contextData : { } , nodeExecutionStack : [ ] , waitingExecution : { } , } , } ; } if ( Array . isArray ( fullExecutionData . data . resultData . runData [ nodeName ] ) ) { fullExecutionData . data . resultData . runData [ nodeName ] . push ( data ) ; } else { fullExecutionData . data . resultData . runData [ nodeName ] = [ data ] ; } fullExecutionData . data . executionData = executionData . executionData ; fullExecutionData . data . resultData . lastNodeExecuted = nodeName ; const flattenedExecutionData = ResponseHelper . flattenExecutionData ( fullExecutionData ) ; await Db . collections . Execution ! . update ( this . executionId , flattenedExecutionData as IExecutionFlattedDb ) ; } catch ( err ) { Logger . error ( `Failed saving execution progress to database for execution ID ${ this . executionId } (hookFunctionsPreExecute, nodeExecuteAfter)` , { ... err , executionId : this . executionId , sessionId : this . sessionId , workflowId : this . workflowData . id , } ) ; } } , ] , } ; } returns { IWorkflowExecuteHooks } function hookFunctionsSave ( parentProcessMode ? ) { return { nodeExecuteBefore : [ ] , nodeExecuteAfter : [ ] , workflowExecuteBefore : [ ] , workflowExecuteAfter : [ async function ( this , fullRunData , newStaticData ) : Promise < void > { Logger . debug ( `Executing hook (hookFunctionsSave)` , { executionId : this . executionId , workflowId : this . workflowData . id , } ) ; if ( config . get ( 'executions.pruneData' ) ) { pruneExecutionData . call ( this ) ; } const isManualMode = [ this . mode , parentProcessMode ] . includes ( 'manual' ) ; try { if ( ! isManualMode && WorkflowHelpers . isWorkflowIdValid ( this . workflowData . id as string ) && newStaticData ) { try { await WorkflowHelpers . saveStaticDataById ( this . workflowData . id as string , newStaticData ) ; } catch ( e ) { Logger . error ( `There was a problem saving the workflow with id "${ this . workflowData . id }" to save changed staticData: "${ e . message }" (hookFunctionsSave)` , { executionId : this . executionId , workflowId : this . workflowData . id } ) ; } } let saveManualExecutions = config . get ( 'executions.saveDataManualExecutions' ) as boolean ; if ( this . workflowData . settings !== undefined && this . workflowData . settings . saveManualExecutions !== undefined ) { saveManualExecutions = this . workflowData . settings . saveManualExecutions as boolean ; } if ( isManualMode && ! saveManualExecutions && ! fullRunData . waitTill ) { await Db . collections . Execution ! . delete ( this . executionId ) ; await BinaryDataManager . getInstance ( ) . markDataForDeletionByExecutionId ( this . executionId ) ; return ; } let saveDataErrorExecution = config . get ( 'executions.saveDataOnError' ) as string ; let saveDataSuccessExecution = config . get ( 'executions.saveDataOnSuccess' ) as string ; if ( this . workflowData . settings !== undefined ) { saveDataErrorExecution = ( this . workflowData . settings . saveDataErrorExecution as string ) || saveDataErrorExecution ; saveDataSuccessExecution = ( this . workflowData . settings . saveDataSuccessExecution as string ) || saveDataSuccessExecution ; } const workflowDidSucceed = ! fullRunData . data . resultData . error ; if ( ( workflowDidSucceed && saveDataSuccessExecution === 'none' ) || ( ! workflowDidSucceed && saveDataErrorExecution === 'none' ) ) { if ( ! fullRunData . waitTill ) { if ( ! isManualMode ) { executeErrorWorkflow ( this . workflowData , fullRunData , this . mode , this . executionId , this . retryOf ) ; } await Db . collections . Execution ! . delete ( this . executionId ) ; await BinaryDataManager . getInstance ( ) . markDataForDeletionByExecutionId ( this . executionId ) ; return ; } } const fullExecutionData = { data : fullRunData . data , mode : fullRunData . mode , finished : fullRunData . finished ? fullRunData . finished : false , startedAt : fullRunData . startedAt , stoppedAt : fullRunData . stoppedAt , workflowData : this . workflowData , waitTill : fullRunData . waitTill , } ; if ( this . retryOf !== undefined ) { fullExecutionData . retryOf = this . retryOf . toString ( ) ; } if ( this . workflowData . id !== undefined && WorkflowHelpers . isWorkflowIdValid ( this . workflowData . id . toString ( ) ) ) { fullExecutionData . workflowId = this . workflowData . id . toString ( ) ; } Logger . debug ( `Save execution data to database for execution ID ${ this . executionId }` , { executionId : this . executionId , workflowId : this . workflowData . id , finished : fullExecutionData . finished , stoppedAt : fullExecutionData . stoppedAt , } ) ; const executionData = ResponseHelper . flattenExecutionData ( fullExecutionData ) ; await Db . collections . Execution ! . update ( this . executionId , executionData as IExecutionFlattedDb ) ; if ( fullRunData . finished === true && this . retryOf !== undefined ) { await Db . collections . Execution ! . update ( this . retryOf , { retrySuccessId : this . executionId , } ) ; } if ( ! isManualMode ) { executeErrorWorkflow ( this . workflowData , fullRunData , this . mode , this . executionId , this . retryOf ) ; } } catch ( error ) { Logger . error ( `Failed saving execution data to DB on execution ID ${ this . executionId }` , { executionId : this . executionId , workflowId : this . workflowData . id , error , } ) ; if ( ! isManualMode ) { executeErrorWorkflow ( this . workflowData , fullRunData , this . mode , this . executionId , this . retryOf ) ; } } } , ] , } ; } returns { IWorkflowExecuteHooks } function hookFunctionsSaveWorker ( ) { return { nodeExecuteBefore : [ ] , nodeExecuteAfter : [ ] , workflowExecuteBefore : [ ] , workflowExecuteAfter : [ async function ( this , fullRunData , newStaticData ) : Promise < void > { try { if ( WorkflowHelpers . isWorkflowIdValid ( this . workflowData . id as string ) && newStaticData ) { try { await WorkflowHelpers . saveStaticDataById ( this . workflowData . id as string , newStaticData ) ; } catch ( e ) { Logger . error ( `There was a problem saving the workflow with id "${ this . workflowData . id }" to save changed staticData: "${ e . message }" (workflowExecuteAfter)` , { sessionId : this . sessionId , workflowId : this . workflowData . id } ) ; } } const workflowDidSucceed = ! fullRunData . data . resultData . error ; if ( ! workflowDidSucceed ) { executeErrorWorkflow ( this . workflowData , fullRunData , this . mode , this . executionId , this . retryOf ) ; } const fullExecutionData = { data : fullRunData . data , mode : fullRunData . mode , finished : fullRunData . finished ? fullRunData . finished : false , startedAt : fullRunData . startedAt , stoppedAt : fullRunData . stoppedAt , workflowData : this . workflowData , waitTill : fullRunData . data . waitTill , } ; if ( this . retryOf !== undefined ) { fullExecutionData . retryOf = this . retryOf . toString ( ) ; } if ( this . workflowData . id !== undefined && WorkflowHelpers . isWorkflowIdValid ( this . workflowData . id . toString ( ) ) ) { fullExecutionData . workflowId = this . workflowData . id . toString ( ) ; } const executionData = ResponseHelper . flattenExecutionData ( fullExecutionData ) ; await Db . collections . Execution ! . update ( this . executionId , executionData as IExecutionFlattedDb ) ; if ( fullRunData . finished === true && this . retryOf !== undefined ) { await Db . collections . Execution ! . update ( this . retryOf , { retrySuccessId : this . executionId , } ) ; } } catch ( error ) { executeErrorWorkflow ( this . workflowData , fullRunData , this . mode , this . executionId , this . retryOf ) ; } } , ] , } ; } export async function getRunData ( workflowData , inputData ? ) { const mode = 'integrated' ; const requiredNodeTypes = [ 'n8n-nodes-base.start' ] ; let startNode ; for ( const node of workflowData . nodes ) { if ( requiredNodeTypes . includes ( node . type ) ) { startNode = node ; break ; } } if ( startNode === undefined ) { throw new Error ( `The workflow does not contain a "Start" node and can so not be executed.` ) ; } inputData = inputData || [ { json : { } , } , ] ; const nodeExecutionStack = [ ] ; nodeExecutionStack . push ( { node : startNode , data : { main : [ inputData ] , } , } ) ; const runExecutionData = { startData : { } , resultData : { runData : { } , } , executionData : { contextData : { } , nodeExecutionStack , waitingExecution : { } , } , } ; const runData = { executionMode : mode , executionData : runExecutionData , workflowData , } ; return runData ; } export async function getWorkflowData ( workflowInfo ) { if ( workflowInfo . id === undefined && workflowInfo . code === undefined ) { throw new Error ( `No information about the workflow to execute found. Please provide either the "id" or "code"!` ) ; } if ( Db . collections . Workflow === null ) { await Db . init ( ) ; } let workflowData ; if ( workflowInfo . id !== undefined ) { workflowData = await Db . collections . Workflow ! . findOne ( workflowInfo . id ) ; if ( workflowData === undefined ) { throw new Error ( `The workflow with the id "${ workflowInfo . id }" does not exist.` ) ; } } else { workflowData = workflowInfo . code ; } return workflowData ! ; } export param { string } workflowId param { IWorkflowExecuteAdditionalData } additionalData param { INodeExecutionData [ ] } inputData returns { ( Promise < Array < INodeExecutionData [ ] | null > > ) } export async function executeWorkflow ( workflowInfo , additionalData , inputData ? , parentExecutionId ? , loadedWorkflowData ? , loadedRunData ? ) { const externalHooks = ExternalHooks ( ) ; await externalHooks . init ( ) ; const nodeTypes = NodeTypes ( ) ; const workflowData = loadedWorkflowData !== undefined ? loadedWorkflowData : await getWorkflowData ( workflowInfo ) ; const workflowName = workflowData ? workflowData . name : undefined ; const workflow = new Workflow ( { id : workflowInfo . id , name : workflowName , nodes : workflowData . nodes , connections : workflowData . connections , active : workflowData . active , nodeTypes , staticData : workflowData . staticData , } ) ; const runData = loadedRunData !== undefined ? loadedRunData : await getRunData ( workflowData , inputData ) ; let executionId ; if ( parentExecutionId !== undefined ) { executionId = parentExecutionId ; } else { executionId = parentExecutionId !== undefined ? parentExecutionId : await ActiveExecutions . getInstance ( ) . add ( runData ) ; } let data ; try { const additionalDataIntegrated = await getBase ( ) ; additionalDataIntegrated . hooks = getWorkflowHooksIntegrated ( runData . executionMode , executionId , workflowData , { parentProcessMode : additionalData . hooks ! . mode } ) ; additionalDataIntegrated . executionId = executionId ; additionalDataIntegrated . executeWorkflow = additionalData . executeWorkflow ; let subworkflowTimeout = additionalData . executionTimeoutTimestamp ; if ( workflowData . settings ?. executionTimeout !== undefined && workflowData . settings . executionTimeout > 0 ) { subworkflowTimeout = Math . min ( additionalData . executionTimeoutTimestamp || Number . MAX_SAFE_INTEGER , Date . now ( ) + ( workflowData . settings . executionTimeout as number ) * 1000 ) ; } additionalDataIntegrated . executionTimeoutTimestamp = subworkflowTimeout ; const runExecutionData = runData . executionData as IRunExecutionData ; const workflowExecute = new WorkflowExecute ( additionalDataIntegrated , runData . executionMode , runExecutionData ) ; if ( parentExecutionId !== undefined ) { return { startedAt : new Date ( ) , workflow , workflowExecute , } ; } data = await workflowExecute . processRunExecutionData ( workflow ) ; } catch ( error ) { const fullRunData = { data : { resultData : { error , runData : { } , } , } , finished : false , mode : 'integrated' , startedAt : new Date ( ) , stoppedAt : new Date ( ) , } ; const fullExecutionData = { data : fullRunData . data , mode : fullRunData . mode , finished : fullRunData . finished ? fullRunData . finished : false , startedAt : fullRunData . startedAt , stoppedAt : fullRunData . stoppedAt , workflowData , } ; const executionData = ResponseHelper . flattenExecutionData ( fullExecutionData ) ; await Db . collections . Execution ! . update ( executionId , executionData as IExecutionFlattedDb ) ; throw { ... error , stack : error . stack , } ; } await externalHooks . run ( 'workflow.postExecute' , [ data , workflowData , executionId ] ) ; void InternalHooksManager . getInstance ( ) . onWorkflowPostExecute ( executionId , workflowData , data ) ; if ( data . finished === true ) { await ActiveExecutions . getInstance ( ) . remove ( executionId , data ) ; const returnData = WorkflowHelpers . getDataLastExecutedNodeData ( data ) ; return returnData ! . data ! . main ; } await ActiveExecutions . getInstance ( ) . remove ( executionId , data ) ; const { error } = data . data . resultData ; throw { ... error , stack : error ! . stack , } ; } export function sendMessageToUI ( source , messages ) { if ( this . sessionId === undefined ) { return ; } try { const pushInstance = Push . getInstance ( ) ; pushInstance . send ( 'sendConsoleMessage' , { source : `Node: "${ source }"` , messages , } , this . sessionId ) ; } catch ( error ) { Logger . warn ( `There was a problem sending messsage to UI: ${ error . message }` ) ; } } export param { IWorkflowCredentials } credentials param { INodeParameters } currentNodeParameters returns { Promise < IWorkflowExecuteAdditionalData > } export async function getBase ( currentNodeParameters ? , executionTimeoutTimestamp ? ) { const urlBaseWebhook = WebhookHelpers . getWebhookBaseUrl ( ) ; const timezone = config . get ( 'generic.timezone' ) as string ; const webhookBaseUrl = urlBaseWebhook + config . get ( 'endpoints.webhook' ) ; const webhookWaitingBaseUrl = urlBaseWebhook + config . get ( 'endpoints.webhookWaiting' ) ; const webhookTestBaseUrl = urlBaseWebhook + config . get ( 'endpoints.webhookTest' ) ; const encryptionKey = await UserSettings . getEncryptionKey ( ) ; if ( encryptionKey === undefined ) { throw new Error ( 'No encryption key got found to decrypt the credentials!' ) ; } return { credentialsHelper : new CredentialsHelper ( encryptionKey ) , encryptionKey , executeWorkflow , restApiUrl : urlBaseWebhook + config . get ( 'endpoints.rest' ) , timezone , webhookBaseUrl , webhookWaitingBaseUrl , webhookTestBaseUrl , currentNodeParameters , executionTimeoutTimestamp , } ; } /**
 * Returns WorkflowHooks instance for running integrated workflows
 * (Workflows which get started inside of another workflow)
 */ export function getWorkflowHooksIntegrated ( mode , executionId , workflowData , optionalParameters ? ) { optionalParameters = optionalParameters || { } ; const hookFunctions = hookFunctionsSave ( optionalParameters . parentProcessMode ) ; const preExecuteFunctions = hookFunctionsPreExecute ( optionalParameters . parentProcessMode ) ; for ( const key of Object . keys ( preExecuteFunctions ) ) { if ( hookFunctions [ key ] === undefined ) { hookFunctions [ key ] = [ ] ; } hookFunctions [ key ] ! . push . apply ( hookFunctions [ key ] , preExecuteFunctions [ key ] ) ; } return new WorkflowHooks ( hookFunctions , mode , executionId , workflowData , optionalParameters ) ; } /**
 * Returns WorkflowHooks instance for running integrated workflows
 * (Workflows which get started inside of another workflow)
 */ export function getWorkflowHooksWorkerExecuter ( mode , executionId , workflowData , optionalParameters ? ) { optionalParameters = optionalParameters || { } ; const hookFunctions = hookFunctionsSaveWorker ( ) ; const preExecuteFunctions = hookFunctionsPreExecute ( optionalParameters . parentProcessMode ) ; for ( const key of Object . keys ( preExecuteFunctions ) ) { if ( hookFunctions [ key ] === undefined ) { hookFunctions [ key ] = [ ] ; } hookFunctions [ key ] ! . push . apply ( hookFunctions [ key ] , preExecuteFunctions [ key ] ) ; } return new WorkflowHooks ( hookFunctions , mode , executionId , workflowData , optionalParameters ) ; } /**
 * Returns WorkflowHooks instance for main process if workflow runs via worker
 */ export function getWorkflowHooksWorkerMain ( mode , executionId , workflowData , optionalParameters ? ) { optionalParameters = optionalParameters || { } ; const hookFunctions = hookFunctionsPush ( ) ; const preExecuteFunctions = hookFunctionsPreExecute ( optionalParameters . parentProcessMode ) ; for ( const key of Object . keys ( preExecuteFunctions ) ) { if ( hookFunctions [ key ] === undefined ) { hookFunctions [ key ] = [ ] ; } hookFunctions [ key ] ! . push . apply ( hookFunctions [ key ] , preExecuteFunctions [ key ] ) ; } hookFunctions . nodeExecuteBefore = [ ] ; hookFunctions . nodeExecuteAfter = [ ] ; return new WorkflowHooks ( hookFunctions , mode , executionId , workflowData , optionalParameters ) ; } export param { IWorkflowExecutionDataProcess } data param { string } executionId returns { WorkflowHooks } export function getWorkflowHooksMain ( data , executionId , isMainProcess = false ) { const hookFunctions = hookFunctionsSave ( ) ; const pushFunctions = hookFunctionsPush ( ) ; for ( const key of Object . keys ( pushFunctions ) ) { if ( hookFunctions [ key ] === undefined ) { hookFunctions [ key ] = [ ] ; } hookFunctions [ key ] ! . push . apply ( hookFunctions [ key ] , pushFunctions [ key ] ) ; } if ( isMainProcess ) { const preExecuteFunctions = hookFunctionsPreExecute ( ) ; for ( const key of Object . keys ( preExecuteFunctions ) ) { if ( hookFunctions [ key ] === undefined ) { hookFunctions [ key ] = [ ] ; } hookFunctions [ key ] ! . push . apply ( hookFunctions [ key ] , preExecuteFunctions [ key ] ) ; } } return new WorkflowHooks ( hookFunctions , data . executionMode , executionId , data . workflowData , { sessionId : data . sessionId , retryOf : data . retryOf as string , } ) ; }