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 ca6efb64728ec..67b7443ef768e 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: { @@ -244,6 +245,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 6f2d8b1f6974f..89931ce67fd39 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, @@ -138,6 +138,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) @@ -146,6 +148,7 @@ export class JsTaskRunner extends TaskRunner { return { result, customData: data.runExecutionData.resultData.metadata, + staticData: workflow.staticData.__dataChanged ? workflow.staticData : undefined, }; } @@ -200,7 +203,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), @@ -273,7 +276,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 19f0e14e7ff3f..d9e3dee8daa90 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.