+ public static insertAt = (str: string, subStr: string, pos: number): string =>
+ `${str.slice(0, pos)}${subStr}${str.slice(pos)}`;
+
+ /**
+ * @param [retryNumber=0]
+ * @returns delay in milliseconds
+ */
+ public static exponentialDelay(retryNumber = 0): number {
+ const delay = Math.pow(2, retryNumber) * 100;
+ const randomSum = delay * 0.2 * Utils.secureRandom(); // 0-20% of the delay
+ return delay + randomSum;
+ }
+
+ public static async promiseWithTimeout<T>(
+ promise: Promise<T>,
+ timeoutMs: number,
+ timeoutError: Error,
+ timeoutCallback: () => void = () => {
+ /* This is intentional */
+ }
+ ): Promise<T> {
+ // Create a timeout promise that rejects in timeout milliseconds
+ const timeoutPromise = new Promise<never>((_, reject) => {
+ setTimeout(() => {
+ timeoutCallback();
+ reject(timeoutError);
+ }, timeoutMs);
+ });
+
+ // Returns a race between timeout promise and the passed promise
+ return Promise.race<T>([promise, timeoutPromise]);
+ }
+
+ /**
+ * Generate a cryptographically secure random number in the [0,1[ range
+ *
+ * @returns
+ */
+ public static secureRandom(): number {
+ return crypto.randomBytes(4).readUInt32LE() / 0x100000000;