@marktoflow/core 2.0.2 → 2.0.4

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 (183) hide show
  1. package/README.md +69 -6
  2. package/dist/built-in-operations.d.ts +2 -136
  3. package/dist/built-in-operations.d.ts.map +1 -1
  4. package/dist/built-in-operations.js +7 -743
  5. package/dist/built-in-operations.js.map +1 -1
  6. package/dist/engine/conditions.d.ts +29 -0
  7. package/dist/engine/conditions.d.ts.map +1 -0
  8. package/dist/engine/conditions.js +109 -0
  9. package/dist/engine/conditions.js.map +1 -0
  10. package/dist/engine/control-flow.d.ts +35 -0
  11. package/dist/engine/control-flow.d.ts.map +1 -0
  12. package/dist/engine/control-flow.js +653 -0
  13. package/dist/engine/control-flow.js.map +1 -0
  14. package/dist/engine/index.d.ts +12 -0
  15. package/dist/engine/index.d.ts.map +1 -0
  16. package/dist/engine/index.js +11 -0
  17. package/dist/engine/index.js.map +1 -0
  18. package/dist/engine/retry.d.ts +35 -0
  19. package/dist/engine/retry.d.ts.map +1 -0
  20. package/dist/engine/retry.js +86 -0
  21. package/dist/engine/retry.js.map +1 -0
  22. package/dist/engine/subworkflow.d.ts +31 -0
  23. package/dist/engine/subworkflow.d.ts.map +1 -0
  24. package/dist/engine/subworkflow.js +240 -0
  25. package/dist/engine/subworkflow.js.map +1 -0
  26. package/dist/engine/types.d.ts +55 -0
  27. package/dist/engine/types.d.ts.map +1 -0
  28. package/dist/engine/types.js +5 -0
  29. package/dist/{secrets → engine}/types.js.map +1 -1
  30. package/dist/engine/variable-resolution.d.ts +29 -0
  31. package/dist/engine/variable-resolution.d.ts.map +1 -0
  32. package/dist/engine/variable-resolution.js +130 -0
  33. package/dist/engine/variable-resolution.js.map +1 -0
  34. package/dist/engine.d.ts +17 -211
  35. package/dist/engine.d.ts.map +1 -1
  36. package/dist/engine.js +84 -1351
  37. package/dist/engine.js.map +1 -1
  38. package/dist/file-operations.js +1 -1
  39. package/dist/file-operations.js.map +1 -1
  40. package/dist/filters/array.d.ts +9 -0
  41. package/dist/filters/array.d.ts.map +1 -0
  42. package/dist/filters/array.js +41 -0
  43. package/dist/filters/array.js.map +1 -0
  44. package/dist/filters/date.d.ts +9 -0
  45. package/dist/filters/date.d.ts.map +1 -0
  46. package/dist/filters/date.js +51 -0
  47. package/dist/filters/date.js.map +1 -0
  48. package/dist/filters/index.d.ts +13 -0
  49. package/dist/filters/index.d.ts.map +1 -0
  50. package/dist/filters/index.js +13 -0
  51. package/dist/filters/index.js.map +1 -0
  52. package/dist/filters/json.d.ts +6 -0
  53. package/dist/filters/json.d.ts.map +1 -0
  54. package/dist/filters/json.js +15 -0
  55. package/dist/filters/json.js.map +1 -0
  56. package/dist/filters/logic.d.ts +8 -0
  57. package/dist/filters/logic.d.ts.map +1 -0
  58. package/dist/filters/logic.js +28 -0
  59. package/dist/filters/logic.js.map +1 -0
  60. package/dist/filters/math.d.ts +13 -0
  61. package/dist/filters/math.d.ts.map +1 -0
  62. package/dist/filters/math.js +39 -0
  63. package/dist/filters/math.js.map +1 -0
  64. package/dist/filters/object.d.ts +11 -0
  65. package/dist/filters/object.d.ts.map +1 -0
  66. package/dist/filters/object.js +64 -0
  67. package/dist/filters/object.js.map +1 -0
  68. package/dist/filters/regex.d.ts +7 -0
  69. package/dist/filters/regex.d.ts.map +1 -0
  70. package/dist/filters/regex.js +38 -0
  71. package/dist/filters/regex.js.map +1 -0
  72. package/dist/filters/string.d.ts +11 -0
  73. package/dist/filters/string.d.ts.map +1 -0
  74. package/dist/filters/string.js +35 -0
  75. package/dist/filters/string.js.map +1 -0
  76. package/dist/filters/type-checks.d.ts +10 -0
  77. package/dist/filters/type-checks.d.ts.map +1 -0
  78. package/dist/filters/type-checks.js +30 -0
  79. package/dist/filters/type-checks.js.map +1 -0
  80. package/dist/index.d.ts +5 -1
  81. package/dist/index.d.ts.map +1 -1
  82. package/dist/index.js +7 -1
  83. package/dist/index.js.map +1 -1
  84. package/dist/nunjucks-filters.d.ts +2 -261
  85. package/dist/nunjucks-filters.d.ts.map +1 -1
  86. package/dist/nunjucks-filters.js +24 -582
  87. package/dist/nunjucks-filters.js.map +1 -1
  88. package/dist/operations/compress.d.ts +6 -0
  89. package/dist/operations/compress.d.ts.map +1 -0
  90. package/dist/operations/compress.js +36 -0
  91. package/dist/operations/compress.js.map +1 -0
  92. package/dist/operations/crypto.d.ts +5 -0
  93. package/dist/operations/crypto.d.ts.map +1 -0
  94. package/dist/operations/crypto.js +61 -0
  95. package/dist/operations/crypto.js.map +1 -0
  96. package/dist/operations/data-ops.d.ts +10 -0
  97. package/dist/operations/data-ops.d.ts.map +1 -0
  98. package/dist/operations/data-ops.js +124 -0
  99. package/dist/operations/data-ops.js.map +1 -0
  100. package/dist/operations/datetime.d.ts +5 -0
  101. package/dist/operations/datetime.d.ts.map +1 -0
  102. package/dist/operations/datetime.js +86 -0
  103. package/dist/operations/datetime.js.map +1 -0
  104. package/dist/operations/extract.d.ts +23 -0
  105. package/dist/operations/extract.d.ts.map +1 -0
  106. package/dist/operations/extract.js +31 -0
  107. package/dist/operations/extract.js.map +1 -0
  108. package/dist/operations/format.d.ts +14 -0
  109. package/dist/operations/format.d.ts.map +1 -0
  110. package/dist/operations/format.js +84 -0
  111. package/dist/operations/format.js.map +1 -0
  112. package/dist/operations/index.d.ts +13 -0
  113. package/dist/operations/index.d.ts.map +1 -0
  114. package/dist/operations/index.js +13 -0
  115. package/dist/operations/index.js.map +1 -0
  116. package/dist/operations/parse.d.ts +5 -0
  117. package/dist/operations/parse.d.ts.map +1 -0
  118. package/dist/operations/parse.js +59 -0
  119. package/dist/operations/parse.js.map +1 -0
  120. package/dist/operations/set.d.ts +21 -0
  121. package/dist/operations/set.d.ts.map +1 -0
  122. package/dist/operations/set.js +25 -0
  123. package/dist/operations/set.js.map +1 -0
  124. package/dist/operations/transform.d.ts +15 -0
  125. package/dist/operations/transform.d.ts.map +1 -0
  126. package/dist/operations/transform.js +110 -0
  127. package/dist/operations/transform.js.map +1 -0
  128. package/dist/parallel.d.ts +114 -0
  129. package/dist/parallel.d.ts.map +1 -0
  130. package/dist/parallel.js +325 -0
  131. package/dist/parallel.js.map +1 -0
  132. package/dist/parser.d.ts.map +1 -1
  133. package/dist/parser.js +2 -0
  134. package/dist/parser.js.map +1 -1
  135. package/dist/routing.js +2 -2
  136. package/dist/routing.js.map +1 -1
  137. package/dist/sdk-registry.d.ts.map +1 -1
  138. package/dist/sdk-registry.js +9 -3
  139. package/dist/sdk-registry.js.map +1 -1
  140. package/dist/utils/duration.d.ts +23 -0
  141. package/dist/utils/duration.d.ts.map +1 -0
  142. package/dist/utils/duration.js +41 -0
  143. package/dist/utils/duration.js.map +1 -0
  144. package/dist/utils/errors.d.ts +20 -0
  145. package/dist/utils/errors.d.ts.map +1 -0
  146. package/dist/utils/errors.js +37 -0
  147. package/dist/utils/errors.js.map +1 -0
  148. package/dist/utils/index.d.ts +3 -0
  149. package/dist/utils/index.d.ts.map +1 -0
  150. package/dist/utils/index.js +3 -0
  151. package/dist/utils/index.js.map +1 -0
  152. package/dist/workflow-templates.d.ts +80 -0
  153. package/dist/workflow-templates.d.ts.map +1 -0
  154. package/dist/workflow-templates.js +248 -0
  155. package/dist/workflow-templates.js.map +1 -0
  156. package/package.json +30 -5
  157. package/dist/secrets/index.d.ts +0 -12
  158. package/dist/secrets/index.d.ts.map +0 -1
  159. package/dist/secrets/index.js +0 -11
  160. package/dist/secrets/index.js.map +0 -1
  161. package/dist/secrets/providers/aws.d.ts +0 -32
  162. package/dist/secrets/providers/aws.d.ts.map +0 -1
  163. package/dist/secrets/providers/aws.js +0 -118
  164. package/dist/secrets/providers/aws.js.map +0 -1
  165. package/dist/secrets/providers/azure.d.ts +0 -40
  166. package/dist/secrets/providers/azure.d.ts.map +0 -1
  167. package/dist/secrets/providers/azure.js +0 -170
  168. package/dist/secrets/providers/azure.js.map +0 -1
  169. package/dist/secrets/providers/env.d.ts +0 -26
  170. package/dist/secrets/providers/env.d.ts.map +0 -1
  171. package/dist/secrets/providers/env.js +0 -59
  172. package/dist/secrets/providers/env.js.map +0 -1
  173. package/dist/secrets/providers/vault.d.ts +0 -39
  174. package/dist/secrets/providers/vault.d.ts.map +0 -1
  175. package/dist/secrets/providers/vault.js +0 -180
  176. package/dist/secrets/providers/vault.js.map +0 -1
  177. package/dist/secrets/secret-manager.d.ts +0 -72
  178. package/dist/secrets/secret-manager.d.ts.map +0 -1
  179. package/dist/secrets/secret-manager.js +0 -226
  180. package/dist/secrets/secret-manager.js.map +0 -1
  181. package/dist/secrets/types.d.ts +0 -105
  182. package/dist/secrets/types.d.ts.map +0 -1
  183. package/dist/secrets/types.js +0 -8
