@naturalcycles/js-lib 14.134.0 → 14.136.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 +2 -2
- package/dist/env.d.ts +14 -0
- package/dist/env.js +23 -0
- package/dist/error/error.util.d.ts +1 -1
- package/dist/error/error.util.js +2 -0
- package/dist/error/tryCatch.js +1 -3
- package/dist/http/fetcher.d.ts +2 -0
- package/dist/http/fetcher.js +104 -92
- package/dist/http/fetcher.model.d.ts +14 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/promise/abortable.d.ts +20 -0
- package/dist/promise/abortable.js +36 -0
- package/dist/promise/pDefer.d.ts +14 -1
- package/dist/promise/pDefer.js +2 -0
- package/dist/promise/pDelay.d.ts +18 -0
- package/dist/promise/pDelay.js +37 -2
- package/dist/promise/pRetry.d.ts +0 -8
- package/dist/promise/pRetry.js +37 -63
- package/dist/promise/pTimeout.d.ts +4 -6
- package/dist/promise/pTimeout.js +8 -10
- package/dist/string/stringifyAny.d.ts +0 -6
- package/dist/string/stringifyAny.js +0 -5
- package/dist/types.d.ts +3 -0
- package/dist/vendor/is.d.ts +2 -2
- package/dist-esm/decorators/logMethod.decorator.js +2 -2
- package/dist-esm/env.js +18 -0
- package/dist-esm/error/error.util.js +2 -0
- package/dist-esm/error/tryCatch.js +2 -4
- package/dist-esm/http/fetcher.js +111 -98
- package/dist-esm/index.js +2 -0
- package/dist-esm/promise/abortable.js +32 -0
- package/dist-esm/promise/pDefer.js +2 -0
- package/dist-esm/promise/pDelay.js +35 -1
- package/dist-esm/promise/pRetry.js +38 -61
- package/dist-esm/promise/pTimeout.js +8 -7
- package/dist-esm/string/stringifyAny.js +0 -5
- package/package.json +1 -1
- package/src/decorators/logMethod.decorator.ts +2 -2
- package/src/env.ts +19 -0
- package/src/error/error.util.ts +3 -1
- package/src/error/tryCatch.ts +2 -6
- package/src/http/fetcher.model.ts +14 -3
- package/src/http/fetcher.ts +117 -95
- package/src/index.ts +2 -0
- package/src/promise/abortable.ts +34 -0
- package/src/promise/pDefer.ts +19 -1
- package/src/promise/pDelay.ts +44 -2
- package/src/promise/pRetry.ts +41 -89
- package/src/promise/pState.ts +1 -1
- package/src/promise/pTimeout.ts +12 -14
- package/src/string/stringifyAny.ts +0 -13
- package/src/types.ts +3 -0
- package/src/vendor/is.ts +3 -3
package/dist/promise/pRetry.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.pRetry = exports.pRetryFn = void 0;
|
|
4
4
|
const __1 = require("..");
|
|
5
|
-
const pTimeout_1 = require("./pTimeout");
|
|
6
5
|
/**
|
|
7
6
|
* Returns a Function (!), enhanced with retry capabilities.
|
|
8
7
|
* Implements "Exponential back-off strategy" by multiplying the delay by `delayMultiplier` with each try.
|
|
@@ -14,8 +13,8 @@ function pRetryFn(fn, opt = {}) {
|
|
|
14
13
|
}
|
|
15
14
|
exports.pRetryFn = pRetryFn;
|
|
16
15
|
async function pRetry(fn, opt = {}) {
|
|
17
|
-
const { maxAttempts = 4, delay: initialDelay = 1000, delayMultiplier = 2, predicate, logger = console, name,
|
|
18
|
-
const fakeError =
|
|
16
|
+
const { maxAttempts = 4, delay: initialDelay = 1000, delayMultiplier = 2, predicate, logger = console, name, timeout, } = opt;
|
|
17
|
+
const fakeError = timeout ? new Error('TimeoutError') : undefined;
|
|
19
18
|
let { logFirstAttempt = false, logRetries = true, logFailures = false, logSuccess = false } = opt;
|
|
20
19
|
if (opt.logAll) {
|
|
21
20
|
logSuccess = logFirstAttempt = logRetries = logFailures = true;
|
|
@@ -26,70 +25,45 @@ async function pRetry(fn, opt = {}) {
|
|
|
26
25
|
const fname = name || fn.name || 'pRetry function';
|
|
27
26
|
let delay = initialDelay;
|
|
28
27
|
let attempt = 0;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
// keep original stack
|
|
37
|
-
err.stack = fakeError.stack.replace('Error: RetryError', 'TimeoutError');
|
|
28
|
+
/* eslint-disable no-await-in-loop, no-constant-condition */
|
|
29
|
+
while (true) {
|
|
30
|
+
const started = Date.now();
|
|
31
|
+
try {
|
|
32
|
+
attempt++;
|
|
33
|
+
if ((attempt === 1 && logFirstAttempt) || (attempt > 1 && logRetries)) {
|
|
34
|
+
logger.log(`${fname} attempt #${attempt}...`);
|
|
38
35
|
}
|
|
39
|
-
|
|
40
|
-
};
|
|
41
|
-
const next = async () => {
|
|
42
|
-
if (timedOut)
|
|
43
|
-
return;
|
|
36
|
+
let result;
|
|
44
37
|
if (timeout) {
|
|
45
|
-
|
|
38
|
+
await (0, __1.pTimeout)(async () => await fn(attempt), {
|
|
39
|
+
timeout,
|
|
40
|
+
name: fname,
|
|
41
|
+
errorData: opt.errorData,
|
|
42
|
+
fakeError,
|
|
43
|
+
});
|
|
46
44
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
attempt++;
|
|
50
|
-
if ((attempt === 1 && logFirstAttempt) || (attempt > 1 && logRetries)) {
|
|
51
|
-
logger.log(`${fname} attempt #${attempt}...`);
|
|
52
|
-
}
|
|
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);
|
|
45
|
+
else {
|
|
46
|
+
result = await fn(attempt);
|
|
59
47
|
}
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
});
|
|
77
|
-
}
|
|
78
|
-
;
|
|
79
|
-
err.data = {
|
|
80
|
-
...err.data,
|
|
81
|
-
...opt.errorData,
|
|
82
|
-
};
|
|
83
|
-
reject(err);
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
// Retry after delay
|
|
87
|
-
delay *= delayMultiplier;
|
|
88
|
-
setTimeout(next, delay);
|
|
89
|
-
}
|
|
48
|
+
if (logSuccess) {
|
|
49
|
+
logger.log(`${fname} attempt #${attempt} succeeded in ${(0, __1._since)(started)}`);
|
|
90
50
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
if (logFailures) {
|
|
55
|
+
logger.warn(`${fname} attempt #${attempt} error in ${(0, __1._since)(started)}:`, err);
|
|
56
|
+
}
|
|
57
|
+
if (attempt >= maxAttempts || (predicate && !predicate(err, attempt, maxAttempts))) {
|
|
58
|
+
// Give up
|
|
59
|
+
(0, __1._errorDataAppend)(err, opt.errorData);
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
62
|
+
// Retry after delay
|
|
63
|
+
delay *= delayMultiplier;
|
|
64
|
+
await (0, __1.pDelay)(delay);
|
|
65
|
+
// back to while(true) loop
|
|
66
|
+
}
|
|
67
|
+
}
|
|
94
68
|
}
|
|
95
69
|
exports.pRetry = pRetry;
|
|
@@ -23,13 +23,11 @@ export interface PTimeoutOptions {
|
|
|
23
23
|
*/
|
|
24
24
|
onTimeout?: (err: TimeoutError) => any;
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* @experimental
|
|
26
|
+
* If passed - fakeError.stack will be used as a stacktrace.
|
|
27
|
+
* This is to "keep stacktrace" when pTimeout is called from another
|
|
28
|
+
* function (like pRetry).
|
|
31
29
|
*/
|
|
32
|
-
|
|
30
|
+
fakeError?: Error;
|
|
33
31
|
/**
|
|
34
32
|
* Will be merged with `err.data` object.
|
|
35
33
|
*/
|
package/dist/promise/pTimeout.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.pTimeout = exports.pTimeoutFn = exports.TimeoutError = void 0;
|
|
4
4
|
const app_error_1 = require("../error/app.error");
|
|
5
|
+
const error_util_1 = require("../error/error.util");
|
|
5
6
|
class TimeoutError extends app_error_1.AppError {
|
|
6
7
|
constructor(message, data = {}, opt) {
|
|
7
8
|
super(message, data, opt, 'TimeoutError');
|
|
@@ -29,26 +30,23 @@ exports.pTimeoutFn = pTimeoutFn;
|
|
|
29
30
|
* If the Function rejects - passes this rejection further.
|
|
30
31
|
*/
|
|
31
32
|
async function pTimeout(fn, opt) {
|
|
32
|
-
const { timeout, name = fn.name || 'pTimeout function', onTimeout
|
|
33
|
-
const fakeError =
|
|
33
|
+
const { timeout, name = fn.name || 'pTimeout function', onTimeout } = opt;
|
|
34
|
+
const fakeError = opt.fakeError || new Error('TimeoutError');
|
|
34
35
|
// eslint-disable-next-line no-async-promise-executor
|
|
35
36
|
return await new Promise(async (resolve, reject) => {
|
|
36
37
|
// Prepare the timeout timer
|
|
37
38
|
const timer = setTimeout(() => {
|
|
38
39
|
const err = new TimeoutError(`"${name}" timed out after ${timeout} ms`, opt.errorData);
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
// keep original stack
|
|
41
|
+
err.stack = fakeError.stack.replace('Error: TimeoutError', 'TimeoutError: ' + err.message);
|
|
41
42
|
if (onTimeout) {
|
|
42
43
|
try {
|
|
43
44
|
resolve(onTimeout(err));
|
|
44
45
|
}
|
|
45
46
|
catch (err) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
err.
|
|
49
|
-
...err.data,
|
|
50
|
-
...opt.errorData,
|
|
51
|
-
};
|
|
47
|
+
// keep original stack
|
|
48
|
+
err.stack = fakeError.stack.replace('Error: TimeoutError', err.name + ': ' + err.message);
|
|
49
|
+
(0, error_util_1._errorDataAppend)(err, opt.errorData);
|
|
52
50
|
reject(err);
|
|
53
51
|
}
|
|
54
52
|
return;
|
|
@@ -20,12 +20,6 @@ export interface StringifyAnyOptions {
|
|
|
20
20
|
* Default limit is less than in Node, cause it's likely to be used e.g in Browser alert()
|
|
21
21
|
*/
|
|
22
22
|
maxLen?: number;
|
|
23
|
-
/**
|
|
24
|
-
* Pass true to include "stringified" `error.data` in the output.
|
|
25
|
-
*
|
|
26
|
-
* @default false
|
|
27
|
-
*/
|
|
28
|
-
includeErrorData?: boolean;
|
|
29
23
|
/**
|
|
30
24
|
* Set to true to print Error.stack instead of just Error.message.
|
|
31
25
|
*
|
|
@@ -86,11 +86,6 @@ function _stringifyAny(obj, opt = {}) {
|
|
|
86
86
|
// `replace` here works ONCE, exactly as we need it
|
|
87
87
|
s = s.replace('HttpError', `HttpError(${obj.data.httpStatusCode})`);
|
|
88
88
|
}
|
|
89
|
-
// Here we ensure it has `data`
|
|
90
|
-
const { data } = obj;
|
|
91
|
-
if (opt.includeErrorData && Object.keys(data).length > 0) {
|
|
92
|
-
s = [s, _stringifyAny(data, opt)].join('\n');
|
|
93
|
-
}
|
|
94
89
|
}
|
|
95
90
|
else if (typeof obj.code === 'string') {
|
|
96
91
|
// Error that has no `data`, but has `code` property
|
package/dist/types.d.ts
CHANGED
|
@@ -59,6 +59,9 @@ export type UnsavedId<T extends Partial<ObjectWithId>> = Omit<T, 'id'> & {
|
|
|
59
59
|
*/
|
|
60
60
|
export type AnyFunction<T = any> = (...args: any[]) => T;
|
|
61
61
|
export type AnyAsyncFunction<T = any> = (...args: any[]) => Promise<T>;
|
|
62
|
+
export type AsyncFunction<T = any> = () => Promise<T>;
|
|
63
|
+
export type AnyPromisableFunction<T = any> = (...args: any[]) => Promisable<T>;
|
|
64
|
+
export type PromisableFunction<T = any> = () => Promisable<T>;
|
|
62
65
|
/**
|
|
63
66
|
* Symbol to indicate END of Sequence.
|
|
64
67
|
*/
|
package/dist/vendor/is.d.ts
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
/// <reference lib="dom" />
|
|
6
6
|
import { Class, ObservableLike, Primitive, TypedArray } from '../typeFest';
|
|
7
7
|
declare const objectTypeNames: readonly ["Function", "Generator", "AsyncGenerator", "GeneratorFunction", "AsyncGeneratorFunction", "AsyncFunction", "Observable", "Array", "Buffer", "Object", "RegExp", "Date", "Error", "Map", "Set", "WeakMap", "WeakSet", "ArrayBuffer", "SharedArrayBuffer", "DataView", "Promise", "URL", "FormData", "URLSearchParams", "HTMLElement", "Int8Array", "Uint8Array", "Uint8ClampedArray", "Int16Array", "Uint16Array", "Int32Array", "Uint32Array", "Float32Array", "Float64Array", "BigInt64Array", "BigUint64Array"];
|
|
8
|
-
type ObjectTypeName = typeof objectTypeNames[number];
|
|
8
|
+
type ObjectTypeName = (typeof objectTypeNames)[number];
|
|
9
9
|
declare const primitiveTypeNames: readonly ["null", "undefined", "string", "number", "bigint", "boolean", "symbol"];
|
|
10
|
-
type PrimitiveTypeName = typeof primitiveTypeNames[number];
|
|
10
|
+
type PrimitiveTypeName = (typeof primitiveTypeNames)[number];
|
|
11
11
|
export type TypeName = ObjectTypeName | PrimitiveTypeName;
|
|
12
12
|
export declare function is(value: unknown): TypeName;
|
|
13
13
|
export declare namespace is {
|
|
@@ -76,10 +76,10 @@ function logFinished(logger, callSignature, started, sma, logResultFn, res, err)
|
|
|
76
76
|
t.push(`(avg ${_ms(sma.push(millis))})`);
|
|
77
77
|
}
|
|
78
78
|
if (err !== undefined) {
|
|
79
|
-
t.push('ERROR:',
|
|
79
|
+
t.push('ERROR:', err);
|
|
80
80
|
}
|
|
81
81
|
else if (logResultFn) {
|
|
82
82
|
t.push(...logResultFn(res));
|
|
83
83
|
}
|
|
84
|
-
logger.log(t.filter(Boolean)
|
|
84
|
+
logger.log(...t.filter(Boolean));
|
|
85
85
|
}
|
package/dist-esm/env.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Use it to detect SSR/Node.js environment.
|
|
3
|
+
*
|
|
4
|
+
* Will return `true` in Node.js.
|
|
5
|
+
* Will return `false` in the Browser.
|
|
6
|
+
*/
|
|
7
|
+
export function isServerSide() {
|
|
8
|
+
return typeof window === 'undefined';
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Use it to detect Browser (not SSR/Node) environment.
|
|
12
|
+
*
|
|
13
|
+
* Will return `true` in the Browser.
|
|
14
|
+
* Will return `false` in Node.js.
|
|
15
|
+
*/
|
|
16
|
+
export function isClientSide() {
|
|
17
|
+
return typeof window !== 'undefined';
|
|
18
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _anyToError, _since
|
|
1
|
+
import { _anyToError, _since } from '../index';
|
|
2
2
|
/**
|
|
3
3
|
* Decorates a function with "try/catch", so it'll never reject/throw.
|
|
4
4
|
* Only applies to async functions (or, turns sync function into async).
|
|
@@ -21,9 +21,7 @@ export function _tryCatch(fn, opt = {}) {
|
|
|
21
21
|
}
|
|
22
22
|
catch (err) {
|
|
23
23
|
if (logError) {
|
|
24
|
-
logger.warn(`tryCatch.${fname} error in ${_since(started)}
|
|
25
|
-
includeErrorData: true,
|
|
26
|
-
})}`);
|
|
24
|
+
logger.warn(`tryCatch.${fname} error in ${_since(started)}:`, err);
|
|
27
25
|
}
|
|
28
26
|
if (onError) {
|
|
29
27
|
try {
|
package/dist-esm/http/fetcher.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/// <reference lib="dom"/>
|
|
2
2
|
import { __asyncValues } from "tslib";
|
|
3
|
+
import { isServerSide } from '../env';
|
|
3
4
|
import { _anyToError, _anyToErrorObject, _errorToErrorObject } from '../error/error.util';
|
|
4
5
|
import { HttpError } from '../error/http.error';
|
|
5
6
|
import { _clamp } from '../number/number.util';
|
|
@@ -82,10 +83,10 @@ export class Fetcher {
|
|
|
82
83
|
*/
|
|
83
84
|
async rawFetch(url, rawOpt = {}) {
|
|
84
85
|
var _a, e_1, _b, _c, _d, e_2, _e, _f;
|
|
85
|
-
var _g
|
|
86
|
+
var _g;
|
|
86
87
|
const { logger } = this.cfg;
|
|
87
88
|
const req = this.normalizeOptions(url, rawOpt);
|
|
88
|
-
const { timeoutSeconds,
|
|
89
|
+
const { timeoutSeconds, init: { method }, } = req;
|
|
89
90
|
// setup timeout
|
|
90
91
|
let timeout;
|
|
91
92
|
if (timeoutSeconds) {
|
|
@@ -96,25 +97,29 @@ export class Fetcher {
|
|
|
96
97
|
}, timeoutSeconds * 1000);
|
|
97
98
|
}
|
|
98
99
|
try {
|
|
99
|
-
for (var
|
|
100
|
-
_c =
|
|
101
|
-
|
|
100
|
+
for (var _h = true, _j = __asyncValues(this.cfg.hooks.beforeRequest || []), _k; _k = await _j.next(), _a = _k.done, !_a;) {
|
|
101
|
+
_c = _k.value;
|
|
102
|
+
_h = false;
|
|
102
103
|
try {
|
|
103
104
|
const hook = _c;
|
|
104
105
|
await hook(req);
|
|
105
106
|
}
|
|
106
107
|
finally {
|
|
107
|
-
|
|
108
|
+
_h = true;
|
|
108
109
|
}
|
|
109
110
|
}
|
|
110
111
|
}
|
|
111
112
|
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
112
113
|
finally {
|
|
113
114
|
try {
|
|
114
|
-
if (!
|
|
115
|
+
if (!_h && !_a && (_b = _j.return)) await _b.call(_j);
|
|
115
116
|
}
|
|
116
117
|
finally { if (e_1) throw e_1.error; }
|
|
117
118
|
}
|
|
119
|
+
const isFullUrl = req.url.includes('://');
|
|
120
|
+
const fullUrl = isFullUrl ? new URL(req.url) : undefined;
|
|
121
|
+
const shortUrl = fullUrl ? this.getShortUrl(fullUrl) : req.url;
|
|
122
|
+
const signature = [method, shortUrl].join(' ');
|
|
118
123
|
const res = {
|
|
119
124
|
req,
|
|
120
125
|
retryStatus: {
|
|
@@ -122,10 +127,8 @@ export class Fetcher {
|
|
|
122
127
|
retryStopped: false,
|
|
123
128
|
retryTimeout: req.retry.timeout,
|
|
124
129
|
},
|
|
130
|
+
signature,
|
|
125
131
|
};
|
|
126
|
-
const fullUrl = new URL(req.url);
|
|
127
|
-
const shortUrl = this.getShortUrl(fullUrl);
|
|
128
|
-
const signature = [method, shortUrl].join(' ');
|
|
129
132
|
/* eslint-disable no-await-in-loop */
|
|
130
133
|
while (!res.retryStatus.retryStopped) {
|
|
131
134
|
const started = Date.now();
|
|
@@ -149,114 +152,124 @@ export class Fetcher {
|
|
|
149
152
|
}
|
|
150
153
|
res.statusFamily = this.getStatusFamily(res);
|
|
151
154
|
if ((_g = res.fetchResponse) === null || _g === void 0 ? void 0 : _g.ok) {
|
|
152
|
-
|
|
153
|
-
if (res.fetchResponse.body) {
|
|
154
|
-
const text = await res.fetchResponse.text();
|
|
155
|
-
if (text) {
|
|
156
|
-
try {
|
|
157
|
-
res.body = text;
|
|
158
|
-
res.body = JSON.parse(text, req.jsonReviver);
|
|
159
|
-
}
|
|
160
|
-
catch (err) {
|
|
161
|
-
const { message } = _anyToError(err);
|
|
162
|
-
res.err = new HttpError([signature, message].join('\n'), {
|
|
163
|
-
httpStatusCode: 0,
|
|
164
|
-
url: req.url,
|
|
165
|
-
});
|
|
166
|
-
res.ok = false;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
else {
|
|
170
|
-
// Body had a '' (empty string)
|
|
171
|
-
res.body = {};
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
// if no body: set responseBody as {}
|
|
176
|
-
// do not throw a "cannot parse null as Json" error
|
|
177
|
-
res.body = {};
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
else if (mode === 'text') {
|
|
181
|
-
res.body = res.fetchResponse.body ? await res.fetchResponse.text() : '';
|
|
182
|
-
}
|
|
183
|
-
else if (mode === 'arrayBuffer') {
|
|
184
|
-
res.body = res.fetchResponse.body ? await res.fetchResponse.arrayBuffer() : {};
|
|
185
|
-
}
|
|
186
|
-
else if (mode === 'blob') {
|
|
187
|
-
res.body = res.fetchResponse.body ? await res.fetchResponse.blob() : {};
|
|
188
|
-
}
|
|
189
|
-
clearTimeout(timeout);
|
|
190
|
-
res.retryStatus.retryStopped = true;
|
|
191
|
-
// res.err can happen on JSON.parse error
|
|
192
|
-
if (!res.err && this.cfg.logResponse) {
|
|
193
|
-
const { retryAttempt } = res.retryStatus;
|
|
194
|
-
logger.log([
|
|
195
|
-
' <<',
|
|
196
|
-
res.fetchResponse.status,
|
|
197
|
-
signature,
|
|
198
|
-
retryAttempt && `try#${retryAttempt + 1}/${req.retry.count + 1}`,
|
|
199
|
-
_since(started),
|
|
200
|
-
]
|
|
201
|
-
.filter(Boolean)
|
|
202
|
-
.join(' '));
|
|
203
|
-
if (this.cfg.logResponseBody) {
|
|
204
|
-
logger.log(res.body);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
155
|
+
await this.onOkResponse(res, started, timeout);
|
|
207
156
|
}
|
|
208
157
|
else {
|
|
209
158
|
// !res.ok
|
|
210
|
-
|
|
211
|
-
let errObj;
|
|
212
|
-
if (res.fetchResponse) {
|
|
213
|
-
const body = _jsonParseIfPossible(await res.fetchResponse.text());
|
|
214
|
-
errObj = _anyToErrorObject(body);
|
|
215
|
-
}
|
|
216
|
-
else if (res.err) {
|
|
217
|
-
errObj = _errorToErrorObject(res.err);
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
220
|
-
errObj = {};
|
|
221
|
-
}
|
|
222
|
-
const originalMessage = errObj.message;
|
|
223
|
-
errObj.message = [
|
|
224
|
-
[(_h = res.fetchResponse) === null || _h === void 0 ? void 0 : _h.status, signature].filter(Boolean).join(' '),
|
|
225
|
-
originalMessage,
|
|
226
|
-
]
|
|
227
|
-
.filter(Boolean)
|
|
228
|
-
.join('\n');
|
|
229
|
-
res.err = new HttpError(errObj.message, _filterNullishValues(Object.assign(Object.assign({}, errObj.data), { originalMessage, httpStatusCode: ((_j = res.fetchResponse) === null || _j === void 0 ? void 0 : _j.status) || 0,
|
|
230
|
-
// These properties are provided to be used in e.g custom Sentry error grouping
|
|
231
|
-
// Actually, disabled now, to avoid unnecessary error printing when both msg and data are printed
|
|
232
|
-
// Enabled, cause `data` is not printed by default when error is HttpError
|
|
233
|
-
// method: req.method,
|
|
234
|
-
url: req.url })));
|
|
235
|
-
await this.processRetry(res);
|
|
159
|
+
await this.onNotOkResponse(res, timeout);
|
|
236
160
|
}
|
|
237
161
|
}
|
|
238
162
|
try {
|
|
239
|
-
for (var
|
|
240
|
-
_f =
|
|
241
|
-
|
|
163
|
+
for (var _l = true, _m = __asyncValues(this.cfg.hooks.afterResponse || []), _o; _o = await _m.next(), _d = _o.done, !_d;) {
|
|
164
|
+
_f = _o.value;
|
|
165
|
+
_l = false;
|
|
242
166
|
try {
|
|
243
167
|
const hook = _f;
|
|
244
168
|
await hook(res);
|
|
245
169
|
}
|
|
246
170
|
finally {
|
|
247
|
-
|
|
171
|
+
_l = true;
|
|
248
172
|
}
|
|
249
173
|
}
|
|
250
174
|
}
|
|
251
175
|
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
252
176
|
finally {
|
|
253
177
|
try {
|
|
254
|
-
if (!
|
|
178
|
+
if (!_l && !_d && (_e = _m.return)) await _e.call(_m);
|
|
255
179
|
}
|
|
256
180
|
finally { if (e_2) throw e_2.error; }
|
|
257
181
|
}
|
|
258
182
|
return res;
|
|
259
183
|
}
|
|
184
|
+
async onOkResponse(res, started, timeout) {
|
|
185
|
+
const { req } = res;
|
|
186
|
+
const { mode } = res.req;
|
|
187
|
+
if (mode === 'json') {
|
|
188
|
+
if (res.fetchResponse.body) {
|
|
189
|
+
const text = await res.fetchResponse.text();
|
|
190
|
+
if (text) {
|
|
191
|
+
try {
|
|
192
|
+
res.body = text;
|
|
193
|
+
res.body = JSON.parse(text, req.jsonReviver);
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
const { message } = _anyToError(err);
|
|
197
|
+
res.err = new HttpError([res.signature, message].join('\n'), {
|
|
198
|
+
httpStatusCode: 0,
|
|
199
|
+
url: req.url,
|
|
200
|
+
});
|
|
201
|
+
res.ok = false;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
// Body had a '' (empty string)
|
|
206
|
+
res.body = {};
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
// if no body: set responseBody as {}
|
|
211
|
+
// do not throw a "cannot parse null as Json" error
|
|
212
|
+
res.body = {};
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else if (mode === 'text') {
|
|
216
|
+
res.body = res.fetchResponse.body ? await res.fetchResponse.text() : '';
|
|
217
|
+
}
|
|
218
|
+
else if (mode === 'arrayBuffer') {
|
|
219
|
+
res.body = res.fetchResponse.body ? await res.fetchResponse.arrayBuffer() : {};
|
|
220
|
+
}
|
|
221
|
+
else if (mode === 'blob') {
|
|
222
|
+
res.body = res.fetchResponse.body ? await res.fetchResponse.blob() : {};
|
|
223
|
+
}
|
|
224
|
+
clearTimeout(timeout);
|
|
225
|
+
res.retryStatus.retryStopped = true;
|
|
226
|
+
// res.err can happen on JSON.parse error
|
|
227
|
+
if (!res.err && this.cfg.logResponse) {
|
|
228
|
+
const { retryAttempt } = res.retryStatus;
|
|
229
|
+
const { logger } = this.cfg;
|
|
230
|
+
logger.log([
|
|
231
|
+
' <<',
|
|
232
|
+
res.fetchResponse.status,
|
|
233
|
+
res.signature,
|
|
234
|
+
retryAttempt && `try#${retryAttempt + 1}/${req.retry.count + 1}`,
|
|
235
|
+
_since(started),
|
|
236
|
+
]
|
|
237
|
+
.filter(Boolean)
|
|
238
|
+
.join(' '));
|
|
239
|
+
if (this.cfg.logResponseBody) {
|
|
240
|
+
logger.log(res.body);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
async onNotOkResponse(res, timeout) {
|
|
245
|
+
var _a, _b;
|
|
246
|
+
clearTimeout(timeout);
|
|
247
|
+
let errObj;
|
|
248
|
+
if (res.fetchResponse) {
|
|
249
|
+
const body = _jsonParseIfPossible(await res.fetchResponse.text());
|
|
250
|
+
errObj = _anyToErrorObject(body);
|
|
251
|
+
}
|
|
252
|
+
else if (res.err) {
|
|
253
|
+
errObj = _errorToErrorObject(res.err);
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
errObj = {};
|
|
257
|
+
}
|
|
258
|
+
const originalMessage = errObj.message;
|
|
259
|
+
errObj.message = [
|
|
260
|
+
[(_a = res.fetchResponse) === null || _a === void 0 ? void 0 : _a.status, res.signature].filter(Boolean).join(' '),
|
|
261
|
+
originalMessage,
|
|
262
|
+
]
|
|
263
|
+
.filter(Boolean)
|
|
264
|
+
.join('\n');
|
|
265
|
+
res.err = new HttpError(errObj.message, _filterNullishValues(Object.assign(Object.assign({}, errObj.data), { originalMessage, httpStatusCode: ((_b = res.fetchResponse) === null || _b === void 0 ? void 0 : _b.status) || 0,
|
|
266
|
+
// These properties are provided to be used in e.g custom Sentry error grouping
|
|
267
|
+
// Actually, disabled now, to avoid unnecessary error printing when both msg and data are printed
|
|
268
|
+
// Enabled, cause `data` is not printed by default when error is HttpError
|
|
269
|
+
// method: req.method,
|
|
270
|
+
url: res.req.url })));
|
|
271
|
+
await this.processRetry(res);
|
|
272
|
+
}
|
|
260
273
|
async processRetry(res) {
|
|
261
274
|
var _a, e_3, _b, _c;
|
|
262
275
|
const { retryStatus } = res;
|
|
@@ -347,7 +360,7 @@ export class Fetcher {
|
|
|
347
360
|
if (!this.cfg.logWithSearchParams) {
|
|
348
361
|
shortUrl = shortUrl.split('?')[0];
|
|
349
362
|
}
|
|
350
|
-
if (!this.cfg.
|
|
363
|
+
if (!this.cfg.logWithBaseUrl && baseUrl && shortUrl.startsWith(baseUrl)) {
|
|
351
364
|
shortUrl = shortUrl.slice(baseUrl.length);
|
|
352
365
|
}
|
|
353
366
|
return shortUrl;
|
|
@@ -375,7 +388,7 @@ export class Fetcher {
|
|
|
375
388
|
logRequestBody: debug,
|
|
376
389
|
logResponse: debug,
|
|
377
390
|
logResponseBody: debug,
|
|
378
|
-
|
|
391
|
+
logWithBaseUrl: isServerSide(),
|
|
379
392
|
logWithSearchParams: true,
|
|
380
393
|
retry: Object.assign({}, defRetryOptions),
|
|
381
394
|
init: {
|
package/dist-esm/index.js
CHANGED
|
@@ -61,6 +61,7 @@ export * from './unit/size.util';
|
|
|
61
61
|
export * from './log/commonLogger';
|
|
62
62
|
export * from './string/safeJsonStringify';
|
|
63
63
|
export * from './promise/pQueue';
|
|
64
|
+
export * from './promise/abortable';
|
|
64
65
|
export * from './seq/seq';
|
|
65
66
|
export * from './math/stack.util';
|
|
66
67
|
export * from './string/leven';
|
|
@@ -72,6 +73,7 @@ export * from './datetime/localDate';
|
|
|
72
73
|
export * from './datetime/localTime';
|
|
73
74
|
export * from './datetime/dateInterval';
|
|
74
75
|
export * from './datetime/timeInterval';
|
|
76
|
+
export * from './env';
|
|
75
77
|
export * from './http/http.model';
|
|
76
78
|
export * from './http/fetcher';
|
|
77
79
|
export * from './http/fetcher.model';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Similar to AbortController and AbortSignal.
|
|
3
|
+
* Similar to pDefer and Promise.
|
|
4
|
+
* Similar to Subject and Observable.
|
|
5
|
+
*
|
|
6
|
+
* Minimal interface for something that can be aborted in the future,
|
|
7
|
+
* but not necessary.
|
|
8
|
+
* Allows to listen to `onAbort` event.
|
|
9
|
+
*
|
|
10
|
+
* @experimental
|
|
11
|
+
*/
|
|
12
|
+
export class Abortable {
|
|
13
|
+
constructor(onAbort) {
|
|
14
|
+
this.onAbort = onAbort;
|
|
15
|
+
this.aborted = false;
|
|
16
|
+
}
|
|
17
|
+
abort() {
|
|
18
|
+
var _a;
|
|
19
|
+
if (this.aborted)
|
|
20
|
+
return;
|
|
21
|
+
this.aborted = true;
|
|
22
|
+
(_a = this.onAbort) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
23
|
+
this.onAbort = undefined; // cleanup listener
|
|
24
|
+
}
|
|
25
|
+
clear() {
|
|
26
|
+
this.onAbort = undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// convenience function
|
|
30
|
+
export function abortable(onAbort) {
|
|
31
|
+
return new Abortable(onAbort);
|
|
32
|
+
}
|
|
@@ -11,5 +11,7 @@ export function pDefer() {
|
|
|
11
11
|
});
|
|
12
12
|
promise.resolve = resolve;
|
|
13
13
|
promise.reject = reject;
|
|
14
|
+
promise.rejectAborted = reason => reject(new Error(['Aborted', reason].filter(Boolean).join(': ')));
|
|
15
|
+
promise.abort = reason => promise.rejectAborted(reason);
|
|
14
16
|
return promise;
|
|
15
17
|
}
|