@bybrave/retry2 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2011:
2
+ Tim Koschützki (tim@debuggable.com)
3
+ Felix Geisendörfer (felix@debuggable.com)
4
+ Copyright (c) 2026 bybrave (maintained fork @bybrave/retry2)
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # @bybrave/retry2
2
+
3
+ Maintained, drop-in fork of [`retry`](https://github.com/tim-kos/node-retry) — an abstraction for exponential-backoff retries of failing operations.
4
+
5
+ The original has had no release since 2021 (`0.13.1`) while still pulling ~450M downloads/month, with types living in a separate `@types/retry` (~133M/month). This fork **bundles the TypeScript types**, fixes an infinite-retries hang, and ships ESM. The API is unchanged.
6
+
7
+ ```sh
8
+ npm install @bybrave/retry2
9
+ ```
10
+
11
+ ```js
12
+ const retry = require('@bybrave/retry2'); // CommonJS
13
+ import retry from '@bybrave/retry2'; // ESM (default)
14
+ import { operation, timeouts } from '@bybrave/retry2'; // ESM (named)
15
+ ```
16
+
17
+ ## What's fixed
18
+
19
+ | Issue | Problem | Fix |
20
+ |---|---|---|
21
+ | — | Types shipped separately as `@types/retry`. | **Types are bundled** — no extra install. |
22
+ | [#83](https://github.com/tim-kos/node-retry/issues/83) | Under TypeScript 4.4+, `import { timeouts } from 'retry'` compiled to an indirect call and broke because `timeouts()` relied on `this`. | `timeouts()` calls `createTimeout` directly — no `this`, so named imports work everywhere. |
23
+ | [#84](https://github.com/tim-kos/node-retry/issues/84) | `retries: Infinity` built an infinite-length timeouts array — a hang / out-of-memory. | `retries: Infinity` is treated as "retry forever" without precomputing an infinite array. |
24
+ | [#73](https://github.com/tim-kos/node-retry/issues/73) | No way to know how long until the next retry (for logging like "will retry in 16s"). | New `operation.getTimeout()` returns the scheduled delay in ms. |
25
+ | [#93](https://github.com/tim-kos/node-retry/issues/93) | `undefined` option values overrode the defaults (`{ retries: undefined }` → 0 retries). | `undefined` values are ignored, so defaults stand. |
26
+ | [#65](https://github.com/tim-kos/node-retry/issues/65) | `reset()` left accumulated errors in place. | `reset()` also clears the error list. |
27
+
28
+ `#60` (forever operations leaking errors) was already fixed in `0.13.1` and is verified by the tests here.
29
+
30
+ ## `operation.getTimeout()`
31
+
32
+ Returns the delay in milliseconds before the next scheduled attempt (or `null` before the first retry), so you can log it:
33
+
34
+ ```js
35
+ const op = retry.operation({ retries: 5 });
36
+ op.attempt(() => {
37
+ doSomething((err) => {
38
+ if (op.retry(err)) {
39
+ console.warn(`request failed, retrying in ${op.getTimeout()}ms`);
40
+ return;
41
+ }
42
+ });
43
+ });
44
+ ```
45
+
46
+ ## Migration from `retry`
47
+
48
+ Replace the dependency and the import, and remove `@types/retry` from your devDependencies — the types are built in. Everything behaves the same, plus the fixes above.
49
+
50
+ ## Support
51
+
52
+ If this package saves you time, you can support maintenance:
53
+
54
+ [![Ko-fi](https://img.shields.io/badge/Ko--fi-buy%20me%20a%20coffee-FF5E5B?logo=kofi&logoColor=white)](https://ko-fi.com/bybrave)
55
+ [![Bitcoin](https://img.shields.io/badge/Bitcoin-BTC-F7931A?logo=bitcoin&logoColor=white)](#support)
56
+
57
+ Bitcoin (BTC): `bc1q37557q5jpeaxqydzwvf3jgj7zhnfpn2td3q40q`
58
+
59
+ ## Credits & license
60
+
61
+ MIT, same as the original — see [LICENSE](./LICENSE).
62
+ Based on [node-retry](https://github.com/tim-kos/node-retry) by Tim Koschützki, Felix Geisendörfer and contributors.
package/dist/index.cjs ADDED
@@ -0,0 +1,268 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // index.js
20
+ var retry2_exports = {};
21
+ __export(retry2_exports, {
22
+ createTimeout: () => createTimeout,
23
+ default: () => retry2_default,
24
+ operation: () => operation,
25
+ timeouts: () => timeouts,
26
+ wrap: () => wrap
27
+ });
28
+ module.exports = __toCommonJS(retry2_exports);
29
+
30
+ // lib/retry.js
31
+ var retry_exports = {};
32
+ __export(retry_exports, {
33
+ createTimeout: () => createTimeout,
34
+ operation: () => operation,
35
+ timeouts: () => timeouts,
36
+ wrap: () => wrap
37
+ });
38
+
39
+ // lib/retry_operation.js
40
+ function RetryOperation(timeouts2, options) {
41
+ if (typeof options === "boolean") {
42
+ options = { forever: options };
43
+ }
44
+ this._originalTimeouts = JSON.parse(JSON.stringify(timeouts2));
45
+ this._timeouts = timeouts2;
46
+ this._options = options || {};
47
+ this._maxRetryTime = options && options.maxRetryTime || Infinity;
48
+ this._fn = null;
49
+ this._errors = [];
50
+ this._attempts = 1;
51
+ this._operationTimeout = null;
52
+ this._operationTimeoutCb = null;
53
+ this._timeout = null;
54
+ this._operationStart = null;
55
+ this._timer = null;
56
+ this._lastTimeout = null;
57
+ if (this._options.forever) {
58
+ this._cachedTimeouts = this._timeouts.slice(0);
59
+ }
60
+ }
61
+ RetryOperation.prototype.reset = function() {
62
+ this._attempts = 1;
63
+ this._timeouts = this._originalTimeouts.slice(0);
64
+ this._errors = [];
65
+ };
66
+ RetryOperation.prototype.stop = function() {
67
+ if (this._timeout) {
68
+ clearTimeout(this._timeout);
69
+ }
70
+ if (this._timer) {
71
+ clearTimeout(this._timer);
72
+ }
73
+ this._timeouts = [];
74
+ this._cachedTimeouts = null;
75
+ };
76
+ RetryOperation.prototype.retry = function(err) {
77
+ if (this._timeout) {
78
+ clearTimeout(this._timeout);
79
+ }
80
+ if (!err) {
81
+ return false;
82
+ }
83
+ var currentTime = (/* @__PURE__ */ new Date()).getTime();
84
+ if (err && currentTime - this._operationStart >= this._maxRetryTime) {
85
+ this._errors.push(err);
86
+ this._errors.unshift(new Error("RetryOperation timeout occurred"));
87
+ return false;
88
+ }
89
+ this._errors.push(err);
90
+ var timeout = this._timeouts.shift();
91
+ if (timeout === void 0) {
92
+ if (this._cachedTimeouts) {
93
+ this._errors.splice(0, this._errors.length - 1);
94
+ timeout = this._cachedTimeouts.slice(-1);
95
+ } else {
96
+ return false;
97
+ }
98
+ }
99
+ this._lastTimeout = Array.isArray(timeout) ? timeout[0] : timeout;
100
+ var self = this;
101
+ this._timer = setTimeout(function() {
102
+ self._attempts++;
103
+ if (self._operationTimeoutCb) {
104
+ self._timeout = setTimeout(function() {
105
+ self._operationTimeoutCb(self._attempts);
106
+ }, self._operationTimeout);
107
+ if (self._options.unref) {
108
+ self._timeout.unref();
109
+ }
110
+ }
111
+ self._fn(self._attempts);
112
+ }, timeout);
113
+ if (this._options.unref) {
114
+ this._timer.unref();
115
+ }
116
+ return true;
117
+ };
118
+ RetryOperation.prototype.attempt = function(fn, timeoutOps) {
119
+ this._fn = fn;
120
+ if (timeoutOps) {
121
+ if (timeoutOps.timeout) {
122
+ this._operationTimeout = timeoutOps.timeout;
123
+ }
124
+ if (timeoutOps.cb) {
125
+ this._operationTimeoutCb = timeoutOps.cb;
126
+ }
127
+ }
128
+ var self = this;
129
+ if (this._operationTimeoutCb) {
130
+ this._timeout = setTimeout(function() {
131
+ self._operationTimeoutCb();
132
+ }, self._operationTimeout);
133
+ }
134
+ this._operationStart = (/* @__PURE__ */ new Date()).getTime();
135
+ this._fn(this._attempts);
136
+ };
137
+ RetryOperation.prototype.try = function(fn) {
138
+ console.log("Using RetryOperation.try() is deprecated");
139
+ this.attempt(fn);
140
+ };
141
+ RetryOperation.prototype.start = function(fn) {
142
+ console.log("Using RetryOperation.start() is deprecated");
143
+ this.attempt(fn);
144
+ };
145
+ RetryOperation.prototype.start = RetryOperation.prototype.try;
146
+ RetryOperation.prototype.errors = function() {
147
+ return this._errors;
148
+ };
149
+ RetryOperation.prototype.attempts = function() {
150
+ return this._attempts;
151
+ };
152
+ RetryOperation.prototype.getTimeout = function() {
153
+ return this._lastTimeout;
154
+ };
155
+ RetryOperation.prototype.mainError = function() {
156
+ if (this._errors.length === 0) {
157
+ return null;
158
+ }
159
+ var counts = {};
160
+ var mainError = null;
161
+ var mainErrorCount = 0;
162
+ for (var i = 0; i < this._errors.length; i++) {
163
+ var error = this._errors[i];
164
+ var message = error.message;
165
+ var count = (counts[message] || 0) + 1;
166
+ counts[message] = count;
167
+ if (count >= mainErrorCount) {
168
+ mainError = error;
169
+ mainErrorCount = count;
170
+ }
171
+ }
172
+ return mainError;
173
+ };
174
+
175
+ // lib/retry.js
176
+ function operation(options) {
177
+ var timeoutsArray = timeouts(options);
178
+ return new RetryOperation(timeoutsArray, {
179
+ forever: options && (options.forever || options.retries === Infinity),
180
+ unref: options && options.unref,
181
+ maxRetryTime: options && options.maxRetryTime
182
+ });
183
+ }
184
+ function timeouts(options) {
185
+ if (options instanceof Array) {
186
+ return [].concat(options);
187
+ }
188
+ var opts = {
189
+ retries: 10,
190
+ factor: 2,
191
+ minTimeout: 1 * 1e3,
192
+ maxTimeout: Infinity,
193
+ randomize: false
194
+ };
195
+ for (var key in options) {
196
+ if (options[key] !== void 0) {
197
+ opts[key] = options[key];
198
+ }
199
+ }
200
+ if (opts.minTimeout > opts.maxTimeout) {
201
+ throw new Error("minTimeout is greater than maxTimeout");
202
+ }
203
+ var forever = opts.forever || opts.retries === Infinity;
204
+ var count = opts.retries === Infinity ? 0 : opts.retries;
205
+ var timeoutsList = [];
206
+ for (var i = 0; i < count; i++) {
207
+ timeoutsList.push(createTimeout(i, opts));
208
+ }
209
+ if (forever && !timeoutsList.length) {
210
+ timeoutsList.push(createTimeout(count, opts));
211
+ }
212
+ timeoutsList.sort(function(a, b) {
213
+ return a - b;
214
+ });
215
+ return timeoutsList;
216
+ }
217
+ function createTimeout(attempt, opts) {
218
+ var random = opts.randomize ? Math.random() + 1 : 1;
219
+ var timeout = Math.round(random * Math.max(opts.minTimeout, 1) * Math.pow(opts.factor, attempt));
220
+ timeout = Math.min(timeout, opts.maxTimeout);
221
+ return timeout;
222
+ }
223
+ function wrap(obj, options, methods) {
224
+ if (options instanceof Array) {
225
+ methods = options;
226
+ options = null;
227
+ }
228
+ if (!methods) {
229
+ methods = [];
230
+ for (var key in obj) {
231
+ if (typeof obj[key] === "function") {
232
+ methods.push(key);
233
+ }
234
+ }
235
+ }
236
+ for (var i = 0; i < methods.length; i++) {
237
+ var method = methods[i];
238
+ var original = obj[method];
239
+ obj[method] = function retryWrapper(original2) {
240
+ var op = operation(options);
241
+ var args = Array.prototype.slice.call(arguments, 1);
242
+ var callback = args.pop();
243
+ args.push(function(err) {
244
+ if (op.retry(err)) {
245
+ return;
246
+ }
247
+ if (err) {
248
+ arguments[0] = op.mainError();
249
+ }
250
+ callback.apply(this, arguments);
251
+ });
252
+ op.attempt(function() {
253
+ original2.apply(obj, args);
254
+ });
255
+ }.bind(obj, original);
256
+ obj[method].options = options;
257
+ }
258
+ }
259
+
260
+ // index.js
261
+ var retry2_default = retry_exports;
262
+ // Annotate the CommonJS export names for ESM import in node:
263
+ 0 && (module.exports = {
264
+ createTimeout,
265
+ operation,
266
+ timeouts,
267
+ wrap
268
+ });
package/index.d.ts ADDED
@@ -0,0 +1,97 @@
1
+ // Type definitions for @bybrave/retry2 (bundled — no separate @types needed).
2
+
3
+ export interface RetryOperation {
4
+ /**
5
+ * Returns an array of all errors that have been passed to
6
+ * `retryOperation.retry()` so far, ordered chronologically.
7
+ */
8
+ errors(): Error[];
9
+
10
+ /**
11
+ * A reference to the error object that occurred most frequently. Errors are
12
+ * compared using the `error.message` property. Returns `null` if no errors
13
+ * occurred so far.
14
+ */
15
+ mainError(): Error | null;
16
+
17
+ /**
18
+ * Defines the function that is to be retried and executes it for the first
19
+ * time right away.
20
+ */
21
+ attempt(fn: (currentAttempt: number) => void, timeoutOps?: AttemptTimeoutOptions): void;
22
+
23
+ /**
24
+ * Returns `false` when no `error` is given, or the maximum amount of retries
25
+ * has been reached. Otherwise returns `true` and retries the operation after
26
+ * the timeout for the current attempt number.
27
+ */
28
+ retry(err?: Error | null): boolean;
29
+
30
+ /**
31
+ * The delay in milliseconds before the next scheduled attempt, or `null` if
32
+ * none has been scheduled yet. Useful for logging, e.g. "will retry in 16s".
33
+ */
34
+ getTimeout(): number | null;
35
+
36
+ /** Stops the operation being retried. */
37
+ stop(): void;
38
+
39
+ /**
40
+ * Resets the internal state of the operation object (attempts, remaining
41
+ * timeouts, and accumulated errors) so it can be reused.
42
+ */
43
+ reset(): void;
44
+
45
+ /** The number of attempts it took to call `fn` before it was successful. */
46
+ attempts(): number;
47
+ }
48
+
49
+ export interface AttemptTimeoutOptions {
50
+ /** A timeout in milliseconds. */
51
+ timeout?: number | undefined;
52
+ /** Callback to execute when the operation takes longer than the timeout. */
53
+ cb?(): void;
54
+ }
55
+
56
+ export interface TimeoutsOptions {
57
+ /** The maximum amount of times to retry the operation. Default 10. */
58
+ retries?: number | undefined;
59
+ /** The exponential factor to use. Default 2. */
60
+ factor?: number | undefined;
61
+ /** Milliseconds before starting the first retry. Default 1000. */
62
+ minTimeout?: number | undefined;
63
+ /** Maximum milliseconds between two retries. Default Infinity. */
64
+ maxTimeout?: number | undefined;
65
+ /** Randomizes the timeouts by multiplying a factor between 1-2. Default false. */
66
+ randomize?: boolean | undefined;
67
+ }
68
+
69
+ export interface OperationOptions extends TimeoutsOptions {
70
+ /** Whether to retry forever. Default false. */
71
+ forever?: boolean | undefined;
72
+ /** Whether to unref the setTimeout's. Default false. */
73
+ unref?: boolean | undefined;
74
+ /** The maximum time (ms) that the retried operation is allowed to run. Default Infinity. */
75
+ maxRetryTime?: number | undefined;
76
+ }
77
+
78
+ /** Create a new RetryOperation object. */
79
+ export function operation(options?: OperationOptions | number[]): RetryOperation;
80
+
81
+ /** Returns an array of timeouts, all representing the time in milliseconds. */
82
+ export function timeouts(options?: TimeoutsOptions | number[]): number[];
83
+
84
+ /** Create a new timeout (in milliseconds) for a given attempt. */
85
+ export function createTimeout(attempt: number, opts: TimeoutsOptions): number;
86
+
87
+ /** Wrap all functions of the `obj` with retry logic. */
88
+ export function wrap(obj: object, options?: OperationOptions | string[], methods?: string[]): void;
89
+
90
+ declare const retry: {
91
+ operation: typeof operation;
92
+ timeouts: typeof timeouts;
93
+ createTimeout: typeof createTimeout;
94
+ wrap: typeof wrap;
95
+ };
96
+
97
+ export default retry;
package/index.js ADDED
@@ -0,0 +1,5 @@
1
+ import * as retry from './lib/retry.js';
2
+
3
+ export {operation, timeouts, createTimeout, wrap} from './lib/retry.js';
4
+
5
+ export default retry;
package/lib/retry.js ADDED
@@ -0,0 +1,113 @@
1
+ import RetryOperation from './retry_operation.js';
2
+
3
+ export function operation(options) {
4
+ var timeoutsArray = timeouts(options);
5
+ return new RetryOperation(timeoutsArray, {
6
+ forever: options && (options.forever || options.retries === Infinity),
7
+ unref: options && options.unref,
8
+ maxRetryTime: options && options.maxRetryTime
9
+ });
10
+ }
11
+
12
+ export function timeouts(options) {
13
+ if (options instanceof Array) {
14
+ return [].concat(options);
15
+ }
16
+
17
+ var opts = {
18
+ retries: 10,
19
+ factor: 2,
20
+ minTimeout: 1 * 1000,
21
+ maxTimeout: Infinity,
22
+ randomize: false
23
+ };
24
+ for (var key in options) {
25
+ // Skip undefined values so that `{ retries: undefined }` does not clobber
26
+ // the default (see #93).
27
+ if (options[key] !== undefined) {
28
+ opts[key] = options[key];
29
+ }
30
+ }
31
+
32
+ if (opts.minTimeout > opts.maxTimeout) {
33
+ throw new Error('minTimeout is greater than maxTimeout');
34
+ }
35
+
36
+ // `retries: Infinity` means retry forever. Building an Infinity-length array
37
+ // would hang / run out of memory, so treat it as forever with no precomputed
38
+ // timeouts (see #84).
39
+ var forever = opts.forever || opts.retries === Infinity;
40
+ var count = opts.retries === Infinity ? 0 : opts.retries;
41
+
42
+ var timeoutsList = [];
43
+ for (var i = 0; i < count; i++) {
44
+ // Call createTimeout directly rather than via `this`, so named imports keep
45
+ // working under bundlers that emit indirect calls, e.g. TypeScript 4.4+
46
+ // `import { timeouts } from ...` (see #83).
47
+ timeoutsList.push(createTimeout(i, opts));
48
+ }
49
+
50
+ if (forever && !timeoutsList.length) {
51
+ timeoutsList.push(createTimeout(count, opts));
52
+ }
53
+
54
+ // sort the array numerically ascending
55
+ timeoutsList.sort(function(a, b) {
56
+ return a - b;
57
+ });
58
+
59
+ return timeoutsList;
60
+ }
61
+
62
+ export function createTimeout(attempt, opts) {
63
+ var random = (opts.randomize)
64
+ ? (Math.random() + 1)
65
+ : 1;
66
+
67
+ var timeout = Math.round(random * Math.max(opts.minTimeout, 1) * Math.pow(opts.factor, attempt));
68
+ timeout = Math.min(timeout, opts.maxTimeout);
69
+
70
+ return timeout;
71
+ }
72
+
73
+ export function wrap(obj, options, methods) {
74
+ if (options instanceof Array) {
75
+ methods = options;
76
+ options = null;
77
+ }
78
+
79
+ if (!methods) {
80
+ methods = [];
81
+ for (var key in obj) {
82
+ if (typeof obj[key] === 'function') {
83
+ methods.push(key);
84
+ }
85
+ }
86
+ }
87
+
88
+ for (var i = 0; i < methods.length; i++) {
89
+ var method = methods[i];
90
+ var original = obj[method];
91
+
92
+ obj[method] = function retryWrapper(original) {
93
+ var op = operation(options);
94
+ var args = Array.prototype.slice.call(arguments, 1);
95
+ var callback = args.pop();
96
+
97
+ args.push(function(err) {
98
+ if (op.retry(err)) {
99
+ return;
100
+ }
101
+ if (err) {
102
+ arguments[0] = op.mainError();
103
+ }
104
+ callback.apply(this, arguments);
105
+ });
106
+
107
+ op.attempt(function() {
108
+ original.apply(obj, args);
109
+ });
110
+ }.bind(obj, original);
111
+ obj[method].options = options;
112
+ }
113
+ }
@@ -0,0 +1,175 @@
1
+ export default function RetryOperation(timeouts, options) {
2
+ // Compatibility for the old (timeouts, retryForever) signature
3
+ if (typeof options === 'boolean') {
4
+ options = { forever: options };
5
+ }
6
+
7
+ this._originalTimeouts = JSON.parse(JSON.stringify(timeouts));
8
+ this._timeouts = timeouts;
9
+ this._options = options || {};
10
+ this._maxRetryTime = (options && options.maxRetryTime) || Infinity;
11
+ this._fn = null;
12
+ this._errors = [];
13
+ this._attempts = 1;
14
+ this._operationTimeout = null;
15
+ this._operationTimeoutCb = null;
16
+ this._timeout = null;
17
+ this._operationStart = null;
18
+ this._timer = null;
19
+ this._lastTimeout = null;
20
+
21
+ if (this._options.forever) {
22
+ this._cachedTimeouts = this._timeouts.slice(0);
23
+ }
24
+ }
25
+
26
+ RetryOperation.prototype.reset = function() {
27
+ this._attempts = 1;
28
+ this._timeouts = this._originalTimeouts.slice(0);
29
+ // Also clear accumulated errors so a reused operation does not keep growing
30
+ // its error list (see #65).
31
+ this._errors = [];
32
+ };
33
+
34
+ RetryOperation.prototype.stop = function() {
35
+ if (this._timeout) {
36
+ clearTimeout(this._timeout);
37
+ }
38
+ if (this._timer) {
39
+ clearTimeout(this._timer);
40
+ }
41
+
42
+ this._timeouts = [];
43
+ this._cachedTimeouts = null;
44
+ };
45
+
46
+ RetryOperation.prototype.retry = function(err) {
47
+ if (this._timeout) {
48
+ clearTimeout(this._timeout);
49
+ }
50
+
51
+ if (!err) {
52
+ return false;
53
+ }
54
+ var currentTime = new Date().getTime();
55
+ if (err && currentTime - this._operationStart >= this._maxRetryTime) {
56
+ this._errors.push(err);
57
+ this._errors.unshift(new Error('RetryOperation timeout occurred'));
58
+ return false;
59
+ }
60
+
61
+ this._errors.push(err);
62
+
63
+ var timeout = this._timeouts.shift();
64
+ if (timeout === undefined) {
65
+ if (this._cachedTimeouts) {
66
+ // retry forever, only keep last error
67
+ this._errors.splice(0, this._errors.length - 1);
68
+ timeout = this._cachedTimeouts.slice(-1);
69
+ } else {
70
+ return false;
71
+ }
72
+ }
73
+
74
+ // Expose the delay (ms) before the next attempt so callers can log it, e.g.
75
+ // "will retry in 16 seconds" (see #73). `getTimeout()` reads this.
76
+ this._lastTimeout = Array.isArray(timeout) ? timeout[0] : timeout;
77
+
78
+ var self = this;
79
+ this._timer = setTimeout(function() {
80
+ self._attempts++;
81
+
82
+ if (self._operationTimeoutCb) {
83
+ self._timeout = setTimeout(function() {
84
+ self._operationTimeoutCb(self._attempts);
85
+ }, self._operationTimeout);
86
+
87
+ if (self._options.unref) {
88
+ self._timeout.unref();
89
+ }
90
+ }
91
+
92
+ self._fn(self._attempts);
93
+ }, timeout);
94
+
95
+ if (this._options.unref) {
96
+ this._timer.unref();
97
+ }
98
+
99
+ return true;
100
+ };
101
+
102
+ RetryOperation.prototype.attempt = function(fn, timeoutOps) {
103
+ this._fn = fn;
104
+
105
+ if (timeoutOps) {
106
+ if (timeoutOps.timeout) {
107
+ this._operationTimeout = timeoutOps.timeout;
108
+ }
109
+ if (timeoutOps.cb) {
110
+ this._operationTimeoutCb = timeoutOps.cb;
111
+ }
112
+ }
113
+
114
+ var self = this;
115
+ if (this._operationTimeoutCb) {
116
+ this._timeout = setTimeout(function() {
117
+ self._operationTimeoutCb();
118
+ }, self._operationTimeout);
119
+ }
120
+
121
+ this._operationStart = new Date().getTime();
122
+
123
+ this._fn(this._attempts);
124
+ };
125
+
126
+ RetryOperation.prototype.try = function(fn) {
127
+ console.log('Using RetryOperation.try() is deprecated');
128
+ this.attempt(fn);
129
+ };
130
+
131
+ RetryOperation.prototype.start = function(fn) {
132
+ console.log('Using RetryOperation.start() is deprecated');
133
+ this.attempt(fn);
134
+ };
135
+
136
+ RetryOperation.prototype.start = RetryOperation.prototype.try;
137
+
138
+ RetryOperation.prototype.errors = function() {
139
+ return this._errors;
140
+ };
141
+
142
+ RetryOperation.prototype.attempts = function() {
143
+ return this._attempts;
144
+ };
145
+
146
+ // Delay in milliseconds before the next scheduled attempt, or null if none has
147
+ // been scheduled yet (see #73).
148
+ RetryOperation.prototype.getTimeout = function() {
149
+ return this._lastTimeout;
150
+ };
151
+
152
+ RetryOperation.prototype.mainError = function() {
153
+ if (this._errors.length === 0) {
154
+ return null;
155
+ }
156
+
157
+ var counts = {};
158
+ var mainError = null;
159
+ var mainErrorCount = 0;
160
+
161
+ for (var i = 0; i < this._errors.length; i++) {
162
+ var error = this._errors[i];
163
+ var message = error.message;
164
+ var count = (counts[message] || 0) + 1;
165
+
166
+ counts[message] = count;
167
+
168
+ if (count >= mainErrorCount) {
169
+ mainError = error;
170
+ mainErrorCount = count;
171
+ }
172
+ }
173
+
174
+ return mainError;
175
+ };
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@bybrave/retry2",
3
+ "version": "1.0.0",
4
+ "description": "Maintained fork of retry — abstraction for exponential-backoff retries, with bundled TypeScript types, an Infinity-retries fix (#84), getTimeout() (#73), and ESM",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./index.js",
8
+ "types": "./index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./index.d.ts",
12
+ "import": "./index.js",
13
+ "require": "./dist/index.cjs"
14
+ },
15
+ "./package.json": "./package.json"
16
+ },
17
+ "files": ["index.js", "index.d.ts", "lib/", "dist/"],
18
+ "scripts": {
19
+ "build": "node build/build.js",
20
+ "test": "node --test test/*.test.js",
21
+ "test-types": "tsc --noEmit --strict test/type-declarations.ts",
22
+ "prepublishOnly": "npm run build"
23
+ },
24
+ "repository": {"type": "git", "url": "git+https://github.com/bybraveHQ/retry2.git"},
25
+ "bugs": {"url": "https://github.com/bybraveHQ/retry2/issues"},
26
+ "homepage": "https://github.com/bybraveHQ/retry2#readme",
27
+ "keywords": ["retry", "backoff", "exponential", "timeout", "repeat", "forever", "esm", "typescript"],
28
+ "author": "bybrave (https://github.com/bybraveHQ)",
29
+ "contributors": ["Tim Koschützki", "Felix Geisendörfer (authors of the original retry)"],
30
+ "license": "MIT",
31
+ "funding": "https://ko-fi.com/bybrave",
32
+ "engines": {"node": ">=18"},
33
+ "devDependencies": {"@types/node": "^20.14.0", "esbuild": "^0.23.0", "typescript": "^5.5.0"}
34
+ }