diff --git a/packages/@n8n/task-runner/src/js-task-runner/__tests__/js-task-runner.test.ts b/packages/@n8n/task-runner/src/js-task-runner/__tests__/js-task-runner.test.ts index e53d246fcb6ae..4048c129bf0e1 100644 --- a/packages/@n8n/task-runner/src/js-task-runner/__tests__/js-task-runner.test.ts +++ b/packages/@n8n/task-runner/src/js-task-runner/__tests__/js-task-runner.test.ts @@ -36,6 +36,7 @@ describe('JsTaskRunner', () => { grantToken: 'grantToken', maxConcurrency: 1, taskBrokerUri: 'http://localhost', + taskTimeout: 60, ...baseRunnerOpts, }, jsRunnerConfig: { @@ -214,6 +215,7 @@ describe('JsTaskRunner', () => { ['$runIndex', 0], ['{ wf: $workflow }', { wf: { active: true, id: '1', name: 'Test Workflow' } }], ['$vars', { var: 'value' }], + ['$getWorkflowStaticData("global")', {}], ], 'Node.js internal functions': [ ['typeof Function', 'function'], diff --git a/packages/@n8n/task-runner/src/js-task-runner/js-task-runner.ts b/packages/@n8n/task-runner/src/js-task-runner/js-task-runner.ts index cbc1b4d832a5a..63aacdf5d84d0 100644 --- a/packages/@n8n/task-runner/src/js-task-runner/js-task-runner.ts +++ b/packages/@n8n/task-runner/src/js-task-runner/js-task-runner.ts @@ -1,5 +1,5 @@ import { getAdditionalKeys } from 'n8n-core'; -import { WorkflowDataProxy, Workflow } from 'n8n-workflow'; +import { WorkflowDataProxy, Workflow, ObservableObject } from 'n8n-workflow'; import type { CodeExecutionMode, IWorkflowExecuteAdditionalData, @@ -132,6 +132,8 @@ export class JsTaskRunner extends TaskRunner { }, }; + workflow.staticData = ObservableObject.create(workflow.staticData); + const result = settings.nodeMode === 'runOnceForAllItems' ? await this.runForAllItems(task.taskId, settings, data, workflow, customConsole, signal) @@ -140,6 +142,7 @@ export class JsTaskRunner extends TaskRunner { return { result, customData: data.runExecutionData.resultData.metadata, + staticData: workflow.staticData.__dataChanged ? workflow.staticData : undefined, }; } @@ -194,7 +197,7 @@ export class JsTaskRunner extends TaskRunner { module: {}, console: customConsole, items: inputItems, - + $getWorkflowStaticData: (type: 'global' | 'node') => workflow.getStaticData(type, data.node), ...this.getNativeVariables(), ...dataProxy, ...this.buildRpcCallObject(taskId), @@ -267,7 +270,8 @@ export class JsTaskRunner extends TaskRunner { module: {}, console: customConsole, item, - + $getWorkflowStaticData: (type: 'global' | 'node') => + workflow.getStaticData(type, data.node), ...this.getNativeVariables(), ...dataProxy, ...this.buildRpcCallObject(taskId), diff --git a/packages/@n8n/task-runner/src/runner-types.ts b/packages/@n8n/task-runner/src/runner-types.ts index 174d652e7fed5..5075b19db2b47 100644 --- a/packages/@n8n/task-runner/src/runner-types.ts +++ b/packages/@n8n/task-runner/src/runner-types.ts @@ -61,6 +61,7 @@ export interface DataRequestResponse { export interface TaskResultData { result: INodeExecutionData[]; customData?: Record; + staticData?: IDataObject; } export interface TaskData { diff --git a/packages/cli/src/runners/task-managers/task-manager.ts b/packages/cli/src/runners/task-managers/task-manager.ts index 66f07f7b0a817..fd62dc2673c4b 100644 --- a/packages/cli/src/runners/task-managers/task-manager.ts +++ b/packages/cli/src/runners/task-managers/task-manager.ts @@ -1,5 +1,6 @@ import type { TaskResultData, RequesterMessage, BrokerMessage, TaskData } from '@n8n/task-runner'; import { RPC_ALLOW_LIST } from '@n8n/task-runner'; +import { createResultOk, createResultError } from 'n8n-workflow'; import type { EnvProviderState, IExecuteFunctions, @@ -15,7 +16,6 @@ import type { IWorkflowExecuteAdditionalData, Result, } from 'n8n-workflow'; -import { createResultOk, createResultError } from 'n8n-workflow'; import { nanoid } from 'nanoid'; import { Service } from 'typedi'; @@ -158,6 +158,11 @@ export abstract class TaskManager { }); } + const { staticData: incomingStaticData } = resultData; + + // if the runner sent back static data, then it changed, so update it + if (incomingStaticData) workflow.overrideStaticData(incomingStaticData); + return createResultOk(resultData.result as TData); } catch (e: unknown) { return createResultError(e as TError); diff --git a/packages/workflow/src/Workflow.ts b/packages/workflow/src/Workflow.ts index b2eb597e2ed27..46abb1bf6822f 100644 --- a/packages/workflow/src/Workflow.ts +++ b/packages/workflow/src/Workflow.ts @@ -157,6 +157,13 @@ export class Workflow { this.expression = new Expression(this); } + overrideStaticData(staticData?: IDataObject) { + this.staticData = ObservableObject.create(staticData || {}, undefined, { + ignoreEmptyOnFirstChild: true, + }); + this.staticData.__dataChanged = true; + } + /** * The default connections are by source node. This function rewrites them by destination nodes * to easily find parent nodes.