arazzo-runner 0.0.17 → 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 +228 -83
- 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);
|
|
@@ -230,22 +292,42 @@ class Arazzo extends Document {
|
|
|
230
292
|
|
|
231
293
|
if (this.openAPISteps) {
|
|
232
294
|
await this.runOpenAPIStep();
|
|
295
|
+
} else if (this.isAWorkflowId) {
|
|
296
|
+
await this.runWorkflowStep()
|
|
233
297
|
}
|
|
234
298
|
|
|
235
299
|
this.isAnOperationId = false;
|
|
236
300
|
this.isAWorkflowId = false;
|
|
301
|
+
this.isExternalWorkflow = false;
|
|
237
302
|
this.isAnOperationPath = false;
|
|
238
303
|
this.openAPISteps = false;
|
|
239
304
|
|
|
240
305
|
this.logger.success(`Step ${step.stepId} completed`);
|
|
241
|
-
return { noMoreSteps: false };
|
|
242
|
-
} else {
|
|
243
|
-
// this.logger.notice(`All steps in ${this.workflow.workflowId} have run`);
|
|
244
306
|
|
|
245
|
-
|
|
307
|
+
} finally {
|
|
308
|
+
this.executionStack.pop();
|
|
246
309
|
}
|
|
247
310
|
}
|
|
248
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
|
+
}
|
|
324
|
+
} else {
|
|
325
|
+
await this.runWorkflowById(this.step.workflowId);
|
|
326
|
+
}
|
|
327
|
+
this.stepIndex = this.currentStepIndex;
|
|
328
|
+
this.workflow = this.currentWorkflow;
|
|
329
|
+
}
|
|
330
|
+
|
|
249
331
|
/**
|
|
250
332
|
* @private
|
|
251
333
|
*/
|
|
@@ -262,7 +344,7 @@ class Arazzo extends Document {
|
|
|
262
344
|
};
|
|
263
345
|
|
|
264
346
|
const apiOperation = new Operation(this.operation, this.sourceDescriptionFile, this.expression, this.inputs, this.logger, miniStep);
|
|
265
|
-
const response = await apiOperation.runOperation()
|
|
347
|
+
const response = await apiOperation.runOperation(this.retryAfter)
|
|
266
348
|
await this.dealWithResponse(response)
|
|
267
349
|
}
|
|
268
350
|
|
|
@@ -279,23 +361,25 @@ class Arazzo extends Document {
|
|
|
279
361
|
|
|
280
362
|
if (passedSuccessCriteria) {
|
|
281
363
|
this.logger.success("All criteria checks passed");
|
|
364
|
+
|
|
282
365
|
if (this.currentRetryRule) {
|
|
283
366
|
if (this.retryContext.doNotDeleteRetryLimits) {
|
|
284
367
|
this.retryLimits[this.currentRetryRule] = 0;
|
|
285
368
|
this.logger.notice("Retries stopped");
|
|
369
|
+
delete this.retryAfter;
|
|
286
370
|
}
|
|
287
371
|
}
|
|
288
372
|
|
|
289
373
|
await this.dealWithPassedRule(response);
|
|
290
374
|
} else {
|
|
291
375
|
this.logger.error("Not all criteria checks passed");
|
|
292
|
-
if (this.step.onFailure) {
|
|
293
|
-
|
|
294
|
-
} else {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
}
|
|
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
|
+
// }
|
|
299
383
|
}
|
|
300
384
|
} else {
|
|
301
385
|
if (this.step?.outputs) {
|
|
@@ -432,6 +516,11 @@ class Arazzo extends Document {
|
|
|
432
516
|
this.logger.notice(
|
|
433
517
|
`${this.step.stepId} onFailure Retry rule triggered`,
|
|
434
518
|
);
|
|
519
|
+
|
|
520
|
+
if (response.headers.has('retry-after')) {
|
|
521
|
+
this.retryAfter = response.headers.get('retry-after');
|
|
522
|
+
}
|
|
523
|
+
|
|
435
524
|
await this.retryProcessing(whatNext);
|
|
436
525
|
}
|
|
437
526
|
}
|
|
@@ -527,8 +616,15 @@ class Arazzo extends Document {
|
|
|
527
616
|
return `${num}${suffix}`;
|
|
528
617
|
};
|
|
529
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
|
+
|
|
530
625
|
this.retryContext = {
|
|
531
626
|
doNotDeleteRetryLimits: true,
|
|
627
|
+
originContext: currentContext,
|
|
532
628
|
};
|
|
533
629
|
|
|
534
630
|
let shouldRunRule = true;
|
|
@@ -549,34 +645,13 @@ class Arazzo extends Document {
|
|
|
549
645
|
this.retryContext.doNotDeleteRetryLimits = false;
|
|
550
646
|
|
|
551
647
|
if (whatNext.stepId) {
|
|
552
|
-
this.
|
|
553
|
-
`Rule ${whatNext.name} requires Step ${whatNext.stepId} running first`,
|
|
554
|
-
);
|
|
555
|
-
|
|
556
|
-
const stepIndex = this.findStepIndexInWorkflowByStepId(
|
|
557
|
-
whatNext.stepId,
|
|
558
|
-
);
|
|
559
|
-
|
|
560
|
-
await this.runStep(stepIndex);
|
|
561
|
-
|
|
562
|
-
this.logger.notice(
|
|
563
|
-
`Rule ${whatNext.name} Step ${whatNext.stepId} has run`,
|
|
564
|
-
);
|
|
648
|
+
await this.retryStep(whatNext)
|
|
565
649
|
} else {
|
|
566
|
-
this.
|
|
567
|
-
`Rule ${whatNext.name} requires Workflow ${whatNext.workflowId} running first`,
|
|
568
|
-
);
|
|
569
|
-
|
|
570
|
-
const workflowIndex = this.findWorkflowIndexByWorkflowId(
|
|
571
|
-
whatNext.workflowId,
|
|
572
|
-
);
|
|
573
|
-
|
|
574
|
-
await this.runWorkflow(workflowIndex);
|
|
575
|
-
|
|
576
|
-
this.logger.notice(
|
|
577
|
-
`Rule ${whatNext.name} Workflow ${whatNext.workflowId} has run`,
|
|
578
|
-
);
|
|
650
|
+
await this.retryWorkflow(whatNext)
|
|
579
651
|
}
|
|
652
|
+
|
|
653
|
+
// After retry step/workflow completes, restore our context
|
|
654
|
+
await this.restoreContextFromRetry(currentContext);
|
|
580
655
|
}
|
|
581
656
|
|
|
582
657
|
if (!this.retryAfter && whatNext.retryAfter)
|
|
@@ -590,7 +665,7 @@ class Arazzo extends Document {
|
|
|
590
665
|
|
|
591
666
|
let count = this.retryLimits[whatNext.name];
|
|
592
667
|
|
|
593
|
-
await this.runStep(
|
|
668
|
+
await this.runStep(currentContext.stepIndex);
|
|
594
669
|
|
|
595
670
|
if (this.retryLimits[whatNext.name] !== 0) {
|
|
596
671
|
count--;
|
|
@@ -600,10 +675,69 @@ class Arazzo extends Document {
|
|
|
600
675
|
} while (this.retryLimits[whatNext.name] > 0);
|
|
601
676
|
}
|
|
602
677
|
|
|
678
|
+
currentContext.isRetrying = false;
|
|
679
|
+
|
|
603
680
|
if (this.retryLimits[whatNext.name] === 0)
|
|
604
681
|
this.retrySet.delete(whatNext.name);
|
|
605
682
|
}
|
|
606
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
|
+
|
|
607
741
|
/**
|
|
608
742
|
* @private
|
|
609
743
|
* @param {*} response
|
|
@@ -653,11 +787,13 @@ class Arazzo extends Document {
|
|
|
653
787
|
} else {
|
|
654
788
|
let workflowIdArr = this.step?.workflowId?.split(".") || [];
|
|
655
789
|
|
|
656
|
-
if (workflowIdArr.length
|
|
657
|
-
|
|
658
|
-
|
|
790
|
+
if (workflowIdArr.length !== 1) {
|
|
791
|
+
this.step.workflowId = workflowIdArr.at(-1)
|
|
792
|
+
this.isExternalWorkflow = true;
|
|
793
|
+
// await this.runWorkflowById(workflowIdArr.at(0));
|
|
794
|
+
// } else {
|
|
659
795
|
await this.sourceDescriptionFile.loadWorkflowData(this.inputFile);
|
|
660
|
-
await this.sourceDescriptionFile.runWorkflowById(workflowIdArr.at(-1));
|
|
796
|
+
// await this.sourceDescriptionFile.runWorkflowById(workflowIdArr.at(-1));
|
|
661
797
|
}
|
|
662
798
|
}
|
|
663
799
|
}
|
|
@@ -673,6 +809,15 @@ class Arazzo extends Document {
|
|
|
673
809
|
if (this.sourceDescriptions.length === 1) {
|
|
674
810
|
return this.sourceDescriptions[0];
|
|
675
811
|
} else {
|
|
812
|
+
if (this.isAnOperationId) {
|
|
813
|
+
const multipleOpenAPISourceDescriptions = this.sourceDescriptions.filter(obj => obj.type === 'openapi');
|
|
814
|
+
if (multipleOpenAPISourceDescriptions.length > 1) {
|
|
815
|
+
|
|
816
|
+
} else {
|
|
817
|
+
return multipleOpenAPISourceDescriptions[0]
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
676
821
|
const operationOrWorkflowPointerArr =
|
|
677
822
|
operationOrWorkflowPointer.split(".");
|
|
678
823
|
|
|
@@ -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
|
|