@naturalcycles/js-lib 14.157.0 → 14.158.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/error/assert.d.ts +6 -0
- package/dist/error/assert.js +13 -1
- package/dist/error/httpRequestError.d.ts +7 -1
- package/dist/error/try.d.ts +7 -9
- package/dist/error/try.js +22 -12
- package/dist/http/fetcher.d.ts +23 -2
- package/dist/http/fetcher.js +89 -45
- package/dist/http/fetcher.model.d.ts +15 -5
- package/dist/promise/pTimeout.d.ts +2 -2
- package/dist/types.d.ts +11 -0
- package/dist-esm/error/assert.js +11 -0
- package/dist-esm/error/try.js +20 -11
- package/dist-esm/http/fetcher.js +89 -46
- package/package.json +1 -1
- package/src/error/assert.ts +15 -0
- package/src/error/httpRequestError.ts +8 -1
- package/src/error/try.ts +32 -16
- package/src/http/fetcher.model.ts +17 -5
- package/src/http/fetcher.ts +101 -51
- package/src/promise/pTimeout.ts +2 -2
- package/src/types.ts +12 -0
package/dist-esm/http/fetcher.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
/// <reference lib="dom"/>
|
|
2
2
|
/// <reference lib="dom.iterable"/>
|
|
3
3
|
import { isServerSide } from '../env';
|
|
4
|
+
import { _assertErrorClassOrRethrow } from '../error/assert';
|
|
4
5
|
import { _anyToError, _anyToErrorObject, _errorLikeToErrorObject } from '../error/error.util';
|
|
5
6
|
import { HttpRequestError } from '../error/httpRequestError';
|
|
6
7
|
import { _clamp } from '../number/number.util';
|
|
7
8
|
import { _filterNullishValues, _filterUndefinedValues, _mapKeys, _merge, _omit, _pick, } from '../object/object.util';
|
|
8
9
|
import { pDelay } from '../promise/pDelay';
|
|
9
|
-
import { TimeoutError } from '../promise/pTimeout';
|
|
10
|
+
import { pTimeout, TimeoutError } from '../promise/pTimeout';
|
|
10
11
|
import { _jsonParse, _jsonParseIfPossible } from '../string/json.util';
|
|
11
12
|
import { _stringifyAny } from '../string/stringifyAny';
|
|
12
|
-
import { _since } from '../time/time.util';
|
|
13
|
+
import { _ms, _since } from '../time/time.util';
|
|
13
14
|
import { HTTP_METHODS } from './http.model';
|
|
14
15
|
const acceptByResponseType = {
|
|
15
16
|
text: 'text/plain',
|
|
@@ -25,6 +26,18 @@ const defRetryOptions = {
|
|
|
25
26
|
timeoutMax: 30000,
|
|
26
27
|
timeoutMultiplier: 2,
|
|
27
28
|
};
|
|
29
|
+
let globalFetcherMock = null;
|
|
30
|
+
/**
|
|
31
|
+
* Allows to mock Fetcher.callNativeFetch globally (for all Fetcher instances out there).
|
|
32
|
+
*
|
|
33
|
+
* Call it with `null` to reset/unmock.
|
|
34
|
+
*
|
|
35
|
+
* Please note that jest.spyOn(someFetcher, 'callNativeFetch') has higher priority than
|
|
36
|
+
* a global mock, so it'll run instead of a global mock.
|
|
37
|
+
*/
|
|
38
|
+
export function setGlobalFetcherMock(fn) {
|
|
39
|
+
globalFetcherMock = fn;
|
|
40
|
+
}
|
|
28
41
|
/**
|
|
29
42
|
* Experimental wrapper around Fetch.
|
|
30
43
|
* Works in both Browser and Node, using `globalThis.fetch`.
|
|
@@ -96,29 +109,33 @@ export class Fetcher {
|
|
|
96
109
|
}
|
|
97
110
|
return res.body;
|
|
98
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Like pTry - returns a [err, data] tuple (aka ErrorDataTuple).
|
|
114
|
+
* err, if defined, is strictly HttpRequestError.
|
|
115
|
+
* UPD: actually not, err is typed as Error, as it feels unsafe to guarantee error type.
|
|
116
|
+
* UPD: actually yes - it will return HttpRequestError, and throw if there's an error
|
|
117
|
+
* of any other type.
|
|
118
|
+
*/
|
|
119
|
+
async tryFetch(opt) {
|
|
120
|
+
const res = await this.doFetch(opt);
|
|
121
|
+
if (res.err) {
|
|
122
|
+
_assertErrorClassOrRethrow(res.err, HttpRequestError);
|
|
123
|
+
return [res.err, null];
|
|
124
|
+
}
|
|
125
|
+
return [null, res.body];
|
|
126
|
+
}
|
|
99
127
|
/**
|
|
100
128
|
* Returns FetcherResponse.
|
|
101
129
|
* Never throws, returns `err` property in the response instead.
|
|
102
130
|
* Use this method instead of `throwHttpErrors: false` or try-catching.
|
|
131
|
+
*
|
|
132
|
+
* Note: responseType defaults to `void`, so, override it if you expect different.
|
|
103
133
|
*/
|
|
104
134
|
async doFetch(opt) {
|
|
105
135
|
var _a, _b;
|
|
106
136
|
const req = this.normalizeOptions(opt);
|
|
107
137
|
const { logger } = this.cfg;
|
|
108
138
|
const { timeoutSeconds, init: { method }, } = req;
|
|
109
|
-
// setup timeout
|
|
110
|
-
let timeout;
|
|
111
|
-
if (timeoutSeconds) {
|
|
112
|
-
const abortController = new AbortController();
|
|
113
|
-
req.init.signal = abortController.signal;
|
|
114
|
-
timeout = setTimeout(() => {
|
|
115
|
-
// Apparently, providing a `string` reason to abort() causes Undici to throw `invalid_argument` error,
|
|
116
|
-
// so, we're wrapping it in a TimeoutError instance
|
|
117
|
-
abortController.abort(new TimeoutError(`request timed out after ${timeoutSeconds} sec`));
|
|
118
|
-
// abortController.abort(`timeout of ${timeoutSeconds} sec`)
|
|
119
|
-
// abortController.abort()
|
|
120
|
-
}, timeoutSeconds * 1000);
|
|
121
|
-
}
|
|
122
139
|
for (const hook of this.cfg.hooks.beforeRequest || []) {
|
|
123
140
|
await hook(req);
|
|
124
141
|
}
|
|
@@ -137,6 +154,18 @@ export class Fetcher {
|
|
|
137
154
|
};
|
|
138
155
|
while (!res.retryStatus.retryStopped) {
|
|
139
156
|
req.started = Date.now();
|
|
157
|
+
// setup timeout
|
|
158
|
+
let timeoutId;
|
|
159
|
+
if (timeoutSeconds) {
|
|
160
|
+
const abortController = new AbortController();
|
|
161
|
+
req.init.signal = abortController.signal;
|
|
162
|
+
timeoutId = setTimeout(() => {
|
|
163
|
+
// console.log(`actual request timed out in ${_since(req.started)}`)
|
|
164
|
+
// Apparently, providing a `string` reason to abort() causes Undici to throw `invalid_argument` error,
|
|
165
|
+
// so, we're wrapping it in a TimeoutError instance
|
|
166
|
+
abortController.abort(new TimeoutError(`request timed out after ${timeoutSeconds} sec`));
|
|
167
|
+
}, timeoutSeconds * 1000);
|
|
168
|
+
}
|
|
140
169
|
if (this.cfg.logRequest) {
|
|
141
170
|
const { retryAttempt } = res.retryStatus;
|
|
142
171
|
logger.log([' >>', signature, retryAttempt && `try#${retryAttempt + 1}/${req.retry.count + 1}`]
|
|
@@ -160,14 +189,30 @@ export class Fetcher {
|
|
|
160
189
|
// important to set it to undefined, otherwise it can keep the previous value (from previous try)
|
|
161
190
|
res.fetchResponse = undefined;
|
|
162
191
|
}
|
|
192
|
+
finally {
|
|
193
|
+
clearTimeout(timeoutId);
|
|
194
|
+
// Separate Timeout will be introduced to "download and parse the body"
|
|
195
|
+
}
|
|
163
196
|
res.statusFamily = this.getStatusFamily(res);
|
|
164
197
|
res.statusCode = (_a = res.fetchResponse) === null || _a === void 0 ? void 0 : _a.status;
|
|
165
198
|
if ((_b = res.fetchResponse) === null || _b === void 0 ? void 0 : _b.ok) {
|
|
166
|
-
|
|
199
|
+
try {
|
|
200
|
+
// We are applying a separate Timeout (as long as original Timeout for now) to "download and parse the body"
|
|
201
|
+
await pTimeout(async () => await this.onOkResponse(res), {
|
|
202
|
+
timeout: timeoutSeconds * 1000 || Number.POSITIVE_INFINITY,
|
|
203
|
+
name: 'Fetcher.onOkResponse',
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
// onOkResponse can still fail, e.g when loading/parsing json, text or doing other response manipulation
|
|
208
|
+
res.err = _anyToError(err);
|
|
209
|
+
res.ok = false;
|
|
210
|
+
await this.onNotOkResponse(res);
|
|
211
|
+
}
|
|
167
212
|
}
|
|
168
213
|
else {
|
|
169
214
|
// !res.ok
|
|
170
|
-
await this.onNotOkResponse(res
|
|
215
|
+
await this.onNotOkResponse(res);
|
|
171
216
|
}
|
|
172
217
|
}
|
|
173
218
|
for (const hook of this.cfg.hooks.afterResponse || []) {
|
|
@@ -175,31 +220,17 @@ export class Fetcher {
|
|
|
175
220
|
}
|
|
176
221
|
return res;
|
|
177
222
|
}
|
|
178
|
-
async onOkResponse(res
|
|
223
|
+
async onOkResponse(res) {
|
|
179
224
|
const { req } = res;
|
|
180
225
|
const { responseType } = res.req;
|
|
226
|
+
// This function is subject to a separate timeout to "download and parse the data"
|
|
181
227
|
if (responseType === 'json') {
|
|
182
228
|
if (res.fetchResponse.body) {
|
|
183
229
|
const text = await res.fetchResponse.text();
|
|
184
230
|
if (text) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
catch (err) {
|
|
190
|
-
// Error while parsing json
|
|
191
|
-
// res.err = _anyToError(err, HttpRequestError, {
|
|
192
|
-
// requestUrl: res.req.url,
|
|
193
|
-
// requestBaseUrl: this.cfg.baseUrl,
|
|
194
|
-
// requestMethod: res.req.init.method,
|
|
195
|
-
// requestSignature: res.signature,
|
|
196
|
-
// requestDuration: Date.now() - started,
|
|
197
|
-
// responseStatusCode: res.fetchResponse.status,
|
|
198
|
-
// } satisfies HttpRequestErrorData)
|
|
199
|
-
res.err = _anyToError(err);
|
|
200
|
-
res.ok = false;
|
|
201
|
-
return await this.onNotOkResponse(res, timeout);
|
|
202
|
-
}
|
|
231
|
+
res.body = text;
|
|
232
|
+
res.body = _jsonParse(text, req.jsonReviver);
|
|
233
|
+
// Error while parsing json can happen - it'll be handled upstream
|
|
203
234
|
}
|
|
204
235
|
else {
|
|
205
236
|
// Body had a '' (empty string)
|
|
@@ -224,12 +255,10 @@ export class Fetcher {
|
|
|
224
255
|
else if (responseType === 'readableStream') {
|
|
225
256
|
res.body = res.fetchResponse.body;
|
|
226
257
|
if (res.body === null) {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
return await this.onNotOkResponse(res, timeout);
|
|
258
|
+
// Error is to be handled upstream
|
|
259
|
+
throw new Error(`fetchResponse.body is null`);
|
|
230
260
|
}
|
|
231
261
|
}
|
|
232
|
-
clearTimeout(timeout);
|
|
233
262
|
res.retryStatus.retryStopped = true;
|
|
234
263
|
// res.err can happen on `failed to fetch` type of error, e.g JSON.parse, CORS, unexpected redirect
|
|
235
264
|
if (!res.err && this.cfg.logResponse) {
|
|
@@ -253,11 +282,14 @@ export class Fetcher {
|
|
|
253
282
|
* This method exists to be able to easily mock it.
|
|
254
283
|
*/
|
|
255
284
|
async callNativeFetch(url, init) {
|
|
285
|
+
// If global mock is defined - call it instead of the native Fetch
|
|
286
|
+
if (globalFetcherMock) {
|
|
287
|
+
return await globalFetcherMock(url, init);
|
|
288
|
+
}
|
|
256
289
|
return await globalThis.fetch(url, init);
|
|
257
290
|
}
|
|
258
|
-
async onNotOkResponse(res
|
|
291
|
+
async onNotOkResponse(res) {
|
|
259
292
|
var _a, _b;
|
|
260
|
-
clearTimeout(timeout);
|
|
261
293
|
let cause;
|
|
262
294
|
if (res.err) {
|
|
263
295
|
// This is only possible on JSON.parse error, or CORS error,
|
|
@@ -271,6 +303,11 @@ export class Fetcher {
|
|
|
271
303
|
cause = _anyToErrorObject(body);
|
|
272
304
|
}
|
|
273
305
|
}
|
|
306
|
+
cause || (cause = {
|
|
307
|
+
name: 'Error',
|
|
308
|
+
message: 'Fetch failed',
|
|
309
|
+
data: {},
|
|
310
|
+
});
|
|
274
311
|
const message = [(_a = res.fetchResponse) === null || _a === void 0 ? void 0 : _a.status, res.signature].filter(Boolean).join(' ');
|
|
275
312
|
res.err = new HttpRequestError(message, _filterNullishValues({
|
|
276
313
|
responseStatusCode: ((_b = res.fetchResponse) === null || _b === void 0 ? void 0 : _b.status) || 0,
|
|
@@ -324,7 +361,11 @@ export class Fetcher {
|
|
|
324
361
|
return;
|
|
325
362
|
retryStatus.retryAttempt++;
|
|
326
363
|
retryStatus.retryTimeout = _clamp(retryStatus.retryTimeout * timeoutMultiplier, 0, timeoutMax);
|
|
327
|
-
|
|
364
|
+
const timeout = this.getRetryTimeout(res);
|
|
365
|
+
if (res.req.debug) {
|
|
366
|
+
this.cfg.logger.log(` .. ${res.signature} waiting ${_ms(timeout)}`);
|
|
367
|
+
}
|
|
368
|
+
await pDelay(timeout);
|
|
328
369
|
}
|
|
329
370
|
getRetryTimeout(res) {
|
|
330
371
|
var _a;
|
|
@@ -380,8 +421,9 @@ export class Fetcher {
|
|
|
380
421
|
if (statusFamily === 3 && !retry3xx)
|
|
381
422
|
return false;
|
|
382
423
|
// should not retry on `unexpected redirect` in error.cause.cause
|
|
383
|
-
if ((_e = (_d = (_c = (_b = res.err) === null || _b === void 0 ? void 0 : _b.cause) === null || _c === void 0 ? void 0 : _c.cause) === null || _d === void 0 ? void 0 : _d.message) === null || _e === void 0 ? void 0 : _e.includes('unexpected redirect'))
|
|
424
|
+
if ((_e = (_d = (_c = (_b = res.err) === null || _b === void 0 ? void 0 : _b.cause) === null || _c === void 0 ? void 0 : _c.cause) === null || _d === void 0 ? void 0 : _d.message) === null || _e === void 0 ? void 0 : _e.includes('unexpected redirect')) {
|
|
384
425
|
return false;
|
|
426
|
+
}
|
|
385
427
|
return true; // default is true
|
|
386
428
|
}
|
|
387
429
|
getStatusFamily(res) {
|
|
@@ -421,14 +463,14 @@ export class Fetcher {
|
|
|
421
463
|
normalizeCfg(cfg) {
|
|
422
464
|
var _a;
|
|
423
465
|
if ((_a = cfg.baseUrl) === null || _a === void 0 ? void 0 : _a.endsWith('/')) {
|
|
424
|
-
console.warn(`Fetcher: baseUrl should not end with
|
|
466
|
+
console.warn(`Fetcher: baseUrl should not end with slash: ${cfg.baseUrl}`);
|
|
425
467
|
cfg.baseUrl = cfg.baseUrl.slice(0, cfg.baseUrl.length - 1);
|
|
426
468
|
}
|
|
427
469
|
const { debug = false } = cfg;
|
|
428
470
|
const norm = _merge({
|
|
429
471
|
baseUrl: '',
|
|
430
472
|
inputUrl: '',
|
|
431
|
-
responseType: '
|
|
473
|
+
responseType: 'json',
|
|
432
474
|
searchParams: {},
|
|
433
475
|
timeoutSeconds: 30,
|
|
434
476
|
retryPost: false,
|
|
@@ -469,6 +511,7 @@ export class Fetcher {
|
|
|
469
511
|
'logRequestBody',
|
|
470
512
|
'logResponse',
|
|
471
513
|
'logResponseBody',
|
|
514
|
+
'debug',
|
|
472
515
|
])), { started: Date.now() }), _omit(opt, ['method', 'headers', 'credentials'])), { inputUrl: opt.url || '', fullUrl: opt.url || '', retry: Object.assign(Object.assign({}, this.cfg.retry), _filterUndefinedValues(opt.retry || {})), init: _merge(Object.assign(Object.assign({}, this.cfg.init), { method: opt.method || this.cfg.init.method, credentials: opt.credentials || this.cfg.init.credentials, redirect: opt.redirect || this.cfg.init.redirect || 'follow' }), {
|
|
473
516
|
headers: _mapKeys(opt.headers || {}, k => k.toLowerCase()),
|
|
474
517
|
}) });
|
package/package.json
CHANGED
package/src/error/assert.ts
CHANGED
|
@@ -102,6 +102,21 @@ export function _assertIsError<ERR extends Error = Error>(
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Asserts that passed object is indeed an Error of defined ErrorClass.
|
|
107
|
+
* If yes - returns peacefully (with TypeScript assertion).
|
|
108
|
+
* In not - throws (re-throws) that error up.
|
|
109
|
+
*/
|
|
110
|
+
export function _assertErrorClassOrRethrow<ERR extends Error>(
|
|
111
|
+
err: any,
|
|
112
|
+
errorClass: Class<ERR>,
|
|
113
|
+
): asserts err is ERR {
|
|
114
|
+
if (!(err instanceof errorClass)) {
|
|
115
|
+
// re-throw
|
|
116
|
+
throw err
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
105
120
|
export function _assertIsErrorObject<DATA_TYPE extends ErrorData = ErrorData>(
|
|
106
121
|
obj: any,
|
|
107
122
|
): asserts obj is ErrorObject<DATA_TYPE> {
|
|
@@ -19,7 +19,14 @@ import type { ErrorObject, HttpRequestErrorData } from './error.model'
|
|
|
19
19
|
* (by default).
|
|
20
20
|
*/
|
|
21
21
|
export class HttpRequestError extends AppError<HttpRequestErrorData> {
|
|
22
|
-
constructor(message: string, data: HttpRequestErrorData, cause
|
|
22
|
+
constructor(message: string, data: HttpRequestErrorData, cause: ErrorObject) {
|
|
23
23
|
super(message, data, cause, 'HttpRequestError')
|
|
24
24
|
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Cause is strictly-defined for HttpRequestError,
|
|
28
|
+
* so it always has a cause.
|
|
29
|
+
* (for dev convenience)
|
|
30
|
+
*/
|
|
31
|
+
override cause!: ErrorObject
|
|
25
32
|
}
|
package/src/error/try.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { _stringifyAny } from '../string/stringifyAny'
|
|
2
2
|
import type { Class } from '../typeFest'
|
|
3
|
-
import type { AnyFunction } from '../types'
|
|
3
|
+
import type { AnyFunction, ErrorDataTuple } from '../types'
|
|
4
4
|
import { AppError } from './app.error'
|
|
5
|
+
import { _assertErrorClassOrRethrow } from './assert'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Calls a function, returns a Tuple of [error, value].
|
|
@@ -10,9 +11,6 @@ import { AppError } from './app.error'
|
|
|
10
11
|
*
|
|
11
12
|
* Similar to pTry, but for sync functions.
|
|
12
13
|
*
|
|
13
|
-
* For convenience, second argument type is non-optional,
|
|
14
|
-
* so you can use it without `!`. But you SHOULD always check `if (err)` first!
|
|
15
|
-
*
|
|
16
14
|
* ERR is typed as Error, not `unknown`. While unknown would be more correct,
|
|
17
15
|
* according to recent TypeScript, Error gives more developer convenience.
|
|
18
16
|
* In our code we NEVER throw non-errors.
|
|
@@ -25,29 +23,35 @@ import { AppError } from './app.error'
|
|
|
25
23
|
* if (err) ...do something...
|
|
26
24
|
* v // go ahead and use v
|
|
27
25
|
*/
|
|
28
|
-
export function _try<ERR
|
|
29
|
-
fn: () =>
|
|
30
|
-
|
|
26
|
+
export function _try<T, ERR extends Error = Error>(
|
|
27
|
+
fn: () => T,
|
|
28
|
+
errorClass?: Class<ERR>,
|
|
29
|
+
): ErrorDataTuple<T, ERR> {
|
|
31
30
|
try {
|
|
32
31
|
return [null, fn()]
|
|
33
32
|
} catch (err) {
|
|
34
|
-
|
|
33
|
+
if (errorClass) {
|
|
34
|
+
_assertErrorClassOrRethrow(err, errorClass)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return [err as ERR, null]
|
|
35
38
|
}
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
/**
|
|
39
42
|
* Like _try, but for Promises.
|
|
40
|
-
*
|
|
41
|
-
* Also, intentionally types second return item as non-optional,
|
|
42
|
-
* but you should check for `err` presense first!
|
|
43
43
|
*/
|
|
44
|
-
export async function pTry<ERR
|
|
45
|
-
promise: Promise<
|
|
46
|
-
|
|
44
|
+
export async function pTry<T, ERR extends Error = Error>(
|
|
45
|
+
promise: Promise<T>,
|
|
46
|
+
errorClass?: Class<ERR>,
|
|
47
|
+
): Promise<ErrorDataTuple<Awaited<T>, ERR>> {
|
|
47
48
|
try {
|
|
48
49
|
return [null, await promise]
|
|
49
50
|
} catch (err) {
|
|
50
|
-
|
|
51
|
+
if (errorClass) {
|
|
52
|
+
_assertErrorClassOrRethrow(err, errorClass)
|
|
53
|
+
}
|
|
54
|
+
return [err as ERR, null]
|
|
51
55
|
}
|
|
52
56
|
}
|
|
53
57
|
|
|
@@ -122,5 +126,17 @@ export async function pExpectedErrorString<ERR = Error>(
|
|
|
122
126
|
promise: Promise<any>,
|
|
123
127
|
errorClass?: Class<ERR>,
|
|
124
128
|
): Promise<string> {
|
|
125
|
-
|
|
129
|
+
const err = await pExpectedError<ERR>(promise, errorClass)
|
|
130
|
+
return _stringifyAny(err)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Shortcut function to simplify error snapshot-matching in tests.
|
|
135
|
+
*/
|
|
136
|
+
export function _expectedErrorString<ERR = Error>(
|
|
137
|
+
fn: AnyFunction,
|
|
138
|
+
errorClass?: Class<ERR>,
|
|
139
|
+
): string {
|
|
140
|
+
const err = _expectedError<ERR>(fn, errorClass)
|
|
141
|
+
return _stringifyAny(err)
|
|
126
142
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CommonLogger } from '../log/commonLogger'
|
|
2
2
|
import type { Promisable } from '../typeFest'
|
|
3
|
-
import type { AnyObject, Reviver, UnixTimestampMillisNumber } from '../types'
|
|
3
|
+
import type { AnyObject, NumberOfMilliseconds, Reviver, UnixTimestampMillisNumber } from '../types'
|
|
4
4
|
import type { HttpMethod, HttpStatusFamily } from './http.model'
|
|
5
5
|
|
|
6
6
|
export interface FetcherNormalizedCfg
|
|
@@ -13,6 +13,7 @@ export interface FetcherNormalizedCfg
|
|
|
13
13
|
| 'logRequestBody'
|
|
14
14
|
| 'logResponse'
|
|
15
15
|
| 'logResponseBody'
|
|
16
|
+
| 'debug'
|
|
16
17
|
| 'redirect'
|
|
17
18
|
| 'credentials'
|
|
18
19
|
> {
|
|
@@ -93,14 +94,14 @@ export interface FetcherCfg {
|
|
|
93
94
|
|
|
94
95
|
export interface FetcherRetryStatus {
|
|
95
96
|
retryAttempt: number
|
|
96
|
-
retryTimeout:
|
|
97
|
+
retryTimeout: NumberOfMilliseconds
|
|
97
98
|
retryStopped: boolean
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
export interface FetcherRetryOptions {
|
|
101
102
|
count: number
|
|
102
|
-
timeout:
|
|
103
|
-
timeoutMax:
|
|
103
|
+
timeout: NumberOfMilliseconds
|
|
104
|
+
timeoutMax: NumberOfMilliseconds
|
|
104
105
|
timeoutMultiplier: number
|
|
105
106
|
}
|
|
106
107
|
|
|
@@ -185,7 +186,7 @@ export interface FetcherOptions {
|
|
|
185
186
|
// init?: Partial<RequestInitNormalized>
|
|
186
187
|
|
|
187
188
|
headers?: Record<string, any>
|
|
188
|
-
responseType?: FetcherResponseType // default to '
|
|
189
|
+
responseType?: FetcherResponseType // default to 'json'
|
|
189
190
|
|
|
190
191
|
searchParams?: Record<string, any>
|
|
191
192
|
|
|
@@ -219,6 +220,10 @@ export interface FetcherOptions {
|
|
|
219
220
|
logRequestBody?: boolean
|
|
220
221
|
logResponse?: boolean
|
|
221
222
|
logResponseBody?: boolean
|
|
223
|
+
/**
|
|
224
|
+
* If true - enables all possible logging.
|
|
225
|
+
*/
|
|
226
|
+
debug?: boolean
|
|
222
227
|
}
|
|
223
228
|
|
|
224
229
|
export type RequestInitNormalized = Omit<RequestInit, 'method' | 'headers'> & {
|
|
@@ -261,3 +266,10 @@ export type FetcherResponseType =
|
|
|
261
266
|
| 'arrayBuffer'
|
|
262
267
|
| 'blob'
|
|
263
268
|
| 'readableStream'
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Function that mocks `Fetcher.callNativeFetch` globally.
|
|
272
|
+
*
|
|
273
|
+
* url is `fullUrl` (includes baseUrl and all).
|
|
274
|
+
*/
|
|
275
|
+
export type FetcherMockFunction = (url: string, init: RequestInitNormalized) => Promise<Response>
|