@fsai-flow/workflow 0.0.1

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 (113) hide show
  1. package/.eslintrc.json +33 -0
  2. package/README.md +11 -0
  3. package/dist/README.md +11 -0
  4. package/dist/package.json +42 -0
  5. package/dist/src/index.d.ts +21 -0
  6. package/dist/src/index.js +33 -0
  7. package/dist/src/index.js.map +1 -0
  8. package/dist/src/lib/Constants.d.ts +68 -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 +6 -0
  12. package/dist/src/lib/DeferredPromise.js +11 -0
  13. package/dist/src/lib/DeferredPromise.js.map +1 -0
  14. package/dist/src/lib/Expression.d.ts +65 -0
  15. package/dist/src/lib/Expression.js +215 -0
  16. package/dist/src/lib/Expression.js.map +1 -0
  17. package/dist/src/lib/Interfaces.d.ts +1569 -0
  18. package/dist/src/lib/Interfaces.js +44 -0
  19. package/dist/src/lib/Interfaces.js.map +1 -0
  20. package/dist/src/lib/LoggerProxy.d.ts +9 -0
  21. package/dist/src/lib/LoggerProxy.js +40 -0
  22. package/dist/src/lib/LoggerProxy.js.map +1 -0
  23. package/dist/src/lib/MetadataUtils.d.ts +4 -0
  24. package/dist/src/lib/MetadataUtils.js +27 -0
  25. package/dist/src/lib/MetadataUtils.js.map +1 -0
  26. package/dist/src/lib/NodeErrors.d.ts +82 -0
  27. package/dist/src/lib/NodeErrors.js +289 -0
  28. package/dist/src/lib/NodeErrors.js.map +1 -0
  29. package/dist/src/lib/NodeHelpers.d.ts +198 -0
  30. package/dist/src/lib/NodeHelpers.js +1348 -0
  31. package/dist/src/lib/NodeHelpers.js.map +1 -0
  32. package/dist/src/lib/ObservableObject.d.ts +5 -0
  33. package/dist/src/lib/ObservableObject.js +61 -0
  34. package/dist/src/lib/ObservableObject.js.map +1 -0
  35. package/dist/src/lib/RoutingNode.d.ts +18 -0
  36. package/dist/src/lib/RoutingNode.js +508 -0
  37. package/dist/src/lib/RoutingNode.js.map +1 -0
  38. package/dist/src/lib/TelemetryHelpers.d.ts +3 -0
  39. package/dist/src/lib/TelemetryHelpers.js +69 -0
  40. package/dist/src/lib/TelemetryHelpers.js.map +1 -0
  41. package/dist/src/lib/TypeValidation.d.ts +21 -0
  42. package/dist/src/lib/TypeValidation.js +385 -0
  43. package/dist/src/lib/TypeValidation.js.map +1 -0
  44. package/dist/src/lib/VersionedNodeType.d.ts +9 -0
  45. package/dist/src/lib/VersionedNodeType.js +26 -0
  46. package/dist/src/lib/VersionedNodeType.js.map +1 -0
  47. package/dist/src/lib/Workflow.d.ts +248 -0
  48. package/dist/src/lib/Workflow.js +901 -0
  49. package/dist/src/lib/Workflow.js.map +1 -0
  50. package/dist/src/lib/WorkflowDataProxy.d.ts +87 -0
  51. package/dist/src/lib/WorkflowDataProxy.js +556 -0
  52. package/dist/src/lib/WorkflowDataProxy.js.map +1 -0
  53. package/dist/src/lib/WorkflowErrors.d.ts +9 -0
  54. package/dist/src/lib/WorkflowErrors.js +18 -0
  55. package/dist/src/lib/WorkflowErrors.js.map +1 -0
  56. package/dist/src/lib/WorkflowHooks.d.ts +11 -0
  57. package/dist/src/lib/WorkflowHooks.js +34 -0
  58. package/dist/src/lib/WorkflowHooks.js.map +1 -0
  59. package/dist/src/lib/errors/base/base.error.d.ts +30 -0
  60. package/dist/src/lib/errors/base/base.error.js +45 -0
  61. package/dist/src/lib/errors/base/base.error.js.map +1 -0
  62. package/dist/src/lib/errors/base/operational.error.d.ts +15 -0
  63. package/dist/src/lib/errors/base/operational.error.js +19 -0
  64. package/dist/src/lib/errors/base/operational.error.js.map +1 -0
  65. package/dist/src/lib/errors/error.types.d.ts +11 -0
  66. package/dist/src/lib/errors/error.types.js +3 -0
  67. package/dist/src/lib/errors/error.types.js.map +1 -0
  68. package/dist/src/lib/errors/index.d.ts +1 -0
  69. package/dist/src/lib/errors/index.js +6 -0
  70. package/dist/src/lib/errors/index.js.map +1 -0
  71. package/dist/src/lib/result.d.ts +19 -0
  72. package/dist/src/lib/result.js +36 -0
  73. package/dist/src/lib/result.js.map +1 -0
  74. package/dist/src/lib/utils.d.ts +50 -0
  75. package/dist/src/lib/utils.js +110 -0
  76. package/dist/src/lib/utils.js.map +1 -0
  77. package/eslint.config.js +19 -0
  78. package/jest.config.ts +10 -0
  79. package/package.json +40 -0
  80. package/project.json +19 -0
  81. package/src/index.ts +33 -0
  82. package/src/lib/Constants.ts +124 -0
  83. package/src/lib/DeferredPromise.ts +14 -0
  84. package/src/lib/Expression.ts +375 -0
  85. package/src/lib/Interfaces.ts +2229 -0
  86. package/src/lib/LoggerProxy.ts +43 -0
  87. package/src/lib/MetadataUtils.ts +34 -0
  88. package/src/lib/NodeErrors.ts +332 -0
  89. package/src/lib/NodeHelpers.ts +1666 -0
  90. package/src/lib/ObservableObject.ts +77 -0
  91. package/src/lib/RoutingNode.ts +862 -0
  92. package/src/lib/TelemetryHelpers.ts +86 -0
  93. package/src/lib/TypeValidation.ts +431 -0
  94. package/src/lib/VersionedNodeType.ts +30 -0
  95. package/src/lib/Workflow.ts +1266 -0
  96. package/src/lib/WorkflowDataProxy.ts +708 -0
  97. package/src/lib/WorkflowErrors.ts +18 -0
  98. package/src/lib/WorkflowHooks.ts +51 -0
  99. package/src/lib/errors/base/base.error.ts +68 -0
  100. package/src/lib/errors/base/operational.error.ts +21 -0
  101. package/src/lib/errors/error.types.ts +14 -0
  102. package/src/lib/errors/index.ts +1 -0
  103. package/src/lib/result.ts +34 -0
  104. package/src/lib/utils.ts +132 -0
  105. package/tests/Helpers.ts +667 -0
  106. package/tests/NodeHelpers.test.ts +3053 -0
  107. package/tests/ObservableObject.test.ts +171 -0
  108. package/tests/RoutingNode.test.ts +1680 -0
  109. package/tests/Workflow.test.ts +1284 -0
  110. package/tests/WorkflowDataProxy.test.ts +199 -0
  111. package/tsconfig.json +27 -0
  112. package/tsconfig.lib.json +11 -0
  113. package/tsconfig.spec.json +14 -0
