arazzo-runner 0.0.18 → 0.0.19
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.
- package/package.json +1 -1
- package/src/Arazzo.js +211 -93
- package/src/ExecutionContext.js +16 -0
- package/src/ExecutionContextStack.js +75 -0
- package/src/Operation.js +3 -2
- package/src/Rules.js +5 -5
package/package.json
CHANGED
package/src/Arazzo.js
CHANGED
|
@@ -8,6 +8,8 @@ const https = require("node:https");
|
|
|
8
8
|
const path = require("node:path");
|
|
9
9
|
|
|
10
10
|
const Document = require("./Document");
|
|
11
|
+
const ExecutionContext = require('./ExecutionContext');
|
|
12
|
+
const ExecutionContextStack = require('./ExecutionContextStack');
|
|
11
13
|
const Expression = require("./Expression");
|
|
12
14
|
const Operation = require('./Operation');
|
|
13
15
|
const Rules = require("./Rules");
|
|
@@ -25,6 +27,8 @@ class Arazzo extends Document {
|
|
|
25
27
|
this.stepRunRules = {};
|
|
26
28
|
this.workflowRunRules = {};
|
|
27
29
|
this.retrySet = new Set();
|
|
30
|
+
this.context = new Map();
|
|
31
|
+
this.executionStack = new ExecutionContextStack();
|
|
28
32
|
this.retryLimits = {};
|
|
29
33
|
this.stepContext = {};
|
|
30
34
|
}
|
|
@@ -92,61 +96,105 @@ class Arazzo extends Document {
|
|
|
92
96
|
this.abortWorkflowController = new AbortController();
|
|
93
97
|
}
|
|
94
98
|
|
|
95
|
-
|
|
99
|
+
|
|
96
100
|
const workflow = await this.JSONPickerToIndex("workflows", index);
|
|
97
101
|
|
|
98
102
|
if (workflow) {
|
|
99
|
-
|
|
103
|
+
const context = new ExecutionContext(
|
|
104
|
+
'workflow',
|
|
105
|
+
workflow.workflowId,
|
|
106
|
+
index,
|
|
107
|
+
null,
|
|
108
|
+
workflow.workflowId,
|
|
109
|
+
null
|
|
110
|
+
);
|
|
111
|
+
this.executionStack.push(context);
|
|
100
112
|
|
|
101
|
-
|
|
102
|
-
|
|
113
|
+
try {
|
|
114
|
+
await this.buildWorkflow(workflow);
|
|
115
|
+
// const prevWorkflowContexts = this.executionStack.getContextsByType('workflow')
|
|
116
|
+
// .slice(0, -1);
|
|
117
|
+
// if (prevWorkflowContexts.some(ctx => ctx.workflowId === workflow.workflowId)) {
|
|
118
|
+
// throw new Error(`Circular workflow dependency detected: ${workflow.workflowId}`);
|
|
119
|
+
// }
|
|
103
120
|
|
|
104
|
-
if (workflow.dependsOn) {
|
|
105
|
-
await this.runDependsOnWorkflows();
|
|
106
121
|
|
|
107
|
-
this.
|
|
108
|
-
}
|
|
122
|
+
this.logger.notice(`Running Workflow: ${this.workflow.workflowId}`);
|
|
109
123
|
|
|
110
|
-
|
|
111
|
-
this.workflow.workflowId
|
|
112
|
-
this.workflow.inputs,
|
|
113
|
-
);
|
|
124
|
+
// this.workflow = workflow;
|
|
125
|
+
// this.workflowId = workflow.workflowId;
|
|
114
126
|
|
|
115
|
-
|
|
127
|
+
if (this.workflow.dependsOn) {
|
|
128
|
+
await this.runDependsOnWorkflows();
|
|
116
129
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
rules.setSuccessRules(this.workflow.onSuccess);
|
|
120
|
-
}
|
|
130
|
+
this.workflow = workflow;
|
|
131
|
+
}
|
|
121
132
|
|
|
122
|
-
|
|
123
|
-
//
|
|
124
|
-
|
|
125
|
-
|
|
133
|
+
// this.inputs = await this.inputFile.getWorkflowInputs(
|
|
134
|
+
// this.workflow.workflowId,
|
|
135
|
+
// this.workflow.inputs,
|
|
136
|
+
// );
|
|
126
137
|
|
|
127
|
-
|
|
138
|
+
// this.expression.addToContext("inputs", this.inputs);
|
|
128
139
|
|
|
129
|
-
|
|
140
|
+
// if (this.workflow.successActions) {
|
|
141
|
+
// rules.setSuccessRules(this.workflow.successActions);
|
|
142
|
+
// }
|
|
130
143
|
|
|
131
|
-
|
|
132
|
-
|
|
144
|
+
// if (this.workflow.failureActions) {
|
|
145
|
+
// rules.setFailureRules(this.workflow.failureActions);
|
|
146
|
+
// }
|
|
133
147
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
148
|
+
// this.workflow.rules = rules;
|
|
149
|
+
|
|
150
|
+
await this.runSteps();
|
|
151
|
+
|
|
152
|
+
if (this.workflow.outputs) {
|
|
153
|
+
const outputs = {};
|
|
154
|
+
|
|
155
|
+
for (const key in this.workflow.outputs) {
|
|
156
|
+
const value = this.expression.resolveExpression(
|
|
157
|
+
this.workflow.outputs[key],
|
|
158
|
+
);
|
|
138
159
|
|
|
139
|
-
|
|
160
|
+
Object.assign(outputs, { [key]: value });
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
this.expression.addToContext("workflows", {
|
|
164
|
+
[this.workflow.workflowId]: { outputs: outputs },
|
|
165
|
+
});
|
|
140
166
|
}
|
|
141
167
|
|
|
142
|
-
this.
|
|
143
|
-
|
|
144
|
-
|
|
168
|
+
this.logger.success(`Workflow ${workflow.workflowId} completed`);
|
|
169
|
+
return { noMoreWorkflows: false };
|
|
170
|
+
} finally {
|
|
171
|
+
this.executionStack.pop();
|
|
145
172
|
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async buildWorkflow(workflow) {
|
|
177
|
+
const rules = new Rules(this.expression, { logger: this.logger });
|
|
178
|
+
|
|
179
|
+
this.workflow = workflow;
|
|
180
|
+
this.workflowId = workflow.workflowId;
|
|
181
|
+
|
|
182
|
+
this.inputs = await this.inputFile.getWorkflowInputs(
|
|
183
|
+
this.workflow.workflowId,
|
|
184
|
+
this.workflow.inputs,
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
this.expression.addToContext("inputs", this.inputs);
|
|
188
|
+
|
|
189
|
+
if (this.workflow.successActions) {
|
|
190
|
+
rules.setSuccessRules(this.workflow.successActions);
|
|
191
|
+
}
|
|
146
192
|
|
|
147
|
-
|
|
148
|
-
|
|
193
|
+
if (this.workflow.failureActions) {
|
|
194
|
+
rules.setFailureRules(this.workflow.failureActions);
|
|
149
195
|
}
|
|
196
|
+
|
|
197
|
+
this.workflow.rules = rules;
|
|
150
198
|
}
|
|
151
199
|
|
|
152
200
|
/**
|
|
@@ -205,22 +253,36 @@ class Arazzo extends Document {
|
|
|
205
253
|
|
|
206
254
|
const step = this.workflow.steps[index];
|
|
207
255
|
|
|
208
|
-
|
|
256
|
+
const context = new ExecutionContext(
|
|
257
|
+
'step',
|
|
258
|
+
step.stepId,
|
|
259
|
+
this.workflowIndex,
|
|
260
|
+
index,
|
|
261
|
+
this.workflow.workflowId,
|
|
262
|
+
step.stepId
|
|
263
|
+
);
|
|
264
|
+
this.executionStack.push(context);
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
// if (this.executionStack.depth > 1) {
|
|
268
|
+
// const prevContexts = this.executionStack.getContextsByType('step')
|
|
269
|
+
// .slice(0, -1); // Exclude current
|
|
270
|
+
// if (prevContexts.some(ctx => ctx.stepId === step.stepId)) {
|
|
271
|
+
// throw new Error(`Circular step dependency detected: ${step.stepId}`);
|
|
272
|
+
// }
|
|
273
|
+
// }
|
|
274
|
+
|
|
209
275
|
this.step = step;
|
|
210
276
|
const rules = new Rules(this.expression, { logger: this.logger });
|
|
211
|
-
// if (!this.stepContext[step.stepId])
|
|
212
|
-
// Object.assign(this.stepContext, { [step.stepId]: {} });
|
|
213
277
|
|
|
214
278
|
this.logger.notice(`Running Step: ${step.stepId}`);
|
|
215
279
|
|
|
216
280
|
if (this.step.onSuccess) {
|
|
217
281
|
rules.setSuccessRules(this.step.onSuccess);
|
|
218
|
-
// this.workflow.rules.setStepSuccesses(this.step.onSuccess);
|
|
219
282
|
}
|
|
220
283
|
|
|
221
284
|
if (this.step.onFailure) {
|
|
222
285
|
rules.setFailureRules(this.step.onFailure);
|
|
223
|
-
// this.workflow.rules.setStepFailures(this.step.onFailure);
|
|
224
286
|
}
|
|
225
287
|
|
|
226
288
|
rules.combineRules(this.workflow.rules);
|
|
@@ -231,22 +293,7 @@ class Arazzo extends Document {
|
|
|
231
293
|
if (this.openAPISteps) {
|
|
232
294
|
await this.runOpenAPIStep();
|
|
233
295
|
} else if (this.isAWorkflowId) {
|
|
234
|
-
|
|
235
|
-
this.currentWorkflow = this.workflow;
|
|
236
|
-
if (this.isExternalWorkflow) {
|
|
237
|
-
await this.sourceDescriptionFile.runWorkflowById(this.step.workflowId)
|
|
238
|
-
const sourceDesc = this.expression.context.sourceDescriptions[this.sourceDescriptionFile.name];
|
|
239
|
-
if (!sourceDesc[this.step.workflowId]) {
|
|
240
|
-
if (this.sourceDescriptionFile.expression?.context?.workflows?.[this.step.workflowId]?.outputs) {
|
|
241
|
-
Object.assign(sourceDesc, { [this.step.workflowId]: { outputs: this.sourceDescriptionFile.expression.context.workflows[this.step.workflowId].outputs } });
|
|
242
|
-
this.expression.context.sourceDescriptions[this.sourceDescriptionFile.name] = sourceDesc;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
} else {
|
|
246
|
-
await this.runWorkflowById(this.step.workflowId);
|
|
247
|
-
}
|
|
248
|
-
this.stepIndex = this.currentStepIndex;
|
|
249
|
-
this.workflow = this.currentWorkflow;
|
|
296
|
+
await this.runWorkflowStep()
|
|
250
297
|
}
|
|
251
298
|
|
|
252
299
|
this.isAnOperationId = false;
|
|
@@ -256,10 +303,29 @@ class Arazzo extends Document {
|
|
|
256
303
|
this.openAPISteps = false;
|
|
257
304
|
|
|
258
305
|
this.logger.success(`Step ${step.stepId} completed`);
|
|
259
|
-
|
|
306
|
+
|
|
307
|
+
} finally {
|
|
308
|
+
this.executionStack.pop();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async runWorkflowStep() {
|
|
313
|
+
this.currentStepIndex = this.stepIndex;
|
|
314
|
+
this.currentWorkflow = this.workflow;
|
|
315
|
+
if (this.isExternalWorkflow) {
|
|
316
|
+
await this.sourceDescriptionFile.runWorkflowById(this.step.workflowId)
|
|
317
|
+
const sourceDesc = this.expression.context.sourceDescriptions[this.sourceDescriptionFile.name];
|
|
318
|
+
if (!sourceDesc[this.step.workflowId]) {
|
|
319
|
+
if (this.sourceDescriptionFile.expression?.context?.workflows?.[this.step.workflowId]?.outputs) {
|
|
320
|
+
Object.assign(sourceDesc, { [this.step.workflowId]: { outputs: this.sourceDescriptionFile.expression.context.workflows[this.step.workflowId].outputs } });
|
|
321
|
+
this.expression.context.sourceDescriptions[this.sourceDescriptionFile.name] = sourceDesc;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
260
324
|
} else {
|
|
261
|
-
|
|
325
|
+
await this.runWorkflowById(this.step.workflowId);
|
|
262
326
|
}
|
|
327
|
+
this.stepIndex = this.currentStepIndex;
|
|
328
|
+
this.workflow = this.currentWorkflow;
|
|
263
329
|
}
|
|
264
330
|
|
|
265
331
|
/**
|
|
@@ -278,7 +344,7 @@ class Arazzo extends Document {
|
|
|
278
344
|
};
|
|
279
345
|
|
|
280
346
|
const apiOperation = new Operation(this.operation, this.sourceDescriptionFile, this.expression, this.inputs, this.logger, miniStep);
|
|
281
|
-
const response = await apiOperation.runOperation()
|
|
347
|
+
const response = await apiOperation.runOperation(this.retryAfter)
|
|
282
348
|
await this.dealWithResponse(response)
|
|
283
349
|
}
|
|
284
350
|
|
|
@@ -295,23 +361,25 @@ class Arazzo extends Document {
|
|
|
295
361
|
|
|
296
362
|
if (passedSuccessCriteria) {
|
|
297
363
|
this.logger.success("All criteria checks passed");
|
|
364
|
+
|
|
298
365
|
if (this.currentRetryRule) {
|
|
299
366
|
if (this.retryContext.doNotDeleteRetryLimits) {
|
|
300
367
|
this.retryLimits[this.currentRetryRule] = 0;
|
|
301
368
|
this.logger.notice("Retries stopped");
|
|
369
|
+
delete this.retryAfter;
|
|
302
370
|
}
|
|
303
371
|
}
|
|
304
372
|
|
|
305
373
|
await this.dealWithPassedRule(response);
|
|
306
374
|
} else {
|
|
307
375
|
this.logger.error("Not all criteria checks passed");
|
|
308
|
-
if (this.step.onFailure) {
|
|
309
|
-
|
|
310
|
-
} else {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
}
|
|
376
|
+
// if (this.step.onFailure) {
|
|
377
|
+
await this.dealWithFailedRule(response);
|
|
378
|
+
// } else {
|
|
379
|
+
// throw new Error(
|
|
380
|
+
// `${this.step.stepId} step of the ${this.workflow.workflowId} workflow failed the successCriteria`,
|
|
381
|
+
// );
|
|
382
|
+
// }
|
|
315
383
|
}
|
|
316
384
|
} else {
|
|
317
385
|
if (this.step?.outputs) {
|
|
@@ -448,6 +516,11 @@ class Arazzo extends Document {
|
|
|
448
516
|
this.logger.notice(
|
|
449
517
|
`${this.step.stepId} onFailure Retry rule triggered`,
|
|
450
518
|
);
|
|
519
|
+
|
|
520
|
+
if (response.headers.has('retry-after')) {
|
|
521
|
+
this.retryAfter = response.headers.get('retry-after');
|
|
522
|
+
}
|
|
523
|
+
|
|
451
524
|
await this.retryProcessing(whatNext);
|
|
452
525
|
}
|
|
453
526
|
}
|
|
@@ -543,8 +616,15 @@ class Arazzo extends Document {
|
|
|
543
616
|
return `${num}${suffix}`;
|
|
544
617
|
};
|
|
545
618
|
|
|
619
|
+
const currentContext = this.executionStack.current;
|
|
620
|
+
currentContext.isRetrying = true;
|
|
621
|
+
currentContext.retryCount++;
|
|
622
|
+
// console.log(this.currentContext)
|
|
623
|
+
// this.currentContext.currentlyBeingRetried = true;
|
|
624
|
+
|
|
546
625
|
this.retryContext = {
|
|
547
626
|
doNotDeleteRetryLimits: true,
|
|
627
|
+
originContext: currentContext,
|
|
548
628
|
};
|
|
549
629
|
|
|
550
630
|
let shouldRunRule = true;
|
|
@@ -565,34 +645,13 @@ class Arazzo extends Document {
|
|
|
565
645
|
this.retryContext.doNotDeleteRetryLimits = false;
|
|
566
646
|
|
|
567
647
|
if (whatNext.stepId) {
|
|
568
|
-
this.
|
|
569
|
-
`Rule ${whatNext.name} requires Step ${whatNext.stepId} running first`,
|
|
570
|
-
);
|
|
571
|
-
|
|
572
|
-
const stepIndex = this.findStepIndexInWorkflowByStepId(
|
|
573
|
-
whatNext.stepId,
|
|
574
|
-
);
|
|
575
|
-
|
|
576
|
-
await this.runStep(stepIndex);
|
|
577
|
-
|
|
578
|
-
this.logger.notice(
|
|
579
|
-
`Rule ${whatNext.name} Step ${whatNext.stepId} has run`,
|
|
580
|
-
);
|
|
648
|
+
await this.retryStep(whatNext)
|
|
581
649
|
} else {
|
|
582
|
-
this.
|
|
583
|
-
`Rule ${whatNext.name} requires Workflow ${whatNext.workflowId} running first`,
|
|
584
|
-
);
|
|
585
|
-
|
|
586
|
-
const workflowIndex = this.findWorkflowIndexByWorkflowId(
|
|
587
|
-
whatNext.workflowId,
|
|
588
|
-
);
|
|
589
|
-
|
|
590
|
-
await this.runWorkflow(workflowIndex);
|
|
591
|
-
|
|
592
|
-
this.logger.notice(
|
|
593
|
-
`Rule ${whatNext.name} Workflow ${whatNext.workflowId} has run`,
|
|
594
|
-
);
|
|
650
|
+
await this.retryWorkflow(whatNext)
|
|
595
651
|
}
|
|
652
|
+
|
|
653
|
+
// After retry step/workflow completes, restore our context
|
|
654
|
+
await this.restoreContextFromRetry(currentContext);
|
|
596
655
|
}
|
|
597
656
|
|
|
598
657
|
if (!this.retryAfter && whatNext.retryAfter)
|
|
@@ -606,7 +665,7 @@ class Arazzo extends Document {
|
|
|
606
665
|
|
|
607
666
|
let count = this.retryLimits[whatNext.name];
|
|
608
667
|
|
|
609
|
-
await this.runStep(
|
|
668
|
+
await this.runStep(currentContext.stepIndex);
|
|
610
669
|
|
|
611
670
|
if (this.retryLimits[whatNext.name] !== 0) {
|
|
612
671
|
count--;
|
|
@@ -616,10 +675,69 @@ class Arazzo extends Document {
|
|
|
616
675
|
} while (this.retryLimits[whatNext.name] > 0);
|
|
617
676
|
}
|
|
618
677
|
|
|
678
|
+
currentContext.isRetrying = false;
|
|
679
|
+
|
|
619
680
|
if (this.retryLimits[whatNext.name] === 0)
|
|
620
681
|
this.retrySet.delete(whatNext.name);
|
|
621
682
|
}
|
|
622
683
|
|
|
684
|
+
/**
|
|
685
|
+
* @private
|
|
686
|
+
* @param {*} whatNext
|
|
687
|
+
*/
|
|
688
|
+
async retryStep(whatNext) {
|
|
689
|
+
const currentContext = this.executionStack.current;
|
|
690
|
+
|
|
691
|
+
this.logger.notice(
|
|
692
|
+
`Rule ${whatNext.name} requires Step ${whatNext.stepId} running first`,
|
|
693
|
+
);
|
|
694
|
+
|
|
695
|
+
const stepIndex = this.findStepIndexInWorkflowByStepId(
|
|
696
|
+
whatNext.stepId,
|
|
697
|
+
);
|
|
698
|
+
|
|
699
|
+
await this.runStep(stepIndex);
|
|
700
|
+
|
|
701
|
+
this.logger.notice(
|
|
702
|
+
`Rule ${whatNext.name} Step ${whatNext.stepId} has run`,
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* @private
|
|
708
|
+
* @param {*} whatNext
|
|
709
|
+
*/
|
|
710
|
+
async retryWorkflow(whatNext) {
|
|
711
|
+
const currentContext = this.executionStack.current;
|
|
712
|
+
|
|
713
|
+
this.logger.notice(
|
|
714
|
+
`Rule ${whatNext.name} requires Workflow ${whatNext.workflowId} running first`,
|
|
715
|
+
);
|
|
716
|
+
|
|
717
|
+
const workflowIndex = this.findWorkflowIndexByWorkflowId(
|
|
718
|
+
whatNext.workflowId,
|
|
719
|
+
);
|
|
720
|
+
|
|
721
|
+
await this.runWorkflow(workflowIndex);
|
|
722
|
+
|
|
723
|
+
this.logger.notice(
|
|
724
|
+
`Rule ${whatNext.name} Workflow ${whatNext.workflowId} has run`,
|
|
725
|
+
);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
async restoreContextFromRetry(context) {
|
|
729
|
+
// Restore the indexes and state
|
|
730
|
+
this.workflowIndex = context.workflowIndex;
|
|
731
|
+
this.stepIndex = context.stepIndex;
|
|
732
|
+
const workflow = await this.JSONPickerToIndex("workflows", context.workflowIndex);
|
|
733
|
+
await this.buildWorkflow(workflow)
|
|
734
|
+
this.step = this.workflow.steps[context.stepIndex];
|
|
735
|
+
|
|
736
|
+
this.logger.notice(
|
|
737
|
+
`Restored context: workflow=${context.workflowId}, step=${context.stepId}`
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
|
|
623
741
|
/**
|
|
624
742
|
* @private
|
|
625
743
|
* @param {*} response
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class ExecutionContext {
|
|
2
|
+
constructor(type, id, workflowIndex, stepIndex, workflowId, stepId, workflow) {
|
|
3
|
+
this.type = type; // 'workflow' or 'step'
|
|
4
|
+
this.id = id;
|
|
5
|
+
this.workflowIndex = workflowIndex;
|
|
6
|
+
this.stepIndex = stepIndex;
|
|
7
|
+
this.workflowId = workflowId;
|
|
8
|
+
this.stepId = stepId;
|
|
9
|
+
this.timestamp = Date.now();
|
|
10
|
+
this.retryCount = 0;
|
|
11
|
+
this.isRetrying = false;
|
|
12
|
+
this.workflow = workflow;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = ExecutionContext;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
class ExecutionContextStack {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.stack = [];
|
|
4
|
+
this.contextMap = new Map(); // Quick lookup by stepId or workflowId
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
push(context) {
|
|
8
|
+
this.stack.push(context);
|
|
9
|
+
this.contextMap.set(context.id, context);
|
|
10
|
+
return context;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
pop() {
|
|
14
|
+
const context = this.stack.pop();
|
|
15
|
+
if (context) {
|
|
16
|
+
this.contextMap.delete(context.id);
|
|
17
|
+
}
|
|
18
|
+
return context;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
peek() {
|
|
22
|
+
return this.stack[this.stack.length - 1];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get current() {
|
|
26
|
+
return this.peek();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get depth() {
|
|
30
|
+
return this.stack.length;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Find the context where we should return after retry completes
|
|
34
|
+
findRetryOrigin() {
|
|
35
|
+
// Walk backwards to find the first context that initiated a retry
|
|
36
|
+
for (let i = this.stack.length - 1; i >= 0; i--) {
|
|
37
|
+
if (this.stack[i].isRetrying) {
|
|
38
|
+
return this.stack[i];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Get all contexts of a specific type
|
|
45
|
+
getContextsByType(type) {
|
|
46
|
+
return this.stack.filter(ctx => ctx.type === type);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Check if we're already executing a specific workflow/step (prevent infinite loops)
|
|
50
|
+
isAlreadyExecuting(type, id) {
|
|
51
|
+
return this.stack.some(ctx => ctx.type === type && ctx.id === id);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
clear() {
|
|
55
|
+
this.stack = [];
|
|
56
|
+
this.contextMap.clear();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Serialize for debugging or persistence
|
|
60
|
+
toJSON() {
|
|
61
|
+
return this.stack.map(ctx => ({
|
|
62
|
+
type: ctx.type,
|
|
63
|
+
id: ctx.id,
|
|
64
|
+
workflowIndex: ctx.workflowIndex,
|
|
65
|
+
stepIndex: ctx.stepIndex,
|
|
66
|
+
workflowId: ctx.workflowId,
|
|
67
|
+
stepId: ctx.stepId,
|
|
68
|
+
retryCount: ctx.retryCount,
|
|
69
|
+
isRetrying: ctx.isRetrying,
|
|
70
|
+
timestamp: ctx.timestamp
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
module.exports = ExecutionContextStack;
|
package/src/Operation.js
CHANGED
|
@@ -17,7 +17,8 @@ class Operation {
|
|
|
17
17
|
this.operation = operation;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
async runOperation() {
|
|
20
|
+
async runOperation(retryAfter) {
|
|
21
|
+
this.retryAfter = retryAfter;
|
|
21
22
|
this.buildOperation()
|
|
22
23
|
|
|
23
24
|
return await this.makeRequest()
|
|
@@ -78,7 +79,7 @@ class Operation {
|
|
|
78
79
|
|
|
79
80
|
if (this.retryAfter) {
|
|
80
81
|
this.logger.notice(
|
|
81
|
-
`retryAfter was set: waiting ${this.retryAfter
|
|
82
|
+
`retryAfter was set: waiting ${this.retryAfter} seconds`,
|
|
82
83
|
);
|
|
83
84
|
await sleep(this.retryAfter * 1000);
|
|
84
85
|
}
|
package/src/Rules.js
CHANGED
|
@@ -40,9 +40,9 @@ class Rules {
|
|
|
40
40
|
this.successRules = [];
|
|
41
41
|
|
|
42
42
|
this.logger = options?.logger || {
|
|
43
|
-
notice: () => {},
|
|
44
|
-
error: () => {},
|
|
45
|
-
success: () => {},
|
|
43
|
+
notice: () => { },
|
|
44
|
+
error: () => { },
|
|
45
|
+
success: () => { },
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -83,9 +83,9 @@ class Rules {
|
|
|
83
83
|
|
|
84
84
|
const obj = {};
|
|
85
85
|
|
|
86
|
-
if (successRules) {
|
|
86
|
+
if (successRules && this.rules.length) {
|
|
87
87
|
this.logger.notice(`Running onSuccess Rules`);
|
|
88
|
-
} else {
|
|
88
|
+
} else if (!successRules && this.rules.length) {
|
|
89
89
|
this.logger.notice(`Running onFailure Rules`);
|
|
90
90
|
}
|
|
91
91
|
|