@fsai-flow/workflow 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/dist/README.md +31 -0
  2. package/dist/package.json +42 -0
  3. package/dist/src/index.d.ts +22 -0
  4. package/dist/src/index.d.ts.map +1 -0
  5. package/dist/src/index.js +68 -0
  6. package/dist/src/index.js.map +1 -0
  7. package/dist/src/lib/Constants.d.ts +69 -0
  8. package/dist/src/lib/Constants.d.ts.map +1 -0
  9. package/dist/src/lib/Constants.js +106 -0
  10. package/dist/src/lib/Constants.js.map +1 -0
  11. package/dist/src/lib/DeferredPromise.d.ts +7 -0
  12. package/dist/src/lib/DeferredPromise.d.ts.map +1 -0
  13. package/dist/src/lib/DeferredPromise.js +11 -0
  14. package/dist/src/lib/DeferredPromise.js.map +1 -0
  15. package/dist/src/lib/Expression.d.ts +66 -0
  16. package/dist/src/lib/Expression.d.ts.map +1 -0
  17. package/dist/src/lib/Expression.js +247 -0
  18. package/dist/src/lib/Expression.js.map +1 -0
  19. package/dist/src/lib/Interfaces.d.ts +1623 -0
  20. package/dist/src/lib/Interfaces.d.ts.map +1 -0
  21. package/dist/src/lib/Interfaces.js +44 -0
  22. package/dist/src/lib/Interfaces.js.map +1 -0
  23. package/dist/src/lib/LoggerProxy.d.ts +10 -0
  24. package/dist/src/lib/LoggerProxy.d.ts.map +1 -0
  25. package/dist/src/lib/LoggerProxy.js +40 -0
  26. package/dist/src/lib/LoggerProxy.js.map +1 -0
  27. package/dist/src/lib/MetadataUtils.d.ts +5 -0
  28. package/dist/src/lib/MetadataUtils.d.ts.map +1 -0
  29. package/dist/src/lib/MetadataUtils.js +27 -0
  30. package/dist/src/lib/MetadataUtils.js.map +1 -0
  31. package/dist/src/lib/NodeErrors.d.ts +83 -0
  32. package/dist/src/lib/NodeErrors.d.ts.map +1 -0
  33. package/dist/src/lib/NodeErrors.js +284 -0
  34. package/dist/src/lib/NodeErrors.js.map +1 -0
  35. package/dist/src/lib/NodeHelpers.d.ts +199 -0
  36. package/dist/src/lib/NodeHelpers.d.ts.map +1 -0
  37. package/dist/src/lib/NodeHelpers.js +1335 -0
  38. package/dist/src/lib/NodeHelpers.js.map +1 -0
  39. package/dist/src/lib/ObservableObject.d.ts +6 -0
  40. package/dist/src/lib/ObservableObject.d.ts.map +1 -0
  41. package/dist/src/lib/ObservableObject.js +61 -0
  42. package/dist/src/lib/ObservableObject.js.map +1 -0
  43. package/dist/src/lib/RoutingNode.d.ts +24 -0
  44. package/dist/src/lib/RoutingNode.d.ts.map +1 -0
  45. package/dist/src/lib/RoutingNode.js +528 -0
  46. package/dist/src/lib/RoutingNode.js.map +1 -0
  47. package/dist/src/lib/TelemetryHelpers.d.ts +4 -0
  48. package/dist/src/lib/TelemetryHelpers.d.ts.map +1 -0
  49. package/dist/src/lib/TelemetryHelpers.js +67 -0
  50. package/dist/src/lib/TelemetryHelpers.js.map +1 -0
  51. package/dist/src/lib/TypeValidation.d.ts +22 -0
  52. package/dist/src/lib/TypeValidation.d.ts.map +1 -0
  53. package/dist/src/lib/TypeValidation.js +376 -0
  54. package/dist/src/lib/TypeValidation.js.map +1 -0
  55. package/dist/src/lib/VersionedNodeType.d.ts +10 -0
  56. package/dist/src/lib/VersionedNodeType.d.ts.map +1 -0
  57. package/dist/src/lib/VersionedNodeType.js +24 -0
  58. package/dist/src/lib/VersionedNodeType.js.map +1 -0
  59. package/dist/src/lib/Workflow.d.ts +249 -0
  60. package/dist/src/lib/Workflow.d.ts.map +1 -0
  61. package/dist/src/lib/Workflow.js +899 -0
  62. package/dist/src/lib/Workflow.js.map +1 -0
  63. package/dist/src/lib/WorkflowDataProxy.d.ts +88 -0
  64. package/dist/src/lib/WorkflowDataProxy.d.ts.map +1 -0
  65. package/dist/src/lib/WorkflowDataProxy.js +583 -0
  66. package/dist/src/lib/WorkflowDataProxy.js.map +1 -0
  67. package/dist/src/lib/WorkflowErrors.d.ts +10 -0
  68. package/dist/src/lib/WorkflowErrors.d.ts.map +1 -0
  69. package/dist/src/lib/WorkflowErrors.js +18 -0
  70. package/dist/src/lib/WorkflowErrors.js.map +1 -0
  71. package/dist/src/lib/WorkflowHooks.d.ts +12 -0
  72. package/dist/src/lib/WorkflowHooks.d.ts.map +1 -0
  73. package/dist/src/lib/WorkflowHooks.js +32 -0
  74. package/dist/src/lib/WorkflowHooks.js.map +1 -0
  75. package/dist/src/lib/errors/base/base.error.d.ts +30 -0
  76. package/dist/src/lib/errors/base/base.error.d.ts.map +1 -0
  77. package/dist/src/lib/errors/base/base.error.js +47 -0
  78. package/dist/src/lib/errors/base/base.error.js.map +1 -0
  79. package/dist/src/lib/errors/base/operational.error.d.ts +16 -0
  80. package/dist/src/lib/errors/base/operational.error.d.ts.map +1 -0
  81. package/dist/src/lib/errors/base/operational.error.js +19 -0
  82. package/dist/src/lib/errors/base/operational.error.js.map +1 -0
  83. package/dist/src/lib/errors/error.types.d.ts +14 -0
  84. package/dist/src/lib/errors/error.types.d.ts.map +1 -0
  85. package/dist/src/lib/errors/error.types.js +3 -0
  86. package/dist/src/lib/errors/error.types.js.map +1 -0
  87. package/dist/src/lib/errors/index.d.ts +2 -0
  88. package/dist/src/lib/errors/index.d.ts.map +1 -0
  89. package/dist/src/lib/errors/index.js +6 -0
  90. package/dist/src/lib/errors/index.js.map +1 -0
  91. package/dist/src/lib/result.d.ts +20 -0
  92. package/dist/src/lib/result.d.ts.map +1 -0
  93. package/dist/src/lib/result.js +36 -0
  94. package/dist/src/lib/result.js.map +1 -0
  95. package/dist/src/lib/utils.d.ts +51 -0
  96. package/dist/src/lib/utils.d.ts.map +1 -0
  97. package/dist/src/lib/utils.js +119 -0
  98. package/dist/src/lib/utils.js.map +1 -0
  99. package/package.json +49 -35
  100. package/.eslintrc.json +0 -33
  101. package/eslint.config.js +0 -19
  102. package/jest.config.ts +0 -10
  103. package/project.json +0 -19
  104. package/src/index.ts +0 -33
  105. package/src/lib/Constants.ts +0 -124
  106. package/src/lib/DeferredPromise.ts +0 -14
  107. package/src/lib/Expression.ts +0 -375
  108. package/src/lib/Interfaces.ts +0 -2262
  109. package/src/lib/LoggerProxy.ts +0 -43
  110. package/src/lib/MetadataUtils.ts +0 -34
  111. package/src/lib/NodeErrors.ts +0 -332
  112. package/src/lib/NodeHelpers.ts +0 -1666
  113. package/src/lib/ObservableObject.ts +0 -77
  114. package/src/lib/RoutingNode.ts +0 -862
  115. package/src/lib/TelemetryHelpers.ts +0 -86
  116. package/src/lib/TypeValidation.ts +0 -431
  117. package/src/lib/VersionedNodeType.ts +0 -30
  118. package/src/lib/Workflow.ts +0 -1270
  119. package/src/lib/WorkflowDataProxy.ts +0 -708
  120. package/src/lib/WorkflowErrors.ts +0 -18
  121. package/src/lib/WorkflowHooks.ts +0 -51
  122. package/src/lib/errors/base/base.error.ts +0 -68
  123. package/src/lib/errors/base/operational.error.ts +0 -21
  124. package/src/lib/errors/error.types.ts +0 -14
  125. package/src/lib/errors/index.ts +0 -1
  126. package/src/lib/result.ts +0 -34
  127. package/src/lib/utils.ts +0 -166
  128. package/tests/Helpers.ts +0 -667
  129. package/tests/NodeHelpers.test.ts +0 -3053
  130. package/tests/ObservableObject.test.ts +0 -171
  131. package/tests/RoutingNode.test.ts +0 -1680
  132. package/tests/Workflow.test.ts +0 -1284
  133. package/tests/WorkflowDataProxy.test.ts +0 -199
  134. package/tsconfig.json +0 -27
  135. package/tsconfig.lib.json +0 -11
  136. package/tsconfig.spec.json +0 -14
