@lido-nestjs/execution 1.19.0 → 1.20.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/common/retrier.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var sleep = require('./sleep.js');
|
|
4
|
+
var sanitizeError = require('./sanitize-error.js');
|
|
4
5
|
|
|
5
6
|
const retrier = (logger, defaultMaxRetryCount = 3, defaultMinBackoffMs = 1000, defaultMaxBackoffMs = 60000, defaultLogWarning = false, defaultErrorFilter) => {
|
|
6
7
|
return async (callback, maxRetryCount, minBackoffMs, maxBackoffMs, logWarning, errorFilter) => {
|
|
@@ -17,7 +18,7 @@ const retrier = (logger, defaultMaxRetryCount = 3, defaultMinBackoffMs = 1000, d
|
|
|
17
18
|
throw err;
|
|
18
19
|
}
|
|
19
20
|
if (logger && logWarning) {
|
|
20
|
-
logger.warn(err, `Retrying after (${minBackoffMs}ms). Remaining retries [${maxRetryCount}]`);
|
|
21
|
+
logger.warn(sanitizeError.sanitizeError(err), `Retrying after (${minBackoffMs}ms). Remaining retries [${maxRetryCount}]`);
|
|
21
22
|
}
|
|
22
23
|
if (maxRetryCount <= 1 || minBackoffMs >= maxBackoffMs) {
|
|
23
24
|
throw err;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns a safe, bounded string representation of an error for logging.
|
|
3
|
+
* inspect() handles everything internally: circular references, throwing
|
|
4
|
+
* getters, frozen objects, huge nested data. No manual field extraction needed.
|
|
5
|
+
*/
|
|
6
|
+
export declare function sanitizeError(error: unknown, maxLength?: number): string;
|
|
7
|
+
/**
|
|
8
|
+
* Truncates unknown data to a safe size.
|
|
9
|
+
* Use for FetchError.data assignment to prevent huge RPC payloads
|
|
10
|
+
* from being attached to error objects.
|
|
11
|
+
*/
|
|
12
|
+
export declare function sanitizeErrorData(data: unknown, maxLength?: number): unknown;
|
|
13
|
+
/**
|
|
14
|
+
* Mutates the error object in place, truncating heavy fields.
|
|
15
|
+
* Preserves the original error type (instanceof checks still work).
|
|
16
|
+
* Use for error objects that are thrown to consumers (e.g. AllProvidersFailedError.cause).
|
|
17
|
+
*/
|
|
18
|
+
export declare function sanitizeErrorInPlace(error: unknown, maxLength?: number,
|
|
19
|
+
/** @internal tracks visited objects to prevent circular-reference stack overflow */
|
|
20
|
+
_seen?: WeakSet<object>): void;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var util = require('util');
|
|
4
|
+
|
|
5
|
+
const DEFAULT_MAX_LENGTH = 1000;
|
|
6
|
+
const INSPECT_OPTIONS = (maxLength) => ({
|
|
7
|
+
depth: 4,
|
|
8
|
+
maxStringLength: maxLength,
|
|
9
|
+
maxArrayLength: 20,
|
|
10
|
+
compact: true,
|
|
11
|
+
breakLength: Infinity,
|
|
12
|
+
});
|
|
13
|
+
/**
|
|
14
|
+
* Uses util.inspect to produce a bounded string representation.
|
|
15
|
+
* Never calls JSON.stringify (avoids V8 "RangeError: Invalid string length"
|
|
16
|
+
* on objects >512 MB). inspect() handles circular references, throwing
|
|
17
|
+
* getters, frozen objects, and huge data internally.
|
|
18
|
+
*/
|
|
19
|
+
function truncate(value, maxLength) {
|
|
20
|
+
if (value === null || value === undefined)
|
|
21
|
+
return value;
|
|
22
|
+
if (typeof value === 'string') {
|
|
23
|
+
return value.length > maxLength
|
|
24
|
+
? value.slice(0, maxLength) +
|
|
25
|
+
`...[truncated, total length: ${value.length}]`
|
|
26
|
+
: value;
|
|
27
|
+
}
|
|
28
|
+
if (typeof value !== 'object')
|
|
29
|
+
return value;
|
|
30
|
+
try {
|
|
31
|
+
const inspected = util.inspect(value, INSPECT_OPTIONS(maxLength));
|
|
32
|
+
if (inspected.length <= maxLength)
|
|
33
|
+
return value;
|
|
34
|
+
return (inspected.slice(0, maxLength) +
|
|
35
|
+
`...[truncated, total length: ${inspected.length}]`);
|
|
36
|
+
}
|
|
37
|
+
catch (_a) {
|
|
38
|
+
return '[unserializable]';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Returns a safe, bounded string representation of an error for logging.
|
|
43
|
+
* inspect() handles everything internally: circular references, throwing
|
|
44
|
+
* getters, frozen objects, huge nested data. No manual field extraction needed.
|
|
45
|
+
*/
|
|
46
|
+
function sanitizeError(error, maxLength = DEFAULT_MAX_LENGTH) {
|
|
47
|
+
try {
|
|
48
|
+
const result = util.inspect(error, INSPECT_OPTIONS(maxLength));
|
|
49
|
+
if (result.length <= maxLength)
|
|
50
|
+
return result;
|
|
51
|
+
return (result.slice(0, maxLength) +
|
|
52
|
+
`...[truncated, total length: ${result.length}]`);
|
|
53
|
+
}
|
|
54
|
+
catch (_a) {
|
|
55
|
+
return '[unserializable error]';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Truncates unknown data to a safe size.
|
|
60
|
+
* Use for FetchError.data assignment to prevent huge RPC payloads
|
|
61
|
+
* from being attached to error objects.
|
|
62
|
+
*/
|
|
63
|
+
function sanitizeErrorData(data, maxLength = DEFAULT_MAX_LENGTH) {
|
|
64
|
+
return truncate(data, maxLength);
|
|
65
|
+
}
|
|
66
|
+
const HEAVY_FIELDS = ['data', 'body', 'requestBody', 'serverError'];
|
|
67
|
+
/**
|
|
68
|
+
* Mutates the error object in place, truncating heavy fields.
|
|
69
|
+
* Preserves the original error type (instanceof checks still work).
|
|
70
|
+
* Use for error objects that are thrown to consumers (e.g. AllProvidersFailedError.cause).
|
|
71
|
+
*/
|
|
72
|
+
function sanitizeErrorInPlace(error, maxLength = DEFAULT_MAX_LENGTH,
|
|
73
|
+
/** @internal tracks visited objects to prevent circular-reference stack overflow */
|
|
74
|
+
_seen) {
|
|
75
|
+
if (error === null || error === undefined || typeof error !== 'object') {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const seen = _seen !== null && _seen !== void 0 ? _seen : new WeakSet();
|
|
79
|
+
if (seen.has(error))
|
|
80
|
+
return;
|
|
81
|
+
seen.add(error);
|
|
82
|
+
const err = error;
|
|
83
|
+
for (const field of HEAVY_FIELDS) {
|
|
84
|
+
try {
|
|
85
|
+
if (err[field] !== undefined) {
|
|
86
|
+
err[field] = truncate(err[field], maxLength);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch (_a) {
|
|
90
|
+
// read-only property or throwing getter
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
if (err.error && typeof err.error === 'object') {
|
|
95
|
+
sanitizeErrorInPlace(err.error, maxLength, seen);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch (_b) {
|
|
99
|
+
// throwing getter on .error
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
if (err.cause && typeof err.cause === 'object') {
|
|
103
|
+
sanitizeErrorInPlace(err.cause, maxLength, seen);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch (_c) {
|
|
107
|
+
// throwing getter on .cause
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
exports.sanitizeError = sanitizeError;
|
|
112
|
+
exports.sanitizeErrorData = sanitizeErrorData;
|
|
113
|
+
exports.sanitizeErrorInPlace = sanitizeErrorInPlace;
|
|
@@ -14,6 +14,7 @@ var feeHistory = require('../ethers/fee-history.js');
|
|
|
14
14
|
var errorCodes = require('../error/codes/error-codes.js');
|
|
15
15
|
var debugTraceBlockByHash = require('../ethers/debug-trace-block-by-hash.js');
|
|
16
16
|
var networks = require('../common/networks.js');
|
|
17
|
+
var sanitizeError = require('../common/sanitize-error.js');
|
|
17
18
|
var lazyEventEmitter = require('../common/lazy-event-emitter.js');
|
|
18
19
|
|
|
19
20
|
exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extends providers.JsonRpcProvider {
|
|
@@ -77,7 +78,7 @@ exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extend
|
|
|
77
78
|
: '';
|
|
78
79
|
const error = new fetch_error.FetchError(errMessage + detailedMessage);
|
|
79
80
|
error.code = errorCodes.ErrorCode.UNEXPECTED_BATCH_RESULT;
|
|
80
|
-
error.data = batchResult.error;
|
|
81
|
+
error.data = sanitizeError.sanitizeErrorData(batchResult.error);
|
|
81
82
|
throw error;
|
|
82
83
|
}
|
|
83
84
|
const resultMap = batchResult.reduce((resultMap, payload) => {
|
|
@@ -101,7 +102,7 @@ exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extend
|
|
|
101
102
|
else if (payload.error) {
|
|
102
103
|
const error = new fetch_error.FetchError(payload.error.message);
|
|
103
104
|
error.code = payload.error.code;
|
|
104
|
-
error.data = payload.error.data;
|
|
105
|
+
error.data = sanitizeError.sanitizeErrorData(payload.error.data);
|
|
105
106
|
inflightRequest.reject(error);
|
|
106
107
|
}
|
|
107
108
|
else {
|
|
@@ -12,6 +12,7 @@ var noNewBlocksWhilePolling_error = require('../error/no-new-blocks-while-pollin
|
|
|
12
12
|
var errors = require('../common/errors.js');
|
|
13
13
|
var allProvidersFailed_error = require('../error/all-providers-failed.error.js');
|
|
14
14
|
var requestTimeout_error = require('../error/request-timeout.error.js');
|
|
15
|
+
var sanitizeError = require('../common/sanitize-error.js');
|
|
15
16
|
var transactionWaitTimeout_error = require('../error/transaction-wait-timeout.error.js');
|
|
16
17
|
var feeHistory = require('../ethers/fee-history.js');
|
|
17
18
|
var debugTraceBlockByHash = require('../ethers/debug-trace-block-by-hash.js');
|
|
@@ -211,18 +212,18 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
|
|
|
211
212
|
// Log context (label + provider index) synchronously before error object
|
|
212
213
|
// to ensure proper ordering in async logging systems
|
|
213
214
|
this.logger.error(this.formatLog(`${method} non-retryable error occurred`, this.activeFallbackProviderIndex));
|
|
214
|
-
this.logger.error(e);
|
|
215
|
+
this.logger.error(sanitizeError.sanitizeError(e));
|
|
215
216
|
throw e;
|
|
216
217
|
}
|
|
217
218
|
// Log context (label + provider index) synchronously before error object
|
|
218
219
|
// to ensure proper ordering in async logging systems
|
|
219
220
|
if (e instanceof requestTimeout_error.RequestTimeoutError) {
|
|
220
221
|
this.logger.error(this.formatLog(`${method} timeout after ${e.timeoutMs}ms. Will switch to next provider.`, this.activeFallbackProviderIndex));
|
|
221
|
-
this.logger.error(e);
|
|
222
|
+
this.logger.error(sanitizeError.sanitizeError(e));
|
|
222
223
|
}
|
|
223
224
|
else {
|
|
224
225
|
this.logger.error(this.formatLog(`${method} error occurred. Will switch to next provider.`, this.activeFallbackProviderIndex));
|
|
225
|
-
this.logger.error(e);
|
|
226
|
+
this.logger.error(sanitizeError.sanitizeError(e));
|
|
226
227
|
}
|
|
227
228
|
// This check is needed to avoid multiple `switchToNextProvider` calls when doing one JSON-RPC batch.
|
|
228
229
|
// This can happen when multiple N calls to `perform` are batched in one JSON-RPC request and
|
|
@@ -236,6 +237,7 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
|
|
|
236
237
|
}
|
|
237
238
|
}
|
|
238
239
|
const allProvidersFailedError = new allProvidersFailed_error.AllProvidersFailedError(`All attempts to do ETH1 RPC request failed for ${method}`);
|
|
240
|
+
sanitizeError.sanitizeErrorInPlace(this.lastError);
|
|
239
241
|
allProvidersFailedError.cause = this.lastError;
|
|
240
242
|
this._eventEmitter.emitLazy('rpc', () => ({
|
|
241
243
|
action: 'fallback-provider:request:failed:all',
|
|
@@ -389,7 +391,7 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
|
|
|
389
391
|
catch (error) {
|
|
390
392
|
// All providers failed - log and retry until timeout
|
|
391
393
|
lastError = error;
|
|
392
|
-
this.logger.warn(this.formatLog(`waitForTransactionWithFallback poll #${pollCount} failed for ${txHash}
|
|
394
|
+
this.logger.warn(this.formatLog(`waitForTransactionWithFallback poll #${pollCount} failed for ${txHash}`), sanitizeError.sanitizeError(error));
|
|
393
395
|
await sleep.sleep(pollInterval);
|
|
394
396
|
}
|
|
395
397
|
}
|