@@ -0,0 +1,708 @@
1
+ /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
2
+ /* eslint-disable @typescript-eslint/no-unused-vars */
3
+ /* eslint-disable no-restricted-syntax */
4
+ /* eslint-disable no-param-reassign */
5
+ /* eslint-disable @typescript-eslint/no-this-alias */
6
+ /* eslint-disable @typescript-eslint/no-unsafe-return */
7
+ /* eslint-disable no-prototype-builtins */
8
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
9
+
10
+ import { DateTime, Duration, Interval } from 'luxon';
11
+ import * as jmespath from 'jmespath';
12
+
13
+ // eslint-disable-next-line import/no-cycle
14
+ import {
15
+ IDataObject,
16
+ INodeExecutionData,
17
+ INodeParameters,
18
+ IRunExecutionData,
19
+ IWorkflowDataProxyAdditionalKeys,
20
+ IWorkflowDataProxyData,
21
+ NodeHelpers,
22
+ NodeParameterValue,
23
+ Workflow,
24
+ WorkflowExecuteMode,
25
+ } from '..';
26
+
27
+ export class WorkflowDataProxy {
28
+ private workflow: Workflow;
29
+
30
+ private runExecutionData: IRunExecutionData | null;
31
+
32
+ private defaultReturnRunIndex: number;
33
+
34
+ private runIndex: number;
35
+
36
+ private itemIndex: number;
37
+
38
+ private activeNodeName: string;
39
+
40
+ private connectionInputData: INodeExecutionData[];
41
+
42
+ private siblingParameters: INodeParameters;
43
+
44
+ private mode: WorkflowExecuteMode;
45
+
46
+ private selfData: IDataObject;
47
+
48
+ private additionalKeys: IWorkflowDataProxyAdditionalKeys;
49
+
50
+ constructor(
51
+ workflow: Workflow,
52
+ runExecutionData: IRunExecutionData | null,
53
+ runIndex: number,
54
+ itemIndex: number,
55
+ activeNodeName: string,
56
+ connectionInputData: INodeExecutionData[],
57
+ siblingParameters: INodeParameters,
58
+ mode: WorkflowExecuteMode,
59
+ additionalKeys: IWorkflowDataProxyAdditionalKeys,
60
+ defaultReturnRunIndex = -1,
61
+ selfData = {},
62
+ ) {
63
+ this.workflow = workflow;
64
+ this.runExecutionData = runExecutionData;
65
+ this.defaultReturnRunIndex = defaultReturnRunIndex;
66
+ this.runIndex = runIndex;
67
+ this.itemIndex = itemIndex;
68
+ this.activeNodeName = activeNodeName;
69
+ this.connectionInputData = connectionInputData;
70
+ this.siblingParameters = siblingParameters;
71
+ this.mode = mode;
72
+ this.selfData = selfData;
73
+ this.additionalKeys = additionalKeys;
74
+ }
75
+
76
+ /**
77
+ * Returns a proxy which allows to query context data of a given node
78
+ *
79
+ * @private
80
+ * @param {string} nodeName The name of the node to get the context from
81
+ * @returns
82
+ * @memberof WorkflowDataProxy
83
+ */
84
+ private nodeContextGetter(nodeName: string) {
85
+ const that = this;
86
+ const node = this.workflow.nodes[nodeName];
87
+
88
+ return new Proxy(
89
+ {},
90
+ {
91
+ ownKeys(target) {
92
+ if (Reflect.ownKeys(target).length === 0) {
93
+ // Target object did not get set yet
94
+ Object.assign(target, NodeHelpers.getContext(that.runExecutionData!, 'node', node));
95
+ }
96
+
97
+ return Reflect.ownKeys(target);
98
+ },
99
+ getOwnPropertyDescriptor(k) {
100
+ return {
101
+ enumerable: true,
102
+ configurable: true,
103
+ };
104
+ },
105
+ get(target, name, receiver) {
106
+ // eslint-disable-next-line no-param-reassign
107
+ name = name.toString();
108
+ const contextData = NodeHelpers.getContext(that.runExecutionData!, 'node', node);
109
+
110
+ if (!contextData.hasOwnProperty(name)) {
111
+ // Parameter does not exist on node
112
+ throw new Error(`Could not find parameter "${name}" on context of node "${nodeName}"`);
113
+ }
114
+
115
+ return contextData[name];
116
+ },
117
+ },
118
+ );
119
+ }
120
+
121
+ private selfGetter() {
122
+ const that = this;
123
+
124
+ return new Proxy(
125
+ {},
126
+ {
127
+ ownKeys(target) {
128
+ return Reflect.ownKeys(target);
129
+ },
130
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
131
+ get(target, name, receiver) {
132
+ name = name.toString();
133
+ return that.selfData[name];
134
+ },
135
+ },
136
+ );
137
+ }
138
+
139
+ /**
140
+ * Returns a proxy which allows to query parameter data of a given node
141
+ *
142
+ * @private
143
+ * @param {string} nodeName The name of the node to query data from
144
+ * @returns
145
+ * @memberof WorkflowDataGetter
146
+ */
147
+ private nodeParameterGetter(nodeName: string) {
148
+ const that = this;
149
+ const node = this.workflow.nodes[nodeName];
150
+
151
+ return new Proxy(node.parameters, {
152
+ ownKeys(target) {
153
+ return Reflect.ownKeys(target);
154
+ },
155
+ getOwnPropertyDescriptor(k) {
156
+ return {
157
+ enumerable: true,
158
+ configurable: true,
159
+ };
160
+ },
161
+ get(target, name, receiver) {
162
+ name = name.toString();
163
+
164
+ let returnValue:
165
+ | INodeParameters
166
+ | NodeParameterValue
167
+ | NodeParameterValue[]
168
+ | INodeParameters[];
169
+ if (name[0] === '&') {
170
+ const key = name.slice(1);
171
+ if (!that.siblingParameters.hasOwnProperty(key)) {
172
+ throw new Error(`Could not find sibling parameter "${key}" on node "${nodeName}"`);
173
+ }
174
+ returnValue = that.siblingParameters[key];
175
+ } else {
176
+ if (!node.parameters.hasOwnProperty(name)) {
177
+ // Parameter does not exist on node
178
+ return undefined;
179
+ }
180
+
181
+ returnValue = node.parameters[name];
182
+ }
183
+
184
+ if (typeof returnValue === 'string' && returnValue.charAt(0) === '=') {
185
+ // The found value is an expression so resolve it
186
+ return that.workflow.expression.getParameterValue(
187
+ returnValue,
188
+ that.runExecutionData,
189
+ that.runIndex,
190
+ that.itemIndex,
191
+ that.activeNodeName,
192
+ that.connectionInputData,
193
+ that.mode,
194
+ that.additionalKeys,
195
+ );
196
+ }
197
+
198
+ return returnValue;
199
+ },
200
+ });
201
+ }
202
+
203
+ /**
204
+ * Returns the node ExecutionData
205
+ *
206
+ * @private
207
+ * @param {string} nodeName The name of the node query data from
208
+ * @param {boolean} [shortSyntax=false] If short syntax got used
209
+ * @param {number} [outputIndex] The index of the output, if not given the first one gets used
210
+ * @param {number} [runIndex] The index of the run, if not given the current one does get used
211
+ * @returns {INodeExecutionData[]}
212
+ * @memberof WorkflowDataProxy
213
+ */
214
+ private getNodeExecutionData(
215
+ nodeName: string,
216
+ shortSyntax = false,
217
+ outputIndex?: number,
218
+ runIndex?: number,
219
+ ): INodeExecutionData[] {
220
+ const that = this;
221
+
222
+ let executionData: INodeExecutionData[];
223
+ if (!shortSyntax) {
224
+ // Long syntax got used to return data from node in path
225
+
226
+ if (that.runExecutionData === null) {
227
+ throw new Error(`Workflow did not run so do not have any execution-data.`);
228
+ }
229
+
230
+ if (!that.runExecutionData.resultData.runData.hasOwnProperty(nodeName)) {
231
+ if (that.workflow.getNode(nodeName)) {
232
+ throw new Error(
233
+ `The node "${nodeName}" hasn't been executed yet, so you can't reference its output data`,
234
+ );
235
+ } else {
236
+ throw new Error(`No node called "${nodeName}" in this workflow`);
237
+ }
238
+ }
239
+
240
+ runIndex = runIndex === undefined ? that.defaultReturnRunIndex : runIndex;
241
+ runIndex =
242
+ runIndex === -1 ? that.runExecutionData.resultData.runData[nodeName].length - 1 : runIndex;
243
+
244
+ if (that.runExecutionData.resultData.runData[nodeName].length <= runIndex) {
245
+ throw new Error(`Run ${runIndex} of node "${nodeName}" not found`);
246
+ }
247
+
248
+ const taskData = that.runExecutionData.resultData.runData[nodeName][runIndex].data!;
249
+
250
+ if (taskData['main'] === null || !taskData['main'].length || taskData['main'][0] === null) {
251
+ // throw new Error(`No data found for item-index: "${itemIndex}"`);
252
+ throw new Error(`No data found from "main" input.`);
253
+ }
254
+
255
+ // Check from which output to read the data.
256
+ // Depends on how the nodes are connected.
257
+ // (example "IF" node. If node is connected to "true" or to "false" output)
258
+ if (outputIndex === undefined) {
259
+ // eslint-disable-next-line @typescript-eslint/no-shadow
260
+ const outputIndex = that.workflow.getNodeConnectionOutputIndex(
261
+ that.activeNodeName,
262
+ nodeName,
263
+ 'main',
264
+ );
265
+
266
+ if (outputIndex === undefined) {
267
+ throw new Error(
268
+ `The node "${that.activeNodeName}" is not connected with node "${nodeName}" so no data can get returned from it.`,
269
+ );
270
+ }
271
+ }
272
+
273
+ if (outputIndex === undefined) {
274
+ outputIndex = 0;
275
+ }
276
+
277
+ if (taskData['main'].length <= outputIndex) {
278
+ throw new Error(`Node "${nodeName}" has no branch with index ${outputIndex}.`);
279
+ }
280
+
281
+ executionData = taskData['main'][outputIndex] as INodeExecutionData[];
282
+ } else {
283
+ // Short syntax got used to return data from active node
284
+
285
+ // TODO: Here have to generate connection Input data for the current node by itself
286
+ // Data needed:
287
+ // #- the run-index
288
+ // - node which did send data (has to be the one from last recent execution)
289
+ // - later also the name of the input and its index (currently not needed as it is always "main" and index "0")
290
+ executionData = that.connectionInputData;
291
+ }
292
+
293
+ return executionData;
294
+ }
295
+
296
+ /**
297
+ * Returns a proxy which allows to query data of a given node
298
+ *
299
+ * @private
300
+ * @param {string} nodeName The name of the node query data from
301
+ * @param {boolean} [shortSyntax=false] If short syntax got used
302
+ * @returns
303
+ * @memberof WorkflowDataGetter
304
+ */
305
+ private nodeDataGetter(nodeName: string, shortSyntax = false) {
306
+ const that = this;
307
+ const node = this.workflow.nodes[nodeName];
308
+
309
+ if (!node) {
310
+ return undefined;
311
+ }
312
+
313
+ return new Proxy(
314
+ {},
315
+ {
316
+ get(target, name, receiver) {
317
+ name = name.toString();
318
+
319
+ if (['binary', 'data', 'json'].includes(name)) {
320
+ const executionData = that.getNodeExecutionData(nodeName, shortSyntax, undefined);
321
+
322
+ if (executionData.length <= that.itemIndex) {
323
+ throw new Error(`No data found for item-index: "${that.itemIndex}"`);
324
+ }
325
+
326
+ if (['data', 'json'].includes(name)) {
327
+ // JSON-Data
328
+ return executionData[that.itemIndex].json;
329
+ }
330
+ if (name === 'binary') {
331
+ // Binary-Data
332
+ const returnData: IDataObject = {};
333
+
334
+ if (!executionData[that.itemIndex].binary) {
335
+ return returnData;
336
+ }
337
+
338
+ const binaryKeyData = executionData[that.itemIndex].binary!;
339
+ for (const keyName of Object.keys(binaryKeyData)) {
340
+ returnData[keyName] = {};
341
+
342
+ const binaryData = binaryKeyData[keyName];
343
+ for (const propertyName in binaryData) {
344
+ if (propertyName === 'data') {
345
+ // Skip the data property
346
+ // eslint-disable-next-line no-continue
347
+ continue;
348
+ }
349
+ (returnData[keyName] as IDataObject)[propertyName] = binaryData[propertyName];
350
+ }
351
+ }
352
+
353
+ return returnData;
354
+ }
355
+ } else if (name === 'context') {
356
+ return that.nodeContextGetter(nodeName);
357
+ } else if (name === 'parameter') {
358
+ // Get node parameter data
359
+ return that.nodeParameterGetter(nodeName);
360
+ } else if (name === 'runIndex') {
361
+ if (
362
+ that.runExecutionData === null ||
363
+ !that.runExecutionData.resultData.runData[nodeName]
364
+ ) {
365
+ return -1;
366
+ }
367
+ return that.runExecutionData.resultData.runData[nodeName].length - 1;
368
+ }
369
+
370
+ return Reflect.get(target, name, receiver);
371
+ },
372
+ },
373
+ );
374
+ }
375
+
376
+ /**
377
+ * Returns a proxy to query data from the environment
378
+ *
379
+ * @private
380
+ * @returns
381
+ * @memberof WorkflowDataGetter
382
+ */
383
+ private envGetter() {
384
+ return new Proxy(
385
+ {},
386
+ {
387
+ get(target, name, receiver) {
388
+ return process.env[name.toString()];
389
+ },
390
+ },
391
+ );
392
+ }
393
+
394
+ /**
395
+ * Returns a proxt to query data from the workflow
396
+ *
397
+ * @private
398
+ * @returns
399
+ * @memberof WorkflowDataProxy
400
+ */
401
+ private workflowGetter() {
402
+ const allowedValues = ['active', 'id', 'name'];
403
+ const that = this;
404
+
405
+ return new Proxy(
406
+ {},
407
+ {
408
+ ownKeys(target) {
409
+ return allowedValues;
410
+ },
411
+ getOwnPropertyDescriptor(k) {
412
+ return {
413
+ enumerable: true,
414
+ configurable: true,
415
+ };
416
+ },
417
+ get(target, name, receiver) {
418
+ if (!allowedValues.includes(name.toString())) {
419
+ throw new Error(`The key "${name.toString()}" is not supported!`);
420
+ }
421
+
422
+ // @ts-ignore
423
+ return that.workflow[name.toString()];
424
+ },
425
+ },
426
+ );
427
+ }
428
+
429
+ /**
430
+ * Returns a proxy to query data of all nodes
431
+ *
432
+ * @private
433
+ * @returns
434
+ * @memberof WorkflowDataGetter
435
+ */
436
+ private nodeGetter() {
437
+ const that = this;
438
+ return new Proxy(
439
+ {},
440
+ {
441
+ get(target, name, receiver) {
442
+ return that.nodeDataGetter(name.toString());
443
+ },
444
+ },
445
+ );
446
+ }
447
+
448
+ /**
449
+ * Returns the data proxy object which allows to query data from current run
450
+ *
451
+ * @returns
452
+ * @memberof WorkflowDataGetter
453
+ */
454
+ getDataProxy(): IWorkflowDataProxyData {
455
+ const that = this;
456
+
457
+ const getNodeOutput = (nodeName?: string, branchIndex?: number, runIndex?: number) => {
458
+ let executionData: INodeExecutionData[];
459
+
460
+ if (nodeName === undefined) {
461
+ executionData = that.connectionInputData;
462
+ } else {
463
+ branchIndex = branchIndex || 0;
464
+ runIndex = runIndex === undefined ? -1 : runIndex;
465
+ executionData = that.getNodeExecutionData(nodeName, false, branchIndex, runIndex);
466
+ }
467
+
468
+ return executionData;
469
+ };
470
+
471
+ // replacing proxies with the actual data.
472
+ const jmespathWrapper = (data: IDataObject | IDataObject[], query: string) => {
473
+ if (!Array.isArray(data) && typeof data === 'object') {
474
+ return jmespath.search({ ...data }, query);
475
+ }
476
+ return jmespath.search(data, query);
477
+ };
478
+
479
+ const base = {
480
+ $: (nodeName: string) => {
481
+ if (!nodeName) {
482
+ throw new Error(`When calling $(), please specify a node`);
483
+ }
484
+
485
+ return new Proxy(
486
+ {},
487
+ {
488
+ get(target, property, receiver) {
489
+ if (property === 'pairedItem') {
490
+ return () => {
491
+ const executionData = getNodeOutput(nodeName, 0, that.runIndex);
492
+ if (executionData[that.itemIndex]) {
493
+ return executionData[that.itemIndex];
494
+ }
495
+ return undefined;
496
+ };
497
+ }
498
+ if (property === 'item') {
499
+ return (itemIndex?: number, branchIndex?: number, runIndex?: number) => {
500
+ if (itemIndex === undefined) {
501
+ itemIndex = that.itemIndex;
502
+ branchIndex = 0;
503
+ runIndex = that.runIndex;
504
+ }
505
+ const executionData = getNodeOutput(nodeName, branchIndex, runIndex);
506
+ if (executionData[itemIndex]) {
507
+ return executionData[itemIndex];
508
+ }
509
+ let errorMessage = '';
510
+
511
+ if (branchIndex === undefined && runIndex === undefined) {
512
+ errorMessage = `
513
+ No item found at index ${itemIndex}
514
+ (for node "${nodeName}")`;
515
+ throw new Error(errorMessage);
516
+ }
517
+ if (branchIndex === undefined) {
518
+ errorMessage = `
519
+ No item found at index ${itemIndex}
520
+ in run ${runIndex || that.runIndex}
521
+ (for node "${nodeName}")`;
522
+ throw new Error(errorMessage);
523
+ }
524
+ if (runIndex === undefined) {
525
+ errorMessage = `
526
+ No item found at index ${itemIndex}
527
+ of branch ${branchIndex || 0}
528
+ (for node "${nodeName}")`;
529
+ throw new Error(errorMessage);
530
+ }
531
+
532
+ errorMessage = `
533
+ No item found at index ${itemIndex}
534
+ of branch ${branchIndex || 0}
535
+ in run ${runIndex || that.runIndex}
536
+ (for node "${nodeName}")`;
537
+ throw new Error(errorMessage);
538
+ };
539
+ }
540
+ if (property === 'first') {
541
+ return (branchIndex?: number, runIndex?: number) => {
542
+ const executionData = getNodeOutput(nodeName, branchIndex, runIndex);
543
+ if (executionData[0]) return executionData[0];
544
+ return undefined;
545
+ };
546
+ }
547
+ if (property === 'last') {
548
+ return (branchIndex?: number, runIndex?: number) => {
549
+ const executionData = getNodeOutput(nodeName, branchIndex, runIndex);
550
+ if (!executionData.length) return undefined;
551
+ if (executionData[executionData.length - 1]) {
552
+ return executionData[executionData.length - 1];
553
+ }
554
+ return undefined;
555
+ };
556
+ }
557
+ if (property === 'all') {
558
+ return (branchIndex?: number, runIndex?: number) =>
559
+ getNodeOutput(nodeName, branchIndex, runIndex);
560
+ }
561
+ if (property === 'context') {
562
+ return that.nodeContextGetter(nodeName);
563
+ }
564
+ if (property === 'params') {
565
+ return that.workflow.getNode(nodeName)?.parameters;
566
+ }
567
+ return Reflect.get(target, property, receiver);
568
+ },
569
+ },
570
+ );
571
+ },
572
+
573
+ $input: new Proxy(
574
+ {},
575
+ {
576
+ get(target, property, receiver) {
577
+ if (property === 'thisItem') {
578
+ return that.connectionInputData[that.itemIndex];
579
+ }
580
+ if (property === 'item') {
581
+ return (itemIndex?: number) => {
582
+ if (itemIndex === undefined) itemIndex = that.itemIndex;
583
+ const result = that.connectionInputData;
584
+ if (result[itemIndex]) {
585
+ return result[itemIndex];
586
+ }
587
+ return undefined;
588
+ };
589
+ }
590
+ if (property === 'first') {
591
+ return () => {
592
+ const result = that.connectionInputData;
593
+ if (result[0]) {
594
+ return result[0];
595
+ }
596
+ return undefined;
597
+ };
598
+ }
599
+ if (property === 'last') {
600
+ return () => {
601
+ const result = that.connectionInputData;
602
+ if (result.length && result[result.length - 1]) {
603
+ return result[result.length - 1];
604
+ }
605
+ return undefined;
606
+ };
607
+ }
608
+ if (property === 'all') {
609
+ return () => {
610
+ const result = that.connectionInputData;
611
+ if (result.length) {
612
+ return result;
613
+ }
614
+ return [];
615
+ };
616
+ }
617
+ return Reflect.get(target, property, receiver);
618
+ },
619
+ },
620
+ ),
621
+
622
+ $thisItem: that.connectionInputData[that.itemIndex],
623
+ $binary: {}, // Placeholder
624
+ $data: {}, // Placeholder
625
+ $env: this.envGetter(),
626
+ $evaluateExpression: (expression: string, itemIndex?: number) => {
627
+ itemIndex = itemIndex || that.itemIndex;
628
+ return that.workflow.expression.getParameterValue(
629
+ `=${expression}`,
630
+ that.runExecutionData,
631
+ that.runIndex,
632
+ itemIndex,
633
+ that.activeNodeName,
634
+ that.connectionInputData,
635
+ that.mode,
636
+ that.additionalKeys,
637
+ );
638
+ },
639
+ $item: (itemIndex: number, runIndex?: number) => {
640
+ const defaultReturnRunIndex = runIndex === undefined ? -1 : runIndex;
641
+ const dataProxy = new WorkflowDataProxy(
642
+ this.workflow,
643
+ this.runExecutionData,
644
+ this.runIndex,
645
+ itemIndex,
646
+ this.activeNodeName,
647
+ this.connectionInputData,
648
+ that.siblingParameters,
649
+ that.mode,
650
+ that.additionalKeys,
651
+ defaultReturnRunIndex,
652
+ );
653
+ return dataProxy.getDataProxy();
654
+ },
655
+ $items: (nodeName?: string, outputIndex?: number, runIndex?: number) => {
656
+ let executionData: INodeExecutionData[];
657
+
658
+ if (nodeName === undefined) {
659
+ executionData = that.connectionInputData;
660
+ } else {
661
+ outputIndex = outputIndex || 0;
662
+ runIndex = runIndex === undefined ? -1 : runIndex;
663
+ executionData = that.getNodeExecutionData(nodeName, false, outputIndex, runIndex);
664
+ }
665
+
666
+ return executionData;
667
+ },
668
+ $json: {}, // Placeholder
669
+ $node: this.nodeGetter(),
670
+ $self: this.selfGetter(),
671
+ $parameter: this.nodeParameterGetter(this.activeNodeName),
672
+ $position: this.itemIndex,
673
+ $runIndex: this.runIndex,
674
+ $mode: this.mode,
675
+ $workflow: this.workflowGetter(),
676
+ $thisRunIndex: this.runIndex,
677
+ $thisItemIndex: this.itemIndex,
678
+ $now: DateTime.now(),
679
+ $today: DateTime.now().set({ hour: 0, minute: 0, second: 0, millisecond: 0 }),
680
+ $jmespath: jmespathWrapper,
681
+ // eslint-disable-next-line @typescript-eslint/naming-convention
682
+ DateTime,
683
+ // eslint-disable-next-line @typescript-eslint/naming-convention
684
+ Interval,
685
+ // eslint-disable-next-line @typescript-eslint/naming-convention
686
+ Duration,
687
+ ...that.additionalKeys,
688
+ $nodeId: that.workflow.getNode(that.activeNodeName)?.id,
689
+ $nodeVersion: that.workflow.getNode(that.activeNodeName)?.typeVersion,
690
+ $webhookId: that.workflow.getNode(that.activeNodeName)?.webhookId,
691
+ };
692
+
693
+ return new Proxy(base, {
694
+ get(target, name, receiver) {
695
+ if (['$data', '$json'].includes(name as string)) {
696
+ // @ts-ignore
697
+ return that.nodeDataGetter(that.activeNodeName, true).json;
698
+ }
699
+ if (name === '$binary') {
700
+ // @ts-ignore
701
+ return that.nodeDataGetter(that.activeNodeName, true).binary;
702
+ }
703
+
704
+ return Reflect.get(target, name, receiver);
705
+ },
706
+ });
707
+ }
708
+ }
@@ -0,0 +1,18 @@
1
+ // eslint-disable-next-line import/no-cycle
2
+ import { INode } from '..';
3
+
4
+ /**
5
+ * Class for instantiating an operational error, e.g. a timeout error.
6
+ */
7
+ export class WorkflowOperationError extends Error {
8
+ node: INode | undefined;
9
+
10
+ timestamp: number;
11
+
12
+ constructor(message: string, node?: INode) {
13
+ super(message);
14
+ this.name = this.constructor.name;
15
+ this.node = node;
16
+ this.timestamp = Date.now();
17
+ }
18
+ }