@@ -1,1270 +0,0 @@
1
- /* eslint-disable no-await-in-loop */
2
- /* eslint-disable @typescript-eslint/no-unsafe-call */
3
- /* eslint-disable @typescript-eslint/no-unsafe-member-access */
4
- /* eslint-disable @typescript-eslint/no-unsafe-assignment */
5
- /* eslint-disable no-param-reassign */
6
- /* eslint-disable @typescript-eslint/no-non-null-assertion */
7
- /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
8
- /* eslint-disable @typescript-eslint/no-unsafe-return */
9
- /* eslint-disable @typescript-eslint/no-for-in-array */
10
- /* eslint-disable no-prototype-builtins */
11
- /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
12
- /* eslint-disable no-underscore-dangle */
13
- /* eslint-disable no-continue */
14
- /* eslint-disable no-restricted-syntax */
15
- /* eslint-disable import/no-cycle */
16
-
17
- import {
18
- Expression,
19
- IConnections,
20
- IDeferredPromise,
21
- IExecuteResponsePromiseData,
22
- IGetExecuteTriggerFunctions,
23
- INode,
24
- INodeExecuteFunctions,
25
- INodeExecutionData,
26
- INodeIssues,
27
- INodeParameters,
28
- INodes,
29
- INodeType,
30
- INodeTypes,
31
- IPollFunctions,
32
- IRunExecutionData,
33
- ITaskDataConnections,
34
- ITriggerResponse,
35
- IWebhookData,
36
- IWebhookResponseData,
37
- IWorfklowIssues,
38
- IWorkflowExecuteAdditionalData,
39
- IWorkflowSettings,
40
- NodeHelpers,
41
- NodeParameterValue,
42
- ObservableObject,
43
- RoutingNode,
44
- WebhookSetupMethodNames,
45
- WorkflowActivateMode,
46
- WorkflowExecuteMode,
47
- } from '..';
48
-
49
- import { CloseFunction, IConnection, IDataObject, IObservableObject } from './Interfaces';
50
-
51
- export class Workflow {
52
- id: string | undefined;
53
-
54
- name: string | undefined;
55
-
56
- nodes: INodes = {};
57
-
58
- connectionsBySourceNode: IConnections;
59
-
60
- connectionsByDestinationNode: IConnections;
61
-
62
- nodeTypes: INodeTypes;
63
-
64
- expression: Expression;
65
-
66
- active: boolean;
67
-
68
- settings: IWorkflowSettings;
69
-
70
- // To save workflow specific static data like for example
71
- // ids of registred webhooks of nodes
72
- staticData: IDataObject;
73
-
74
- // constructor(id: string | undefined, nodes: INode[], connections: IConnections, active: boolean, nodeTypes: INodeTypes, staticData?: IDataObject, settings?: IWorkflowSettings) {
75
- constructor(parameters: {
76
- id?: string;
77
- name?: string;
78
- nodes: INode[];
79
- connections: IConnections;
80
- active: boolean;
81
- nodeTypes: INodeTypes;
82
- staticData?: IDataObject;
83
- settings?: IWorkflowSettings;
84
- }) {
85
- this.id = parameters?.id;
86
- this.name = parameters?.name;
87
- this.nodeTypes = parameters.nodeTypes;
88
-
89
- // Save nodes in workflow as object to be able to get the
90
- // nodes easily by its name.
91
- // Also directly add the default values of the node type.
92
- let nodeType: INodeType | undefined;
93
- for (const node of parameters.nodes) {
94
- this.nodes[node.name] = node;
95
-
96
- nodeType = this.nodeTypes.getByNameAndVersion(node.type, node.typeVersion);
97
-
98
- if (nodeType === undefined) {
99
- // Go on to next node when its type is not known.
100
- // For now do not error because that causes problems with
101
- // expression resolution also then when the unknown node
102
- // does not get used.
103
- continue;
104
- // throw new Error(`The node type "${node.type}" of node "${node.name}" is not known.`);
105
- }
106
-
107
- // Add default values
108
- const nodeParameters = NodeHelpers.getNodeParameters(
109
- nodeType.description.properties,
110
- node.parameters,
111
- true,
112
- false,
113
- );
114
- node.parameters = nodeParameters !== null ? nodeParameters : {};
115
- }
116
- this.connectionsBySourceNode = parameters.connections;
117
-
118
- // Save also the connections by the destionation nodes
119
- this.connectionsByDestinationNode = this.__getConnectionsByDestination(parameters.connections);
120
-
121
- this.active = parameters.active || false;
122
-
123
- this.staticData = ObservableObject.create(parameters.staticData || {}, undefined, {
124
- ignoreEmptyOnFirstChild: true,
125
- });
126
-
127
- this.settings = parameters.settings || {};
128
-
129
- this.expression = new Expression(this);
130
- }
131
-
132
- /**
133
- * The default connections are by source node. This function rewrites them by destination nodes
134
- * to easily find parent nodes.
135
- *
136
- * @param {IConnections} connections
137
- * @returns {IConnections}
138
- * @memberof Workflow
139
- */
140
- __getConnectionsByDestination(connections: IConnections): IConnections {
141
- const returnConnection: IConnections = {};
142
-
143
- let connectionInfo;
144
- let maxIndex: number;
145
- for (const sourceNode in connections) {
146
- if (!connections.hasOwnProperty(sourceNode)) {
147
- continue;
148
- }
149
- for (const type in connections[sourceNode]) {
150
- if (!connections[sourceNode].hasOwnProperty(type)) {
151
- continue;
152
- }
153
- for (const inputIndex in connections[sourceNode][type]) {
154
- if (!connections[sourceNode][type].hasOwnProperty(inputIndex)) {
155
- continue;
156
- }
157
- for (connectionInfo of connections[sourceNode][type][inputIndex]) {
158
- if (connectionInfo.node === '__proto__' || connectionInfo.node === 'constructor' || connectionInfo.node === 'prototype') {
159
- throw new Error('Prototype-polluting assignment detected');
160
- }
161
-
162
- if (!returnConnection.hasOwnProperty(connectionInfo.node)) {
163
- returnConnection[connectionInfo.node] = {};
164
- }
165
- if (!returnConnection[connectionInfo.node].hasOwnProperty(connectionInfo.type)) {
166
- returnConnection[connectionInfo.node][connectionInfo.type] = [];
167
- }
168
-
169
- maxIndex = returnConnection[connectionInfo.node][connectionInfo.type].length - 1;
170
- for (let j = maxIndex; j < connectionInfo.index; j++) {
171
- returnConnection[connectionInfo.node][connectionInfo.type].push([]);
172
- }
173
-
174
- returnConnection[connectionInfo.node][connectionInfo.type][connectionInfo.index].push({
175
- node: sourceNode,
176
- type,
177
- index: parseInt(inputIndex, 10),
178
- });
179
- }
180
- }
181
- }
182
- }
183
-
184
- return returnConnection;
185
- }
186
-
187
- /**
188
- * A workflow can only be activated if it has a node which has either triggers
189
- * or webhooks defined.
190
- *
191
- * @param {string[]} [ignoreNodeTypes] Node-types to ignore in the check
192
- * @returns {boolean}
193
- * @memberof Workflow
194
- */
195
- checkIfWorkflowCanBeActivated(ignoreNodeTypes?: string[]): boolean {
196
- let node: INode;
197
- let nodeType: INodeType | undefined;
198
-
199
- for (const nodeName of Object.keys(this.nodes)) {
200
- node = this.nodes[nodeName];
201
-
202
- if (node.disabled === true) {
203
- // Deactivated nodes can not trigger a run so ignore
204
- continue;
205
- }
206
-
207
- // eslint-disable-next-line @typescript-eslint/prefer-optional-chain
208
- if (ignoreNodeTypes !== undefined && ignoreNodeTypes.includes(node.type)) {
209
- continue;
210
- }
211
-
212
- nodeType = this.nodeTypes.getByNameAndVersion(node.type, node.typeVersion);
213
-
214
- if (nodeType === undefined) {
215
- // Type is not known so check is not possible
216
- continue;
217
- }
218
-
219
- if (
220
- nodeType.poll !== undefined ||
221
- nodeType.trigger !== undefined ||
222
- nodeType.webhook !== undefined
223
- ) {
224
- // Is a trigger node. So workflow can be activated.
225
- return true;
226
- }
227
- }
228
-
229
- return false;
230
- }
231
-
232
- /**
233
- * Checks if everything in the workflow is complete
234
- * and ready to be executed. If it returns null everything
235
- * is fine. If there are issues it returns the issues
236
- * which have been found for the different nodes.
237
- * TODO: Does currently not check for credential issues!
238
- *
239
- * @returns {(IWorfklowIssues | null)}
240
- * @memberof Workflow
241
- */
242
- checkReadyForExecution(inputData: {
243
- startNode?: string;
244
- destinationNode?: string;
245
- }): IWorfklowIssues | null {
246
- let node: INode;
247
- let nodeType: INodeType | undefined;
248
- let nodeIssues: INodeIssues | null = null;
249
- const workflowIssues: IWorfklowIssues = {};
250
-
251
- let checkNodes: string[] = [];
252
- if (inputData.destinationNode) {
253
- // If a destination node is given we have to check all the nodes
254
- // leading up to it
255
- checkNodes = this.getParentNodes(inputData.destinationNode);
256
- checkNodes.push(inputData.destinationNode);
257
- } else if (inputData.startNode) {
258
- // If a start node is given we have to check all nodes which
259
- // come after it
260
- checkNodes = this.getChildNodes(inputData.startNode);
261
- checkNodes.push(inputData.startNode);
262
- }
263
-
264
- for (const nodeName of checkNodes) {
265
- nodeIssues = null;
266
- node = this.nodes[nodeName];
267
-
268
- if (node.disabled === true) {
269
- continue;
270
- }
271
-
272
- nodeType = this.nodeTypes.getByNameAndVersion(node.type, node.typeVersion);
273
-
274
- if (nodeType === undefined) {
275
- // Node type is not known
276
- nodeIssues = {
277
- typeUnknown: true,
278
- };
279
- } else {
280
- nodeIssues = NodeHelpers.getNodeParametersIssues(nodeType.description.properties, node);
281
- }
282
-
283
- if (nodeIssues !== null) {
284
- workflowIssues[node.name] = nodeIssues;
285
- }
286
- }
287
-
288
- if (Object.keys(workflowIssues).length === 0) {
289
- return null;
290
- }
291
-
292
- return workflowIssues;
293
- }
294
-
295
- /**
296
- * Returns the static data of the workflow.
297
- * It gets saved with the workflow and will be the same for
298
- * all workflow-executions.
299
- *
300
- * @param {string} type The type of data to return ("global"|"node")
301
- * @param {INode} [node] If type is set to "node" then the node has to be provided
302
- * @returns {IDataObject}
303
- * @memberof Workflow
304
- */
305
- getStaticData(type: string, node?: INode): IDataObject {
306
- let key: string;
307
- if (type === 'global') {
308
- key = 'global';
309
- } else if (type === 'node') {
310
- if (node === undefined) {
311
- throw new Error(
312
- `The request data of context type "node" the node parameter has to be set!`,
313
- );
314
- }
315
- key = `node:${node.name}`;
316
- } else {
317
- throw new Error(
318
- `The context type "${type}" is not know. Only "global" and node" are supported!`,
319
- );
320
- }
321
-
322
- if (this.staticData[key] === undefined) {
323
- // Create it as ObservableObject that we can easily check if the data changed
324
- // to know if the workflow with its data has to be saved afterwards or not.
325
- this.staticData[key] = ObservableObject.create({}, this.staticData as IObservableObject);
326
- }
327
-
328
- return this.staticData[key] as IDataObject;
329
- }
330
-
331
- /**
332
- * Returns all the trigger nodes in the workflow.
333
- *
334
- * @returns {INode[]}
335
- * @memberof Workflow
336
- */
337
- getTriggerNodes(): INode[] {
338
- return this.queryNodes((nodeType: INodeType) => !!nodeType.trigger);
339
- }
340
-
341
- /**
342
- * Returns all the poll nodes in the workflow
343
- *
344
- * @returns {INode[]}
345
- * @memberof Workflow
346
- */
347
- getPollNodes(): INode[] {
348
- return this.queryNodes((nodeType: INodeType) => !!nodeType.poll);
349
- }
350
-
351
- /**
352
- * Returns all the nodes in the workflow for which the given
353
- * checkFunction return true
354
- *
355
- * @param {(nodeType: INodeType) => boolean} checkFunction
356
- * @returns {INode[]}
357
- * @memberof Workflow
358
- */
359
- queryNodes(checkFunction: (nodeType: INodeType) => boolean): INode[] {
360
- const returnNodes: INode[] = [];
361
-
362
- // Check if it has any of them
363
- let node: INode;
364
- let nodeType: INodeType | undefined;
365
-
366
- for (const nodeName of Object.keys(this.nodes)) {
367
- node = this.nodes[nodeName];
368
-
369
- if (node.disabled === true) {
370
- continue;
371
- }
372
-
373
- nodeType = this.nodeTypes.getByNameAndVersion(node.type, node.typeVersion);
374
-
375
- if (nodeType !== undefined && checkFunction(nodeType)) {
376
- returnNodes.push(node);
377
- }
378
- }
379
-
380
- return returnNodes;
381
- }
382
-
383
- /**
384
- * Returns the node with the given name if it exists else null
385
- *
386
- * @param {string} nodeName Name of the node to return
387
- * @returns {(INode | null)}
388
- * @memberof Workflow
389
- */
390
- getNode(nodeName: string): INode | null {
391
- if (this.nodes.hasOwnProperty(nodeName)) {
392
- return this.nodes[nodeName];
393
- }
394
-
395
- return null;
396
- }
397
-
398
- /**
399
- * Renames nodes in expressions
400
- *
401
- * @param {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[])} parameterValue The parameters to check for expressions
402
- * @param {string} currentName The current name of the node
403
- * @param {string} newName The new name
404
- * @returns {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[])}
405
- * @memberof Workflow
406
- */
407
- renameNodeInExpressions(
408
- parameterValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[],
409
- currentName: string,
410
- newName: string,
411
- ): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] {
412
- if (typeof parameterValue !== 'object') {
413
- // Reached the actual value
414
- if (typeof parameterValue === 'string' && parameterValue.charAt(0) === '=') {
415
- // Is expression so has to be rewritten
416
-
417
- // To not run the "expensive" regex stuff when it is not needed
418
- // make a simple check first if it really contains the the node-name
419
- if (parameterValue.includes(currentName)) {
420
- // Really contains node-name (even though we do not know yet if really as $node-expression)
421
-
422
- // In case some special characters are used in name escape them
423
- const currentNameEscaped = currentName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
424
-
425
- parameterValue = parameterValue.replace(
426
- new RegExp(`(\\$node(\\.|\\["|\\['))${currentNameEscaped}((\\.|"\\]|'\\]))`, 'g'),
427
- `$1${newName}$3`,
428
- );
429
- }
430
- }
431
-
432
- return parameterValue;
433
- }
434
-
435
- if (Array.isArray(parameterValue)) {
436
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
437
- const returnArray: any[] = [];
438
-
439
- for (const currentValue of parameterValue) {
440
- returnArray.push(this.renameNodeInExpressions(currentValue, currentName, newName));
441
- }
442
-
443
- return returnArray;
444
- }
445
-
446
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
447
- const returnData: any = {};
448
-
449
- for (const parameterName of Object.keys(parameterValue || {})) {
450
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
451
- returnData[parameterName] = this.renameNodeInExpressions(
452
- parameterValue![parameterName],
453
- currentName,
454
- newName,
455
- );
456
- }
457
-
458
- return returnData;
459
- }
460
-
461
- /**
462
- * Rename a node in the workflow
463
- *
464
- * @param {string} currentName The current name of the node
465
- * @param {string} newName The new name
466
- * @memberof Workflow
467
- */
468
- renameNode(currentName: string, newName: string) {
469
- // Rename the node itself
470
- if (this.nodes[currentName] !== undefined) {
471
- this.nodes[newName] = this.nodes[currentName];
472
- this.nodes[newName].name = newName;
473
- delete this.nodes[currentName];
474
- }
475
-
476
- // Update the expressions which reference the node
477
- // with its old name
478
- for (const node of Object.values(this.nodes)) {
479
- node.parameters = this.renameNodeInExpressions(
480
- node.parameters,
481
- currentName,
482
- newName,
483
- ) as INodeParameters;
484
- }
485
-
486
- // Change all source connections
487
- if (this.connectionsBySourceNode.hasOwnProperty(currentName)) {
488
- this.connectionsBySourceNode[newName] = this.connectionsBySourceNode[currentName];
489
- delete this.connectionsBySourceNode[currentName];
490
- }
491
-
492
- // Change all destination connections
493
- let sourceNode: string;
494
- let type: string;
495
- let sourceIndex: string;
496
- let connectionIndex: string;
497
- let connectionData: IConnection;
498
- for (sourceNode of Object.keys(this.connectionsBySourceNode)) {
499
- for (type of Object.keys(this.connectionsBySourceNode[sourceNode])) {
500
- for (sourceIndex of Object.keys(this.connectionsBySourceNode[sourceNode][type])) {
501
- for (connectionIndex of Object.keys(
502
- this.connectionsBySourceNode[sourceNode][type][parseInt(sourceIndex, 10)],
503
- )) {
504
- connectionData =
505
- this.connectionsBySourceNode[sourceNode][type][parseInt(sourceIndex, 10)][
506
- parseInt(connectionIndex, 10)
507
- ];
508
- if (connectionData.node === currentName) {
509
- connectionData.node = newName;
510
- }
511
- }
512
- }
513
- }
514
- }
515
-
516
- // Use the updated connections to create updated connections by destionation nodes
517
- this.connectionsByDestinationNode = this.__getConnectionsByDestination(
518
- this.connectionsBySourceNode,
519
- );
520
- }
521
-
522
- /**
523
- * Finds the highest parent nodes of the node with the given name
524
- *
525
- * @param {string} nodeName
526
- * @param {string} [type='main']
527
- * @param {number} [nodeConnectionIndex]
528
- * @returns {string[]}
529
- * @memberof Workflow
530
- */
531
- getHighestNode(
532
- nodeName: string,
533
- type = 'main',
534
- nodeConnectionIndex?: number,
535
- checkedNodes?: string[],
536
- ): string[] {
537
- const currentHighest: string[] = [];
538
- if (this.nodes[nodeName].disabled === false) {
539
- // If the current node is not disabled itself is the highest
540
- currentHighest.push(nodeName);
541
- }
542
-
543
- if (!this.connectionsByDestinationNode.hasOwnProperty(nodeName)) {
544
- // Node does not have incoming connections
545
- return currentHighest;
546
- }
547
-
548
- if (!this.connectionsByDestinationNode[nodeName].hasOwnProperty(type)) {
549
- // Node does not have incoming connections of given type
550
- return currentHighest;
551
- }
552
-
553
- checkedNodes = checkedNodes || [];
554
-
555
- if (checkedNodes.includes(nodeName)) {
556
- // Node got checked already before
557
- return currentHighest;
558
- }
559
-
560
- checkedNodes.push(nodeName);
561
-
562
- const returnNodes: string[] = [];
563
- let addNodes: string[];
564
-
565
- let connectionsByIndex: IConnection[];
566
- for (
567
- let connectionIndex = 0;
568
- connectionIndex < this.connectionsByDestinationNode[nodeName][type].length;
569
- connectionIndex++
570
- ) {
571
- if (nodeConnectionIndex !== undefined && nodeConnectionIndex !== connectionIndex) {
572
- // If a connection-index is given ignore all other ones
573
- continue;
574
- }
575
- connectionsByIndex = this.connectionsByDestinationNode[nodeName][type][connectionIndex];
576
- // eslint-disable-next-line @typescript-eslint/no-loop-func
577
- connectionsByIndex.forEach((connection) => {
578
- if (checkedNodes!.includes(connection.node)) {
579
- // Node got checked already before
580
- return;
581
- }
582
-
583
- addNodes = this.getHighestNode(connection.node, type, undefined, checkedNodes);
584
-
585
- if (addNodes.length === 0) {
586
- // The checked node does not have any further parents so add it
587
- // if it is not disabled
588
- if (this.nodes[connection.node].disabled !== true) {
589
- addNodes = [connection.node];
590
- }
591
- }
592
-
593
- addNodes.forEach((name) => {
594
- // Only add if node is not on the list already anyway
595
- if (returnNodes.indexOf(name) === -1) {
596
- returnNodes.push(name);
597
- }
598
- });
599
- });
600
- }
601
-
602
- return returnNodes;
603
- }
604
-
605
- /**
606
- * Returns all the after the given one
607
- *
608
- * @param {string} nodeName
609
- * @param {string} [type='main']
610
- * @param {*} [depth=-1]
611
- * @returns {string[]}
612
- * @memberof Workflow
613
- */
614
- getChildNodes(nodeName: string, type = 'main', depth = -1): string[] {
615
- return this.getConnectedNodes(this.connectionsBySourceNode, nodeName, type, depth);
616
- }
617
-
618
- /**
619
- * Returns all the nodes before the given one
620
- *
621
- * @param {string} nodeName
622
- * @param {string} [type='main']
623
- * @param {*} [depth=-1]
624
- * @returns {string[]}
625
- * @memberof Workflow
626
- */
627
- getParentNodes(nodeName: string, type = 'main', depth = -1): string[] {
628
- return this.getConnectedNodes(this.connectionsByDestinationNode, nodeName, type, depth);
629
- }
630
-
631
- /**
632
- * Gets all the nodes which are connected nodes starting from
633
- * the given one
634
- *
635
- * @param {IConnections} connections
636
- * @param {string} nodeName
637
- * @param {string} [type='main']
638
- * @param {*} [depth=-1]
639
- * @param {string[]} [checkedNodes]
640
- * @returns {string[]}
641
- * @memberof Workflow
642
- */
643
- getConnectedNodes(
644
- connections: IConnections,
645
- nodeName: string,
646
- type = 'main',
647
- depth = -1,
648
- checkedNodes?: string[],
649
- ): string[] {
650
- depth = depth === -1 ? -1 : depth;
651
- const newDepth = depth === -1 ? depth : depth - 1;
652
- if (depth === 0) {
653
- // Reached max depth
654
- return [];
655
- }
656
-
657
- if (!connections.hasOwnProperty(nodeName)) {
658
- // Node does not have incoming connections
659
- return [];
660
- }
661
-
662
- if (!connections[nodeName].hasOwnProperty(type)) {
663
- // Node does not have incoming connections of given type
664
- return [];
665
- }
666
-
667
- checkedNodes = checkedNodes || [];
668
-
669
- if (checkedNodes.includes(nodeName)) {
670
- // Node got checked already before
671
- return [];
672
- }
673
-
674
- checkedNodes.push(nodeName);
675
-
676
- const returnNodes: string[] = [];
677
- let addNodes: string[];
678
- let nodeIndex: number;
679
- let i: number;
680
- let parentNodeName: string;
681
- connections[nodeName][type].forEach((connectionsByIndex: IConnection[]) => {
682
- connectionsByIndex.forEach((connection: IConnection) => {
683
- if (checkedNodes!.includes(connection.node)) {
684
- // Node got checked already before
685
- return;
686
- }
687
-
688
- returnNodes.unshift(connection.node);
689
-
690
- addNodes = this.getConnectedNodes(
691
- connections,
692
- connection.node,
693
- type,
694
- newDepth,
695
- checkedNodes,
696
- );
697
-
698
- for (i = addNodes.length; i--; i > 0) {
699
- // Because nodes can have multiple parents it is possible that
700
- // parts of the tree is parent of both and to not add nodes
701
- // twice check first if they already got added before.
702
- parentNodeName = addNodes[i];
703
- nodeIndex = returnNodes.indexOf(parentNodeName);
704
-
705
- if (nodeIndex !== -1) {
706
- // Node got found before so remove it from current location
707
- // that node-order stays correct
708
- returnNodes.splice(nodeIndex, 1);
709
- }
710
-
711
- returnNodes.unshift(parentNodeName);
712
- }
713
- });
714
- });
715
-
716
- return returnNodes;
717
- }
718
-
719
- /**
720
- * Returns via which output of the parent-node the node
721
- * is connected to.
722
- *
723
- * @param {string} nodeName The node to check how it is connected with parent node
724
- * @param {string} parentNodeName The parent node to get the output index of
725
- * @param {string} [type='main']
726
- * @param {*} [depth=-1]
727
- * @param {string[]} [checkedNodes]
728
- * @returns {(number | undefined)}
729
- * @memberof Workflow
730
- */
731
- getNodeConnectionOutputIndex(
732
- nodeName: string,
733
- parentNodeName: string,
734
- type = 'main',
735
- depth = -1,
736
- checkedNodes?: string[],
737
- ): number | undefined {
738
- const node = this.getNode(parentNodeName);
739
- if (node === null) {
740
- return undefined;
741
- }
742
- const nodeType = this.nodeTypes.getByNameAndVersion(node.type, node.typeVersion) as INodeType;
743
- if (nodeType.description.outputs.length === 1) {
744
- // If the parent node has only one output, it can only be connected
745
- // to that one. So no further checking is required.
746
- return 0;
747
- }
748
-
749
- depth = depth === -1 ? -1 : depth;
750
- const newDepth = depth === -1 ? depth : depth - 1;
751
- if (depth === 0) {
752
- // Reached max depth
753
- return undefined;
754
- }
755
-
756
- if (!this.connectionsByDestinationNode.hasOwnProperty(nodeName)) {
757
- // Node does not have incoming connections
758
- return undefined;
759
- }
760
-
761
- if (!this.connectionsByDestinationNode[nodeName].hasOwnProperty(type)) {
762
- // Node does not have incoming connections of given type
763
- return undefined;
764
- }
765
-
766
- checkedNodes = checkedNodes || [];
767
-
768
- if (checkedNodes.includes(nodeName)) {
769
- // Node got checked already before
770
- return undefined;
771
- }
772
-
773
- checkedNodes.push(nodeName);
774
-
775
- let outputIndex: number | undefined;
776
- for (const connectionsByIndex of this.connectionsByDestinationNode[nodeName][type]) {
777
- for (const connection of connectionsByIndex) {
778
- if (parentNodeName === connection.node) {
779
- return connection.index;
780
- }
781
-
782
- if (checkedNodes.includes(connection.node)) {
783
- // Node got checked already before so continue with the next one
784
- continue;
785
- }
786
-
787
- outputIndex = this.getNodeConnectionOutputIndex(
788
- connection.node,
789
- parentNodeName,
790
- type,
791
- newDepth,
792
- checkedNodes,
793
- );
794
-
795
- if (outputIndex !== undefined) {
796
- return outputIndex;
797
- }
798
- }
799
- }
800
-
801
- return undefined;
802
- }
803
-
804
- /**
805
- * Returns from which of the given nodes the workflow should get started from
806
- *
807
- * @param {string[]} nodeNames The potential start nodes
808
- * @returns {(INode | undefined)}
809
- * @memberof Workflow
810
- */
811
- __getStartNode(nodeNames: string[]): INode | undefined {
812
- // Check if there are any trigger or poll nodes and then return the first one
813
- let node: INode;
814
- let nodeType: INodeType;
815
- for (const nodeName of nodeNames) {
816
- node = this.nodes[nodeName];
817
-
818
- nodeType = this.nodeTypes.getByNameAndVersion(node.type, node.typeVersion) as INodeType;
819
-
820
- if (nodeType.trigger !== undefined || nodeType.poll !== undefined) {
821
- if (node.disabled === true) {
822
- continue;
823
- }
824
- return node;
825
- }
826
- }
827
-
828
- // Check if there is the actual "start" node
829
- const startNodeType = 'n8n-nodes-base.start';
830
- for (const nodeName of nodeNames) {
831
- node = this.nodes[nodeName];
832
- if (node.type === startNodeType) {
833
- return node;
834
- }
835
- }
836
-
837
- return undefined;
838
- }
839
-
840
- /**
841
- * Returns the start node to start the worfklow from
842
- *
843
- * @param {string} [destinationNode]
844
- * @returns {(INode | undefined)}
845
- * @memberof Workflow
846
- */
847
- getStartNode(destinationNode?: string): INode | undefined {
848
- if (destinationNode) {
849
- // Find the highest parent nodes of the given one
850
- const nodeNames = this.getHighestNode(destinationNode);
851
-
852
- if (nodeNames.length === 0) {
853
- // If no parent nodes have been found then only the destination-node
854
- // is in the tree so add that one
855
- nodeNames.push(destinationNode);
856
- }
857
-
858
- // Check which node to return as start node
859
- const node = this.__getStartNode(nodeNames);
860
- if (node !== undefined) {
861
- return node;
862
- }
863
-
864
- // If none of the above did find anything simply return the
865
- // first parent node in the list
866
- return this.nodes[nodeNames[0]];
867
- }
868
-
869
- return this.__getStartNode(Object.keys(this.nodes));
870
- }
871
-
872
- /**
873
- * Executes the Webhooks method of the node
874
- *
875
- * @param {WebhookSetupMethodNames} method The name of the method to execute
876
- * @param {IWebhookData} webhookData
877
- * @param {INodeExecuteFunctions} nodeExecuteFunctions
878
- * @param {WorkflowExecuteMode} mode
879
- * @returns {(Promise<boolean | undefined>)}
880
- * @memberof Workflow
881
- */
882
- async runWebhookMethod(
883
- method: WebhookSetupMethodNames,
884
- webhookData: IWebhookData,
885
- nodeExecuteFunctions: INodeExecuteFunctions,
886
- mode: WorkflowExecuteMode,
887
- activation: WorkflowActivateMode,
888
- isTest?: boolean,
889
- ): Promise<boolean | undefined> {
890
- const node = this.getNode(webhookData.node) as INode;
891
- const nodeType = this.nodeTypes.getByNameAndVersion(node.type, node.typeVersion) as INodeType;
892
-
893
- if (nodeType.webhookMethods === undefined) {
894
- return;
895
- }
896
-
897
- if (nodeType.webhookMethods[webhookData.webhookDescription.name] === undefined) {
898
- return;
899
- }
900
-
901
- if (nodeType.webhookMethods[webhookData.webhookDescription.name][method] === undefined) {
902
- return;
903
- }
904
-
905
- const thisArgs = nodeExecuteFunctions.getExecuteHookFunctions(
906
- this,
907
- node,
908
- webhookData.workflowExecuteAdditionalData,
909
- mode,
910
- activation,
911
- isTest,
912
- webhookData,
913
- );
914
- // eslint-disable-next-line consistent-return
915
- return nodeType.webhookMethods[webhookData.webhookDescription.name][method]!.call(thisArgs);
916
- }
917
-
918
- /**
919
- * Runs the given trigger node so that it can trigger the workflow
920
- * when the node has data.
921
- *
922
- * @param {INode} node
923
- * @param {IGetExecuteTriggerFunctions} getTriggerFunctions
924
- * @param {IWorkflowExecuteAdditionalData} additionalData
925
- * @param {WorkflowExecuteMode} mode
926
- * @returns {(Promise<ITriggerResponse | undefined>)}
927
- * @memberof Workflow
928
- */
929
- async runTrigger(
930
- node: INode,
931
- getTriggerFunctions: IGetExecuteTriggerFunctions,
932
- additionalData: IWorkflowExecuteAdditionalData,
933
- mode: WorkflowExecuteMode,
934
- activation: WorkflowActivateMode,
935
- ): Promise<ITriggerResponse | undefined> {
936
- const triggerFunctions = getTriggerFunctions(this, node, additionalData, mode, activation);
937
-
938
- const nodeType = this.nodeTypes.getByNameAndVersion(node.type, node.typeVersion);
939
-
940
- if (nodeType === undefined) {
941
- throw new Error(`The node type "${node.type}" of node "${node.name}" is not known.`);
942
- }
943
-
944
- if (!nodeType.trigger) {
945
- throw new Error(
946
- `The node type "${node.type}" of node "${node.name}" does not have a trigger function defined.`,
947
- );
948
- }
949
-
950
- if (mode === 'manual') {
951
- // In manual mode we do not just start the trigger function we also
952
- // want to be able to get informed as soon as the first data got emitted
953
- const triggerResponse = await nodeType.trigger.call(triggerFunctions);
954
-
955
- // Add the manual trigger response which resolves when the first time data got emitted
956
- triggerResponse!.manualTriggerResponse = new Promise((resolve) => {
957
- triggerFunctions.emit = (
958
- (resolveEmit) =>
959
- (
960
- data: INodeExecutionData[][],
961
- responsePromise?: IDeferredPromise<IExecuteResponsePromiseData>,
962
- ) => {
963
- additionalData.hooks!.hookFunctions.sendResponse = [
964
- async (response: IExecuteResponsePromiseData): Promise<void> => {
965
- if (responsePromise) {
966
- responsePromise.resolve(response);
967
- }
968
- },
969
- ];
970
-
971
- resolveEmit(data);
972
- }
973
- )(resolve);
974
- });
975
-
976
- return triggerResponse;
977
- }
978
- // In all other modes simply start the trigger
979
- return nodeType.trigger.call(triggerFunctions);
980
- }
981
-
982
- /**
983
- * Runs the given trigger node so that it can trigger the workflow
984
- * when the node has data.
985
- *
986
- * @param {INode} node
987
- * @param {IPollFunctions} pollFunctions
988
- * @returns
989
- * @memberof Workflow
990
- */
991
-
992
- async runPoll(
993
- node: INode,
994
- pollFunctions: IPollFunctions,
995
- ): Promise<INodeExecutionData[][] | null> {
996
- const nodeType = this.nodeTypes.getByNameAndVersion(node.type, node.typeVersion);
997
-
998
- if (nodeType === undefined) {
999
- throw new Error(`The node type "${node.type}" of node "${node.name}" is not known.`);
1000
- }
1001
-
1002
- if (!nodeType.poll) {
1003
- throw new Error(
1004
- `The node type "${node.type}" of node "${node.name}" does not have a poll function defined.`,
1005
- );
1006
- }
1007
-
1008
- return nodeType.poll.call(pollFunctions);
1009
- }
1010
-
1011
- /**
1012
- * Executes the webhook data to see what it should return and if the
1013
- * workflow should be started or not
1014
- *
1015
- * @param {INode} node
1016
- * @param {IWorkflowExecuteAdditionalData} additionalData
1017
- * @param {INodeExecuteFunctions} nodeExecuteFunctions
1018
- * @param {WorkflowExecuteMode} mode
1019
- * @param {IRunExecutionData | undefined} runExecutionData
1020
- * @returns {Promise<IWebhookResponseData>}
1021
- * @memberof Workflow
1022
- */
1023
- async runWebhook(
1024
- webhookData: IWebhookData,
1025
- node: INode,
1026
- additionalData: IWorkflowExecuteAdditionalData,
1027
- nodeExecuteFunctions: INodeExecuteFunctions,
1028
- mode: WorkflowExecuteMode,
1029
- runExecutionData: IRunExecutionData | undefined,
1030
- workflow?: Workflow,
1031
- ): Promise<IWebhookResponseData> {
1032
- const nodeType = this.nodeTypes.getByNameAndVersion(node.type, node.typeVersion);
1033
- if (nodeType === undefined) {
1034
- throw new Error(`The type of the webhook node "${node.name}" is not known.`);
1035
- } else if (nodeType.webhook === undefined) {
1036
- throw new Error(`The node "${node.name}" does not have any webhooks defined.`);
1037
- }
1038
-
1039
- const thisArgs = nodeExecuteFunctions.getExecuteWebhookFunctions(
1040
- this,
1041
- node,
1042
- additionalData,
1043
- mode,
1044
- webhookData,
1045
- runExecutionData,
1046
- );
1047
-
1048
- return nodeType.webhook.call(thisArgs, workflow);
1049
- }
1050
-
1051
- /**
1052
- * Executes the given node.
1053
- *
1054
- * @param {INode} node
1055
- * @param {ITaskDataConnections} inputData
1056
- * @param {IRunExecutionData} runExecutionData
1057
- * @param {number} runIndex
1058
- * @param {IWorkflowExecuteAdditionalData} additionalData
1059
- * @param {INodeExecuteFunctions} nodeExecuteFunctions
1060
- * @param {WorkflowExecuteMode} mode
1061
- * @returns {(Promise<INodeExecutionData[][] | null>)}
1062
- * @memberof Workflow
1063
- */
1064
- async runNode(
1065
- node: INode,
1066
- inputData: ITaskDataConnections,
1067
- runExecutionData: IRunExecutionData,
1068
- runIndex: number,
1069
- additionalData: IWorkflowExecuteAdditionalData,
1070
- nodeExecuteFunctions: INodeExecuteFunctions,
1071
- mode: WorkflowExecuteMode,
1072
- ): Promise<INodeExecutionData[][] | null | undefined> {
1073
- if (node.disabled === true) {
1074
- // If node is disabled simply pass the data through
1075
- // return NodeRunHelpers.
1076
- if (inputData.hasOwnProperty('main') && inputData['main'].length > 0) {
1077
- // If the node is disabled simply return the data from the first main input
1078
- if (inputData['main'][0] === null) {
1079
- return undefined;
1080
- }
1081
- return [inputData['main'][0]];
1082
- }
1083
- return undefined;
1084
- }
1085
-
1086
- const nodeType = this.nodeTypes.getByNameAndVersion(node.type, node.typeVersion);
1087
- if (nodeType === undefined) {
1088
- throw new Error(`Node type "${node.type}" is not known so can not run it!`);
1089
- }
1090
-
1091
- let connectionInputData: INodeExecutionData[] = [];
1092
- if (
1093
- nodeType.execute ||
1094
- nodeType.executeSingle ||
1095
- (!nodeType.poll && !nodeType.trigger && !nodeType.webhook)
1096
- ) {
1097
- // Only stop if first input is empty for execute & executeSingle runs. For all others run anyways
1098
- // because then it is a trigger node. As they only pass data through and so the input-data
1099
- // becomes output-data it has to be possible.
1100
-
1101
- if (inputData.hasOwnProperty('main') && inputData['main'].length > 0) {
1102
- // We always use the data of main input and the first input for executeSingle
1103
- connectionInputData = inputData['main'][0] as INodeExecutionData[];
1104
- }
1105
-
1106
- if (connectionInputData.length === 0) {
1107
- // No data for node so return
1108
- return undefined;
1109
- }
1110
- }
1111
-
1112
- if (
1113
- runExecutionData.resultData.lastNodeExecuted === node.name &&
1114
- runExecutionData.resultData.error !== undefined
1115
- ) {
1116
- // The node did already fail. So throw an error here that it displays and logs it correctly.
1117
- // Does get used by webhook and trigger nodes in case they throw an error that it is possible
1118
- // to log the error and display in Editor-UI.
1119
-
1120
- const error = new Error(runExecutionData.resultData.error.message);
1121
- error.stack = runExecutionData.resultData.error.stack;
1122
- throw error;
1123
- }
1124
-
1125
- if (node.executeOnce === true) {
1126
- // If node should be executed only once so use only the first input item
1127
- connectionInputData = connectionInputData.slice(0, 1);
1128
- const newInputData: ITaskDataConnections = {};
1129
- for (const inputName of Object.keys(inputData)) {
1130
- newInputData[inputName] = inputData[inputName].map((input: any) => {
1131
- // eslint-disable-next-line @typescript-eslint/prefer-optional-chain
1132
- return input && input.slice(0, 1);
1133
- });
1134
- }
1135
- inputData = newInputData;
1136
- }
1137
-
1138
- if (nodeType.executeSingle) {
1139
- const returnPromises: Array<Promise<INodeExecutionData>> = [];
1140
-
1141
- for (let itemIndex = 0; itemIndex < connectionInputData.length; itemIndex++) {
1142
- const thisArgs = nodeExecuteFunctions.getExecuteSingleFunctions(
1143
- this,
1144
- runExecutionData,
1145
- runIndex,
1146
- connectionInputData,
1147
- inputData,
1148
- node,
1149
- itemIndex,
1150
- additionalData,
1151
- mode,
1152
- );
1153
-
1154
- returnPromises.push(nodeType.executeSingle.call(thisArgs));
1155
- }
1156
-
1157
- if (returnPromises.length === 0) {
1158
- return null;
1159
- }
1160
-
1161
- let promiseResults;
1162
- try {
1163
- promiseResults = await Promise.all(returnPromises);
1164
- } catch (error) {
1165
- return Promise.reject(error);
1166
- }
1167
-
1168
- if (promiseResults) {
1169
- return [promiseResults];
1170
- }
1171
- } else if (nodeType.execute) {
1172
- const closeFunctions: CloseFunction[] = [];
1173
- const thisArgs = nodeExecuteFunctions.getExecuteFunctions(
1174
- this,
1175
- runExecutionData,
1176
- runIndex,
1177
- connectionInputData,
1178
- inputData,
1179
- node,
1180
- additionalData,
1181
- mode,
1182
- nodeType,
1183
- closeFunctions,
1184
- );
1185
-
1186
- const data = await nodeType.execute.call(thisArgs);
1187
- const closeFunctionsResults = await Promise.allSettled(
1188
- closeFunctions.map(async (fn) => await fn()),
1189
- );
1190
-
1191
- const closingErrors = closeFunctionsResults
1192
- .filter((result): result is PromiseRejectedResult => result.status === 'rejected')
1193
- .map((result) => result.reason);
1194
-
1195
- if (closingErrors.length > 0) {
1196
- if (closingErrors[0] instanceof Error) throw closingErrors[0];
1197
- throw new Error(`Error on execution node's close function(s). Node Name: ${node.name}, Node Type: ${node.type}, Errors: ${closingErrors.join(', ')}`);
1198
- }
1199
-
1200
- return data;
1201
- } else if (nodeType.poll) {
1202
- if (mode === 'manual') {
1203
- // In manual mode run the poll function
1204
- const thisArgs = nodeExecuteFunctions.getExecutePollFunctions(
1205
- this,
1206
- node,
1207
- additionalData,
1208
- mode,
1209
- 'manual',
1210
- );
1211
- return nodeType.poll.call(thisArgs);
1212
- }
1213
- // In any other mode pass data through as it already contains the result of the poll
1214
- return inputData['main'] as INodeExecutionData[][];
1215
- } else if (nodeType.trigger) {
1216
- if (mode === 'manual') {
1217
- // In manual mode start the trigger
1218
- const triggerResponse = await this.runTrigger(
1219
- node,
1220
- nodeExecuteFunctions.getExecuteTriggerFunctions,
1221
- additionalData,
1222
- mode,
1223
- 'manual',
1224
- );
1225
-
1226
- if (triggerResponse === undefined) {
1227
- return null;
1228
- }
1229
-
1230
- if (triggerResponse.manualTriggerFunction !== undefined) {
1231
- // If a manual trigger function is defined call it and wait till it did run
1232
- await triggerResponse.manualTriggerFunction();
1233
- }
1234
-
1235
- const response = await triggerResponse.manualTriggerResponse!;
1236
-
1237
- // And then close it again after it did execute
1238
- if (triggerResponse.closeFunction) {
1239
- await triggerResponse.closeFunction();
1240
- }
1241
-
1242
- if (response.length === 0) {
1243
- return null;
1244
- }
1245
-
1246
- return response;
1247
- }
1248
- // For trigger nodes in any mode except "manual" do we simply pass the data through
1249
- return inputData['main'] as INodeExecutionData[][];
1250
- } else if (nodeType.webhook) {
1251
- // For webhook nodes always simply pass the data through
1252
- return inputData['main'] as INodeExecutionData[][];
1253
- } else {
1254
- // For nodes which have routing information on properties
1255
-
1256
- const routingNode = new RoutingNode(
1257
- this,
1258
- node,
1259
- connectionInputData,
1260
- runExecutionData ?? null,
1261
- additionalData,
1262
- mode,
1263
- );
1264
-
1265
- return routingNode.runNode(inputData, runIndex, nodeType, nodeExecuteFunctions);
1266
- }
1267
-
1268
- return null;
1269
- }
1270
- }