@naturalcycles/js-lib 14.78.1 → 14.81.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/decorators/logMethod.decorator.js +1 -1
- package/dist/decorators/memo.decorator.js +1 -1
- package/dist/decorators/memoFn.js +1 -1
- package/dist/decorators/retry.decorator.js +1 -1
- package/dist/error/try.js +0 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -1
- package/dist/json-schema/jsonSchemaBuilder.d.ts +2 -2
- package/dist/json-schema/jsonSchemaBuilder.js +4 -4
- package/dist/json-schema/jsonSchemas.d.ts +2 -2
- package/dist/promise/pProps.d.ts +12 -3
- package/dist/promise/pProps.js +13 -17
- package/dist/promise/pRetry.d.ts +17 -2
- package/dist/promise/pRetry.js +72 -40
- package/dist/types.d.ts +10 -10
- package/dist-esm/decorators/logMethod.decorator.js +1 -1
- package/dist-esm/decorators/memo.decorator.js +1 -1
- package/dist-esm/decorators/memoFn.js +1 -1
- package/dist-esm/decorators/retry.decorator.js +2 -2
- package/dist-esm/error/try.js +0 -2
- package/dist-esm/index.js +2 -2
- package/dist-esm/json-schema/jsonSchemaBuilder.js +4 -4
- package/dist-esm/promise/pProps.js +13 -17
- package/dist-esm/promise/pRetry.js +70 -39
- package/package.json +1 -1
- package/src/decorators/logMethod.decorator.ts +1 -1
- package/src/decorators/memo.decorator.ts +1 -1
- package/src/decorators/memoFn.ts +1 -1
- package/src/decorators/retry.decorator.ts +2 -2
- package/src/error/try.ts +0 -3
- package/src/index.ts +2 -1
- package/src/json-schema/jsonSchemaBuilder.ts +4 -4
- package/src/promise/pProps.ts +16 -24
- package/src/promise/pRetry.ts +105 -41
- package/src/types.ts +10 -10
|
@@ -6,7 +6,7 @@ const __1 = require("..");
|
|
|
6
6
|
function _Retry(opt = {}) {
|
|
7
7
|
return (target, key, descriptor) => {
|
|
8
8
|
const originalFn = descriptor.value;
|
|
9
|
-
descriptor.value = (0, __1.
|
|
9
|
+
descriptor.value = (0, __1.pRetryFn)(originalFn, opt);
|
|
10
10
|
return descriptor;
|
|
11
11
|
};
|
|
12
12
|
}
|
package/dist/error/try.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ export * from './promise/pFilter';
|
|
|
41
41
|
export * from './promise/pHang';
|
|
42
42
|
import { pMap, PMapOptions } from './promise/pMap';
|
|
43
43
|
export * from './promise/pProps';
|
|
44
|
-
import { pRetry, PRetryOptions } from './promise/pRetry';
|
|
44
|
+
import { pRetry, pRetryFn, PRetryOptions } from './promise/pRetry';
|
|
45
45
|
export * from './promise/pState';
|
|
46
46
|
import { pTimeout, pTimeoutFn, PTimeoutOptions } from './promise/pTimeout';
|
|
47
47
|
export * from './promise/pTuple';
|
|
@@ -60,4 +60,4 @@ import { PQueue, PQueueCfg } from './promise/pQueue';
|
|
|
60
60
|
export * from './seq/seq';
|
|
61
61
|
export * from './math/stack.util';
|
|
62
62
|
export type { AbortableMapper, AbortablePredicate, AbortableAsyncPredicate, AbortableAsyncMapper, PQueueCfg, MemoCache, PromiseDecoratorCfg, PromiseDecoratorResp, ErrorData, ErrorObject, HttpErrorData, HttpErrorResponse, Admin401ErrorData, Admin403ErrorData, StringMap, PromiseMap, AnyObject, AnyFunction, ValuesOf, ValueOf, KeyValueTuple, ObjectMapper, ObjectPredicate, InstanceId, IsoDate, IsoDateTime, Reviver, PMapOptions, Mapper, AsyncMapper, Predicate, AsyncPredicate, BatchResult, DeferredPromise, PRetryOptions, PTimeoutOptions, TryCatchOptions, StringifyAnyOptions, JsonStringifyFunction, Merge, ReadonlyDeep, Promisable, Simplify, ConditionalPick, ConditionalExcept, Class, UnixTimestamp, BaseDBEntity, SavedDBEntity, Saved, Unsaved, CreatedUpdated, CreatedUpdatedId, ObjectWithId, AnyObjectWithId, JsonSchema, JsonSchemaAny, JsonSchemaOneOf, JsonSchemaAllOf, JsonSchemaAnyOf, JsonSchemaNot, JsonSchemaRef, JsonSchemaConst, JsonSchemaEnum, JsonSchemaString, JsonSchemaNumber, JsonSchemaBoolean, JsonSchemaNull, JsonSchemaRootObject, JsonSchemaObject, JsonSchemaArray, JsonSchemaTuple, JsonSchemaBuilder, CommonLogLevel, CommonLogWithLevelFunction, CommonLogFunction, CommonLogger, };
|
|
63
|
-
export { is, _createPromiseDecorator, _stringMapValues, _stringMapEntries, _objectKeys, pMap, _passthroughMapper, _passUndefinedMapper, _passthroughPredicate, _passNothingPredicate, _noop, ErrorMode, pDefer, AggregatedError, pRetry, pTimeout, pTimeoutFn, _tryCatch, _TryCatch, _stringifyAny, jsonSchema, JsonSchemaAnyBuilder, commonLoggerMinLevel, commonLoggerNoop, commonLogLevelNumber, commonLoggerPipe, commonLoggerPrefix, commonLoggerCreate, PQueue, END, SKIP, };
|
|
63
|
+
export { is, _createPromiseDecorator, _stringMapValues, _stringMapEntries, _objectKeys, pMap, _passthroughMapper, _passUndefinedMapper, _passthroughPredicate, _passNothingPredicate, _noop, ErrorMode, pDefer, AggregatedError, pRetry, pRetryFn, pTimeout, pTimeoutFn, _tryCatch, _TryCatch, _stringifyAny, jsonSchema, JsonSchemaAnyBuilder, commonLoggerMinLevel, commonLoggerNoop, commonLogLevelNumber, commonLoggerPipe, commonLoggerPrefix, commonLoggerCreate, PQueue, END, SKIP, };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SKIP = exports.END = exports.PQueue = exports.commonLoggerCreate = exports.commonLoggerPrefix = exports.commonLoggerPipe = exports.commonLogLevelNumber = exports.commonLoggerNoop = exports.commonLoggerMinLevel = exports.JsonSchemaAnyBuilder = exports.jsonSchema = exports._stringifyAny = exports._TryCatch = exports._tryCatch = exports.pTimeoutFn = exports.pTimeout = exports.pRetry = exports.AggregatedError = exports.pDefer = exports.ErrorMode = exports._noop = exports._passNothingPredicate = exports._passthroughPredicate = exports._passUndefinedMapper = exports._passthroughMapper = exports.pMap = exports._objectKeys = exports._stringMapEntries = exports._stringMapValues = exports._createPromiseDecorator = exports.is = void 0;
|
|
3
|
+
exports.SKIP = exports.END = exports.PQueue = exports.commonLoggerCreate = exports.commonLoggerPrefix = exports.commonLoggerPipe = exports.commonLogLevelNumber = exports.commonLoggerNoop = exports.commonLoggerMinLevel = exports.JsonSchemaAnyBuilder = exports.jsonSchema = exports._stringifyAny = exports._TryCatch = exports._tryCatch = exports.pTimeoutFn = exports.pTimeout = exports.pRetryFn = exports.pRetry = exports.AggregatedError = exports.pDefer = exports.ErrorMode = exports._noop = exports._passNothingPredicate = exports._passthroughPredicate = exports._passUndefinedMapper = exports._passthroughMapper = exports.pMap = exports._objectKeys = exports._stringMapEntries = exports._stringMapValues = exports._createPromiseDecorator = exports.is = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
(0, tslib_1.__exportStar)(require("./array/array.util"), exports);
|
|
6
6
|
(0, tslib_1.__exportStar)(require("./lazy"), exports);
|
|
@@ -53,6 +53,7 @@ Object.defineProperty(exports, "pMap", { enumerable: true, get: function () { re
|
|
|
53
53
|
(0, tslib_1.__exportStar)(require("./promise/pProps"), exports);
|
|
54
54
|
const pRetry_1 = require("./promise/pRetry");
|
|
55
55
|
Object.defineProperty(exports, "pRetry", { enumerable: true, get: function () { return pRetry_1.pRetry; } });
|
|
56
|
+
Object.defineProperty(exports, "pRetryFn", { enumerable: true, get: function () { return pRetry_1.pRetryFn; } });
|
|
56
57
|
(0, tslib_1.__exportStar)(require("./promise/pState"), exports);
|
|
57
58
|
const pTimeout_1 = require("./promise/pTimeout");
|
|
58
59
|
Object.defineProperty(exports, "pTimeout", { enumerable: true, get: function () { return pTimeout_1.pTimeout; } });
|
|
@@ -113,8 +113,8 @@ export declare class JsonSchemaObjectBuilder<T extends AnyObject> extends JsonSc
|
|
|
113
113
|
minProps(minProperties: number): this;
|
|
114
114
|
maxProps(maxProperties: number): this;
|
|
115
115
|
additionalProps(additionalProperties: boolean): this;
|
|
116
|
-
baseDBEntity(): JsonSchemaObjectBuilder<T & BaseDBEntity
|
|
117
|
-
savedDBEntity(): JsonSchemaObjectBuilder<T & SavedDBEntity
|
|
116
|
+
baseDBEntity<ID = string>(idType?: string): JsonSchemaObjectBuilder<T & BaseDBEntity<ID>>;
|
|
117
|
+
savedDBEntity<ID = string>(idType?: string): JsonSchemaObjectBuilder<T & SavedDBEntity<ID>>;
|
|
118
118
|
extend<T2 extends AnyObject>(s2: JsonSchemaObjectBuilder<T2>): JsonSchemaObjectBuilder<T & T2>;
|
|
119
119
|
}
|
|
120
120
|
export declare class JsonSchemaArrayBuilder<ITEM> extends JsonSchemaAnyBuilder<ITEM[], JsonSchemaArray<ITEM>> {
|
|
@@ -306,16 +306,16 @@ class JsonSchemaObjectBuilder extends JsonSchemaAnyBuilder {
|
|
|
306
306
|
Object.assign(this.schema, { additionalProperties });
|
|
307
307
|
return this;
|
|
308
308
|
}
|
|
309
|
-
baseDBEntity() {
|
|
309
|
+
baseDBEntity(idType = 'string') {
|
|
310
310
|
Object.assign(this.schema.properties, {
|
|
311
|
-
id: { type:
|
|
311
|
+
id: { type: idType },
|
|
312
312
|
created: { type: 'number', format: 'unixTimestamp' },
|
|
313
313
|
updated: { type: 'number', format: 'unixTimestamp' },
|
|
314
314
|
});
|
|
315
315
|
return this;
|
|
316
316
|
}
|
|
317
|
-
savedDBEntity() {
|
|
318
|
-
return this.baseDBEntity().addRequired(['id', 'created', 'updated']);
|
|
317
|
+
savedDBEntity(idType = 'string') {
|
|
318
|
+
return this.baseDBEntity(idType).addRequired(['id', 'created', 'updated']);
|
|
319
319
|
}
|
|
320
320
|
extend(s2) {
|
|
321
321
|
const builder = new JsonSchemaObjectBuilder();
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { SavedDBEntity } from '../types';
|
|
2
|
-
export declare const baseDBEntityJsonSchema: import("./jsonSchemaBuilder").JsonSchemaObjectBuilder<Partial<SavedDBEntity
|
|
3
|
-
export declare const savedDBEntityJsonSchema: import("./jsonSchemaBuilder").JsonSchemaObjectBuilder<SavedDBEntity
|
|
2
|
+
export declare const baseDBEntityJsonSchema: import("./jsonSchemaBuilder").JsonSchemaObjectBuilder<Partial<SavedDBEntity<string>>>;
|
|
3
|
+
export declare const savedDBEntityJsonSchema: import("./jsonSchemaBuilder").JsonSchemaObjectBuilder<SavedDBEntity<string>>;
|
package/dist/promise/pProps.d.ts
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
import { PMapOptions } from './pMap';
|
|
2
1
|
/**
|
|
3
2
|
* Promise.all for Object instead of Array.
|
|
4
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Inspired by Bluebird Promise.props() and https://github.com/sindresorhus/p-props
|
|
5
|
+
*
|
|
6
|
+
* Improvements:
|
|
7
|
+
*
|
|
8
|
+
* - Exported as { pProps }, so IDE auto-completion works
|
|
9
|
+
* - Simpler: no support for Map, Mapper, Options
|
|
10
|
+
* - Included Typescript typings (no need for @types/p-props)
|
|
11
|
+
*
|
|
12
|
+
* Concurrency implementation via pMap was removed in favor of preserving async
|
|
13
|
+
* stack traces (more important!).
|
|
5
14
|
*/
|
|
6
15
|
export declare function pProps<T>(input: {
|
|
7
16
|
[K in keyof T]: T[K] | Promise<T[K]>;
|
|
8
|
-
}
|
|
17
|
+
}): Promise<{
|
|
9
18
|
[K in keyof T]: Awaited<T[K]>;
|
|
10
19
|
}>;
|
package/dist/promise/pProps.js
CHANGED
|
@@ -1,26 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/*
|
|
3
|
-
Inspired by Bluebird Promise.props() and https://github.com/sindresorhus/p-props
|
|
4
|
-
|
|
5
|
-
Improvements:
|
|
6
|
-
|
|
7
|
-
- Exported as { pProps }, so IDE auto-completion works
|
|
8
|
-
- Simpler: no support for Map, Mapper, Options
|
|
9
|
-
- Included Typescript typings (no need for @types/p-props)
|
|
10
|
-
*/
|
|
11
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
3
|
exports.pProps = void 0;
|
|
13
|
-
const pMap_1 = require("./pMap");
|
|
14
|
-
// todo: remove when eslint starts to know about Awaited
|
|
15
|
-
/* eslint-disable no-undef */
|
|
16
4
|
/**
|
|
17
5
|
* Promise.all for Object instead of Array.
|
|
18
|
-
*
|
|
6
|
+
*
|
|
7
|
+
* Inspired by Bluebird Promise.props() and https://github.com/sindresorhus/p-props
|
|
8
|
+
*
|
|
9
|
+
* Improvements:
|
|
10
|
+
*
|
|
11
|
+
* - Exported as { pProps }, so IDE auto-completion works
|
|
12
|
+
* - Simpler: no support for Map, Mapper, Options
|
|
13
|
+
* - Included Typescript typings (no need for @types/p-props)
|
|
14
|
+
*
|
|
15
|
+
* Concurrency implementation via pMap was removed in favor of preserving async
|
|
16
|
+
* stack traces (more important!).
|
|
19
17
|
*/
|
|
20
|
-
async function pProps(input
|
|
21
|
-
const r = {};
|
|
18
|
+
async function pProps(input) {
|
|
22
19
|
const keys = Object.keys(input);
|
|
23
|
-
|
|
24
|
-
return r;
|
|
20
|
+
return Object.fromEntries((await Promise.all(Object.values(input))).map((v, i) => [keys[i], v]));
|
|
25
21
|
}
|
|
26
22
|
exports.pProps = pProps;
|
package/dist/promise/pRetry.d.ts
CHANGED
|
@@ -5,6 +5,12 @@ export interface PRetryOptions {
|
|
|
5
5
|
* Can be used to identify the place in the code that failed.
|
|
6
6
|
*/
|
|
7
7
|
name?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Timeout for each Try, in milliseconds.
|
|
10
|
+
*
|
|
11
|
+
* Defaults to 60_000
|
|
12
|
+
*/
|
|
13
|
+
timeout?: number;
|
|
8
14
|
/**
|
|
9
15
|
* How many attempts to try.
|
|
10
16
|
* First attempt is not a retry, but "initial try". It still counts.
|
|
@@ -29,7 +35,7 @@ export interface PRetryOptions {
|
|
|
29
35
|
*
|
|
30
36
|
* @default () => true
|
|
31
37
|
*/
|
|
32
|
-
predicate?: (err:
|
|
38
|
+
predicate?: (err: Error, attempt: number, maxAttempts: number) => boolean;
|
|
33
39
|
/**
|
|
34
40
|
* Log the first attempt (which is not a "retry" yet).
|
|
35
41
|
*
|
|
@@ -62,9 +68,18 @@ export interface PRetryOptions {
|
|
|
62
68
|
* Default to `console`
|
|
63
69
|
*/
|
|
64
70
|
logger?: CommonLogger;
|
|
71
|
+
/**
|
|
72
|
+
* Defaults to true.
|
|
73
|
+
* If true - preserves the stack trace in case of a Timeout (usually - very useful!).
|
|
74
|
+
* It has a certain perf cost.
|
|
75
|
+
*
|
|
76
|
+
* @experimental
|
|
77
|
+
*/
|
|
78
|
+
keepStackTrace?: boolean;
|
|
65
79
|
}
|
|
66
80
|
/**
|
|
67
81
|
* Returns a Function (!), enhanced with retry capabilities.
|
|
68
82
|
* Implements "Exponential back-off strategy" by multiplying the delay by `delayMultiplier` with each try.
|
|
69
83
|
*/
|
|
70
|
-
export declare function
|
|
84
|
+
export declare function pRetryFn<T extends AnyFunction>(fn: T, opt?: PRetryOptions): T;
|
|
85
|
+
export declare function pRetry<T>(fn: (attempt: number) => Promise<T>, opt?: PRetryOptions): Promise<T>;
|
package/dist/promise/pRetry.js
CHANGED
|
@@ -1,58 +1,90 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.pRetry = void 0;
|
|
3
|
+
exports.pRetry = exports.pRetryFn = void 0;
|
|
4
4
|
const __1 = require("..");
|
|
5
|
+
const pTimeout_1 = require("./pTimeout");
|
|
5
6
|
/**
|
|
6
7
|
* Returns a Function (!), enhanced with retry capabilities.
|
|
7
8
|
* Implements "Exponential back-off strategy" by multiplying the delay by `delayMultiplier` with each try.
|
|
8
9
|
*/
|
|
9
|
-
|
|
10
|
-
function
|
|
11
|
-
|
|
10
|
+
function pRetryFn(fn, opt = {}) {
|
|
11
|
+
return async function pRetryFunction(...args) {
|
|
12
|
+
return await pRetry(() => fn.call(this, ...args), opt);
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
exports.pRetryFn = pRetryFn;
|
|
16
|
+
async function pRetry(fn, opt = {}) {
|
|
17
|
+
const { maxAttempts = 4, delay: initialDelay = 1000, delayMultiplier = 2, predicate, logger = console, name, keepStackTrace = true, timeout, } = opt;
|
|
18
|
+
const fakeError = keepStackTrace ? new Error('RetryError') : undefined;
|
|
12
19
|
let { logFirstAttempt = false, logRetries = true, logFailures = false, logSuccess = false } = opt;
|
|
13
20
|
if (opt.logAll) {
|
|
14
|
-
logFirstAttempt = logRetries = logFailures = true;
|
|
21
|
+
logSuccess = logFirstAttempt = logRetries = logFailures = true;
|
|
15
22
|
}
|
|
16
23
|
if (opt.logNone) {
|
|
17
24
|
logSuccess = logFirstAttempt = logRetries = logFailures = false;
|
|
18
25
|
}
|
|
19
|
-
const fname =
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
26
|
+
const fname = name || fn.name || 'pRetry function';
|
|
27
|
+
let delay = initialDelay;
|
|
28
|
+
let attempt = 0;
|
|
29
|
+
let timer;
|
|
30
|
+
let timedOut = false;
|
|
31
|
+
return await new Promise((resolve, reject) => {
|
|
32
|
+
const rejectWithTimeout = () => {
|
|
33
|
+
timedOut = true; // to prevent more tries
|
|
34
|
+
const err = new pTimeout_1.TimeoutError(`"${fname}" timed out after ${timeout} ms`);
|
|
35
|
+
if (fakeError) {
|
|
36
|
+
// keep original stack
|
|
37
|
+
err.stack = fakeError.stack.replace('Error: RetryError', 'TimeoutError');
|
|
38
|
+
}
|
|
39
|
+
reject(err);
|
|
40
|
+
};
|
|
41
|
+
const next = async () => {
|
|
42
|
+
if (timedOut)
|
|
43
|
+
return;
|
|
44
|
+
if (timeout) {
|
|
45
|
+
timer = setTimeout(rejectWithTimeout, timeout);
|
|
46
|
+
}
|
|
47
|
+
const started = Date.now();
|
|
48
|
+
try {
|
|
49
|
+
attempt++;
|
|
50
|
+
if ((attempt === 1 && logFirstAttempt) || (attempt > 1 && logRetries)) {
|
|
51
|
+
logger.log(`${fname} attempt #${attempt}...`);
|
|
36
52
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
const r = await fn(attempt);
|
|
54
|
+
clearTimeout(timer);
|
|
55
|
+
if (logSuccess) {
|
|
56
|
+
logger.log(`${fname} attempt #${attempt} succeeded in ${(0, __1._since)(started)}`);
|
|
57
|
+
}
|
|
58
|
+
resolve(r);
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
clearTimeout(timer);
|
|
62
|
+
if (logFailures) {
|
|
63
|
+
logger.warn(`${fname} attempt #${attempt} error in ${(0, __1._since)(started)}:`, (0, __1._stringifyAny)(err, {
|
|
64
|
+
includeErrorData: true,
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
if (attempt >= maxAttempts ||
|
|
68
|
+
(predicate && !predicate(err, attempt, maxAttempts))) {
|
|
69
|
+
// Give up
|
|
70
|
+
if (fakeError) {
|
|
71
|
+
// Preserve the original call stack
|
|
72
|
+
Object.defineProperty(err, 'stack', {
|
|
73
|
+
value: err.stack +
|
|
74
|
+
'\n --' +
|
|
75
|
+
fakeError.stack.replace('Error: RetryError', ''),
|
|
76
|
+
});
|
|
51
77
|
}
|
|
78
|
+
reject(err);
|
|
52
79
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
80
|
+
else {
|
|
81
|
+
// Retry after delay
|
|
82
|
+
delay *= delayMultiplier;
|
|
83
|
+
setTimeout(next, delay);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
void next();
|
|
88
|
+
});
|
|
57
89
|
}
|
|
58
90
|
exports.pRetry = pRetry;
|
package/dist/types.d.ts
CHANGED
|
@@ -24,13 +24,13 @@ export interface CreatedUpdated {
|
|
|
24
24
|
created: number;
|
|
25
25
|
updated: number;
|
|
26
26
|
}
|
|
27
|
-
export interface CreatedUpdatedId extends CreatedUpdated {
|
|
28
|
-
id:
|
|
27
|
+
export interface CreatedUpdatedId<ID = string> extends CreatedUpdated {
|
|
28
|
+
id: ID;
|
|
29
29
|
}
|
|
30
|
-
export interface ObjectWithId {
|
|
31
|
-
id:
|
|
30
|
+
export interface ObjectWithId<ID = string> {
|
|
31
|
+
id: ID;
|
|
32
32
|
}
|
|
33
|
-
export interface AnyObjectWithId extends AnyObject, ObjectWithId {
|
|
33
|
+
export interface AnyObjectWithId<ID = string> extends AnyObject, ObjectWithId<ID> {
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
36
|
* Convenience type shorthand.
|
|
@@ -127,8 +127,8 @@ export declare type UnixTimestamp = number;
|
|
|
127
127
|
/**
|
|
128
128
|
* Base interface for any Entity that was saved to DB.
|
|
129
129
|
*/
|
|
130
|
-
export interface SavedDBEntity {
|
|
131
|
-
id:
|
|
130
|
+
export interface SavedDBEntity<ID = string> {
|
|
131
|
+
id: ID;
|
|
132
132
|
/**
|
|
133
133
|
* unixTimestamp of when the entity was first created (in the DB).
|
|
134
134
|
*/
|
|
@@ -144,9 +144,9 @@ export interface SavedDBEntity {
|
|
|
144
144
|
* hence `id`, `created` and `updated` fields CAN BE undefined (yet).
|
|
145
145
|
* When it's known to be saved - `SavedDBEntity` interface can be used instead.
|
|
146
146
|
*/
|
|
147
|
-
export declare type BaseDBEntity = Partial<SavedDBEntity
|
|
148
|
-
export declare type Saved<E> = Merge<E, SavedDBEntity
|
|
149
|
-
export declare type Unsaved<E> = Merge<E, BaseDBEntity
|
|
147
|
+
export declare type BaseDBEntity<ID = string> = Partial<SavedDBEntity<ID>>;
|
|
148
|
+
export declare type Saved<E, ID = string> = Merge<E, SavedDBEntity<ID>>;
|
|
149
|
+
export declare type Unsaved<E, ID = string> = Merge<E, BaseDBEntity<ID>>;
|
|
150
150
|
/**
|
|
151
151
|
* Named type for JSON.parse / JSON.stringify second argument
|
|
152
152
|
*/
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { pRetryFn } from '..';
|
|
2
2
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
3
3
|
export function _Retry(opt = {}) {
|
|
4
4
|
return (target, key, descriptor) => {
|
|
5
5
|
const originalFn = descriptor.value;
|
|
6
|
-
descriptor.value =
|
|
6
|
+
descriptor.value = pRetryFn(originalFn, opt);
|
|
7
7
|
return descriptor;
|
|
8
8
|
};
|
|
9
9
|
}
|
package/dist-esm/error/try.js
CHANGED
package/dist-esm/index.js
CHANGED
|
@@ -38,7 +38,7 @@ export * from './promise/pFilter';
|
|
|
38
38
|
export * from './promise/pHang';
|
|
39
39
|
import { pMap } from './promise/pMap';
|
|
40
40
|
export * from './promise/pProps';
|
|
41
|
-
import { pRetry } from './promise/pRetry';
|
|
41
|
+
import { pRetry, pRetryFn } from './promise/pRetry';
|
|
42
42
|
export * from './promise/pState';
|
|
43
43
|
import { pTimeout, pTimeoutFn } from './promise/pTimeout';
|
|
44
44
|
export * from './promise/pTuple';
|
|
@@ -55,4 +55,4 @@ export * from './string/safeJsonStringify';
|
|
|
55
55
|
import { PQueue } from './promise/pQueue';
|
|
56
56
|
export * from './seq/seq';
|
|
57
57
|
export * from './math/stack.util';
|
|
58
|
-
export { is, _createPromiseDecorator, _stringMapValues, _stringMapEntries, _objectKeys, pMap, _passthroughMapper, _passUndefinedMapper, _passthroughPredicate, _passNothingPredicate, _noop, ErrorMode, pDefer, AggregatedError, pRetry, pTimeout, pTimeoutFn, _tryCatch, _TryCatch, _stringifyAny, jsonSchema, JsonSchemaAnyBuilder, commonLoggerMinLevel, commonLoggerNoop, commonLogLevelNumber, commonLoggerPipe, commonLoggerPrefix, commonLoggerCreate, PQueue, END, SKIP, };
|
|
58
|
+
export { is, _createPromiseDecorator, _stringMapValues, _stringMapEntries, _objectKeys, pMap, _passthroughMapper, _passUndefinedMapper, _passthroughPredicate, _passNothingPredicate, _noop, ErrorMode, pDefer, AggregatedError, pRetry, pRetryFn, pTimeout, pTimeoutFn, _tryCatch, _TryCatch, _stringifyAny, jsonSchema, JsonSchemaAnyBuilder, commonLoggerMinLevel, commonLoggerNoop, commonLogLevelNumber, commonLoggerPipe, commonLoggerPrefix, commonLoggerCreate, PQueue, END, SKIP, };
|
|
@@ -301,16 +301,16 @@ export class JsonSchemaObjectBuilder extends JsonSchemaAnyBuilder {
|
|
|
301
301
|
Object.assign(this.schema, { additionalProperties });
|
|
302
302
|
return this;
|
|
303
303
|
}
|
|
304
|
-
baseDBEntity() {
|
|
304
|
+
baseDBEntity(idType = 'string') {
|
|
305
305
|
Object.assign(this.schema.properties, {
|
|
306
|
-
id: { type:
|
|
306
|
+
id: { type: idType },
|
|
307
307
|
created: { type: 'number', format: 'unixTimestamp' },
|
|
308
308
|
updated: { type: 'number', format: 'unixTimestamp' },
|
|
309
309
|
});
|
|
310
310
|
return this;
|
|
311
311
|
}
|
|
312
|
-
savedDBEntity() {
|
|
313
|
-
return this.baseDBEntity().addRequired(['id', 'created', 'updated']);
|
|
312
|
+
savedDBEntity(idType = 'string') {
|
|
313
|
+
return this.baseDBEntity(idType).addRequired(['id', 'created', 'updated']);
|
|
314
314
|
}
|
|
315
315
|
extend(s2) {
|
|
316
316
|
const builder = new JsonSchemaObjectBuilder();
|
|
@@ -1,22 +1,18 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Inspired by Bluebird Promise.props() and https://github.com/sindresorhus/p-props
|
|
3
|
-
|
|
4
|
-
Improvements:
|
|
5
|
-
|
|
6
|
-
- Exported as { pProps }, so IDE auto-completion works
|
|
7
|
-
- Simpler: no support for Map, Mapper, Options
|
|
8
|
-
- Included Typescript typings (no need for @types/p-props)
|
|
9
|
-
*/
|
|
10
|
-
import { pMap } from './pMap';
|
|
11
|
-
// todo: remove when eslint starts to know about Awaited
|
|
12
|
-
/* eslint-disable no-undef */
|
|
13
1
|
/**
|
|
14
2
|
* Promise.all for Object instead of Array.
|
|
15
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Inspired by Bluebird Promise.props() and https://github.com/sindresorhus/p-props
|
|
5
|
+
*
|
|
6
|
+
* Improvements:
|
|
7
|
+
*
|
|
8
|
+
* - Exported as { pProps }, so IDE auto-completion works
|
|
9
|
+
* - Simpler: no support for Map, Mapper, Options
|
|
10
|
+
* - Included Typescript typings (no need for @types/p-props)
|
|
11
|
+
*
|
|
12
|
+
* Concurrency implementation via pMap was removed in favor of preserving async
|
|
13
|
+
* stack traces (more important!).
|
|
16
14
|
*/
|
|
17
|
-
export async function pProps(input
|
|
18
|
-
const r = {};
|
|
15
|
+
export async function pProps(input) {
|
|
19
16
|
const keys = Object.keys(input);
|
|
20
|
-
await
|
|
21
|
-
return r;
|
|
17
|
+
return Object.fromEntries((await Promise.all(Object.values(input))).map((v, i) => [keys[i], v]));
|
|
22
18
|
}
|
|
@@ -1,54 +1,85 @@
|
|
|
1
1
|
import { _since, _stringifyAny } from '..';
|
|
2
|
+
import { TimeoutError } from './pTimeout';
|
|
2
3
|
/**
|
|
3
4
|
* Returns a Function (!), enhanced with retry capabilities.
|
|
4
5
|
* Implements "Exponential back-off strategy" by multiplying the delay by `delayMultiplier` with each try.
|
|
5
6
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
export function pRetryFn(fn, opt = {}) {
|
|
8
|
+
return async function pRetryFunction(...args) {
|
|
9
|
+
return await pRetry(() => fn.call(this, ...args), opt);
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export async function pRetry(fn, opt = {}) {
|
|
13
|
+
const { maxAttempts = 4, delay: initialDelay = 1000, delayMultiplier = 2, predicate, logger = console, name, keepStackTrace = true, timeout, } = opt;
|
|
14
|
+
const fakeError = keepStackTrace ? new Error('RetryError') : undefined;
|
|
9
15
|
let { logFirstAttempt = false, logRetries = true, logFailures = false, logSuccess = false } = opt;
|
|
10
16
|
if (opt.logAll) {
|
|
11
|
-
logFirstAttempt = logRetries = logFailures = true;
|
|
17
|
+
logSuccess = logFirstAttempt = logRetries = logFailures = true;
|
|
12
18
|
}
|
|
13
19
|
if (opt.logNone) {
|
|
14
20
|
logSuccess = logFirstAttempt = logRetries = logFailures = false;
|
|
15
21
|
}
|
|
16
|
-
const fname =
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
22
|
+
const fname = name || fn.name || 'pRetry function';
|
|
23
|
+
let delay = initialDelay;
|
|
24
|
+
let attempt = 0;
|
|
25
|
+
let timer;
|
|
26
|
+
let timedOut = false;
|
|
27
|
+
return await new Promise((resolve, reject) => {
|
|
28
|
+
const rejectWithTimeout = () => {
|
|
29
|
+
timedOut = true; // to prevent more tries
|
|
30
|
+
const err = new TimeoutError(`"${fname}" timed out after ${timeout} ms`);
|
|
31
|
+
if (fakeError) {
|
|
32
|
+
// keep original stack
|
|
33
|
+
err.stack = fakeError.stack.replace('Error: RetryError', 'TimeoutError');
|
|
34
|
+
}
|
|
35
|
+
reject(err);
|
|
36
|
+
};
|
|
37
|
+
const next = async () => {
|
|
38
|
+
if (timedOut)
|
|
39
|
+
return;
|
|
40
|
+
if (timeout) {
|
|
41
|
+
timer = setTimeout(rejectWithTimeout, timeout);
|
|
42
|
+
}
|
|
43
|
+
const started = Date.now();
|
|
44
|
+
try {
|
|
45
|
+
attempt++;
|
|
46
|
+
if ((attempt === 1 && logFirstAttempt) || (attempt > 1 && logRetries)) {
|
|
47
|
+
logger.log(`${fname} attempt #${attempt}...`);
|
|
33
48
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
const r = await fn(attempt);
|
|
50
|
+
clearTimeout(timer);
|
|
51
|
+
if (logSuccess) {
|
|
52
|
+
logger.log(`${fname} attempt #${attempt} succeeded in ${_since(started)}`);
|
|
53
|
+
}
|
|
54
|
+
resolve(r);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
clearTimeout(timer);
|
|
58
|
+
if (logFailures) {
|
|
59
|
+
logger.warn(`${fname} attempt #${attempt} error in ${_since(started)}:`, _stringifyAny(err, {
|
|
60
|
+
includeErrorData: true,
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
if (attempt >= maxAttempts ||
|
|
64
|
+
(predicate && !predicate(err, attempt, maxAttempts))) {
|
|
65
|
+
// Give up
|
|
66
|
+
if (fakeError) {
|
|
67
|
+
// Preserve the original call stack
|
|
68
|
+
Object.defineProperty(err, 'stack', {
|
|
69
|
+
value: err.stack +
|
|
70
|
+
'\n --' +
|
|
71
|
+
fakeError.stack.replace('Error: RetryError', ''),
|
|
72
|
+
});
|
|
48
73
|
}
|
|
74
|
+
reject(err);
|
|
49
75
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
76
|
+
else {
|
|
77
|
+
// Retry after delay
|
|
78
|
+
delay *= delayMultiplier;
|
|
79
|
+
setTimeout(next, delay);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
void next();
|
|
84
|
+
});
|
|
54
85
|
}
|
package/package.json
CHANGED
|
@@ -110,7 +110,7 @@ export function _LogMethod(opt: LogMethodOptions = {}): MethodDecorator {
|
|
|
110
110
|
})
|
|
111
111
|
.catch((err: any) => {
|
|
112
112
|
logFinished(logger, callSignature, started, sma, logResultFn, undefined, err)
|
|
113
|
-
|
|
113
|
+
throw err
|
|
114
114
|
})
|
|
115
115
|
} else {
|
|
116
116
|
// not a Promise
|
package/src/decorators/memoFn.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { pRetryFn, PRetryOptions } from '..'
|
|
2
2
|
|
|
3
3
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
4
4
|
export function _Retry(opt: PRetryOptions = {}): MethodDecorator {
|
|
5
5
|
return (target, key, descriptor) => {
|
|
6
6
|
const originalFn = descriptor.value
|
|
7
|
-
descriptor.value =
|
|
7
|
+
descriptor.value = pRetryFn(originalFn as any, opt)
|
|
8
8
|
return descriptor
|
|
9
9
|
}
|
|
10
10
|
}
|
package/src/error/try.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -74,7 +74,7 @@ export * from './promise/pFilter'
|
|
|
74
74
|
export * from './promise/pHang'
|
|
75
75
|
import { pMap, PMapOptions } from './promise/pMap'
|
|
76
76
|
export * from './promise/pProps'
|
|
77
|
-
import { pRetry, PRetryOptions } from './promise/pRetry'
|
|
77
|
+
import { pRetry, pRetryFn, PRetryOptions } from './promise/pRetry'
|
|
78
78
|
export * from './promise/pState'
|
|
79
79
|
import { pTimeout, pTimeoutFn, PTimeoutOptions } from './promise/pTimeout'
|
|
80
80
|
export * from './promise/pTuple'
|
|
@@ -250,6 +250,7 @@ export {
|
|
|
250
250
|
pDefer,
|
|
251
251
|
AggregatedError,
|
|
252
252
|
pRetry,
|
|
253
|
+
pRetryFn,
|
|
253
254
|
pTimeout,
|
|
254
255
|
pTimeoutFn,
|
|
255
256
|
_tryCatch,
|
|
@@ -365,9 +365,9 @@ export class JsonSchemaObjectBuilder<T extends AnyObject> extends JsonSchemaAnyB
|
|
|
365
365
|
return this
|
|
366
366
|
}
|
|
367
367
|
|
|
368
|
-
baseDBEntity(): JsonSchemaObjectBuilder<T & BaseDBEntity
|
|
368
|
+
baseDBEntity<ID = string>(idType = 'string'): JsonSchemaObjectBuilder<T & BaseDBEntity<ID>> {
|
|
369
369
|
Object.assign(this.schema.properties, {
|
|
370
|
-
id: { type:
|
|
370
|
+
id: { type: idType },
|
|
371
371
|
created: { type: 'number', format: 'unixTimestamp' },
|
|
372
372
|
updated: { type: 'number', format: 'unixTimestamp' },
|
|
373
373
|
})
|
|
@@ -375,8 +375,8 @@ export class JsonSchemaObjectBuilder<T extends AnyObject> extends JsonSchemaAnyB
|
|
|
375
375
|
return this
|
|
376
376
|
}
|
|
377
377
|
|
|
378
|
-
savedDBEntity(): JsonSchemaObjectBuilder<T & SavedDBEntity
|
|
379
|
-
return this.baseDBEntity().addRequired(['id', 'created', 'updated']) as any
|
|
378
|
+
savedDBEntity<ID = string>(idType = 'string'): JsonSchemaObjectBuilder<T & SavedDBEntity<ID>> {
|
|
379
|
+
return this.baseDBEntity(idType).addRequired(['id', 'created', 'updated']) as any
|
|
380
380
|
}
|
|
381
381
|
|
|
382
382
|
extend<T2 extends AnyObject>(s2: JsonSchemaObjectBuilder<T2>): JsonSchemaObjectBuilder<T & T2> {
|
package/src/promise/pProps.ts
CHANGED
|
@@ -1,28 +1,20 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Inspired by Bluebird Promise.props() and https://github.com/sindresorhus/p-props
|
|
3
|
-
|
|
4
|
-
Improvements:
|
|
5
|
-
|
|
6
|
-
- Exported as { pProps }, so IDE auto-completion works
|
|
7
|
-
- Simpler: no support for Map, Mapper, Options
|
|
8
|
-
- Included Typescript typings (no need for @types/p-props)
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { pMap, PMapOptions } from './pMap'
|
|
12
|
-
|
|
13
|
-
// todo: remove when eslint starts to know about Awaited
|
|
14
|
-
/* eslint-disable no-undef */
|
|
15
|
-
|
|
16
1
|
/**
|
|
17
2
|
* Promise.all for Object instead of Array.
|
|
18
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Inspired by Bluebird Promise.props() and https://github.com/sindresorhus/p-props
|
|
5
|
+
*
|
|
6
|
+
* Improvements:
|
|
7
|
+
*
|
|
8
|
+
* - Exported as { pProps }, so IDE auto-completion works
|
|
9
|
+
* - Simpler: no support for Map, Mapper, Options
|
|
10
|
+
* - Included Typescript typings (no need for @types/p-props)
|
|
11
|
+
*
|
|
12
|
+
* Concurrency implementation via pMap was removed in favor of preserving async
|
|
13
|
+
* stack traces (more important!).
|
|
19
14
|
*/
|
|
20
|
-
export async function pProps<T>(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const keys = Object.keys(input) as (keyof T)[]
|
|
26
|
-
await pMap(Object.values(input), (v, i) => (r[keys[i]!] = v), opt)
|
|
27
|
-
return r
|
|
15
|
+
export async function pProps<T>(input: { [K in keyof T]: T[K] | Promise<T[K]> }): Promise<{
|
|
16
|
+
[K in keyof T]: Awaited<T[K]>
|
|
17
|
+
}> {
|
|
18
|
+
const keys = Object.keys(input)
|
|
19
|
+
return Object.fromEntries((await Promise.all(Object.values(input))).map((v, i) => [keys[i], v]))
|
|
28
20
|
}
|
package/src/promise/pRetry.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { _since, _stringifyAny, AnyFunction, CommonLogger } from '..'
|
|
2
|
+
import { TimeoutError } from './pTimeout'
|
|
2
3
|
|
|
3
4
|
export interface PRetryOptions {
|
|
4
5
|
/**
|
|
@@ -7,6 +8,13 @@ export interface PRetryOptions {
|
|
|
7
8
|
*/
|
|
8
9
|
name?: string
|
|
9
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Timeout for each Try, in milliseconds.
|
|
13
|
+
*
|
|
14
|
+
* Defaults to 60_000
|
|
15
|
+
*/
|
|
16
|
+
timeout?: number
|
|
17
|
+
|
|
10
18
|
/**
|
|
11
19
|
* How many attempts to try.
|
|
12
20
|
* First attempt is not a retry, but "initial try". It still counts.
|
|
@@ -34,7 +42,7 @@ export interface PRetryOptions {
|
|
|
34
42
|
*
|
|
35
43
|
* @default () => true
|
|
36
44
|
*/
|
|
37
|
-
predicate?: (err:
|
|
45
|
+
predicate?: (err: Error, attempt: number, maxAttempts: number) => boolean
|
|
38
46
|
|
|
39
47
|
/**
|
|
40
48
|
* Log the first attempt (which is not a "retry" yet).
|
|
@@ -74,76 +82,132 @@ export interface PRetryOptions {
|
|
|
74
82
|
* Default to `console`
|
|
75
83
|
*/
|
|
76
84
|
logger?: CommonLogger
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Defaults to true.
|
|
88
|
+
* If true - preserves the stack trace in case of a Timeout (usually - very useful!).
|
|
89
|
+
* It has a certain perf cost.
|
|
90
|
+
*
|
|
91
|
+
* @experimental
|
|
92
|
+
*/
|
|
93
|
+
keepStackTrace?: boolean
|
|
77
94
|
}
|
|
78
95
|
|
|
79
96
|
/**
|
|
80
97
|
* Returns a Function (!), enhanced with retry capabilities.
|
|
81
98
|
* Implements "Exponential back-off strategy" by multiplying the delay by `delayMultiplier` with each try.
|
|
82
99
|
*/
|
|
83
|
-
|
|
84
|
-
|
|
100
|
+
export function pRetryFn<T extends AnyFunction>(fn: T, opt: PRetryOptions = {}): T {
|
|
101
|
+
return async function pRetryFunction(this: any, ...args: any[]) {
|
|
102
|
+
return await pRetry(() => fn.call(this, ...args), opt)
|
|
103
|
+
} as any
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function pRetry<T>(
|
|
107
|
+
fn: (attempt: number) => Promise<T>,
|
|
108
|
+
opt: PRetryOptions = {},
|
|
109
|
+
): Promise<T> {
|
|
85
110
|
const {
|
|
86
111
|
maxAttempts = 4,
|
|
87
112
|
delay: initialDelay = 1000,
|
|
88
113
|
delayMultiplier = 2,
|
|
89
114
|
predicate,
|
|
90
115
|
logger = console,
|
|
91
|
-
name
|
|
116
|
+
name,
|
|
117
|
+
keepStackTrace = true,
|
|
118
|
+
timeout,
|
|
92
119
|
} = opt
|
|
93
120
|
|
|
121
|
+
const fakeError = keepStackTrace ? new Error('RetryError') : undefined
|
|
122
|
+
|
|
94
123
|
let { logFirstAttempt = false, logRetries = true, logFailures = false, logSuccess = false } = opt
|
|
95
124
|
|
|
96
125
|
if (opt.logAll) {
|
|
97
|
-
logFirstAttempt = logRetries = logFailures = true
|
|
126
|
+
logSuccess = logFirstAttempt = logRetries = logFailures = true
|
|
98
127
|
}
|
|
99
128
|
if (opt.logNone) {
|
|
100
129
|
logSuccess = logFirstAttempt = logRetries = logFailures = false
|
|
101
130
|
}
|
|
102
131
|
|
|
103
|
-
const fname =
|
|
132
|
+
const fname = name || fn.name || 'pRetry function'
|
|
104
133
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
134
|
+
let delay = initialDelay
|
|
135
|
+
let attempt = 0
|
|
136
|
+
let timer: NodeJS.Timeout | undefined
|
|
137
|
+
let timedOut = false
|
|
108
138
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
139
|
+
return await new Promise((resolve, reject) => {
|
|
140
|
+
const rejectWithTimeout = () => {
|
|
141
|
+
timedOut = true // to prevent more tries
|
|
142
|
+
const err = new TimeoutError(`"${fname}" timed out after ${timeout} ms`)
|
|
143
|
+
if (fakeError) {
|
|
144
|
+
// keep original stack
|
|
145
|
+
err.stack = fakeError.stack!.replace('Error: RetryError', 'TimeoutError')
|
|
146
|
+
}
|
|
147
|
+
reject(err)
|
|
148
|
+
}
|
|
112
149
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if ((attempt === 1 && logFirstAttempt) || (attempt > 1 && logRetries)) {
|
|
116
|
-
logger.log(`${fname} attempt #${attempt}...`)
|
|
117
|
-
}
|
|
150
|
+
const next = async () => {
|
|
151
|
+
if (timedOut) return
|
|
118
152
|
|
|
119
|
-
|
|
153
|
+
if (timeout) {
|
|
154
|
+
timer = setTimeout(rejectWithTimeout, timeout)
|
|
155
|
+
}
|
|
120
156
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
)
|
|
133
|
-
}
|
|
157
|
+
const started = Date.now()
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
attempt++
|
|
161
|
+
if ((attempt === 1 && logFirstAttempt) || (attempt > 1 && logRetries)) {
|
|
162
|
+
logger.log(`${fname} attempt #${attempt}...`)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const r = await fn(attempt)
|
|
166
|
+
|
|
167
|
+
clearTimeout(timer!)
|
|
134
168
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
169
|
+
if (logSuccess) {
|
|
170
|
+
logger.log(`${fname} attempt #${attempt} succeeded in ${_since(started)}`)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
resolve(r)
|
|
174
|
+
} catch (err) {
|
|
175
|
+
clearTimeout(timer!)
|
|
176
|
+
|
|
177
|
+
if (logFailures) {
|
|
178
|
+
logger.warn(
|
|
179
|
+
`${fname} attempt #${attempt} error in ${_since(started)}:`,
|
|
180
|
+
_stringifyAny(err, {
|
|
181
|
+
includeErrorData: true,
|
|
182
|
+
}),
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (
|
|
187
|
+
attempt >= maxAttempts ||
|
|
188
|
+
(predicate && !predicate(err as Error, attempt, maxAttempts))
|
|
189
|
+
) {
|
|
190
|
+
// Give up
|
|
191
|
+
|
|
192
|
+
if (fakeError) {
|
|
193
|
+
// Preserve the original call stack
|
|
194
|
+
Object.defineProperty(err, 'stack', {
|
|
195
|
+
value:
|
|
196
|
+
(err as Error).stack +
|
|
197
|
+
'\n --' +
|
|
198
|
+
fakeError.stack!.replace('Error: RetryError', ''),
|
|
199
|
+
})
|
|
142
200
|
}
|
|
201
|
+
|
|
202
|
+
reject(err)
|
|
203
|
+
} else {
|
|
204
|
+
// Retry after delay
|
|
205
|
+
delay *= delayMultiplier
|
|
206
|
+
setTimeout(next, delay)
|
|
143
207
|
}
|
|
144
208
|
}
|
|
209
|
+
}
|
|
145
210
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
} as any
|
|
211
|
+
void next()
|
|
212
|
+
})
|
|
149
213
|
}
|
package/src/types.ts
CHANGED
|
@@ -29,15 +29,15 @@ export interface CreatedUpdated {
|
|
|
29
29
|
updated: number
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export interface CreatedUpdatedId extends CreatedUpdated {
|
|
33
|
-
id:
|
|
32
|
+
export interface CreatedUpdatedId<ID = string> extends CreatedUpdated {
|
|
33
|
+
id: ID
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
export interface ObjectWithId {
|
|
37
|
-
id:
|
|
36
|
+
export interface ObjectWithId<ID = string> {
|
|
37
|
+
id: ID
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
export interface AnyObjectWithId extends AnyObject, ObjectWithId {}
|
|
40
|
+
export interface AnyObjectWithId<ID = string> extends AnyObject, ObjectWithId<ID> {}
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
43
|
* Convenience type shorthand.
|
|
@@ -169,8 +169,8 @@ export type UnixTimestamp = number
|
|
|
169
169
|
/**
|
|
170
170
|
* Base interface for any Entity that was saved to DB.
|
|
171
171
|
*/
|
|
172
|
-
export interface SavedDBEntity {
|
|
173
|
-
id:
|
|
172
|
+
export interface SavedDBEntity<ID = string> {
|
|
173
|
+
id: ID
|
|
174
174
|
|
|
175
175
|
/**
|
|
176
176
|
* unixTimestamp of when the entity was first created (in the DB).
|
|
@@ -189,10 +189,10 @@ export interface SavedDBEntity {
|
|
|
189
189
|
* hence `id`, `created` and `updated` fields CAN BE undefined (yet).
|
|
190
190
|
* When it's known to be saved - `SavedDBEntity` interface can be used instead.
|
|
191
191
|
*/
|
|
192
|
-
export type BaseDBEntity = Partial<SavedDBEntity
|
|
192
|
+
export type BaseDBEntity<ID = string> = Partial<SavedDBEntity<ID>>
|
|
193
193
|
|
|
194
|
-
export type Saved<E> = Merge<E, SavedDBEntity
|
|
195
|
-
export type Unsaved<E> = Merge<E, BaseDBEntity
|
|
194
|
+
export type Saved<E, ID = string> = Merge<E, SavedDBEntity<ID>>
|
|
195
|
+
export type Unsaved<E, ID = string> = Merge<E, BaseDBEntity<ID>>
|
|
196
196
|
|
|
197
197
|
/**
|
|
198
198
|
* Named type for JSON.parse / JSON.stringify second argument
|