@oscarpalmer/atoms 0.152.1 → 0.154.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/dist/atoms.full.js +77 -1
- package/dist/function/retry.js +79 -0
- package/dist/index.js +2 -1
- package/package.json +10 -2
- package/src/function/assert.ts +67 -9
- package/src/function/retry.ts +162 -0
- package/src/index.ts +1 -0
- package/src/math.ts +4 -4
- package/types/function/retry.d.ts +35 -0
- package/types/index.d.ts +1 -0
package/dist/atoms.full.js
CHANGED
|
@@ -937,6 +937,82 @@ function debounce(callback, time) {
|
|
|
937
937
|
function throttle(callback, time) {
|
|
938
938
|
return getTimer(TIMER_THROTTLE, callback, time);
|
|
939
939
|
}
|
|
940
|
+
var RetryError = class extends Error {
|
|
941
|
+
constructor(message, original) {
|
|
942
|
+
super(message);
|
|
943
|
+
this.original = original;
|
|
944
|
+
this.name = ERROR_NAME$1;
|
|
945
|
+
}
|
|
946
|
+
};
|
|
947
|
+
/**
|
|
948
|
+
* Retry a callback a specified number of times, with a delay between attempts
|
|
949
|
+
* @param callback Callback to retry
|
|
950
|
+
* @param options Retry options
|
|
951
|
+
* @returns Callback result
|
|
952
|
+
*/
|
|
953
|
+
async function asyncRetry(callback, options) {
|
|
954
|
+
if (typeof callback !== "function") throw new TypeError(MESSAGE_EXPECTATION);
|
|
955
|
+
async function handle() {
|
|
956
|
+
try {
|
|
957
|
+
const result = await callback();
|
|
958
|
+
resolver(result);
|
|
959
|
+
} catch (error) {
|
|
960
|
+
if (attempts >= times || !when(error)) rejector(new RetryError(MESSAGE_FAILED, error));
|
|
961
|
+
else {
|
|
962
|
+
attempts += 1;
|
|
963
|
+
timer();
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
const { delay, times, when } = getRetryOptions(options);
|
|
968
|
+
const timer = getTimer(TIMER_WAIT, handle, delay);
|
|
969
|
+
let attempts = 0;
|
|
970
|
+
let rejector;
|
|
971
|
+
let resolver;
|
|
972
|
+
return new Promise((resolve, reject) => {
|
|
973
|
+
rejector = reject;
|
|
974
|
+
resolver = resolve;
|
|
975
|
+
handle();
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
function getRetryNumber(value) {
|
|
979
|
+
return typeof value === "number" && value > 0 ? value : 0;
|
|
980
|
+
}
|
|
981
|
+
function getRetryOptions(input) {
|
|
982
|
+
const options = isPlainObject(input) ? input : {};
|
|
983
|
+
return {
|
|
984
|
+
delay: getRetryNumber(options.delay),
|
|
985
|
+
times: getRetryNumber(options.times),
|
|
986
|
+
when: typeof options.when === "function" ? options.when : shouldRetry
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
990
|
+
* Retry a callback a specified number of times
|
|
991
|
+
* @param callback Callback to retry
|
|
992
|
+
* @param options Retry options
|
|
993
|
+
* @returns Callback result
|
|
994
|
+
*/
|
|
995
|
+
function retry(callback, options) {
|
|
996
|
+
if (typeof callback !== "function") throw new TypeError(MESSAGE_EXPECTATION);
|
|
997
|
+
const { times, when } = getRetryOptions(options);
|
|
998
|
+
let last;
|
|
999
|
+
for (let index = 0; index <= times; index += 1) try {
|
|
1000
|
+
return callback();
|
|
1001
|
+
} catch (error) {
|
|
1002
|
+
if (index >= times || !when(error)) {
|
|
1003
|
+
last = error;
|
|
1004
|
+
break;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
throw new RetryError(MESSAGE_FAILED, last);
|
|
1008
|
+
}
|
|
1009
|
+
retry.async = asyncRetry;
|
|
1010
|
+
function shouldRetry() {
|
|
1011
|
+
return true;
|
|
1012
|
+
}
|
|
1013
|
+
const ERROR_NAME$1 = "RetryError";
|
|
1014
|
+
const MESSAGE_EXPECTATION = "Retry expected a function";
|
|
1015
|
+
const MESSAGE_FAILED = "Retry failed";
|
|
940
1016
|
function _isResult(value, okValue) {
|
|
941
1017
|
if (!isPlainObject(value)) return false;
|
|
942
1018
|
return value.ok === okValue && (okValue ? "value" : "error") in value;
|
|
@@ -3696,4 +3772,4 @@ var SizedSet = class extends Set {
|
|
|
3696
3772
|
}
|
|
3697
3773
|
}
|
|
3698
3774
|
};
|
|
3699
|
-
export { CancelablePromise, PromiseTimeoutError, QueueError, SizedMap, SizedSet, attempt, attemptPromise, average, beacon, between, camelCase, cancelable, capitalize, ceil, chunk, clamp, clone, compact, compare, count, debounce, delay, diff, endsWith, equal, error, exists, filter, find, flatten, floor, flow, toResult as fromPromise, toResult, fromQuery, toPromise as fromResult, toPromise, getArray, getColor, getForegroundColor, getHexColor, getHexaColor, getHslColor, getHslaColor, getNormalizedHex, getNumber, getRandomBoolean, getRandomCharacters, getRandomColor, getRandomFloat, getRandomHex, getRandomInteger, getRandomItem, getRandomItems, getRgbColor, getRgbaColor, getString, getUuid, getValue, groupBy, hexToHsl, hexToHsla, hexToRgb, hexToRgba, hslToHex, hslToRgb, hslToRgba, ignoreKey, includes, indexOf, insert, isArrayOrPlainObject, isColor, isConstructor, isEmpty, isError, isFulfilled, isHexColor, isHslColor, isHslLike, isHslaColor, isInstanceOf, isKey, isNonNullable, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumber, isNumerical, isObject, isOk, isPlainObject, isPrimitive, isRejected, isResult, isRgbColor, isRgbLike, isRgbaColor, isTypedArray, join, kebabCase, logger, lowerCase, max, median, memoize, merge, min, noop, ok, omit, parse, partition, pascalCase, pick, pipe, promises, push, queue, range, rgbToHex, rgbToHsl, rgbToHsla, round, select, setValue, shuffle, smush, snakeCase, sort, splice, startsWith, sum, template, throttle, timed, times, titleCase, toMap, toQuery, toRecord, toSet, toggle, trim, truncate, tryDecode, tryEncode, unique, unsmush, unwrap, update, upperCase, words };
|
|
3775
|
+
export { CancelablePromise, PromiseTimeoutError, QueueError, RetryError, SizedMap, SizedSet, attempt, attemptPromise, average, beacon, between, camelCase, cancelable, capitalize, ceil, chunk, clamp, clone, compact, compare, count, debounce, delay, diff, endsWith, equal, error, exists, filter, find, flatten, floor, flow, toResult as fromPromise, toResult, fromQuery, toPromise as fromResult, toPromise, getArray, getColor, getForegroundColor, getHexColor, getHexaColor, getHslColor, getHslaColor, getNormalizedHex, getNumber, getRandomBoolean, getRandomCharacters, getRandomColor, getRandomFloat, getRandomHex, getRandomInteger, getRandomItem, getRandomItems, getRgbColor, getRgbaColor, getString, getUuid, getValue, groupBy, hexToHsl, hexToHsla, hexToRgb, hexToRgba, hslToHex, hslToRgb, hslToRgba, ignoreKey, includes, indexOf, insert, isArrayOrPlainObject, isColor, isConstructor, isEmpty, isError, isFulfilled, isHexColor, isHslColor, isHslLike, isHslaColor, isInstanceOf, isKey, isNonNullable, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumber, isNumerical, isObject, isOk, isPlainObject, isPrimitive, isRejected, isResult, isRgbColor, isRgbLike, isRgbaColor, isTypedArray, join, kebabCase, logger, lowerCase, max, median, memoize, merge, min, noop, ok, omit, parse, partition, pascalCase, pick, pipe, promises, push, queue, range, retry, rgbToHex, rgbToHsl, rgbToHsla, round, select, setValue, shuffle, smush, snakeCase, sort, splice, startsWith, sum, template, throttle, timed, times, titleCase, toMap, toQuery, toRecord, toSet, toggle, trim, truncate, tryDecode, tryEncode, unique, unsmush, unwrap, update, upperCase, words };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { isPlainObject } from "../internal/is.js";
|
|
2
|
+
import { TIMER_WAIT, getTimer } from "../internal/function/timer.js";
|
|
3
|
+
var RetryError = class extends Error {
|
|
4
|
+
constructor(message, original) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.original = original;
|
|
7
|
+
this.name = ERROR_NAME;
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Retry a callback a specified number of times, with a delay between attempts
|
|
12
|
+
* @param callback Callback to retry
|
|
13
|
+
* @param options Retry options
|
|
14
|
+
* @returns Callback result
|
|
15
|
+
*/
|
|
16
|
+
async function asyncRetry(callback, options) {
|
|
17
|
+
if (typeof callback !== "function") throw new TypeError(MESSAGE_EXPECTATION);
|
|
18
|
+
async function handle() {
|
|
19
|
+
try {
|
|
20
|
+
const result = await callback();
|
|
21
|
+
resolver(result);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
if (attempts >= times || !when(error)) rejector(new RetryError(MESSAGE_FAILED, error));
|
|
24
|
+
else {
|
|
25
|
+
attempts += 1;
|
|
26
|
+
timer();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const { delay, times, when } = getRetryOptions(options);
|
|
31
|
+
const timer = getTimer(TIMER_WAIT, handle, delay);
|
|
32
|
+
let attempts = 0;
|
|
33
|
+
let rejector;
|
|
34
|
+
let resolver;
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
rejector = reject;
|
|
37
|
+
resolver = resolve;
|
|
38
|
+
handle();
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function getRetryNumber(value) {
|
|
42
|
+
return typeof value === "number" && value > 0 ? value : 0;
|
|
43
|
+
}
|
|
44
|
+
function getRetryOptions(input) {
|
|
45
|
+
const options = isPlainObject(input) ? input : {};
|
|
46
|
+
return {
|
|
47
|
+
delay: getRetryNumber(options.delay),
|
|
48
|
+
times: getRetryNumber(options.times),
|
|
49
|
+
when: typeof options.when === "function" ? options.when : shouldRetry
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Retry a callback a specified number of times
|
|
54
|
+
* @param callback Callback to retry
|
|
55
|
+
* @param options Retry options
|
|
56
|
+
* @returns Callback result
|
|
57
|
+
*/
|
|
58
|
+
function retry(callback, options) {
|
|
59
|
+
if (typeof callback !== "function") throw new TypeError(MESSAGE_EXPECTATION);
|
|
60
|
+
const { times, when } = getRetryOptions(options);
|
|
61
|
+
let last;
|
|
62
|
+
for (let index = 0; index <= times; index += 1) try {
|
|
63
|
+
return callback();
|
|
64
|
+
} catch (error) {
|
|
65
|
+
if (index >= times || !when(error)) {
|
|
66
|
+
last = error;
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
throw new RetryError(MESSAGE_FAILED, last);
|
|
71
|
+
}
|
|
72
|
+
retry.async = asyncRetry;
|
|
73
|
+
function shouldRetry() {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
var ERROR_NAME = "RetryError";
|
|
77
|
+
var MESSAGE_EXPECTATION = "Retry expected a function";
|
|
78
|
+
var MESSAGE_FAILED = "Retry failed";
|
|
79
|
+
export { RetryError, retry };
|
package/dist/index.js
CHANGED
|
@@ -39,6 +39,7 @@ import { getColor } from "./color/index.js";
|
|
|
39
39
|
import { SizedMap } from "./sized/map.js";
|
|
40
40
|
import { memoize } from "./function/memoize.js";
|
|
41
41
|
import { debounce, throttle } from "./function/index.js";
|
|
42
|
+
import { RetryError, retry } from "./function/retry.js";
|
|
42
43
|
import { isError, isOk, isResult } from "./internal/result.js";
|
|
43
44
|
import { flow, pipe } from "./function/work.js";
|
|
44
45
|
import { equal } from "./internal/value/equal.js";
|
|
@@ -70,4 +71,4 @@ import { QueueError, queue } from "./queue.js";
|
|
|
70
71
|
import { getRandomBoolean, getRandomCharacters, getRandomColor, getRandomHex, getRandomItem, getRandomItems } from "./random.js";
|
|
71
72
|
import { attempt } from "./result/index.js";
|
|
72
73
|
import { SizedSet } from "./sized/set.js";
|
|
73
|
-
export { CancelablePromise, PromiseTimeoutError, QueueError, SizedMap, SizedSet, attempt, attemptPromise, average, beacon, between, camelCase, cancelable, capitalize, ceil, chunk, clamp, clone, compact, compare, count, debounce, delay, diff, endsWith, equal, error, exists, filter, find, flatten, floor, flow, toResult as fromPromise, fromQuery, toPromise as fromResult, getArray, getColor, getForegroundColor, getHexColor, getHexaColor, getHslColor, getHslaColor, getNormalizedHex, getNumber, getRandomBoolean, getRandomCharacters, getRandomColor, getRandomFloat, getRandomHex, getRandomInteger, getRandomItem, getRandomItems, getRgbColor, getRgbaColor, getString, getUuid, getValue, groupBy, hexToHsl, hexToHsla, hexToRgb, hexToRgba, hslToHex, hslToRgb, hslToRgba, ignoreKey, includes, indexOf, insert, isArrayOrPlainObject, isColor, isConstructor, isEmpty, isError, isFulfilled, isHexColor, isHslColor, isHslLike, isHslaColor, isInstanceOf, isKey, isNonNullable, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumber, isNumerical, isObject, isOk, isPlainObject, isPrimitive, isRejected, isResult, isRgbColor, isRgbLike, isRgbaColor, isTypedArray, join, kebabCase, logger, lowerCase, max, median, memoize, merge, min, noop, ok, omit, parse, partition, pascalCase, pick, pipe, promises, push, queue, range, rgbToHex, rgbToHsl, rgbToHsla, round, select, setValue, shuffle, smush, snakeCase, sort, splice, startsWith, sum, template, throttle, timed, times, titleCase, toMap, toPromise, toQuery, toRecord, toResult, toSet, toggle, trim, truncate, tryDecode, tryEncode, unique, unsmush, unwrap, update, upperCase, words };
|
|
74
|
+
export { CancelablePromise, PromiseTimeoutError, QueueError, RetryError, SizedMap, SizedSet, attempt, attemptPromise, average, beacon, between, camelCase, cancelable, capitalize, ceil, chunk, clamp, clone, compact, compare, count, debounce, delay, diff, endsWith, equal, error, exists, filter, find, flatten, floor, flow, toResult as fromPromise, fromQuery, toPromise as fromResult, getArray, getColor, getForegroundColor, getHexColor, getHexaColor, getHslColor, getHslaColor, getNormalizedHex, getNumber, getRandomBoolean, getRandomCharacters, getRandomColor, getRandomFloat, getRandomHex, getRandomInteger, getRandomItem, getRandomItems, getRgbColor, getRgbaColor, getString, getUuid, getValue, groupBy, hexToHsl, hexToHsla, hexToRgb, hexToRgba, hslToHex, hslToRgb, hslToRgba, ignoreKey, includes, indexOf, insert, isArrayOrPlainObject, isColor, isConstructor, isEmpty, isError, isFulfilled, isHexColor, isHslColor, isHslLike, isHslaColor, isInstanceOf, isKey, isNonNullable, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumber, isNumerical, isObject, isOk, isPlainObject, isPrimitive, isRejected, isResult, isRgbColor, isRgbLike, isRgbaColor, isTypedArray, join, kebabCase, logger, lowerCase, max, median, memoize, merge, min, noop, ok, omit, parse, partition, pascalCase, pick, pipe, promises, push, queue, range, retry, rgbToHex, rgbToHsl, rgbToHsla, round, select, setValue, shuffle, smush, snakeCase, sort, splice, startsWith, sum, template, throttle, timed, times, titleCase, toMap, toPromise, toQuery, toRecord, toResult, toSet, toggle, trim, truncate, tryDecode, tryEncode, unique, unsmush, unwrap, update, upperCase, words };
|
package/package.json
CHANGED
|
@@ -54,6 +54,14 @@
|
|
|
54
54
|
"types": "./types/function/index.d.ts",
|
|
55
55
|
"default": "./dist/function/index.js"
|
|
56
56
|
},
|
|
57
|
+
"./function/assert": {
|
|
58
|
+
"types": "./types/function/assert.d.ts",
|
|
59
|
+
"default": "./dist/function/assert.js"
|
|
60
|
+
},
|
|
61
|
+
"./function/retry": {
|
|
62
|
+
"types": "./types/function/retry.d.ts",
|
|
63
|
+
"default": "./dist/function/retry.js"
|
|
64
|
+
},
|
|
57
65
|
"./function/work": {
|
|
58
66
|
"types": "./types/function/work.d.ts",
|
|
59
67
|
"default": "./dist/function/work.js"
|
|
@@ -176,5 +184,5 @@
|
|
|
176
184
|
},
|
|
177
185
|
"type": "module",
|
|
178
186
|
"types": "./types/index.d.ts",
|
|
179
|
-
"version": "0.
|
|
180
|
-
}
|
|
187
|
+
"version": "0.154.0"
|
|
188
|
+
}
|
package/src/function/assert.ts
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import type {Constructor} from '../models';
|
|
2
2
|
|
|
3
|
+
// #region Types
|
|
4
|
+
|
|
3
5
|
export type Asserter<Value> = (value: unknown) => asserts value is Value;
|
|
4
6
|
|
|
7
|
+
// #endregion
|
|
8
|
+
|
|
9
|
+
// #region Functions
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Asserts that a condition is true, throwing an error if it is not
|
|
13
|
+
* @param condition Condition to assert
|
|
14
|
+
* @param message Error message
|
|
15
|
+
* @param error Error constructor
|
|
16
|
+
*/
|
|
5
17
|
export function assert<Condition extends () => boolean>(
|
|
6
18
|
condition: Condition,
|
|
7
19
|
message: string,
|
|
@@ -12,32 +24,78 @@ export function assert<Condition extends () => boolean>(
|
|
|
12
24
|
}
|
|
13
25
|
}
|
|
14
26
|
|
|
15
|
-
assert.condition =
|
|
27
|
+
assert.condition = assertCondition;
|
|
28
|
+
assert.defined = assertDefined;
|
|
29
|
+
assert.instanceOf = assertInstanceOf;
|
|
30
|
+
assert.is = assertIs;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Creates an asserter that asserts a condition is true, throwing an error if it is not
|
|
34
|
+
* @param condition Condition to assert
|
|
35
|
+
* @param message Error message
|
|
36
|
+
* @param error Error constructor
|
|
37
|
+
* @returns Asserter
|
|
38
|
+
*/
|
|
39
|
+
function assertCondition<Value>(
|
|
16
40
|
condition: (value: unknown) => boolean,
|
|
17
41
|
message: string,
|
|
18
42
|
error?: ErrorConstructor,
|
|
19
|
-
): Asserter<Value>
|
|
43
|
+
): Asserter<Value> {
|
|
20
44
|
return value => {
|
|
21
45
|
assert(() => condition(value), message, error);
|
|
22
46
|
};
|
|
23
|
-
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Asserts that a value is defined _(not `null` or `undefined`)_, throwing an error if it is not
|
|
51
|
+
* @param value Value to assert
|
|
52
|
+
* @param message Error message
|
|
53
|
+
*/
|
|
54
|
+
function assertDefined<Value>(
|
|
55
|
+
value: unknown,
|
|
56
|
+
message?: string,
|
|
57
|
+
): asserts value is Exclude<Value, null | undefined> {
|
|
58
|
+
assert(() => value != null, message ?? MESSAGE_DEFINED);
|
|
59
|
+
}
|
|
24
60
|
|
|
25
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Creates an asserter that asserts a value is an instance of a constructor, throwing an error if it is not
|
|
63
|
+
* @param constructor Constructor to check against
|
|
64
|
+
* @param message Error message
|
|
65
|
+
* @param error Error constructor
|
|
66
|
+
* @returns Asserter
|
|
67
|
+
*/
|
|
68
|
+
function assertInstanceOf<Value>(
|
|
26
69
|
constructor: Constructor<Value>,
|
|
27
70
|
message: string,
|
|
28
71
|
error?: ErrorConstructor,
|
|
29
|
-
): Asserter<Value>
|
|
72
|
+
): Asserter<Value> {
|
|
30
73
|
return value => {
|
|
31
74
|
assert(() => value instanceof constructor, message, error);
|
|
32
75
|
};
|
|
33
|
-
}
|
|
76
|
+
}
|
|
34
77
|
|
|
35
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Creates an asserter that asserts a value is of a specific type, throwing an error if it is not
|
|
80
|
+
* @param condition Type guard function to check the value
|
|
81
|
+
* @param message Error message
|
|
82
|
+
* @param error Error constructor
|
|
83
|
+
* @returns Asserter
|
|
84
|
+
*/
|
|
85
|
+
function assertIs<Value>(
|
|
36
86
|
condition: (value: unknown) => value is Value,
|
|
37
87
|
message: string,
|
|
38
88
|
error?: ErrorConstructor,
|
|
39
|
-
): Asserter<Value>
|
|
89
|
+
): Asserter<Value> {
|
|
40
90
|
return value => {
|
|
41
91
|
assert(() => condition(value), message, error);
|
|
42
92
|
};
|
|
43
|
-
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// #endregion
|
|
96
|
+
|
|
97
|
+
// #region Variables
|
|
98
|
+
|
|
99
|
+
const MESSAGE_DEFINED = 'Expected value to be defined';
|
|
100
|
+
|
|
101
|
+
// #endregion
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import {getTimer, TIMER_WAIT} from '../internal/function/timer';
|
|
2
|
+
import {isPlainObject} from '../internal/is';
|
|
3
|
+
import type {GenericAsyncCallback, GenericCallback} from '../models';
|
|
4
|
+
|
|
5
|
+
// #region Types
|
|
6
|
+
|
|
7
|
+
export class RetryError extends Error {
|
|
8
|
+
constructor(
|
|
9
|
+
message: string,
|
|
10
|
+
readonly original: unknown,
|
|
11
|
+
) {
|
|
12
|
+
super(message);
|
|
13
|
+
|
|
14
|
+
this.name = ERROR_NAME;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type RetryOptions = {
|
|
19
|
+
delay?: number;
|
|
20
|
+
times?: number;
|
|
21
|
+
when?: (error: unknown) => boolean;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// #endregion
|
|
25
|
+
|
|
26
|
+
// #region Functions
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Retry a callback a specified number of times, with a delay between attempts
|
|
30
|
+
* @param callback Callback to retry
|
|
31
|
+
* @param options Retry options
|
|
32
|
+
* @returns Callback result
|
|
33
|
+
*/
|
|
34
|
+
async function asyncRetry<Callback extends GenericAsyncCallback>(
|
|
35
|
+
callback: Callback,
|
|
36
|
+
options?: RetryOptions,
|
|
37
|
+
): Promise<Awaited<ReturnType<Callback>>>;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Retry a callback a specified number of times, with a delay between attempts
|
|
41
|
+
* @param callback Callback to retry
|
|
42
|
+
* @param options Retry options
|
|
43
|
+
* @returns Callback result
|
|
44
|
+
*/
|
|
45
|
+
async function asyncRetry<Callback extends GenericCallback>(
|
|
46
|
+
callback: Callback,
|
|
47
|
+
options?: RetryOptions,
|
|
48
|
+
): Promise<ReturnType<Callback>>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Retry a callback a specified number of times, with a delay between attempts
|
|
52
|
+
* @param callback Callback to retry
|
|
53
|
+
* @param options Retry options
|
|
54
|
+
* @returns Callback result
|
|
55
|
+
*/
|
|
56
|
+
async function asyncRetry<Callback extends GenericCallback>(
|
|
57
|
+
callback: Callback,
|
|
58
|
+
options?: RetryOptions,
|
|
59
|
+
): Promise<ReturnType<Callback>> {
|
|
60
|
+
if (typeof callback !== 'function') {
|
|
61
|
+
throw new TypeError(MESSAGE_EXPECTATION);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function handle(): Promise<void> {
|
|
65
|
+
try {
|
|
66
|
+
const result = await callback();
|
|
67
|
+
|
|
68
|
+
resolver(result);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
if (attempts >= times || !when(error)) {
|
|
71
|
+
rejector(new RetryError(MESSAGE_FAILED, error));
|
|
72
|
+
} else {
|
|
73
|
+
attempts += 1;
|
|
74
|
+
|
|
75
|
+
timer();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const {delay, times, when} = getRetryOptions(options);
|
|
81
|
+
|
|
82
|
+
const timer = getTimer(TIMER_WAIT, handle, delay);
|
|
83
|
+
|
|
84
|
+
let attempts = 0;
|
|
85
|
+
|
|
86
|
+
let rejector: (reason?: unknown) => void;
|
|
87
|
+
let resolver: (value: Awaited<ReturnType<Callback>>) => void;
|
|
88
|
+
|
|
89
|
+
return new Promise<Awaited<ReturnType<Callback>>>((resolve, reject) => {
|
|
90
|
+
rejector = reject;
|
|
91
|
+
resolver = resolve;
|
|
92
|
+
|
|
93
|
+
handle();
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function getRetryNumber(value?: unknown): number {
|
|
98
|
+
return typeof value === 'number' && value > 0 ? value : 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function getRetryOptions(input?: RetryOptions): Required<RetryOptions> {
|
|
102
|
+
const options = isPlainObject(input) ? input : {};
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
delay: getRetryNumber(options.delay),
|
|
106
|
+
times: getRetryNumber(options.times),
|
|
107
|
+
when: typeof options.when === 'function' ? options.when : shouldRetry,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Retry a callback a specified number of times
|
|
113
|
+
* @param callback Callback to retry
|
|
114
|
+
* @param options Retry options
|
|
115
|
+
* @returns Callback result
|
|
116
|
+
*/
|
|
117
|
+
export function retry<Callback extends GenericCallback>(
|
|
118
|
+
callback: Callback,
|
|
119
|
+
options?: Omit<RetryOptions, 'delay'>,
|
|
120
|
+
): ReturnType<Callback> {
|
|
121
|
+
if (typeof callback !== 'function') {
|
|
122
|
+
throw new TypeError(MESSAGE_EXPECTATION);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const {times, when} = getRetryOptions(options);
|
|
126
|
+
|
|
127
|
+
let last: unknown;
|
|
128
|
+
|
|
129
|
+
for (let index = 0; index <= times; index += 1) {
|
|
130
|
+
try {
|
|
131
|
+
const result = callback();
|
|
132
|
+
|
|
133
|
+
return result;
|
|
134
|
+
} catch (error) {
|
|
135
|
+
if (index >= times || !when(error)) {
|
|
136
|
+
last = error;
|
|
137
|
+
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
throw new RetryError(MESSAGE_FAILED, last);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
retry.async = asyncRetry;
|
|
147
|
+
|
|
148
|
+
function shouldRetry(): boolean {
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// #endregion
|
|
153
|
+
|
|
154
|
+
// #region Variables
|
|
155
|
+
|
|
156
|
+
const ERROR_NAME = 'RetryError';
|
|
157
|
+
|
|
158
|
+
const MESSAGE_EXPECTATION = 'Retry expected a function';
|
|
159
|
+
|
|
160
|
+
const MESSAGE_FAILED = 'Retry failed';
|
|
161
|
+
|
|
162
|
+
// #endregion
|
package/src/index.ts
CHANGED
package/src/math.ts
CHANGED
|
@@ -154,10 +154,6 @@ export function median(array: unknown[], key?: unknown): number {
|
|
|
154
154
|
return Number.NaN;
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
if (length === 1) {
|
|
158
|
-
return isNumber(array[0]) ? array[0] : Number.NaN;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
157
|
let values: unknown[] = array;
|
|
162
158
|
|
|
163
159
|
const callback = getAggregateCallback(key);
|
|
@@ -170,6 +166,10 @@ export function median(array: unknown[], key?: unknown): number {
|
|
|
170
166
|
|
|
171
167
|
length = numbers.length;
|
|
172
168
|
|
|
169
|
+
if (length === 1) {
|
|
170
|
+
return numbers[0];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
173
|
if (length % 2 === 0) {
|
|
174
174
|
const first = length / 2 - 1;
|
|
175
175
|
const second = length / 2;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { GenericAsyncCallback, GenericCallback } from '../models';
|
|
2
|
+
export declare class RetryError extends Error {
|
|
3
|
+
readonly original: unknown;
|
|
4
|
+
constructor(message: string, original: unknown);
|
|
5
|
+
}
|
|
6
|
+
export type RetryOptions = {
|
|
7
|
+
delay?: number;
|
|
8
|
+
times?: number;
|
|
9
|
+
when?: (error: unknown) => boolean;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Retry a callback a specified number of times, with a delay between attempts
|
|
13
|
+
* @param callback Callback to retry
|
|
14
|
+
* @param options Retry options
|
|
15
|
+
* @returns Callback result
|
|
16
|
+
*/
|
|
17
|
+
declare function asyncRetry<Callback extends GenericAsyncCallback>(callback: Callback, options?: RetryOptions): Promise<Awaited<ReturnType<Callback>>>;
|
|
18
|
+
/**
|
|
19
|
+
* Retry a callback a specified number of times, with a delay between attempts
|
|
20
|
+
* @param callback Callback to retry
|
|
21
|
+
* @param options Retry options
|
|
22
|
+
* @returns Callback result
|
|
23
|
+
*/
|
|
24
|
+
declare function asyncRetry<Callback extends GenericCallback>(callback: Callback, options?: RetryOptions): Promise<ReturnType<Callback>>;
|
|
25
|
+
/**
|
|
26
|
+
* Retry a callback a specified number of times
|
|
27
|
+
* @param callback Callback to retry
|
|
28
|
+
* @param options Retry options
|
|
29
|
+
* @returns Callback result
|
|
30
|
+
*/
|
|
31
|
+
export declare function retry<Callback extends GenericCallback>(callback: Callback, options?: Omit<RetryOptions, 'delay'>): ReturnType<Callback>;
|
|
32
|
+
export declare namespace retry {
|
|
33
|
+
var async: typeof asyncRetry;
|
|
34
|
+
}
|
|
35
|
+
export {};
|
package/types/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * from './array/to-map';
|
|
|
4
4
|
export * from './array/to-record';
|
|
5
5
|
export * from './array/unique';
|
|
6
6
|
export * from './function/index';
|
|
7
|
+
export * from './function/retry';
|
|
7
8
|
export * from './function/work';
|
|
8
9
|
export * from './internal/function/misc';
|
|
9
10
|
export * from './internal/string';
|