From dbb9475b7bb7227a335b199a2380f2e6e4256fc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Mon, 10 Feb 2025 16:51:01 +0100 Subject: [PATCH] refactor(core): Remove NodeExecutionOutput. Add execution hints directly to the context (#13111) --- .../__tests__/workflow-execute.test.ts | 13 ----- .../node-execution-context/execute-context.ts | 7 +++ .../src/execution-engine/workflow-execute.ts | 9 ++-- packages/nodes-base/nodes/Code/Code.node.ts | 6 ++- .../nodes-base/nodes/Code/test/utils.test.ts | 47 +++++++++---------- packages/nodes-base/nodes/Code/utils.ts | 23 ++++----- .../Google/Calendar/GoogleCalendar.node.ts | 9 +--- .../Calendar/test/node/event.getAll.test.ts | 16 +++---- .../HttpRequest/V3/HttpRequestV3.node.ts | 16 ++----- .../v3/actions/mode/combineByPosition.ts | 24 ++++------ .../nodes/Postgres/v2/actions/router.ts | 15 ++---- .../Transform/Aggregate/Aggregate.node.ts | 5 +- .../v2/RemoveDuplicatesV2.node.ts | 20 +++----- .../nodes/Transform/SplitOut/SplitOut.node.ts | 11 ++--- .../Transform/Summarize/Summarize.node.ts | 11 +++-- .../Summarize/test/unitTests/execute.test.ts | 13 ++--- packages/workflow/src/Interfaces.ts | 29 ++---------- 17 files changed, 103 insertions(+), 171 deletions(-) diff --git a/packages/core/src/execution-engine/__tests__/workflow-execute.test.ts b/packages/core/src/execution-engine/__tests__/workflow-execute.test.ts index 10af4a1e7b..75b4270d3d 100644 --- a/packages/core/src/execution-engine/__tests__/workflow-execute.test.ts +++ b/packages/core/src/execution-engine/__tests__/workflow-execute.test.ts @@ -36,7 +36,6 @@ import { ApplicationError, createDeferredPromise, NodeConnectionType, - NodeExecutionOutput, NodeHelpers, Workflow, } from 'n8n-workflow'; @@ -246,18 +245,6 @@ describe('WorkflowExecute', () => { } }); - test('WorkflowExecute, NodeExecutionOutput type test', () => { - //TODO Add more tests here when execution hints are added to some node types - const nodeExecutionOutput = new NodeExecutionOutput( - [[{ json: { data: 123 } }]], - [{ message: 'TEXT HINT' }], - ); - - expect(nodeExecutionOutput).toBeInstanceOf(NodeExecutionOutput); - expect(nodeExecutionOutput[0][0].json.data).toEqual(123); - expect(nodeExecutionOutput.getHints()[0].message).toEqual('TEXT HINT'); - }); - describe('runPartialWorkflow2', () => { // Dirty ► // ┌───────┐1 ┌─────┐1 ┌─────┐ diff --git a/packages/core/src/execution-engine/node-execution-context/execute-context.ts b/packages/core/src/execution-engine/node-execution-context/execute-context.ts index bf2e89f8ec..ae0ebbd3fb 100644 --- a/packages/core/src/execution-engine/node-execution-context/execute-context.ts +++ b/packages/core/src/execution-engine/node-execution-context/execute-context.ts @@ -11,6 +11,7 @@ import type { IRunExecutionData, ITaskDataConnections, IWorkflowExecuteAdditionalData, + NodeExecutionHint, Result, Workflow, WorkflowExecuteMode, @@ -51,6 +52,8 @@ export class ExecuteContext extends BaseExecuteContext implements IExecuteFuncti readonly getNodeParameter: IExecuteFunctions['getNodeParameter']; + readonly hints: NodeExecutionHint[] = []; + constructor( workflow: Workflow, node: INode, @@ -210,4 +213,8 @@ export class ExecuteContext extends BaseExecuteContext implements IExecuteFuncti getParentCallbackManager(): CallbackManager | undefined { return this.additionalData.parentCallbackManager; } + + addExecutionHints(...hints: NodeExecutionHint[]) { + this.hints.push(...hints); + } } diff --git a/packages/core/src/execution-engine/workflow-execute.ts b/packages/core/src/execution-engine/workflow-execute.ts index 8c38f2207d..5e1b56bf35 100644 --- a/packages/core/src/execution-engine/workflow-execute.ts +++ b/packages/core/src/execution-engine/workflow-execute.ts @@ -47,7 +47,6 @@ import { NodeHelpers, NodeConnectionType, ApplicationError, - NodeExecutionOutput, sleep, ExecutionCancelledError, Node, @@ -1101,7 +1100,7 @@ export class WorkflowExecute { }); } - return { data }; + return { data, hints: context.hints }; } else if (nodeType.poll) { if (mode === 'manual') { // In manual mode run the poll function @@ -1507,10 +1506,8 @@ export class WorkflowExecute { tryIndex++; } - if (nodeSuccessData instanceof NodeExecutionOutput) { - const hints = (nodeSuccessData as NodeExecutionOutput).getHints(); - - executionHints.push(...hints); + if (runNodeData.hints?.length) { + executionHints.push(...runNodeData.hints); } if (nodeSuccessData && executionData.node.onError === 'continueErrorOutput') { diff --git a/packages/nodes-base/nodes/Code/Code.node.ts b/packages/nodes-base/nodes/Code/Code.node.ts index 24a26923e8..b79a21cef6 100644 --- a/packages/nodes-base/nodes/Code/Code.node.ts +++ b/packages/nodes-base/nodes/Code/Code.node.ts @@ -165,7 +165,8 @@ export class Code implements INodeType { standardizeOutput(item.json); } - return addPostExecutionWarning(items, inputDataItems?.length); + addPostExecutionWarning(this, items, inputDataItems?.length); + return [items]; } // ---------------------------------- @@ -201,6 +202,7 @@ export class Code implements INodeType { } } - return addPostExecutionWarning(returnData, inputDataItems?.length); + addPostExecutionWarning(this, returnData, inputDataItems?.length); + return [returnData]; } } diff --git a/packages/nodes-base/nodes/Code/test/utils.test.ts b/packages/nodes-base/nodes/Code/test/utils.test.ts index 437d8b443b..ba5b6af9c8 100644 --- a/packages/nodes-base/nodes/Code/test/utils.test.ts +++ b/packages/nodes-base/nodes/Code/test/utils.test.ts @@ -1,49 +1,46 @@ -import type { INodeExecutionData } from 'n8n-workflow'; -import { NodeExecutionOutput } from 'n8n-workflow'; +import { mock } from 'jest-mock-extended'; +import type { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow'; import { addPostExecutionWarning } from '../utils'; describe('addPostExecutionWarning', () => { + const context = mock(); const inputItemsLength = 2; - it('should return a NodeExecutionOutput warning when returnData length differs from inputItemsLength', () => { + beforeEach(() => jest.resetAllMocks()); + + it('should add execution hints when returnData length differs from inputItemsLength', () => { const returnData: INodeExecutionData[] = [{ json: {}, pairedItem: 0 }]; - const result = addPostExecutionWarning(returnData, inputItemsLength); + addPostExecutionWarning(context, returnData, inputItemsLength); - expect(result).toBeInstanceOf(NodeExecutionOutput); - expect((result as NodeExecutionOutput)?.getHints()).toEqual([ - { - message: - 'To make sure expressions after this node work, return the input items that produced each output item. More info', - location: 'outputPane', - }, - ]); + expect(context.addExecutionHints).toHaveBeenCalledWith({ + message: + 'To make sure expressions after this node work, return the input items that produced each output item. More info', + location: 'outputPane', + }); }); - it('should return a NodeExecutionOutput warning when any item has undefined pairedItem', () => { + it('should add execution hints when any item has undefined pairedItem', () => { const returnData: INodeExecutionData[] = [{ json: {}, pairedItem: 0 }, { json: {} }]; - const result = addPostExecutionWarning(returnData, inputItemsLength); + addPostExecutionWarning(context, returnData, inputItemsLength); - expect(result).toBeInstanceOf(NodeExecutionOutput); - expect((result as NodeExecutionOutput)?.getHints()).toEqual([ - { - message: - 'To make sure expressions after this node work, return the input items that produced each output item. More info', - location: 'outputPane', - }, - ]); + expect(context.addExecutionHints).toHaveBeenCalledWith({ + message: + 'To make sure expressions after this node work, return the input items that produced each output item. More info', + location: 'outputPane', + }); }); - it('should return returnData array when all items match inputItemsLength and have defined pairedItem', () => { + it('should not add execution hints when all items match inputItemsLength and have defined pairedItem', () => { const returnData: INodeExecutionData[] = [ { json: {}, pairedItem: 0 }, { json: {}, pairedItem: 1 }, ]; - const result = addPostExecutionWarning(returnData, inputItemsLength); + addPostExecutionWarning(context, returnData, inputItemsLength); - expect(result).toEqual([returnData]); + expect(context.addExecutionHints).not.toHaveBeenCalled(); }); }); diff --git a/packages/nodes-base/nodes/Code/utils.ts b/packages/nodes-base/nodes/Code/utils.ts index 410da572b9..8be812f6dd 100644 --- a/packages/nodes-base/nodes/Code/utils.ts +++ b/packages/nodes-base/nodes/Code/utils.ts @@ -1,5 +1,4 @@ -import type { INodeExecutionData, IDataObject } from 'n8n-workflow'; -import { NodeExecutionOutput } from 'n8n-workflow'; +import type { INodeExecutionData, IDataObject, IExecuteFunctions } from 'n8n-workflow'; export function isObject(maybe: unknown): maybe is { [key: string]: unknown } { return ( @@ -39,24 +38,18 @@ export function standardizeOutput(output: IDataObject) { } export const addPostExecutionWarning = ( + context: IExecuteFunctions, returnData: INodeExecutionData[], inputItemsLength: number, -) => { +): void => { if ( returnData.length !== inputItemsLength || returnData.some((item) => item.pairedItem === undefined) ) { - return new NodeExecutionOutput( - [returnData], - [ - { - message: - 'To make sure expressions after this node work, return the input items that produced each output item. More info', - location: 'outputPane', - }, - ], - ); + context.addExecutionHints({ + message: + 'To make sure expressions after this node work, return the input items that produced each output item. More info', + location: 'outputPane', + }); } - - return [returnData]; }; diff --git a/packages/nodes-base/nodes/Google/Calendar/GoogleCalendar.node.ts b/packages/nodes-base/nodes/Google/Calendar/GoogleCalendar.node.ts index 3ae9ab7142..f8d606a382 100644 --- a/packages/nodes-base/nodes/Google/Calendar/GoogleCalendar.node.ts +++ b/packages/nodes-base/nodes/Google/Calendar/GoogleCalendar.node.ts @@ -10,12 +10,7 @@ import type { JsonObject, NodeExecutionHint, } from 'n8n-workflow'; -import { - NodeConnectionType, - NodeApiError, - NodeOperationError, - NodeExecutionOutput, -} from 'n8n-workflow'; +import { NodeConnectionType, NodeApiError, NodeOperationError } from 'n8n-workflow'; import { v4 as uuid } from 'uuid'; import { calendarFields, calendarOperations } from './CalendarDescription'; @@ -811,7 +806,7 @@ export class GoogleCalendar implements INodeType { } if (hints.length) { - return new NodeExecutionOutput([nodeExecutionData], hints); + this.addExecutionHints(...hints); } return [nodeExecutionData]; diff --git a/packages/nodes-base/nodes/Google/Calendar/test/node/event.getAll.test.ts b/packages/nodes-base/nodes/Google/Calendar/test/node/event.getAll.test.ts index 5cdaf3e25b..8a102d5e81 100644 --- a/packages/nodes-base/nodes/Google/Calendar/test/node/event.getAll.test.ts +++ b/packages/nodes-base/nodes/Google/Calendar/test/node/event.getAll.test.ts @@ -1,6 +1,6 @@ import type { MockProxy } from 'jest-mock-extended'; import { mock } from 'jest-mock-extended'; -import type { INode, IExecuteFunctions, IDataObject, NodeExecutionOutput } from 'n8n-workflow'; +import type { INode, IExecuteFunctions, IDataObject } from 'n8n-workflow'; import * as genericFunctions from '../../GenericFunctions'; import { GoogleCalendar } from '../../GoogleCalendar.node'; @@ -207,15 +207,13 @@ describe('Google Calendar Node', () => { }, ]; - const result = await googleCalendar.execute.call(mockExecuteFunctions); + await googleCalendar.execute.call(mockExecuteFunctions); - expect((result as NodeExecutionOutput).getHints()).toEqual([ - { - message: - "Some events repeat far into the future. To return less of them, add a 'Before' date or change the 'Recurring Event Handling' option.", - location: 'outputPane', - }, - ]); + expect(mockExecuteFunctions.addExecutionHints).toHaveBeenCalledWith({ + message: + "Some events repeat far into the future. To return less of them, add a 'Before' date or change the 'Recurring Event Handling' option.", + location: 'outputPane', + }); }); }); }); diff --git a/packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts b/packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts index 0e9c34de6a..e6aed36e4c 100644 --- a/packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts +++ b/packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts @@ -16,7 +16,6 @@ import type { import { BINARY_ENCODING, NodeApiError, - NodeExecutionOutput, NodeConnectionType, NodeOperationError, jsonParse, @@ -1002,16 +1001,11 @@ export class HttpRequestV3 implements INodeType { returnItems[0].json.data && Array.isArray(returnItems[0].json.data) ) { - return new NodeExecutionOutput( - [returnItems], - [ - { - message: - 'To split the contents of ‘data’ into separate items for easier processing, add a ‘Split Out’ node after this one', - location: 'outputPane', - }, - ], - ); + this.addExecutionHints({ + message: + 'To split the contents of ‘data’ into separate items for easier processing, add a ‘Split Out’ node after this one', + location: 'outputPane', + }); } return [returnItems]; diff --git a/packages/nodes-base/nodes/Merge/v3/actions/mode/combineByPosition.ts b/packages/nodes-base/nodes/Merge/v3/actions/mode/combineByPosition.ts index 8b9067156c..9702160948 100644 --- a/packages/nodes-base/nodes/Merge/v3/actions/mode/combineByPosition.ts +++ b/packages/nodes-base/nodes/Merge/v3/actions/mode/combineByPosition.ts @@ -1,10 +1,9 @@ import merge from 'lodash/merge'; -import { - NodeExecutionOutput, - type IExecuteFunctions, - type INodeExecutionData, - type INodeProperties, - type IPairedItemData, +import type { + IExecuteFunctions, + INodeExecutionData, + INodeProperties, + IPairedItemData, } from 'n8n-workflow'; import { updateDisplayOptions } from '@utils/utilities'; @@ -82,15 +81,10 @@ export async function execute( } else { numEntries = Math.min(...inputsData.map((input) => input.length), preferred.length); if (numEntries === 0) { - return new NodeExecutionOutput( - [returnData], - [ - { - message: - 'Consider enabling "Include Any Unpaired Items" in options or check your inputs', - }, - ], - ); + this.addExecutionHints({ + message: 'Consider enabling "Include Any Unpaired Items" in options or check your inputs', + }); + return [returnData]; } } diff --git a/packages/nodes-base/nodes/Postgres/v2/actions/router.ts b/packages/nodes-base/nodes/Postgres/v2/actions/router.ts index 4d64a7bba1..4c35994dd1 100644 --- a/packages/nodes-base/nodes/Postgres/v2/actions/router.ts +++ b/packages/nodes-base/nodes/Postgres/v2/actions/router.ts @@ -1,5 +1,5 @@ import type { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow'; -import { NodeExecutionOutput, NodeOperationError } from 'n8n-workflow'; +import { NodeOperationError } from 'n8n-workflow'; import * as database from './database/Database.resource'; import type { PostgresType } from './node.type'; @@ -54,15 +54,10 @@ export async function router(this: IExecuteFunctions): Promise 1 && !node.executeOnce) { - return new NodeExecutionOutput( - [returnData], - [ - { - message: `This node ran ${items.length} times, once for each input item. To run for the first item only, enable 'execute once' in the node settings`, - location: 'outputPane', - }, - ], - ); + this.addExecutionHints({ + message: `This node ran ${items.length} times, once for each input item. To run for the first item only, enable 'execute once' in the node settings`, + location: 'outputPane', + }); } return [returnData]; diff --git a/packages/nodes-base/nodes/Transform/Aggregate/Aggregate.node.ts b/packages/nodes-base/nodes/Transform/Aggregate/Aggregate.node.ts index 36791d57f7..21f92c4f27 100644 --- a/packages/nodes-base/nodes/Transform/Aggregate/Aggregate.node.ts +++ b/packages/nodes-base/nodes/Transform/Aggregate/Aggregate.node.ts @@ -11,7 +11,6 @@ import { type IPairedItemData, NodeConnectionType, type NodeExecutionHint, - NodeExecutionOutput, } from 'n8n-workflow'; import { addBinariesToItem } from './utils'; @@ -432,7 +431,9 @@ export class Aggregate implements INodeType { } } - if (hints.length) return new NodeExecutionOutput([[returnData]], hints); + if (hints.length) { + this.addExecutionHints(...hints); + } } return [[returnData]]; diff --git a/packages/nodes-base/nodes/Transform/RemoveDuplicates/v2/RemoveDuplicatesV2.node.ts b/packages/nodes-base/nodes/Transform/RemoveDuplicates/v2/RemoveDuplicatesV2.node.ts index a4fa34d997..87535e1428 100644 --- a/packages/nodes-base/nodes/Transform/RemoveDuplicates/v2/RemoveDuplicatesV2.node.ts +++ b/packages/nodes-base/nodes/Transform/RemoveDuplicates/v2/RemoveDuplicatesV2.node.ts @@ -1,9 +1,4 @@ -import { - NodeConnectionType, - NodeExecutionOutput, - NodeOperationError, - tryToParseDateTime, -} from 'n8n-workflow'; +import { NodeConnectionType, NodeOperationError, tryToParseDateTime } from 'n8n-workflow'; import type { INodeTypeBaseDescription, IExecuteFunctions, @@ -126,13 +121,12 @@ export class RemoveDuplicatesV2 implements INodeType { ); if (maxEntriesNum > 0 && processedDataCount / maxEntriesNum > 0.5) { - return new NodeExecutionOutput(returnData, [ - { - message: `Some duplicates may be not be removed since you're approaching the maximum history size (${maxEntriesNum} items). You can raise this limit using the ‘history size’ option.`, - location: 'outputPane', - }, - ]); - } else return returnData; + this.addExecutionHints({ + message: `Some duplicates may be not be removed since you're approaching the maximum history size (${maxEntriesNum} items). You can raise this limit using the ‘history size’ option.`, + location: 'outputPane', + }); + } + return returnData; } else if (logic === 'removeItemsUpToStoredIncrementalKey') { if (!['node', 'workflow'].includes(scope)) { throw new NodeOperationError( diff --git a/packages/nodes-base/nodes/Transform/SplitOut/SplitOut.node.ts b/packages/nodes-base/nodes/Transform/SplitOut/SplitOut.node.ts index 57b77527e6..90e01b09bb 100644 --- a/packages/nodes-base/nodes/Transform/SplitOut/SplitOut.node.ts +++ b/packages/nodes-base/nodes/Transform/SplitOut/SplitOut.node.ts @@ -1,11 +1,6 @@ import get from 'lodash/get'; import unset from 'lodash/unset'; -import { - NodeOperationError, - deepCopy, - NodeExecutionOutput, - NodeConnectionType, -} from 'n8n-workflow'; +import { NodeOperationError, deepCopy, NodeConnectionType } from 'n8n-workflow'; import type { IBinaryData, IDataObject, @@ -281,7 +276,9 @@ export class SplitOut implements INodeType { } } - if (hints.length) return new NodeExecutionOutput([returnData], hints); + if (hints.length) { + this.addExecutionHints(...hints); + } } return [returnData]; diff --git a/packages/nodes-base/nodes/Transform/Summarize/Summarize.node.ts b/packages/nodes-base/nodes/Transform/Summarize/Summarize.node.ts index 8ed73eb3f3..ca36081060 100644 --- a/packages/nodes-base/nodes/Transform/Summarize/Summarize.node.ts +++ b/packages/nodes-base/nodes/Transform/Summarize/Summarize.node.ts @@ -5,7 +5,6 @@ import { type INodeExecutionData, type INodeType, type INodeTypeDescription, - NodeExecutionOutput, type NodeExecutionHint, type IDataObject, } from 'n8n-workflow'; @@ -345,6 +344,10 @@ export class Summarize implements INodeType { } } + if (fieldsNotFound.length) { + this.addExecutionHints(...fieldsNotFound); + } + if (options.outputFormat === 'singleItem') { const executionData: INodeExecutionData = { json: aggregationResult, @@ -352,7 +355,7 @@ export class Summarize implements INodeType { item: index, })), }; - return new NodeExecutionOutput([[executionData]], fieldsNotFound); + return [[executionData]]; } else { if (!fieldsToSplitBy.length) { const { pairedItems, ...json } = aggregationResult; @@ -362,7 +365,7 @@ export class Summarize implements INodeType { item: index, })), }; - return new NodeExecutionOutput([[executionData]], fieldsNotFound); + return [[executionData]]; } let returnData: IDataObject[] = []; if (nodeVersion > 1) { @@ -379,7 +382,7 @@ export class Summarize implements INodeType { })), }; }); - return new NodeExecutionOutput([executionData], fieldsNotFound); + return [executionData]; } } } diff --git a/packages/nodes-base/nodes/Transform/Summarize/test/unitTests/execute.test.ts b/packages/nodes-base/nodes/Transform/Summarize/test/unitTests/execute.test.ts index d675e4be6d..910f58ff91 100644 --- a/packages/nodes-base/nodes/Transform/Summarize/test/unitTests/execute.test.ts +++ b/packages/nodes-base/nodes/Transform/Summarize/test/unitTests/execute.test.ts @@ -1,7 +1,7 @@ import type { MockProxy } from 'jest-mock-extended'; import { mock } from 'jest-mock-extended'; import type { IExecuteFunctions } from 'n8n-workflow'; -import { NodeExecutionOutput, NodeOperationError } from 'n8n-workflow'; +import { NodeOperationError } from 'n8n-workflow'; import { Summarize } from '../../Summarize.node'; import type { Aggregations } from '../../utils'; @@ -43,14 +43,11 @@ describe('Test Summarize Node, execute', () => { const result = await summarizeNode.execute.call(mockExecuteFunctions); - expect(result).toBeInstanceOf(NodeExecutionOutput); expect(result).toEqual([[{ json: { sum_nonexistentField: 0 }, pairedItem: [{ item: 0 }] }]]); - expect((result as NodeExecutionOutput).getHints()).toEqual([ - { - location: 'outputPane', - message: "The field 'nonexistentField' does not exist in any items", - }, - ]); + expect(mockExecuteFunctions.addExecutionHints).toHaveBeenCalledWith({ + location: 'outputPane', + message: "The field 'nonexistentField' does not exist in any items", + }); }); it('should throw error if node version is < 1.1 and fields not found', async () => { diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index dbba497357..fa2ee53641 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -14,7 +14,7 @@ import type { URLSearchParams } from 'url'; import type { CODE_EXECUTION_MODES, CODE_LANGUAGES, LOG_LEVELS } from './Constants'; import type { IDeferredPromise } from './DeferredPromise'; -import { ApplicationError, type ExecutionCancelledError } from './errors'; +import type { ExecutionCancelledError } from './errors'; import type { ExpressionError } from './errors/expression.error'; import type { NodeApiError } from './errors/node-api.error'; import type { NodeOperationError } from './errors/node-operation.error'; @@ -398,7 +398,8 @@ export interface INodeTypeNameVersion { } export interface IRunNodeResponse { - data: INodeExecutionData[][] | NodeExecutionOutput | null | undefined; + data: INodeExecutionData[][] | null | undefined; + hints?: NodeExecutionHint[]; closeFunction?: CloseFunction; } @@ -918,6 +919,8 @@ export type IExecuteFunctions = ExecuteFunctions.GetNodeParameterFn & metadata?: ITaskMetadata, ): void; + addExecutionHints(...hints: NodeExecutionHint[]): void; + nodeHelpers: NodeHelperFunctions; helpers: RequestHelperFunctions & BaseHelperFunctions & @@ -1553,28 +1556,6 @@ export interface SupplyData { closeFunction?: CloseFunction; } -export class NodeExecutionOutput extends Array { - constructor(data: INodeExecutionData[][], hints: NodeExecutionHint[] = []) { - super(); - // TODO: This is a temporary solution for NODE-1740, until we move away from extending native Array class - Object.defineProperty(data, 'getHints', { - value: () => hints, - enumerable: false, - writable: false, - configurable: false, - }); - return data as NodeExecutionOutput; - } - - static [Symbol.hasInstance](instance: unknown) { - return Array.isArray(instance) && 'getHints' in instance; - } - - getHints(): NodeExecutionHint[] { - throw new ApplicationError('This should not have been called'); - } -} - export interface INodeType { description: INodeTypeDescription; supplyData?(this: ISupplyDataFunctions, itemIndex: number): Promise;