1// Alchemy modifications are licensed under Apache-2.0.
2// This file includes third-party code; see /THIRD_PARTY_LICENSES.md.
3import { ms } from "itty-time";
4import type { ResolvedStepConfig, StepState } from "../context.ts";
5import type { WorkflowSleepDuration } from "cloudflare:workers";
6
7export class DelayFunctionError extends Error {
8 constructor(reason: string) {
9 super(reason);
10 this.name = "DelayFunctionError";
11 }
12}
13
14export function calcRetryDuration(
15 config: ResolvedStepConfig,
16 stepState: StepState,
17 delayValue: unknown
18): number {
19 const { attemptedCount: attemptCount } = stepState;
20 const { retries } = config;
21
22 let base: number;
23 try {
24 base = ms(delayValue as WorkflowSleepDuration);
25 } catch {
26 throw new DelayFunctionError(
27 'returned an invalid delay value (expected a number of ms or a duration string like "30 seconds")'
28 );
29 }
30 if (!Number.isFinite(base) || base < 0) {
31 throw new DelayFunctionError(
32 'returned an invalid delay value (expected a number of ms or a duration string like "30 seconds")'
33 );
34 }
35
36 switch (retries.backoff) {
37 case "exponential": {
38 return base * Math.pow(2, attemptCount - 1);
39 }
40 case "linear": {
41 return base * attemptCount;
42 }
43 case "constant":
44 default: {
45 return base;
46 }
47 }
48}