aboutsummaryrefslogtreecommitdiffstats
path: root/node_modules/exponential-backoff
diff options
context:
space:
mode:
authorpack <pack@packgekko.xyz>2026-08-09 10:37:07 +0000
committerpack <pack@packgekko.xyz>2026-08-09 10:37:07 +0000
commit55a4f1fc869e41aca748c63d3018f0448b1606e0 (patch)
treed132d1d01772b6d53faee3e63c534dea5706b78a /node_modules/exponential-backoff
downloadcrud-55a4f1fc869e41aca748c63d3018f0448b1606e0.tar.gz
first commit
Diffstat (limited to 'node_modules/exponential-backoff')
-rw-r--r--node_modules/exponential-backoff/LICENSE202
-rw-r--r--node_modules/exponential-backoff/README.md89
-rw-r--r--node_modules/exponential-backoff/dist/backoff.d.ts9
-rw-r--r--node_modules/exponential-backoff/dist/backoff.js124
-rw-r--r--node_modules/exponential-backoff/dist/delay/always/always.delay.d.ts3
-rw-r--r--node_modules/exponential-backoff/dist/delay/always/always.delay.js25
-rw-r--r--node_modules/exponential-backoff/dist/delay/delay.base.d.ts12
-rw-r--r--node_modules/exponential-backoff/dist/delay/delay.base.js45
-rw-r--r--node_modules/exponential-backoff/dist/delay/delay.factory.d.ts3
-rw-r--r--node_modules/exponential-backoff/dist/delay/delay.factory.js17
-rw-r--r--node_modules/exponential-backoff/dist/delay/delay.interface.d.ts4
-rw-r--r--node_modules/exponential-backoff/dist/delay/delay.interface.js3
-rw-r--r--node_modules/exponential-backoff/dist/delay/skip-first/skip-first.delay.d.ts6
-rw-r--r--node_modules/exponential-backoff/dist/delay/skip-first/skip-first.delay.js82
-rw-r--r--node_modules/exponential-backoff/dist/jitter/full/full.jitter.d.ts1
-rw-r--r--node_modules/exponential-backoff/dist/jitter/full/full.jitter.js8
-rw-r--r--node_modules/exponential-backoff/dist/jitter/jitter.factory.d.ts3
-rw-r--r--node_modules/exponential-backoff/dist/jitter/jitter.factory.js15
-rw-r--r--node_modules/exponential-backoff/dist/jitter/no/no.jitter.d.ts1
-rw-r--r--node_modules/exponential-backoff/dist/jitter/no/no.jitter.js7
-rw-r--r--node_modules/exponential-backoff/dist/options.d.ts55
-rw-r--r--node_modules/exponential-backoff/dist/options.js31
-rw-r--r--node_modules/exponential-backoff/package.json62
-rw-r--r--node_modules/exponential-backoff/src/backoff.spec.ts194
-rw-r--r--node_modules/exponential-backoff/src/backoff.ts60
-rw-r--r--node_modules/exponential-backoff/src/delay/always/always.delay.spec.ts65
-rw-r--r--node_modules/exponential-backoff/src/delay/always/always.delay.ts3
-rw-r--r--node_modules/exponential-backoff/src/delay/delay.base.ts34
-rw-r--r--node_modules/exponential-backoff/src/delay/delay.factory.ts18
-rw-r--r--node_modules/exponential-backoff/src/delay/delay.interface.ts4
-rw-r--r--node_modules/exponential-backoff/src/delay/skip-first/skip-first.delay.ts15
-rw-r--r--node_modules/exponential-backoff/src/jitter/full/full.jitter.spec.ts31
-rw-r--r--node_modules/exponential-backoff/src/jitter/full/full.jitter.ts4
-rw-r--r--node_modules/exponential-backoff/src/jitter/jitter.factory.ts16
-rw-r--r--node_modules/exponential-backoff/src/jitter/no/no.jitter.spec.ts9
-rw-r--r--node_modules/exponential-backoff/src/jitter/no/no.jitter.ts3
-rw-r--r--node_modules/exponential-backoff/src/options.ts76
37 files changed, 1339 insertions, 0 deletions
diff --git a/node_modules/exponential-backoff/LICENSE b/node_modules/exponential-backoff/LICENSE
new file mode 100644
index 0000000..4be46a9
--- /dev/null
+++ b/node_modules/exponential-backoff/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2019 Coveo Solutions Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/node_modules/exponential-backoff/README.md b/node_modules/exponential-backoff/README.md
new file mode 100644
index 0000000..10c7568
--- /dev/null
+++ b/node_modules/exponential-backoff/README.md
@@ -0,0 +1,89 @@
+# exponential-backoff
+
+A utility that allows retrying a function with an exponential delay between attempts.
+
+## Installation
+
+```
+npm i exponential-backoff
+```
+
+## Usage
+
+The `backOff<T>` function takes a promise-returning function to retry, and an optional `BackOffOptions` object. It returns a `Promise<T>`.
+
+```ts
+function backOff<T>(
+ request: () => Promise<T>,
+ options?: BackOffOptions
+): Promise<T>;
+```
+
+Here is an example retrying a function that calls a hypothetical weather endpoint:
+
+```js
+import { backOff } from "exponential-backoff";
+
+function getWeather() {
+ return fetch("weather-endpoint");
+}
+
+async function main() {
+ try {
+ const response = await backOff(() => getWeather());
+ // process response
+ } catch (e) {
+ // handle error
+ }
+}
+
+main();
+```
+
+Migrating across major versions? Here are our [breaking changes](https://github.com/coveo/exponential-backoff/tree/master/doc/migration-guide.md).
+
+### `BackOffOptions`
+
+- `delayFirstAttempt?: boolean`
+
+ Decides whether the `startingDelay` should be applied before the first call. If `false`, the first call will occur without a delay.
+
+ Default value is `false`.
+
+- `jitter?: JitterType | string`
+
+ Decides whether a [jitter](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/) should be applied to the delay. Possible values are `full` and `none`.
+
+ Default value is `none`.
+
+- `maxDelay?: number`
+
+ The maximum delay, in milliseconds, between two consecutive attempts.
+
+ Default value is `Infinity`.
+
+- `numOfAttempts?: number`
+
+ The maximum number of times to attempt the function.
+
+ Default value is `10`.
+
+ Minimum value is `1`.
+
+- `retry?: (e: any, attemptNumber: number) => boolean | Promise<boolean>`
+
+ The `retry` function can be used to run logic after every failed attempt (e.g. logging a message, assessing the last error, etc.). It is called with the last error and the upcoming attempt number. Returning `true` will retry the function as long as the `numOfAttempts` has not been exceeded. Returning `false` will end the execution.
+
+ Default value is a function that always returns `true`.
+
+- `startingDelay?: number`
+
+ The delay, in milliseconds, before executing the function for the first time.
+
+ Default value is `100` ms.
+
+- `timeMultiple?: number`
+
+ The `startingDelay` is multiplied by the `timeMultiple` to increase the delay between reattempts.
+
+ Default value is `2`.
diff --git a/node_modules/exponential-backoff/dist/backoff.d.ts b/node_modules/exponential-backoff/dist/backoff.d.ts
new file mode 100644
index 0000000..2ea9505
--- /dev/null
+++ b/node_modules/exponential-backoff/dist/backoff.d.ts
@@ -0,0 +1,9 @@
+import { IBackOffOptions, BackoffOptions } from "./options";
+export { BackoffOptions, IBackOffOptions };
+/**
+ * Executes a function with exponential backoff.
+ * @param request the function to be executed
+ * @param options options to customize the backoff behavior
+ * @returns Promise that resolves to the result of the `request` function
+ */
+export declare function backOff<T>(request: () => Promise<T>, options?: BackoffOptions): Promise<T>;
diff --git a/node_modules/exponential-backoff/dist/backoff.js b/node_modules/exponential-backoff/dist/backoff.js
new file mode 100644
index 0000000..6a1b6bd
--- /dev/null
+++ b/node_modules/exponential-backoff/dist/backoff.js
@@ -0,0 +1,124 @@
+"use strict";
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+ function verb(n) { return function (v) { return step([n, v]); }; }
+ function step(op) {
+ if (f) throw new TypeError("Generator is already executing.");
+ while (_) try {
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+ if (y = 0, t) op = [op[0] & 2, t.value];
+ switch (op[0]) {
+ case 0: case 1: t = op; break;
+ case 4: _.label++; return { value: op[1], done: false };
+ case 5: _.label++; y = op[1]; op = [0]; continue;
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
+ default:
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+ if (t[2]) _.ops.pop();
+ _.trys.pop(); continue;
+ }
+ op = body.call(thisArg, _);
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+ }
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+var options_1 = require("./options");
+var delay_factory_1 = require("./delay/delay.factory");
+/**
+ * Executes a function with exponential backoff.
+ * @param request the function to be executed
+ * @param options options to customize the backoff behavior
+ * @returns Promise that resolves to the result of the `request` function
+ */
+function backOff(request, options) {
+ if (options === void 0) { options = {}; }
+ return __awaiter(this, void 0, void 0, function () {
+ var sanitizedOptions, backOff;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ sanitizedOptions = options_1.getSanitizedOptions(options);
+ backOff = new BackOff(request, sanitizedOptions);
+ return [4 /*yield*/, backOff.execute()];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+}
+exports.backOff = backOff;
+var BackOff = /** @class */ (function () {
+ function BackOff(request, options) {
+ this.request = request;
+ this.options = options;
+ this.attemptNumber = 0;
+ }
+ BackOff.prototype.execute = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ var e_1, shouldRetry;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!!this.attemptLimitReached) return [3 /*break*/, 7];
+ _a.label = 1;
+ case 1:
+ _a.trys.push([1, 4, , 6]);
+ return [4 /*yield*/, this.applyDelay()];
+ case 2:
+ _a.sent();
+ return [4 /*yield*/, this.request()];
+ case 3: return [2 /*return*/, _a.sent()];
+ case 4:
+ e_1 = _a.sent();
+ this.attemptNumber++;
+ return [4 /*yield*/, this.options.retry(e_1, this.attemptNumber)];
+ case 5:
+ shouldRetry = _a.sent();
+ if (!shouldRetry || this.attemptLimitReached) {
+ throw e_1;
+ }
+ return [3 /*break*/, 6];
+ case 6: return [3 /*break*/, 0];
+ case 7: throw new Error("Something went wrong.");
+ }
+ });
+ });
+ };
+ Object.defineProperty(BackOff.prototype, "attemptLimitReached", {
+ get: function () {
+ return this.attemptNumber >= this.options.numOfAttempts;
+ },
+ enumerable: true,
+ configurable: true
+ });
+ BackOff.prototype.applyDelay = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ var delay;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ delay = delay_factory_1.DelayFactory(this.options, this.attemptNumber);
+ return [4 /*yield*/, delay.apply()];
+ case 1:
+ _a.sent();
+ return [2 /*return*/];
+ }
+ });
+ });
+ };
+ return BackOff;
+}());
+//# sourceMappingURL=backoff.js.map \ No newline at end of file
diff --git a/node_modules/exponential-backoff/dist/delay/always/always.delay.d.ts b/node_modules/exponential-backoff/dist/delay/always/always.delay.d.ts
new file mode 100644
index 0000000..641d0bc
--- /dev/null
+++ b/node_modules/exponential-backoff/dist/delay/always/always.delay.d.ts
@@ -0,0 +1,3 @@
+import { Delay } from "../delay.base";
+export declare class AlwaysDelay extends Delay {
+}
diff --git a/node_modules/exponential-backoff/dist/delay/always/always.delay.js b/node_modules/exponential-backoff/dist/delay/always/always.delay.js
new file mode 100644
index 0000000..40e3407
--- /dev/null
+++ b/node_modules/exponential-backoff/dist/delay/always/always.delay.js
@@ -0,0 +1,25 @@
+"use strict";
+var __extends = (this && this.__extends) || (function () {
+ var extendStatics = function (d, b) {
+ extendStatics = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+ return extendStatics(d, b);
+ };
+ return function (d, b) {
+ extendStatics(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ };
+})();
+Object.defineProperty(exports, "__esModule", { value: true });
+var delay_base_1 = require("../delay.base");
+var AlwaysDelay = /** @class */ (function (_super) {
+ __extends(AlwaysDelay, _super);
+ function AlwaysDelay() {
+ return _super !== null && _super.apply(this, arguments) || this;
+ }
+ return AlwaysDelay;
+}(delay_base_1.Delay));
+exports.AlwaysDelay = AlwaysDelay;
+//# sourceMappingURL=always.delay.js.map \ No newline at end of file
diff --git a/node_modules/exponential-backoff/dist/delay/delay.base.d.ts b/node_modules/exponential-backoff/dist/delay/delay.base.d.ts
new file mode 100644
index 0000000..3cdd15d
--- /dev/null
+++ b/node_modules/exponential-backoff/dist/delay/delay.base.d.ts
@@ -0,0 +1,12 @@
+import { IDelay } from "./delay.interface";
+import { IBackOffOptions } from "../options";
+export declare abstract class Delay implements IDelay {
+ private options;
+ protected attempt: number;
+ constructor(options: IBackOffOptions);
+ apply(): Promise<unknown>;
+ setAttemptNumber(attempt: number): void;
+ private readonly jitteredDelay;
+ private readonly delay;
+ protected readonly numOfDelayedAttempts: number;
+}
diff --git a/node_modules/exponential-backoff/dist/delay/delay.base.js b/node_modules/exponential-backoff/dist/delay/delay.base.js
new file mode 100644
index 0000000..b146c2f
--- /dev/null
+++ b/node_modules/exponential-backoff/dist/delay/delay.base.js
@@ -0,0 +1,45 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+var jitter_factory_1 = require("../jitter/jitter.factory");
+var Delay = /** @class */ (function () {
+ function Delay(options) {
+ this.options = options;
+ this.attempt = 0;
+ }
+ Delay.prototype.apply = function () {
+ var _this = this;
+ return new Promise(function (resolve) { return setTimeout(resolve, _this.jitteredDelay); });
+ };
+ Delay.prototype.setAttemptNumber = function (attempt) {
+ this.attempt = attempt;
+ };
+ Object.defineProperty(Delay.prototype, "jitteredDelay", {
+ get: function () {
+ var jitter = jitter_factory_1.JitterFactory(this.options);
+ return jitter(this.delay);
+ },
+ enumerable: true,
+ configurable: true
+ });
+ Object.defineProperty(Delay.prototype, "delay", {
+ get: function () {
+ var constant = this.options.startingDelay;
+ var base = this.options.timeMultiple;
+ var power = this.numOfDelayedAttempts;
+ var delay = constant * Math.pow(base, power);
+ return Math.min(delay, this.options.maxDelay);
+ },
+ enumerable: true,
+ configurable: true
+ });
+ Object.defineProperty(Delay.prototype, "numOfDelayedAttempts", {
+ get: function () {
+ return this.attempt;
+ },
+ enumerable: true,
+ configurable: true
+ });
+ return Delay;
+}());
+exports.Delay = Delay;
+//# sourceMappingURL=delay.base.js.map \ No newline at end of file
diff --git a/node_modules/exponential-backoff/dist/delay/delay.factory.d.ts b/node_modules/exponential-backoff/dist/delay/delay.factory.d.ts
new file mode 100644
index 0000000..618ca4e
--- /dev/null
+++ b/node_modules/exponential-backoff/dist/delay/delay.factory.d.ts
@@ -0,0 +1,3 @@
+import { IBackOffOptions } from "../options";
+import { IDelay } from "./delay.interface";
+export declare function DelayFactory(options: IBackOffOptions, attempt: number): IDelay;
diff --git a/node_modules/exponential-backoff/dist/delay/delay.factory.js b/node_modules/exponential-backoff/dist/delay/delay.factory.js
new file mode 100644
index 0000000..33008db
--- /dev/null
+++ b/node_modules/exponential-backoff/dist/delay/delay.factory.js
@@ -0,0 +1,17 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+var skip_first_delay_1 = require("./skip-first/skip-first.delay");
+var always_delay_1 = require("./always/always.delay");
+function DelayFactory(options, attempt) {
+ var delay = initDelayClass(options);
+ delay.setAttemptNumber(attempt);
+ return delay;
+}
+exports.DelayFactory = DelayFactory;
+function initDelayClass(options) {
+ if (!options.delayFirstAttempt) {
+ return new skip_first_delay_1.SkipFirstDelay(options);
+ }
+ return new always_delay_1.AlwaysDelay(options);
+}
+//# sourceMappingURL=delay.factory.js.map \ No newline at end of file
diff --git a/node_modules/exponential-backoff/dist/delay/delay.interface.d.ts b/node_modules/exponential-backoff/dist/delay/delay.interface.d.ts
new file mode 100644
index 0000000..6f2a10b
--- /dev/null
+++ b/node_modules/exponential-backoff/dist/delay/delay.interface.d.ts
@@ -0,0 +1,4 @@
+export interface IDelay {
+ apply: () => Promise<unknown>;
+ setAttemptNumber: (attempt: number) => void;
+}
diff --git a/node_modules/exponential-backoff/dist/delay/delay.interface.js b/node_modules/exponential-backoff/dist/delay/delay.interface.js
new file mode 100644
index 0000000..6fe2a5a
--- /dev/null
+++ b/node_modules/exponential-backoff/dist/delay/delay.interface.js
@@ -0,0 +1,3 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+//# sourceMappingURL=delay.interface.js.map \ No newline at end of file
diff --git a/node_modules/exponential-backoff/dist/delay/skip-first/skip-first.delay.d.ts b/node_modules/exponential-backoff/dist/delay/skip-first/skip-first.delay.d.ts
new file mode 100644
index 0000000..d4d7caf
--- /dev/null
+++ b/node_modules/exponential-backoff/dist/delay/skip-first/skip-first.delay.d.ts
@@ -0,0 +1,6 @@
+import { Delay } from "../delay.base";
+export declare class SkipFirstDelay extends Delay {
+ apply(): Promise<unknown>;
+ private readonly isFirstAttempt;
+ protected readonly numOfDelayedAttempts: number;
+}
diff --git a/node_modules/exponential-backoff/dist/delay/skip-first/skip-first.delay.js b/node_modules/exponential-backoff/dist/delay/skip-first/skip-first.delay.js
new file mode 100644
index 0000000..73f8841
--- /dev/null
+++ b/node_modules/exponential-backoff/dist/delay/skip-first/skip-first.delay.js
@@ -0,0 +1,82 @@
+"use strict";
+var __extends = (this && this.__extends) || (function () {
+ var extendStatics = function (d, b) {
+ extendStatics = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+ return extendStatics(d, b);
+ };
+ return function (d, b) {
+ extendStatics(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ };
+})();
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+ function verb(n) { return function (v) { return step([n, v]); }; }
+ function step(op) {
+ if (f) throw new TypeError("Generator is already executing.");
+ while (_) try {
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+ if (y = 0, t) op = [op[0] & 2, t.value];
+ switch (op[0]) {
+ case 0: case 1: t = op; break;
+ case 4: _.label++; return { value: op[1], done: false };
+ case 5: _.label++; y = op[1]; op = [0]; continue;
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
+ default:
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+ if (t[2]) _.ops.pop();
+ _.trys.pop(); continue;
+ }
+ op = body.call(thisArg, _);
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+ }
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+var delay_base_1 = require("../delay.base");
+var SkipFirstDelay = /** @class */ (function (_super) {
+ __extends(SkipFirstDelay, _super);
+ function SkipFirstDelay() {
+ return _super !== null && _super.apply(this, arguments) || this;
+ }
+ SkipFirstDelay.prototype.apply = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.isFirstAttempt ? true : _super.prototype.apply.call(this)];
+ });
+ });
+ };
+ Object.defineProperty(SkipFirstDelay.prototype, "isFirstAttempt", {
+ get: function () {
+ return this.attempt === 0;
+ },
+ enumerable: true,
+ configurable: true
+ });
+ Object.defineProperty(SkipFirstDelay.prototype, "numOfDelayedAttempts", {
+ get: function () {
+ return this.attempt - 1;
+ },
+ enumerable: true,
+ configurable: true
+ });
+ return SkipFirstDelay;
+}(delay_base_1.Delay));
+exports.SkipFirstDelay = SkipFirstDelay;
+//# sourceMappingURL=skip-first.delay.js.map \ No newline at end of file
diff --git a/node_modules/exponential-backoff/dist/jitter/full/full.jitter.d.ts b/node_modules/exponential-backoff/dist/jitter/full/full.jitter.d.ts
new file mode 100644
index 0000000..d034464
--- /dev/null
+++ b/node_modules/exponential-backoff/dist/jitter/full/full.jitter.d.ts
@@ -0,0 +1 @@
+export declare function fullJitter(delay: number): number;
diff --git a/node_modules/exponential-backoff/dist/jitter/full/full.jitter.js b/node_modules/exponential-backoff/dist/jitter/full/full.jitter.js
new file mode 100644
index 0000000..16cee36
--- /dev/null
+++ b/node_modules/exponential-backoff/dist/jitter/full/full.jitter.js
@@ -0,0 +1,8 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+function fullJitter(delay) {
+ var jitteredDelay = Math.random() * delay;
+ return Math.round(jitteredDelay);
+}
+exports.fullJitter = fullJitter;
+//# sourceMappingURL=full.jitter.js.map \ No newline at end of file
diff --git a/node_modules/exponential-backoff/dist/jitter/jitter.factory.d.ts b/node_modules/exponential-backoff/dist/jitter/jitter.factory.d.ts
new file mode 100644
index 0000000..f84bdea
--- /dev/null
+++ b/node_modules/exponential-backoff/dist/jitter/jitter.factory.d.ts
@@ -0,0 +1,3 @@
+import { IBackOffOptions } from "../options";
+export declare type Jitter = (delay: number) => number;
+export declare function JitterFactory(options: IBackOffOptions): Jitter;
diff --git a/node_modules/exponential-backoff/dist/jitter/jitter.factory.js b/node_modules/exponential-backoff/dist/jitter/jitter.factory.js
new file mode 100644
index 0000000..8aafe45
--- /dev/null
+++ b/node_modules/exponential-backoff/dist/jitter/jitter.factory.js
@@ -0,0 +1,15 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+var full_jitter_1 = require("./full/full.jitter");
+var no_jitter_1 = require("./no/no.jitter");
+function JitterFactory(options) {
+ switch (options.jitter) {
+ case "full":
+ return full_jitter_1.fullJitter;
+ case "none":
+ default:
+ return no_jitter_1.noJitter;
+ }
+}
+exports.JitterFactory = JitterFactory;
+//# sourceMappingURL=jitter.factory.js.map \ No newline at end of file
diff --git a/node_modules/exponential-backoff/dist/jitter/no/no.jitter.d.ts b/node_modules/exponential-backoff/dist/jitter/no/no.jitter.d.ts
new file mode 100644
index 0000000..3afaddd
--- /dev/null
+++ b/node_modules/exponential-backoff/dist/jitter/no/no.jitter.d.ts
@@ -0,0 +1 @@
+export declare function noJitter(delay: number): number;
diff --git a/node_modules/exponential-backoff/dist/jitter/no/no.jitter.js b/node_modules/exponential-backoff/dist/jitter/no/no.jitter.js
new file mode 100644
index 0000000..15a40bb
--- /dev/null
+++ b/node_modules/exponential-backoff/dist/jitter/no/no.jitter.js
@@ -0,0 +1,7 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+function noJitter(delay) {
+ return delay;
+}
+exports.noJitter = noJitter;
+//# sourceMappingURL=no.jitter.js.map \ No newline at end of file
diff --git a/node_modules/exponential-backoff/dist/options.d.ts b/node_modules/exponential-backoff/dist/options.d.ts
new file mode 100644
index 0000000..94e0bb4
--- /dev/null
+++ b/node_modules/exponential-backoff/dist/options.d.ts
@@ -0,0 +1,55 @@
+/**
+ * Type of jitter to apply to the delay.
+ * - `"none"`: no jitter is applied
+ * - `"full"`: full jitter is applied (random value between `0` and `delay`)
+ */
+export declare type JitterType = "none" | "full";
+export declare type BackoffOptions = Partial<IBackOffOptions>;
+export interface IBackOffOptions {
+ /**
+ * Decides whether the `startingDelay` should be applied before the first call.
+ * If `false`, the first call will occur without a delay.
+ * @defaultValue `false`
+ */
+ delayFirstAttempt: boolean;
+ /**
+ * Decides whether a [jitter](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/)
+ * should be applied to the delay. Possible values are `"full"` and `"none"`.
+ * @defaultValue `"none"`
+ */
+ jitter: JitterType;
+ /**
+ * The maximum delay, in milliseconds, between two consecutive attempts.
+ * @defaultValue `Infinity`
+ */
+ maxDelay: number;
+ /**
+ * The maximum number of times to attempt the function.
+ * Must be at least `1`.
+ * @defaultValue `10`
+ */
+ numOfAttempts: number;
+ /**
+ * The `retry` function can be used to run logic after every failed attempt (e.g. logging a message,
+ * assessing the last error, etc.).
+ * It is called with the last error and the upcoming attempt number.
+ * Returning `true` will retry the function as long as the `numOfAttempts` has not been exceeded.
+ * Returning `false` will end the execution.
+ * @defaultValue a function that always returns `true`.
+ * @param e The last error thrown by the function.
+ * @param attemptNumber The upcoming attempt number.
+ * @returns `true` to retry the function, `false` to end the execution
+ */
+ retry: (e: any, attemptNumber: number) => boolean | Promise<boolean>;
+ /**
+ * The delay, in milliseconds, before executing the function for the first time.
+ * @defaultValue `100`
+ */
+ startingDelay: number;
+ /**
+ * The `startingDelay` is multiplied by the `timeMultiple` to increase the delay between reattempts.
+ * @defaultValue `2`
+ */
+ timeMultiple: number;
+}
+export declare function getSanitizedOptions(options: BackoffOptions): IBackOffOptions;
diff --git a/node_modules/exponential-backoff/dist/options.js b/node_modules/exponential-backoff/dist/options.js
new file mode 100644
index 0000000..1d2ca17
--- /dev/null
+++ b/node_modules/exponential-backoff/dist/options.js
@@ -0,0 +1,31 @@
+"use strict";
+var __assign = (this && this.__assign) || function () {
+ __assign = Object.assign || function(t) {
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
+ s = arguments[i];
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
+ t[p] = s[p];
+ }
+ return t;
+ };
+ return __assign.apply(this, arguments);
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+var defaultOptions = {
+ delayFirstAttempt: false,
+ jitter: "none",
+ maxDelay: Infinity,
+ numOfAttempts: 10,
+ retry: function () { return true; },
+ startingDelay: 100,
+ timeMultiple: 2
+};
+function getSanitizedOptions(options) {
+ var sanitized = __assign(__assign({}, defaultOptions), options);
+ if (sanitized.numOfAttempts < 1) {
+ sanitized.numOfAttempts = 1;
+ }
+ return sanitized;
+}
+exports.getSanitizedOptions = getSanitizedOptions;
+//# sourceMappingURL=options.js.map \ No newline at end of file
diff --git a/node_modules/exponential-backoff/package.json b/node_modules/exponential-backoff/package.json
new file mode 100644
index 0000000..e3e8dc9
--- /dev/null
+++ b/node_modules/exponential-backoff/package.json
@@ -0,0 +1,62 @@
+{
+ "name": "exponential-backoff",
+ "version": "3.1.3",
+ "description": "A utility that allows retrying a function with an exponential delay between attempts.",
+ "files": [
+ "dist/",
+ "src/"
+ ],
+ "main": "dist/backoff.js",
+ "types": "dist/backoff.d.ts",
+ "scripts": {
+ "build": "tsc",
+ "test": "jest",
+ "test:watch": "jest --watch"
+ },
+ "husky": {
+ "hooks": {
+ "pre-commit": "lint-staged"
+ }
+ },
+ "lint-staged": {
+ "*.{ts,json,md}": [
+ "prettier --write",
+ "git add"
+ ]
+ },
+ "jest": {
+ "transform": {
+ "^.+\\.ts$": "ts-jest"
+ },
+ "testRegex": "\\.spec\\.ts$",
+ "moduleFileExtensions": [
+ "ts",
+ "js"
+ ]
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/coveooss/exponential-backoff.git"
+ },
+ "keywords": [
+ "exponential",
+ "backoff",
+ "retry"
+ ],
+ "author": "Sami Sayegh",
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/coveooss/exponential-backoff/issues"
+ },
+ "homepage": "https://github.com/coveooss/exponential-backoff#readme",
+ "devDependencies": {
+ "@types/jest": "^24.0.18",
+ "@types/node": "^10.14.21",
+ "husky": "^3.0.9",
+ "jest": "^24.9.0",
+ "lint-staged": "^9.4.2",
+ "prettier": "^1.18.2",
+ "ts-jest": "^24.1.0",
+ "typescript": "^3.6.4"
+ }
+}
diff --git a/node_modules/exponential-backoff/src/backoff.spec.ts b/node_modules/exponential-backoff/src/backoff.spec.ts
new file mode 100644
index 0000000..24fd95b
--- /dev/null
+++ b/node_modules/exponential-backoff/src/backoff.spec.ts
@@ -0,0 +1,194 @@
+import { backOff } from "./backoff";
+import { BackoffOptions } from "./options";
+
+describe("BackOff", () => {
+ const mockSuccessResponse = { success: true };
+ const mockFailResponse = { success: false };
+ let backOffRequest: () => Promise<any>;
+ let backOffOptions: BackoffOptions;
+
+ function initBackOff() {
+ return backOff(backOffRequest, backOffOptions);
+ }
+
+ function promiseThatIsResolved() {
+ return () => Promise.resolve(mockSuccessResponse);
+ }
+
+ function promiseThatIsRejected() {
+ return () => Promise.reject(mockFailResponse);
+ }
+
+ function promiseThatFailsOnceThenSucceeds() {
+ return (() => {
+ let firstAttempt = true;
+
+ const request = () => {
+ if (firstAttempt) {
+ firstAttempt = false;
+ return Promise.reject(mockFailResponse);
+ }
+
+ return Promise.resolve(mockSuccessResponse);
+ };
+
+ return request;
+ })();
+ }
+
+ beforeEach(() => {
+ backOffOptions = { startingDelay: 0 };
+ backOffRequest = jest.fn(promiseThatIsResolved());
+ });
+
+ describe("when request function is a promise that resolves", () => {
+ it("returns the resolved value", () => {
+ const request = initBackOff();
+ return request.then(response =>
+ expect(response).toBe(mockSuccessResponse)
+ );
+ });
+
+ it("calls the request function only once", () => {
+ const request = initBackOff();
+ return request.then(() =>
+ expect(backOffRequest).toHaveBeenCalledTimes(1)
+ );
+ });
+
+ it(`when the #backOffOptions.numOfAttempts is 0,
+ it overrides the value and calls the method only once`, () => {
+ backOffOptions.numOfAttempts = 0;
+ const request = initBackOff();
+
+ return request.then(() =>
+ expect(backOffRequest).toHaveBeenCalledTimes(1)
+ );
+ });
+ });
+
+ describe(`when the #backOffOptions.startingDelay is 100ms`, () => {
+ const startingDelay = 100;
+
+ beforeEach(() => (backOffOptions.startingDelay = startingDelay));
+
+ it(`does not delay the first attempt`, () => {
+ const startTime = Date.now();
+ const request = initBackOff();
+
+ return request.then(() => {
+ const endTime = Date.now();
+ const duration = endTime - startTime;
+ const roundedDuration = Math.round(duration / 100) * 100;
+
+ expect(roundedDuration).toBe(0);
+ });
+ });
+
+ it(`when #backOffOptions.delayFirstAttempt is 'true',
+ it delays the first attempt`, () => {
+ backOffOptions.delayFirstAttempt = true;
+ const startTime = Date.now();
+ const request = initBackOff();
+
+ return request.then(() => {
+ const endTime = Date.now();
+ const duration = endTime - startTime;
+ const roundedDuration = Math.round(duration / 100) * 100;
+
+ expect(roundedDuration).toBe(startingDelay);
+ });
+ });
+ });
+
+ describe("when request function is a promise that is rejected", () => {
+ beforeEach(() => (backOffRequest = promiseThatIsRejected()));
+
+ it("returns the rejected value", () => {
+ const request = initBackOff();
+ return request.catch(err => expect(err).toBe(mockFailResponse));
+ });
+
+ it("retries the request as many times as specified in #BackOffOptions.numOfAttempts", async () => {
+ const numOfAttemps = 2;
+ backOffOptions.numOfAttempts = numOfAttemps;
+ backOffRequest = jest.fn(() => Promise.reject(mockFailResponse));
+
+ try {
+ await initBackOff();
+ } catch {
+ expect(backOffRequest).toHaveBeenCalledTimes(numOfAttemps);
+ }
+ });
+
+ it(`when the #BackOffOptions.retry function is set to always return false,
+ it only calls request function one time`, async () => {
+ backOffOptions.retry = () => false;
+ backOffOptions.numOfAttempts = 2;
+ backOffRequest = jest.fn(() => Promise.reject(mockFailResponse));
+
+ try {
+ await initBackOff();
+ } catch {
+ expect(backOffRequest).toHaveBeenCalledTimes(1);
+ }
+ });
+ });
+
+ it("when the #BackOffOptions.retry function returns a promise, it awaits it", async () => {
+ const retryDuration = 100;
+ backOffOptions.retry = () =>
+ new Promise(resolve => setTimeout(() => resolve(true), retryDuration));
+ backOffRequest = promiseThatFailsOnceThenSucceeds();
+
+ const start = Date.now();
+ await initBackOff();
+ const end = Date.now();
+
+ const duration = end - start;
+ const roundedDuration =
+ Math.round(duration / retryDuration) * retryDuration;
+
+ expect(roundedDuration).toBe(retryDuration);
+ });
+
+ describe(`when calling #backOff with a function that throws an error the first time, and succeeds the second time`, () => {
+ beforeEach(
+ () => (backOffRequest = jest.fn(promiseThatFailsOnceThenSucceeds()))
+ );
+
+ it(`returns a successful response`, () => {
+ const request = initBackOff();
+ return request.then(response =>
+ expect(response).toBe(mockSuccessResponse)
+ );
+ });
+
+ it("calls the request function two times", async () => {
+ await initBackOff();
+ expect(backOffRequest).toHaveBeenCalledTimes(2);
+ });
+
+ it(`when setting the #BackOffOption.timeMultiple to a value,
+ when setting the #BackOffOption.delayFirstAttempt to true,
+ it applies a delay between the first and the second call`, async () => {
+ const startingDelay = 100;
+ const timeMultiple = 3;
+ const totalExpectedDelay = startingDelay + timeMultiple * startingDelay;
+
+ backOffOptions.startingDelay = startingDelay;
+ backOffOptions.timeMultiple = timeMultiple;
+ backOffOptions.delayFirstAttempt = true;
+
+ const start = Date.now();
+ await initBackOff();
+ const end = Date.now();
+
+ const duration = end - start;
+ const roundedDuration =
+ Math.round(duration / startingDelay) * startingDelay;
+
+ expect(roundedDuration).toBe(totalExpectedDelay);
+ });
+ });
+});
diff --git a/node_modules/exponential-backoff/src/backoff.ts b/node_modules/exponential-backoff/src/backoff.ts
new file mode 100644
index 0000000..549770b
--- /dev/null
+++ b/node_modules/exponential-backoff/src/backoff.ts
@@ -0,0 +1,60 @@
+import {
+ IBackOffOptions,
+ getSanitizedOptions,
+ BackoffOptions
+} from "./options";
+import { DelayFactory } from "./delay/delay.factory";
+
+export { BackoffOptions, IBackOffOptions };
+
+/**
+ * Executes a function with exponential backoff.
+ * @param request the function to be executed
+ * @param options options to customize the backoff behavior
+ * @returns Promise that resolves to the result of the `request` function
+ */
+export async function backOff<T>(
+ request: () => Promise<T>,
+ options: BackoffOptions = {}
+): Promise<T> {
+ const sanitizedOptions = getSanitizedOptions(options);
+ const backOff = new BackOff(request, sanitizedOptions);
+
+ return await backOff.execute();
+}
+
+class BackOff<T> {
+ private attemptNumber = 0;
+
+ constructor(
+ private request: () => Promise<T>,
+ private options: IBackOffOptions
+ ) {}
+
+ public async execute(): Promise<T> {
+ while (!this.attemptLimitReached) {
+ try {
+ await this.applyDelay();
+ return await this.request();
+ } catch (e) {
+ this.attemptNumber++;
+ const shouldRetry = await this.options.retry(e, this.attemptNumber);
+
+ if (!shouldRetry || this.attemptLimitReached) {
+ throw e;
+ }
+ }
+ }
+
+ throw new Error("Something went wrong.");
+ }
+
+ private get attemptLimitReached() {
+ return this.attemptNumber >= this.options.numOfAttempts;
+ }
+
+ private async applyDelay() {
+ const delay = DelayFactory(this.options, this.attemptNumber);
+ await delay.apply();
+ }
+}
diff --git a/node_modules/exponential-backoff/src/delay/always/always.delay.spec.ts b/node_modules/exponential-backoff/src/delay/always/always.delay.spec.ts
new file mode 100644
index 0000000..8a08cd8
--- /dev/null
+++ b/node_modules/exponential-backoff/src/delay/always/always.delay.spec.ts
@@ -0,0 +1,65 @@
+import { AlwaysDelay } from "./always.delay";
+import { IBackOffOptions, getSanitizedOptions } from "../../options";
+
+describe(AlwaysDelay.name, () => {
+ let options: IBackOffOptions;
+ let delay: AlwaysDelay;
+
+ function initClass() {
+ delay = new AlwaysDelay(options);
+ }
+
+ beforeEach(() => {
+ options = getSanitizedOptions({});
+ initClass();
+ jest.useFakeTimers();
+ });
+
+ it(`when calling #apply, the delay is equal to the starting delay`, async () => {
+ const spy = jest.fn();
+ delay.apply().then(spy);
+ jest.runTimersToTime(options.startingDelay);
+ await Promise.resolve();
+
+ expect(spy).toHaveBeenCalledTimes(1);
+ });
+
+ it(`when the attempt number is 1, when calling #apply,
+ the delay is equal to the starting delay multiplied by the time multiple`, async () => {
+ delay.setAttemptNumber(1);
+
+ const spy = jest.fn();
+ delay.apply().then(spy);
+ jest.runTimersToTime(options.startingDelay * options.timeMultiple);
+ await Promise.resolve();
+
+ expect(spy).toHaveBeenCalledTimes(1);
+ });
+
+ it(`when the attempt number is 2, when calling #apply,
+ the delay is equal to the starting delay multiplied by the time multiple raised by the attempt number`, async () => {
+ const attemptNumber = 2;
+ delay.setAttemptNumber(attemptNumber);
+
+ const spy = jest.fn();
+ delay.apply().then(spy);
+ jest.runTimersToTime(
+ options.startingDelay * Math.pow(options.timeMultiple, attemptNumber)
+ );
+ await Promise.resolve();
+
+ expect(spy).toHaveBeenCalledTimes(1);
+ });
+
+ it(`when the #maxDelay is less than #startingDelay, when calling #apply,
+ the delay is equal to the #maxDelay`, async () => {
+ options.maxDelay = options.startingDelay - 1;
+
+ const spy = jest.fn();
+ delay.apply().then(spy);
+ jest.runTimersToTime(options.maxDelay);
+ await Promise.resolve();
+
+ expect(spy).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/node_modules/exponential-backoff/src/delay/always/always.delay.ts b/node_modules/exponential-backoff/src/delay/always/always.delay.ts
new file mode 100644
index 0000000..ec86c62
--- /dev/null
+++ b/node_modules/exponential-backoff/src/delay/always/always.delay.ts
@@ -0,0 +1,3 @@
+import { Delay } from "../delay.base";
+
+export class AlwaysDelay extends Delay {} \ No newline at end of file
diff --git a/node_modules/exponential-backoff/src/delay/delay.base.ts b/node_modules/exponential-backoff/src/delay/delay.base.ts
new file mode 100644
index 0000000..bed90d6
--- /dev/null
+++ b/node_modules/exponential-backoff/src/delay/delay.base.ts
@@ -0,0 +1,34 @@
+import { IDelay } from "./delay.interface";
+import { IBackOffOptions } from "../options";
+import { JitterFactory } from "../jitter/jitter.factory";
+
+export abstract class Delay implements IDelay {
+ protected attempt = 0;
+ constructor(private options: IBackOffOptions) {}
+
+ public apply() {
+ return new Promise(resolve => setTimeout(resolve, this.jitteredDelay));
+ }
+
+ public setAttemptNumber(attempt: number) {
+ this.attempt = attempt;
+ }
+
+ private get jitteredDelay() {
+ const jitter = JitterFactory(this.options);
+ return jitter(this.delay);
+ }
+
+ private get delay() {
+ const constant = this.options.startingDelay;
+ const base = this.options.timeMultiple;
+ const power = this.numOfDelayedAttempts;
+ const delay = constant * Math.pow(base, power);
+
+ return Math.min(delay, this.options.maxDelay);
+ }
+
+ protected get numOfDelayedAttempts() {
+ return this.attempt;
+ }
+}
diff --git a/node_modules/exponential-backoff/src/delay/delay.factory.ts b/node_modules/exponential-backoff/src/delay/delay.factory.ts
new file mode 100644
index 0000000..95e89fb
--- /dev/null
+++ b/node_modules/exponential-backoff/src/delay/delay.factory.ts
@@ -0,0 +1,18 @@
+import { IBackOffOptions } from "../options";
+import { SkipFirstDelay } from "./skip-first/skip-first.delay";
+import { AlwaysDelay } from "./always/always.delay";
+import { IDelay } from "./delay.interface";
+
+export function DelayFactory(options: IBackOffOptions, attempt: number): IDelay {
+ const delay = initDelayClass(options);
+ delay.setAttemptNumber(attempt);
+ return delay;
+}
+
+function initDelayClass(options: IBackOffOptions) {
+ if (!options.delayFirstAttempt) {
+ return new SkipFirstDelay(options);
+ }
+
+ return new AlwaysDelay(options);
+} \ No newline at end of file
diff --git a/node_modules/exponential-backoff/src/delay/delay.interface.ts b/node_modules/exponential-backoff/src/delay/delay.interface.ts
new file mode 100644
index 0000000..6ecc8da
--- /dev/null
+++ b/node_modules/exponential-backoff/src/delay/delay.interface.ts
@@ -0,0 +1,4 @@
+export interface IDelay {
+ apply: () => Promise<unknown>;
+ setAttemptNumber: (attempt: number) => void;
+}
diff --git a/node_modules/exponential-backoff/src/delay/skip-first/skip-first.delay.ts b/node_modules/exponential-backoff/src/delay/skip-first/skip-first.delay.ts
new file mode 100644
index 0000000..9f2b7f5
--- /dev/null
+++ b/node_modules/exponential-backoff/src/delay/skip-first/skip-first.delay.ts
@@ -0,0 +1,15 @@
+import { Delay } from "../delay.base";
+
+export class SkipFirstDelay extends Delay {
+ public async apply() {
+ return this.isFirstAttempt ? true : super.apply();
+ }
+
+ private get isFirstAttempt() {
+ return this.attempt === 0;
+ }
+
+ protected get numOfDelayedAttempts() {
+ return this.attempt - 1;
+ }
+} \ No newline at end of file
diff --git a/node_modules/exponential-backoff/src/jitter/full/full.jitter.spec.ts b/node_modules/exponential-backoff/src/jitter/full/full.jitter.spec.ts
new file mode 100644
index 0000000..003d08d
--- /dev/null
+++ b/node_modules/exponential-backoff/src/jitter/full/full.jitter.spec.ts
@@ -0,0 +1,31 @@
+import { fullJitter } from './full.jitter';
+
+describe(`Testing ${fullJitter.name}`, () => {
+ const delay = 100;
+
+ function arrayWith5FullJitterDelays() {
+ return Array(5).fill(delay).map(fullJitter)
+ }
+
+ describe(`when calling #fullJitter on the same delay multiple times`, () => {
+ it('all the delays are greater than or equal to 0', () => {
+ arrayWith5FullJitterDelays()
+ .forEach(value => expect(value).toBeGreaterThanOrEqual(0));
+ })
+
+ it('all the delays are less than or equal to the original delay', () => {
+ arrayWith5FullJitterDelays()
+ .forEach(value => expect(value).toBeLessThanOrEqual(delay));
+ })
+
+ it('the delays are not equal to one another', () => {
+ const delays = arrayWith5FullJitterDelays();
+ expect(new Set(delays).size).not.toBe(1);
+ })
+
+ it('the delays are integers', () => {
+ arrayWith5FullJitterDelays()
+ .forEach(value => expect(Number.isInteger(value)).toBe(true))
+ })
+ })
+}) \ No newline at end of file
diff --git a/node_modules/exponential-backoff/src/jitter/full/full.jitter.ts b/node_modules/exponential-backoff/src/jitter/full/full.jitter.ts
new file mode 100644
index 0000000..b3c447b
--- /dev/null
+++ b/node_modules/exponential-backoff/src/jitter/full/full.jitter.ts
@@ -0,0 +1,4 @@
+export function fullJitter(delay: number) {
+ const jitteredDelay = Math.random() * delay;
+ return Math.round(jitteredDelay);
+} \ No newline at end of file
diff --git a/node_modules/exponential-backoff/src/jitter/jitter.factory.ts b/node_modules/exponential-backoff/src/jitter/jitter.factory.ts
new file mode 100644
index 0000000..b549887
--- /dev/null
+++ b/node_modules/exponential-backoff/src/jitter/jitter.factory.ts
@@ -0,0 +1,16 @@
+import { IBackOffOptions } from "../options";
+import { fullJitter } from "./full/full.jitter";
+import { noJitter } from "./no/no.jitter";
+
+export type Jitter = (delay: number) => number;
+
+export function JitterFactory(options: IBackOffOptions): Jitter {
+ switch (options.jitter) {
+ case "full":
+ return fullJitter;
+
+ case "none":
+ default:
+ return noJitter;
+ }
+}
diff --git a/node_modules/exponential-backoff/src/jitter/no/no.jitter.spec.ts b/node_modules/exponential-backoff/src/jitter/no/no.jitter.spec.ts
new file mode 100644
index 0000000..1d56524
--- /dev/null
+++ b/node_modules/exponential-backoff/src/jitter/no/no.jitter.spec.ts
@@ -0,0 +1,9 @@
+import { noJitter } from './no.jitter';
+
+describe(`Testing ${noJitter.name}`, () => {
+ it(`when calling #noJitter with a delay,
+ it returns the same delay`, () => {
+ const delay = 100;
+ expect(noJitter(delay)).toBe(delay);
+ })
+}) \ No newline at end of file
diff --git a/node_modules/exponential-backoff/src/jitter/no/no.jitter.ts b/node_modules/exponential-backoff/src/jitter/no/no.jitter.ts
new file mode 100644
index 0000000..bc1b56f
--- /dev/null
+++ b/node_modules/exponential-backoff/src/jitter/no/no.jitter.ts
@@ -0,0 +1,3 @@
+export function noJitter(delay: number) {
+ return delay;
+} \ No newline at end of file
diff --git a/node_modules/exponential-backoff/src/options.ts b/node_modules/exponential-backoff/src/options.ts
new file mode 100644
index 0000000..ccdee3a
--- /dev/null
+++ b/node_modules/exponential-backoff/src/options.ts
@@ -0,0 +1,76 @@
+/**
+ * Type of jitter to apply to the delay.
+ * - `"none"`: no jitter is applied
+ * - `"full"`: full jitter is applied (random value between `0` and `delay`)
+ */
+export type JitterType = "none" | "full";
+
+export type BackoffOptions = Partial<IBackOffOptions>;
+
+export interface IBackOffOptions {
+ /**
+ * Decides whether the `startingDelay` should be applied before the first call.
+ * If `false`, the first call will occur without a delay.
+ * @defaultValue `false`
+ */
+ delayFirstAttempt: boolean;
+ /**
+ * Decides whether a [jitter](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/)
+ * should be applied to the delay. Possible values are `"full"` and `"none"`.
+ * @defaultValue `"none"`
+ */
+ jitter: JitterType;
+ /**
+ * The maximum delay, in milliseconds, between two consecutive attempts.
+ * @defaultValue `Infinity`
+ */
+ maxDelay: number;
+ /**
+ * The maximum number of times to attempt the function.
+ * Must be at least `1`.
+ * @defaultValue `10`
+ */
+ numOfAttempts: number;
+ /**
+ * The `retry` function can be used to run logic after every failed attempt (e.g. logging a message,
+ * assessing the last error, etc.).
+ * It is called with the last error and the upcoming attempt number.
+ * Returning `true` will retry the function as long as the `numOfAttempts` has not been exceeded.
+ * Returning `false` will end the execution.
+ * @defaultValue a function that always returns `true`.
+ * @param e The last error thrown by the function.
+ * @param attemptNumber The upcoming attempt number.
+ * @returns `true` to retry the function, `false` to end the execution
+ */
+ retry: (e: any, attemptNumber: number) => boolean | Promise<boolean>;
+ /**
+ * The delay, in milliseconds, before executing the function for the first time.
+ * @defaultValue `100`
+ */
+ startingDelay: number;
+ /**
+ * The `startingDelay` is multiplied by the `timeMultiple` to increase the delay between reattempts.
+ * @defaultValue `2`
+ */
+ timeMultiple: number;
+}
+
+const defaultOptions: IBackOffOptions = {
+ delayFirstAttempt: false,
+ jitter: "none",
+ maxDelay: Infinity,
+ numOfAttempts: 10,
+ retry: () => true,
+ startingDelay: 100,
+ timeMultiple: 2
+};
+
+export function getSanitizedOptions(options: BackoffOptions) {
+ const sanitized: IBackOffOptions = { ...defaultOptions, ...options };
+
+ if (sanitized.numOfAttempts < 1) {
+ sanitized.numOfAttempts = 1;
+ }
+
+ return sanitized;
+}