@@ -0,0 +1,653 @@
1
+ /**
2
+ * Control flow step execution for marktoflow workflow engine.
3
+ *
4
+ * Handles all control flow step types: if, switch, for-each, while,
5
+ * map, filter, reduce, parallel, try, script, wait, and merge.
6
+ */
7
+ import { StepStatus, createStepResult, } from '../models.js';
8
+ import { resolveTemplates } from './variable-resolution.js';
9
+ import { evaluateCondition } from './conditions.js';
10
+ import { parseDuration } from '../utils/duration.js';
11
+ import { errorToString } from '../utils/errors.js';
12
+ import { executeScriptAsync } from '../script-executor.js';
13
+ // ============================================================================
14
+ // If Step
15
+ // ============================================================================
16
+ export async function executeIfStep(step, context, sdkRegistry, stepExecutor, dispatch) {
17
+ const startedAt = new Date();
18
+ try {
19
+ // Evaluate condition
20
+ const conditionResult = evaluateCondition(step.condition, context);
21
+ // Determine which branch to execute
22
+ const branchSteps = conditionResult
23
+ ? step.then || step.steps // 'steps' is alias for 'then'
24
+ : step.else;
25
+ if (!branchSteps || branchSteps.length === 0) {
26
+ return createStepResult(step.id, StepStatus.SKIPPED, null, startedAt);
27
+ }
28
+ // Execute the branch steps
29
+ const branchResults = [];
30
+ for (const branchStep of branchSteps) {
31
+ const result = await dispatch(branchStep, context, sdkRegistry, stepExecutor);
32
+ if (result.status === StepStatus.COMPLETED && branchStep.outputVariable) {
33
+ context.variables[branchStep.outputVariable] = result.output;
34
+ branchResults.push(result.output);
35
+ }
36
+ if (result.status === StepStatus.FAILED) {
37
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, result.error);
38
+ }
39
+ }
40
+ return createStepResult(step.id, StepStatus.COMPLETED, branchResults, startedAt);
41
+ }
42
+ catch (error) {
43
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, error instanceof Error ? error.message : String(error));
44
+ }
45
+ }
46
+ // ============================================================================
47
+ // Switch Step
48
+ // ============================================================================
49
+ export async function executeSwitchStep(step, context, sdkRegistry, stepExecutor, dispatch) {
50
+ const startedAt = new Date();
51
+ try {
52
+ // Resolve the switch expression
53
+ const expressionValue = String(resolveTemplates(step.expression, context));
54
+ // Find matching case
55
+ const caseSteps = step.cases[expressionValue] || step.default;
56
+ if (!caseSteps || caseSteps.length === 0) {
57
+ return createStepResult(step.id, StepStatus.SKIPPED, null, startedAt);
58
+ }
59
+ // Execute case steps
60
+ const caseResults = [];
61
+ for (const caseStep of caseSteps) {
62
+ const result = await dispatch(caseStep, context, sdkRegistry, stepExecutor);
63
+ if (result.status === StepStatus.COMPLETED && caseStep.outputVariable) {
64
+ context.variables[caseStep.outputVariable] = result.output;
65
+ caseResults.push(result.output);
66
+ }
67
+ if (result.status === StepStatus.FAILED) {
68
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, result.error);
69
+ }
70
+ }
71
+ return createStepResult(step.id, StepStatus.COMPLETED, caseResults, startedAt);
72
+ }
73
+ catch (error) {
74
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, error instanceof Error ? error.message : String(error));
75
+ }
76
+ }
77
+ // ============================================================================
78
+ // For-Each Step
79
+ // ============================================================================
80
+ export async function executeForEachStep(step, context, sdkRegistry, stepExecutor, dispatch) {
81
+ const startedAt = new Date();
82
+ const cleanupLoopVars = () => {
83
+ delete context.variables[step.itemVariable];
84
+ delete context.variables['loop'];
85
+ delete context.variables['batch'];
86
+ if (step.indexVariable)
87
+ delete context.variables[step.indexVariable];
88
+ };
89
+ try {
90
+ // Resolve items array
91
+ const items = resolveTemplates(step.items, context);
92
+ if (!Array.isArray(items)) {
93
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, 'Items must be an array');
94
+ }
95
+ if (items.length === 0) {
96
+ return createStepResult(step.id, StepStatus.SKIPPED, [], startedAt);
97
+ }
98
+ const batchSize = step.batchSize;
99
+ const pauseBetweenBatches = step.pauseBetweenBatches;
100
+ // If batch mode, process items in batches
101
+ if (batchSize && batchSize > 0) {
102
+ return await executeForEachBatched(step, items, batchSize, pauseBetweenBatches ?? 0, context, sdkRegistry, stepExecutor, dispatch, startedAt);
103
+ }
104
+ // Standard item-by-item execution
105
+ const results = [];
106
+ for (let i = 0; i < items.length; i++) {
107
+ context.variables[step.itemVariable] = items[i];
108
+ context.variables['loop'] = {
109
+ index: i,
110
+ first: i === 0,
111
+ last: i === items.length - 1,
112
+ length: items.length,
113
+ };
114
+ if (step.indexVariable) {
115
+ context.variables[step.indexVariable] = i;
116
+ }
117
+ // Execute iteration steps
118
+ for (const iterStep of step.steps) {
119
+ const result = await dispatch(iterStep, context, sdkRegistry, stepExecutor);
120
+ if (result.status === StepStatus.COMPLETED && iterStep.outputVariable) {
121
+ context.variables[iterStep.outputVariable] = result.output;
122
+ }
123
+ if (result.status === StepStatus.FAILED) {
124
+ const errorAction = step.errorHandling?.action ?? 'stop';
125
+ if (errorAction === 'stop') {
126
+ cleanupLoopVars();
127
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, result.error);
128
+ }
129
+ break;
130
+ }
131
+ }
132
+ results.push(context.variables[step.itemVariable]);
133
+ }
134
+ cleanupLoopVars();
135
+ return createStepResult(step.id, StepStatus.COMPLETED, results, startedAt);
136
+ }
137
+ catch (error) {
138
+ cleanupLoopVars();
139
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, error instanceof Error ? error.message : String(error));
140
+ }
141
+ }
142
+ /**
143
+ * Execute for-each in batch mode.
144
+ * Items are split into batches; {{ batch }} contains the current batch array.
145
+ */
146
+ async function executeForEachBatched(step, items, batchSize, pauseBetweenBatches, context, sdkRegistry, stepExecutor, dispatch, startedAt) {
147
+ const results = [];
148
+ const totalBatches = Math.ceil(items.length / batchSize);
149
+ for (let batchIndex = 0; batchIndex < totalBatches; batchIndex++) {
150
+ const batchStart = batchIndex * batchSize;
151
+ const batchItems = items.slice(batchStart, batchStart + batchSize);
152
+ // Pause between batches (not before first batch)
153
+ if (batchIndex > 0 && pauseBetweenBatches > 0) {
154
+ await new Promise((resolve) => setTimeout(resolve, pauseBetweenBatches));
155
+ }
156
+ // Expose batch-level variables
157
+ context.variables['batch'] = batchItems;
158
+ context.variables['loop'] = {
159
+ index: batchIndex,
160
+ first: batchIndex === 0,
161
+ last: batchIndex === totalBatches - 1,
162
+ length: totalBatches,
163
+ batchSize,
164
+ batchStart,
165
+ totalItems: items.length,
166
+ };
167
+ // Process each item in the batch
168
+ for (let i = 0; i < batchItems.length; i++) {
169
+ const globalIndex = batchStart + i;
170
+ context.variables[step.itemVariable] = batchItems[i];
171
+ if (step.indexVariable) {
172
+ context.variables[step.indexVariable] = globalIndex;
173
+ }
174
+ for (const iterStep of step.steps) {
175
+ const result = await dispatch(iterStep, context, sdkRegistry, stepExecutor);
176
+ if (result.status === StepStatus.COMPLETED && iterStep.outputVariable) {
177
+ context.variables[iterStep.outputVariable] = result.output;
178
+ }
179
+ if (result.status === StepStatus.FAILED) {
180
+ const errorAction = step.errorHandling?.action ?? 'stop';
181
+ if (errorAction === 'stop') {
182
+ delete context.variables[step.itemVariable];
183
+ delete context.variables['loop'];
184
+ delete context.variables['batch'];
185
+ if (step.indexVariable)
186
+ delete context.variables[step.indexVariable];
187
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, result.error);
188
+ }
189
+ break;
190
+ }
191
+ }
192
+ results.push(context.variables[step.itemVariable]);
193
+ }
194
+ }
195
+ delete context.variables[step.itemVariable];
196
+ delete context.variables['loop'];
197
+ delete context.variables['batch'];
198
+ if (step.indexVariable)
199
+ delete context.variables[step.indexVariable];
200
+ return createStepResult(step.id, StepStatus.COMPLETED, results, startedAt);
201
+ }
202
+ // ============================================================================
203
+ // While Step
204
+ // ============================================================================
205
+ export async function executeWhileStep(step, context, sdkRegistry, stepExecutor, dispatch) {
206
+ const startedAt = new Date();
207
+ let iterations = 0;
208
+ try {
209
+ while (evaluateCondition(step.condition, context)) {
210
+ if (iterations >= step.maxIterations) {
211
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, `Max iterations (${step.maxIterations}) exceeded`);
212
+ }
213
+ // Execute iteration steps
214
+ for (const iterStep of step.steps) {
215
+ const result = await dispatch(iterStep, context, sdkRegistry, stepExecutor);
216
+ if (result.status === StepStatus.COMPLETED && iterStep.outputVariable) {
217
+ context.variables[iterStep.outputVariable] = result.output;
218
+ }
219
+ if (result.status === StepStatus.FAILED) {
220
+ const errorAction = step.errorHandling?.action ?? 'stop';
221
+ if (errorAction === 'stop') {
222
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, result.error);
223
+ }
224
+ // 'continue' - skip to next iteration
225
+ break;
226
+ }
227
+ }
228
+ iterations++;
229
+ }
230
+ return createStepResult(step.id, StepStatus.COMPLETED, { iterations }, startedAt);
231
+ }
232
+ catch (error) {
233
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, error instanceof Error ? error.message : String(error));
234
+ }
235
+ }
236
+ // ============================================================================
237
+ // Map Step
238
+ // ============================================================================
239
+ export async function executeMapStep(step, context) {
240
+ const startedAt = new Date();
241
+ try {
242
+ // Resolve items array
243
+ const items = resolveTemplates(step.items, context);
244
+ if (!Array.isArray(items)) {
245
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, 'Items must be an array');
246
+ }
247
+ // Map each item using the expression
248
+ const mapped = items.map((item) => {
249
+ context.variables[step.itemVariable] = item;
250
+ const result = resolveTemplates(step.expression, context);
251
+ delete context.variables[step.itemVariable];
252
+ return result;
253
+ });
254
+ return createStepResult(step.id, StepStatus.COMPLETED, mapped, startedAt);
255
+ }
256
+ catch (error) {
257
+ delete context.variables[step.itemVariable];
258
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, error instanceof Error ? error.message : String(error));
259
+ }
260
+ }
261
+ // ============================================================================
262
+ // Filter Step
263
+ // ============================================================================
264
+ export async function executeFilterStep(step, context) {
265
+ const startedAt = new Date();
266
+ try {
267
+ // Resolve items array
268
+ const items = resolveTemplates(step.items, context);
269
+ if (!Array.isArray(items)) {
270
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, 'Items must be an array');
271
+ }
272
+ // Filter items using the condition
273
+ const filtered = items.filter((item) => {
274
+ context.variables[step.itemVariable] = item;
275
+ const result = evaluateCondition(step.condition, context);
276
+ delete context.variables[step.itemVariable];
277
+ return result;
278
+ });
279
+ return createStepResult(step.id, StepStatus.COMPLETED, filtered, startedAt);
280
+ }
281
+ catch (error) {
282
+ delete context.variables[step.itemVariable];
283
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, error instanceof Error ? error.message : String(error));
284
+ }
285
+ }
286
+ // ============================================================================
287
+ // Reduce Step
288
+ // ============================================================================
289
+ export async function executeReduceStep(step, context) {
290
+ const startedAt = new Date();
291
+ try {
292
+ // Resolve items array
293
+ const items = resolveTemplates(step.items, context);
294
+ if (!Array.isArray(items)) {
295
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, 'Items must be an array');
296
+ }
297
+ // Reduce items using the expression
298
+ let accumulator = step.initialValue ?? null;
299
+ for (const item of items) {
300
+ context.variables[step.itemVariable] = item;
301
+ context.variables[step.accumulatorVariable] = accumulator;
302
+ accumulator = resolveTemplates(step.expression, context);
303
+ delete context.variables[step.itemVariable];
304
+ delete context.variables[step.accumulatorVariable];
305
+ }
306
+ return createStepResult(step.id, StepStatus.COMPLETED, accumulator, startedAt);
307
+ }
308
+ catch (error) {
309
+ delete context.variables[step.itemVariable];
310
+ delete context.variables[step.accumulatorVariable];
311
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, error instanceof Error ? error.message : String(error));
312
+ }
313
+ }
314
+ // ============================================================================
315
+ // Parallel Step
316
+ // ============================================================================
317
+ export async function executeParallelStep(step, context, sdkRegistry, stepExecutor, dispatch, cloneContext, mergeContexts, executeConcurrentlyWithLimit) {
318
+ const startedAt = new Date();
319
+ try {
320
+ // Execute branches in parallel
321
+ const branchPromises = step.branches.map(async (branch) => {
322
+ // Clone context for isolation
323
+ const branchContext = cloneContext(context);
324
+ // Execute branch steps
325
+ const branchResults = [];
326
+ for (const branchStep of branch.steps) {
327
+ const result = await dispatch(branchStep, branchContext, sdkRegistry, stepExecutor);
328
+ if (result.status === StepStatus.COMPLETED && branchStep.outputVariable) {
329
+ branchContext.variables[branchStep.outputVariable] = result.output;
330
+ branchResults.push(result.output);
331
+ }
332
+ if (result.status === StepStatus.FAILED) {
333
+ throw new Error(`Branch ${branch.id} failed: ${errorToString(result.error)}`);
334
+ }
335
+ }
336
+ return { branchId: branch.id, context: branchContext, results: branchResults };
337
+ });
338
+ // Wait for all branches (or limited concurrency)
339
+ const branchResults = step.maxConcurrent
340
+ ? await executeConcurrentlyWithLimit(branchPromises, step.maxConcurrent)
341
+ : await Promise.all(branchPromises);
342
+ // Merge branch contexts back into main context
343
+ for (const { branchId, context: branchContext } of branchResults) {
344
+ mergeContexts(context, branchContext, branchId);
345
+ }
346
+ const outputs = branchResults.map((br) => br.results);
347
+ return createStepResult(step.id, StepStatus.COMPLETED, outputs, startedAt);
348
+ }
349
+ catch (error) {
350
+ if (step.onError === 'continue') {
351
+ return createStepResult(step.id, StepStatus.COMPLETED, null, startedAt);
352
+ }
353
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, error instanceof Error ? error.message : String(error));
354
+ }
355
+ }
356
+ // ============================================================================
357
+ // Try Step
358
+ // ============================================================================
359
+ export async function executeTryStep(step, context, sdkRegistry, stepExecutor, dispatch) {
360
+ const startedAt = new Date();
361
+ let tryError;
362
+ try {
363
+ // Execute try block
364
+ for (const tryStep of step.try) {
365
+ const result = await dispatch(tryStep, context, sdkRegistry, stepExecutor);
366
+ if (result.status === StepStatus.COMPLETED && tryStep.outputVariable) {
367
+ context.variables[tryStep.outputVariable] = result.output;
368
+ }
369
+ if (result.status === StepStatus.FAILED) {
370
+ tryError = new Error(result.error ? errorToString(result.error) : 'Step failed');
371
+ break;
372
+ }
373
+ }
374
+ // If error occurred and catch block exists, execute catch
375
+ let catchError;
376
+ if (tryError && step.catch) {
377
+ // Inject error object into context
378
+ context.variables['error'] = {
379
+ message: tryError.message,
380
+ step: tryError,
381
+ };
382
+ for (const catchStep of step.catch) {
383
+ const result = await dispatch(catchStep, context, sdkRegistry, stepExecutor);
384
+ if (result.status === StepStatus.COMPLETED && catchStep.outputVariable) {
385
+ context.variables[catchStep.outputVariable] = result.output;
386
+ }
387
+ if (result.status === StepStatus.FAILED) {
388
+ catchError = new Error(result.error ? errorToString(result.error) : 'Catch block failed');
389
+ break;
390
+ }
391
+ }
392
+ delete context.variables['error'];
393
+ }
394
+ // Execute finally block (always runs)
395
+ if (step.finally) {
396
+ for (const finallyStep of step.finally) {
397
+ const result = await dispatch(finallyStep, context, sdkRegistry, stepExecutor);
398
+ if (result.status === StepStatus.COMPLETED && finallyStep.outputVariable) {
399
+ context.variables[finallyStep.outputVariable] = result.output;
400
+ }
401
+ }
402
+ }
403
+ // Return success if catch handled the error, or error if not
404
+ if (tryError && !step.catch) {
405
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, tryError.message);
406
+ }
407
+ if (catchError) {
408
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, catchError.message);
409
+ }
410
+ return createStepResult(step.id, StepStatus.COMPLETED, null, startedAt);
411
+ }
412
+ catch (error) {
413
+ // Execute finally even on unexpected error
414
+ if (step.finally) {
415
+ try {
416
+ for (const finallyStep of step.finally) {
417
+ const result = await dispatch(finallyStep, context, sdkRegistry, stepExecutor);
418
+ if (result.status === StepStatus.COMPLETED && finallyStep.outputVariable) {
419
+ context.variables[finallyStep.outputVariable] = result.output;
420
+ }
421
+ }
422
+ }
423
+ catch {
424
+ // Ignore finally errors
425
+ }
426
+ }
427
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, error instanceof Error ? error.message : String(error));
428
+ }
429
+ }
430
+ // ============================================================================
431
+ // Script Step
432
+ // ============================================================================
433
+ export async function executeScriptStep(step, context) {
434
+ const startedAt = new Date();
435
+ try {
436
+ // Resolve any templates in the code
437
+ const resolvedInputs = resolveTemplates(step.inputs, context);
438
+ // Execute the script with the workflow context
439
+ const result = await executeScriptAsync(resolvedInputs.code, {
440
+ variables: context.variables,
441
+ inputs: context.inputs,
442
+ steps: context.stepMetadata,
443
+ }, {
444
+ timeout: resolvedInputs.timeout ?? 5000,
445
+ });
446
+ if (!result.success) {
447
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, result.error ?? 'Script execution failed');
448
+ }
449
+ return createStepResult(step.id, StepStatus.COMPLETED, result.value, startedAt);
450
+ }
451
+ catch (error) {
452
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, error instanceof Error ? error.message : String(error));
453
+ }
454
+ }
455
+ // ============================================================================
456
+ // Wait Step
457
+ // ============================================================================
458
+ export async function executeWaitStep(step, context, stateStore) {
459
+ const startedAt = new Date();
460
+ try {
461
+ switch (step.mode) {
462
+ case 'duration': {
463
+ if (!step.duration) {
464
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, 'Wait step with mode=duration requires a duration');
465
+ }
466
+ const resolvedDuration = resolveTemplates(step.duration, context);
467
+ const ms = parseDuration(resolvedDuration);
468
+ // For short durations (under 5 minutes), do in-process wait
469
+ if (ms <= 300000) {
470
+ await new Promise((resolve) => setTimeout(resolve, ms));
471
+ return createStepResult(step.id, StepStatus.COMPLETED, { waited: ms }, startedAt);
472
+ }
473
+ // For longer durations, checkpoint and schedule resume
474
+ if (stateStore) {
475
+ stateStore.saveCheckpoint({
476
+ runId: context.runId,
477
+ stepIndex: context.currentStepIndex,
478
+ stepName: step.id,
479
+ status: StepStatus.COMPLETED,
480
+ startedAt: startedAt,
481
+ completedAt: new Date(),
482
+ inputs: { mode: 'duration', resumeAt: new Date(Date.now() + ms).toISOString() },
483
+ outputs: { waiting: true },
484
+ error: null,
485
+ retryCount: 0,
486
+ });
487
+ }
488
+ const resumeAt = new Date(Date.now() + ms).toISOString();
489
+ return createStepResult(step.id, StepStatus.COMPLETED, {
490
+ waiting: true,
491
+ mode: 'duration',
492
+ resumeAt,
493
+ durationMs: ms,
494
+ }, startedAt);
495
+ }
496
+ case 'webhook': {
497
+ const resumeToken = crypto.randomUUID();
498
+ const webhookPath = step.webhookPath
499
+ ? resolveTemplates(step.webhookPath, context)
500
+ : `/resume/${context.runId}/${step.id}/${resumeToken}`;
501
+ if (stateStore) {
502
+ stateStore.saveCheckpoint({
503
+ runId: context.runId,
504
+ stepIndex: context.currentStepIndex,
505
+ stepName: step.id,
506
+ status: StepStatus.COMPLETED,
507
+ startedAt: startedAt,
508
+ completedAt: new Date(),
509
+ inputs: { mode: 'webhook', resumeToken, webhookPath },
510
+ outputs: { waiting: true },
511
+ error: null,
512
+ retryCount: 0,
513
+ });
514
+ }
515
+ return createStepResult(step.id, StepStatus.COMPLETED, {
516
+ waiting: true,
517
+ mode: 'webhook',
518
+ resumeToken,
519
+ webhookPath,
520
+ }, startedAt);
521
+ }
522
+ case 'form': {
523
+ if (!step.fields || Object.keys(step.fields).length === 0) {
524
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, 'Wait step with mode=form requires fields');
525
+ }
526
+ const resumeToken = crypto.randomUUID();
527
+ if (stateStore) {
528
+ stateStore.saveCheckpoint({
529
+ runId: context.runId,
530
+ stepIndex: context.currentStepIndex,
531
+ stepName: step.id,
532
+ status: StepStatus.COMPLETED,
533
+ startedAt: startedAt,
534
+ completedAt: new Date(),
535
+ inputs: { mode: 'form', resumeToken, fields: step.fields },
536
+ outputs: { waiting: true },
537
+ error: null,
538
+ retryCount: 0,
539
+ });
540
+ }
541
+ return createStepResult(step.id, StepStatus.COMPLETED, {
542
+ waiting: true,
543
+ mode: 'form',
544
+ resumeToken,
545
+ fields: step.fields,
546
+ formPath: `/form/${context.runId}/${step.id}/${resumeToken}`,
547
+ }, startedAt);
548
+ }
549
+ default:
550
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, `Unknown wait mode: ${step.mode}`);
551
+ }
552
+ }
553
+ catch (error) {
554
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, error instanceof Error ? error.message : String(error));
555
+ }
556
+ }
557
+ // ============================================================================
558
+ // Merge Step
559
+ // ============================================================================
560
+ export async function executeMergeStep(step, context) {
561
+ const startedAt = new Date();
562
+ try {
563
+ // Resolve all source expressions to arrays
564
+ const resolvedSources = [];
565
+ for (const source of step.sources) {
566
+ const resolved = resolveTemplates(source, context);
567
+ if (!Array.isArray(resolved)) {
568
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, `Merge source "${source}" did not resolve to an array`);
569
+ }
570
+ resolvedSources.push(resolved);
571
+ }
572
+ let result;
573
+ switch (step.mode) {
574
+ case 'append':
575
+ result = resolvedSources.flat();
576
+ break;
577
+ case 'match': {
578
+ if (!step.matchField) {
579
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, 'Merge mode "match" requires matchField');
580
+ }
581
+ const fieldSets = resolvedSources.map((source) => new Set(source.map((item) => getNestedValue(item, step.matchField))));
582
+ const commonKeys = fieldSets.reduce((acc, set) => new Set([...acc].filter((key) => set.has(key))));
583
+ result = resolvedSources[0].filter((item) => commonKeys.has(getNestedValue(item, step.matchField)));
584
+ break;
585
+ }
586
+ case 'diff': {
587
+ if (!step.matchField) {
588
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, 'Merge mode "diff" requires matchField');
589
+ }
590
+ const otherKeys = new Set(resolvedSources.slice(1).flat().map((item) => getNestedValue(item, step.matchField)));
591
+ result = resolvedSources[0].filter((item) => !otherKeys.has(getNestedValue(item, step.matchField)));
592
+ break;
593
+ }
594
+ case 'combine_by_field': {
595
+ if (!step.matchField) {
596
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, 'Merge mode "combine_by_field" requires matchField');
597
+ }
598
+ const grouped = new Map();
599
+ const onConflict = step.onConflict ?? 'keep_last';
600
+ for (const source of resolvedSources) {
601
+ for (const item of source) {
602
+ if (!item || typeof item !== 'object')
603
+ continue;
604
+ const key = getNestedValue(item, step.matchField);
605
+ const existing = grouped.get(key);
606
+ if (existing) {
607
+ if (onConflict === 'keep_first') {
608
+ for (const [k, v] of Object.entries(item)) {
609
+ if (!(k in existing))
610
+ existing[k] = v;
611
+ }
612
+ }
613
+ else if (onConflict === 'keep_last') {
614
+ Object.assign(existing, item);
615
+ }
616
+ else {
617
+ Object.assign(existing, item);
618
+ }
619
+ }
620
+ else {
621
+ grouped.set(key, { ...item });
622
+ }
623
+ }
624
+ }
625
+ result = Array.from(grouped.values());
626
+ break;
627
+ }
628
+ default:
629
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, `Unknown merge mode: ${step.mode}`);
630
+ }
631
+ return createStepResult(step.id, StepStatus.COMPLETED, result, startedAt);
632
+ }
633
+ catch (error) {
634
+ return createStepResult(step.id, StepStatus.FAILED, null, startedAt, 0, error instanceof Error ? error.message : String(error));
635
+ }
636
+ }
637
+ /**
638
+ * Get a nested value from an object using dot notation.
639
+ * Used by merge step for field matching (replaces the old getField function).
640
+ */
641
+ function getNestedValue(item, field) {
642
+ if (!item || typeof item !== 'object')
643
+ return undefined;
644
+ const parts = field.split('.');
645
+ let current = item;
646
+ for (const part of parts) {
647
+ if (!current || typeof current !== 'object')
648
+ return undefined;
649
+ current = current[part];
650
+ }
651
+ return current;
652
+ }
653
+ //# sourceMappingURL=control-flow.js.map