@naturalcycles/js-lib 14.156.0 → 14.157.1
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/form.util.d.ts +9 -0
- package/dist/form.util.js +19 -0
- package/dist/http/fetcher.d.ts +1 -0
- package/dist/http/fetcher.js +85 -58
- package/dist/http/fetcher.model.d.ts +29 -10
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/promise/pTimeout.d.ts +2 -2
- package/dist-esm/form.util.js +14 -0
- package/dist-esm/http/fetcher.js +85 -60
- package/dist-esm/index.js +1 -0
- package/package.json +1 -2
- package/src/form.util.ts +17 -0
- package/src/http/fetcher.model.ts +38 -9
- package/src/http/fetcher.ts +96 -67
- package/src/index.ts +1 -0
- package/src/promise/pTimeout.ts +2 -2
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AnyObject } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Convert any object to FormData.
|
|
4
|
+
* Please note that every key and value of FormData is `string`.
|
|
5
|
+
* Even if you pass a number - it'll be converted to string.
|
|
6
|
+
* Think URLSearchParams.
|
|
7
|
+
*/
|
|
8
|
+
export declare function objectToFormData(obj?: AnyObject): FormData;
|
|
9
|
+
export declare function formDataToObject<T extends AnyObject>(formData: FormData): T;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formDataToObject = exports.objectToFormData = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Convert any object to FormData.
|
|
6
|
+
* Please note that every key and value of FormData is `string`.
|
|
7
|
+
* Even if you pass a number - it'll be converted to string.
|
|
8
|
+
* Think URLSearchParams.
|
|
9
|
+
*/
|
|
10
|
+
function objectToFormData(obj = {}) {
|
|
11
|
+
const fd = new FormData();
|
|
12
|
+
Object.entries(obj).forEach(([k, v]) => fd.append(k, v));
|
|
13
|
+
return fd;
|
|
14
|
+
}
|
|
15
|
+
exports.objectToFormData = objectToFormData;
|
|
16
|
+
function formDataToObject(formData) {
|
|
17
|
+
return Object.fromEntries(formData);
|
|
18
|
+
}
|
|
19
|
+
exports.formDataToObject = formDataToObject;
|
package/dist/http/fetcher.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
|
+
/// <reference lib="dom.iterable" />
|
|
2
3
|
import type { FetcherAfterResponseHook, FetcherBeforeRequestHook, FetcherBeforeRetryHook, FetcherCfg, FetcherNormalizedCfg, FetcherOptions, FetcherResponse } from './fetcher.model';
|
|
3
4
|
/**
|
|
4
5
|
* Experimental wrapper around Fetch.
|
package/dist/http/fetcher.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/// <reference lib="dom"/>
|
|
3
|
+
/// <reference lib="dom.iterable"/>
|
|
3
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
5
|
exports.getFetcher = exports.Fetcher = void 0;
|
|
5
6
|
const env_1 = require("../env");
|
|
@@ -13,6 +14,14 @@ const json_util_1 = require("../string/json.util");
|
|
|
13
14
|
const stringifyAny_1 = require("../string/stringifyAny");
|
|
14
15
|
const time_util_1 = require("../time/time.util");
|
|
15
16
|
const http_model_1 = require("./http.model");
|
|
17
|
+
const acceptByResponseType = {
|
|
18
|
+
text: 'text/plain',
|
|
19
|
+
json: 'application/json',
|
|
20
|
+
void: '*/*',
|
|
21
|
+
readableStream: 'application/octet-stream',
|
|
22
|
+
arrayBuffer: 'application/octet-stream',
|
|
23
|
+
blob: 'application/octet-stream',
|
|
24
|
+
};
|
|
16
25
|
const defRetryOptions = {
|
|
17
26
|
count: 2,
|
|
18
27
|
timeout: 1000,
|
|
@@ -36,18 +45,18 @@ class Fetcher {
|
|
|
36
45
|
return await this.fetch({
|
|
37
46
|
url,
|
|
38
47
|
method,
|
|
39
|
-
|
|
48
|
+
responseType: 'void',
|
|
40
49
|
...opt,
|
|
41
50
|
});
|
|
42
51
|
};
|
|
43
52
|
if (method === 'HEAD')
|
|
44
|
-
return //
|
|
53
|
+
return // responseType=text
|
|
45
54
|
;
|
|
46
55
|
this[`${m}Text`] = async (url, opt) => {
|
|
47
56
|
return await this.fetch({
|
|
48
57
|
url,
|
|
49
58
|
method,
|
|
50
|
-
|
|
59
|
+
responseType: 'text',
|
|
51
60
|
...opt,
|
|
52
61
|
});
|
|
53
62
|
};
|
|
@@ -55,7 +64,7 @@ class Fetcher {
|
|
|
55
64
|
return await this.fetch({
|
|
56
65
|
url,
|
|
57
66
|
method,
|
|
58
|
-
|
|
67
|
+
responseType: 'json',
|
|
59
68
|
...opt,
|
|
60
69
|
});
|
|
61
70
|
};
|
|
@@ -82,7 +91,7 @@ class Fetcher {
|
|
|
82
91
|
static create(cfg = {}) {
|
|
83
92
|
return new Fetcher(cfg);
|
|
84
93
|
}
|
|
85
|
-
//
|
|
94
|
+
// responseType=readableStream
|
|
86
95
|
/**
|
|
87
96
|
* Returns raw fetchResponse.body, which is a ReadableStream<Uint8Array>
|
|
88
97
|
*
|
|
@@ -92,7 +101,7 @@ class Fetcher {
|
|
|
92
101
|
async getReadableStream(url, opt) {
|
|
93
102
|
return await this.fetch({
|
|
94
103
|
url,
|
|
95
|
-
|
|
104
|
+
responseType: 'readableStream',
|
|
96
105
|
...opt,
|
|
97
106
|
});
|
|
98
107
|
}
|
|
@@ -112,19 +121,6 @@ class Fetcher {
|
|
|
112
121
|
const req = this.normalizeOptions(opt);
|
|
113
122
|
const { logger } = this.cfg;
|
|
114
123
|
const { timeoutSeconds, init: { method }, } = req;
|
|
115
|
-
// setup timeout
|
|
116
|
-
let timeout;
|
|
117
|
-
if (timeoutSeconds) {
|
|
118
|
-
const abortController = new AbortController();
|
|
119
|
-
req.init.signal = abortController.signal;
|
|
120
|
-
timeout = setTimeout(() => {
|
|
121
|
-
// Apparently, providing a `string` reason to abort() causes Undici to throw `invalid_argument` error,
|
|
122
|
-
// so, we're wrapping it in a TimeoutError instance
|
|
123
|
-
abortController.abort(new pTimeout_1.TimeoutError(`request timed out after ${timeoutSeconds} sec`));
|
|
124
|
-
// abortController.abort(`timeout of ${timeoutSeconds} sec`)
|
|
125
|
-
// abortController.abort()
|
|
126
|
-
}, timeoutSeconds * 1000);
|
|
127
|
-
}
|
|
128
124
|
for (const hook of this.cfg.hooks.beforeRequest || []) {
|
|
129
125
|
await hook(req);
|
|
130
126
|
}
|
|
@@ -143,6 +139,18 @@ class Fetcher {
|
|
|
143
139
|
};
|
|
144
140
|
while (!res.retryStatus.retryStopped) {
|
|
145
141
|
req.started = Date.now();
|
|
142
|
+
// setup timeout
|
|
143
|
+
let timeoutId;
|
|
144
|
+
if (timeoutSeconds) {
|
|
145
|
+
const abortController = new AbortController();
|
|
146
|
+
req.init.signal = abortController.signal;
|
|
147
|
+
timeoutId = setTimeout(() => {
|
|
148
|
+
// console.log(`actual request timed out in ${_since(req.started)}`)
|
|
149
|
+
// Apparently, providing a `string` reason to abort() causes Undici to throw `invalid_argument` error,
|
|
150
|
+
// so, we're wrapping it in a TimeoutError instance
|
|
151
|
+
abortController.abort(new pTimeout_1.TimeoutError(`request timed out after ${timeoutSeconds} sec`));
|
|
152
|
+
}, timeoutSeconds * 1000);
|
|
153
|
+
}
|
|
146
154
|
if (this.cfg.logRequest) {
|
|
147
155
|
const { retryAttempt } = res.retryStatus;
|
|
148
156
|
logger.log([' >>', signature, retryAttempt && `try#${retryAttempt + 1}/${req.retry.count + 1}`]
|
|
@@ -166,14 +174,30 @@ class Fetcher {
|
|
|
166
174
|
// important to set it to undefined, otherwise it can keep the previous value (from previous try)
|
|
167
175
|
res.fetchResponse = undefined;
|
|
168
176
|
}
|
|
177
|
+
finally {
|
|
178
|
+
clearTimeout(timeoutId);
|
|
179
|
+
// Separate Timeout will be introduced to "download and parse the body"
|
|
180
|
+
}
|
|
169
181
|
res.statusFamily = this.getStatusFamily(res);
|
|
170
182
|
res.statusCode = res.fetchResponse?.status;
|
|
171
183
|
if (res.fetchResponse?.ok) {
|
|
172
|
-
|
|
184
|
+
try {
|
|
185
|
+
// We are applying a separate Timeout (as long as original Timeout for now) to "download and parse the body"
|
|
186
|
+
await (0, pTimeout_1.pTimeout)(async () => await this.onOkResponse(res), {
|
|
187
|
+
timeout: timeoutSeconds * 1000 || Number.POSITIVE_INFINITY,
|
|
188
|
+
name: 'Fetcher.onOkResponse',
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
// onOkResponse can still fail, e.g when loading/parsing json, text or doing other response manipulation
|
|
193
|
+
res.err = (0, error_util_1._anyToError)(err);
|
|
194
|
+
res.ok = false;
|
|
195
|
+
await this.onNotOkResponse(res);
|
|
196
|
+
}
|
|
173
197
|
}
|
|
174
198
|
else {
|
|
175
199
|
// !res.ok
|
|
176
|
-
await this.onNotOkResponse(res
|
|
200
|
+
await this.onNotOkResponse(res);
|
|
177
201
|
}
|
|
178
202
|
}
|
|
179
203
|
for (const hook of this.cfg.hooks.afterResponse || []) {
|
|
@@ -181,31 +205,17 @@ class Fetcher {
|
|
|
181
205
|
}
|
|
182
206
|
return res;
|
|
183
207
|
}
|
|
184
|
-
async onOkResponse(res
|
|
208
|
+
async onOkResponse(res) {
|
|
185
209
|
const { req } = res;
|
|
186
|
-
const {
|
|
187
|
-
|
|
210
|
+
const { responseType } = res.req;
|
|
211
|
+
// This function is subject to a separate timeout to "download and parse the data"
|
|
212
|
+
if (responseType === 'json') {
|
|
188
213
|
if (res.fetchResponse.body) {
|
|
189
214
|
const text = await res.fetchResponse.text();
|
|
190
215
|
if (text) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
catch (err) {
|
|
196
|
-
// Error while parsing json
|
|
197
|
-
// res.err = _anyToError(err, HttpRequestError, {
|
|
198
|
-
// requestUrl: res.req.url,
|
|
199
|
-
// requestBaseUrl: this.cfg.baseUrl,
|
|
200
|
-
// requestMethod: res.req.init.method,
|
|
201
|
-
// requestSignature: res.signature,
|
|
202
|
-
// requestDuration: Date.now() - started,
|
|
203
|
-
// responseStatusCode: res.fetchResponse.status,
|
|
204
|
-
// } satisfies HttpRequestErrorData)
|
|
205
|
-
res.err = (0, error_util_1._anyToError)(err);
|
|
206
|
-
res.ok = false;
|
|
207
|
-
return await this.onNotOkResponse(res, timeout);
|
|
208
|
-
}
|
|
216
|
+
res.body = text;
|
|
217
|
+
res.body = (0, json_util_1._jsonParse)(text, req.jsonReviver);
|
|
218
|
+
// Error while parsing json can happen - it'll be handled upstream
|
|
209
219
|
}
|
|
210
220
|
else {
|
|
211
221
|
// Body had a '' (empty string)
|
|
@@ -218,24 +228,22 @@ class Fetcher {
|
|
|
218
228
|
res.body = {};
|
|
219
229
|
}
|
|
220
230
|
}
|
|
221
|
-
else if (
|
|
231
|
+
else if (responseType === 'text') {
|
|
222
232
|
res.body = res.fetchResponse.body ? await res.fetchResponse.text() : '';
|
|
223
233
|
}
|
|
224
|
-
else if (
|
|
234
|
+
else if (responseType === 'arrayBuffer') {
|
|
225
235
|
res.body = res.fetchResponse.body ? await res.fetchResponse.arrayBuffer() : {};
|
|
226
236
|
}
|
|
227
|
-
else if (
|
|
237
|
+
else if (responseType === 'blob') {
|
|
228
238
|
res.body = res.fetchResponse.body ? await res.fetchResponse.blob() : {};
|
|
229
239
|
}
|
|
230
|
-
else if (
|
|
240
|
+
else if (responseType === 'readableStream') {
|
|
231
241
|
res.body = res.fetchResponse.body;
|
|
232
242
|
if (res.body === null) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
return await this.onNotOkResponse(res, timeout);
|
|
243
|
+
// Error is to be handled upstream
|
|
244
|
+
throw new Error(`fetchResponse.body is null`);
|
|
236
245
|
}
|
|
237
246
|
}
|
|
238
|
-
clearTimeout(timeout);
|
|
239
247
|
res.retryStatus.retryStopped = true;
|
|
240
248
|
// res.err can happen on `failed to fetch` type of error, e.g JSON.parse, CORS, unexpected redirect
|
|
241
249
|
if (!res.err && this.cfg.logResponse) {
|
|
@@ -261,8 +269,7 @@ class Fetcher {
|
|
|
261
269
|
async callNativeFetch(url, init) {
|
|
262
270
|
return await globalThis.fetch(url, init);
|
|
263
271
|
}
|
|
264
|
-
async onNotOkResponse(res
|
|
265
|
-
clearTimeout(timeout);
|
|
272
|
+
async onNotOkResponse(res) {
|
|
266
273
|
let cause;
|
|
267
274
|
if (res.err) {
|
|
268
275
|
// This is only possible on JSON.parse error, or CORS error,
|
|
@@ -328,7 +335,11 @@ class Fetcher {
|
|
|
328
335
|
return;
|
|
329
336
|
retryStatus.retryAttempt++;
|
|
330
337
|
retryStatus.retryTimeout = (0, number_util_1._clamp)(retryStatus.retryTimeout * timeoutMultiplier, 0, timeoutMax);
|
|
331
|
-
|
|
338
|
+
const timeout = this.getRetryTimeout(res);
|
|
339
|
+
if (res.req.debug) {
|
|
340
|
+
this.cfg.logger.log(` .. ${res.signature} waiting ${(0, time_util_1._ms)(timeout)}`);
|
|
341
|
+
}
|
|
342
|
+
await (0, pDelay_1.pDelay)(timeout);
|
|
332
343
|
}
|
|
333
344
|
getRetryTimeout(res) {
|
|
334
345
|
let timeout = 0;
|
|
@@ -383,8 +394,9 @@ class Fetcher {
|
|
|
383
394
|
if (statusFamily === 3 && !retry3xx)
|
|
384
395
|
return false;
|
|
385
396
|
// should not retry on `unexpected redirect` in error.cause.cause
|
|
386
|
-
if (res.err?.cause?.cause?.message?.includes('unexpected redirect'))
|
|
397
|
+
if (res.err?.cause?.cause?.message?.includes('unexpected redirect')) {
|
|
387
398
|
return false;
|
|
399
|
+
}
|
|
388
400
|
return true; // default is true
|
|
389
401
|
}
|
|
390
402
|
getStatusFamily(res) {
|
|
@@ -422,14 +434,14 @@ class Fetcher {
|
|
|
422
434
|
}
|
|
423
435
|
normalizeCfg(cfg) {
|
|
424
436
|
if (cfg.baseUrl?.endsWith('/')) {
|
|
425
|
-
console.warn(`Fetcher: baseUrl should not end with
|
|
437
|
+
console.warn(`Fetcher: baseUrl should not end with slash: ${cfg.baseUrl}`);
|
|
426
438
|
cfg.baseUrl = cfg.baseUrl.slice(0, cfg.baseUrl.length - 1);
|
|
427
439
|
}
|
|
428
440
|
const { debug = false } = cfg;
|
|
429
441
|
const norm = (0, object_util_1._merge)({
|
|
430
442
|
baseUrl: '',
|
|
431
443
|
inputUrl: '',
|
|
432
|
-
|
|
444
|
+
responseType: 'void',
|
|
433
445
|
searchParams: {},
|
|
434
446
|
timeoutSeconds: 30,
|
|
435
447
|
retryPost: false,
|
|
@@ -448,7 +460,10 @@ class Fetcher {
|
|
|
448
460
|
retry: { ...defRetryOptions },
|
|
449
461
|
init: {
|
|
450
462
|
method: cfg.method || 'GET',
|
|
451
|
-
headers:
|
|
463
|
+
headers: {
|
|
464
|
+
'user-agent': 'fetcher',
|
|
465
|
+
...cfg.headers,
|
|
466
|
+
},
|
|
452
467
|
credentials: cfg.credentials,
|
|
453
468
|
redirect: cfg.redirect,
|
|
454
469
|
},
|
|
@@ -464,12 +479,13 @@ class Fetcher {
|
|
|
464
479
|
'retryPost',
|
|
465
480
|
'retry4xx',
|
|
466
481
|
'retry5xx',
|
|
467
|
-
'
|
|
482
|
+
'responseType',
|
|
468
483
|
'jsonReviver',
|
|
469
484
|
'logRequest',
|
|
470
485
|
'logRequestBody',
|
|
471
486
|
'logResponse',
|
|
472
487
|
'logResponseBody',
|
|
488
|
+
'debug',
|
|
473
489
|
]),
|
|
474
490
|
started: Date.now(),
|
|
475
491
|
...(0, object_util_1._omit)(opt, ['method', 'headers', 'credentials']),
|
|
@@ -514,9 +530,20 @@ class Fetcher {
|
|
|
514
530
|
req.init.body = opt.text;
|
|
515
531
|
req.init.headers['content-type'] = 'text/plain';
|
|
516
532
|
}
|
|
533
|
+
else if (opt.form) {
|
|
534
|
+
if (opt.form instanceof URLSearchParams || opt.form instanceof FormData) {
|
|
535
|
+
req.init.body = opt.form;
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
req.init.body = new URLSearchParams(opt.form);
|
|
539
|
+
}
|
|
540
|
+
req.init.headers['content-type'] = 'application/x-www-form-urlencoded';
|
|
541
|
+
}
|
|
517
542
|
else if (opt.body !== undefined) {
|
|
518
543
|
req.init.body = opt.body;
|
|
519
544
|
}
|
|
545
|
+
// Unless `accept` header was already set - set it based on responseType
|
|
546
|
+
req.init.headers['accept'] ||= acceptByResponseType[req.responseType];
|
|
520
547
|
return req;
|
|
521
548
|
}
|
|
522
549
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { CommonLogger } from '../log/commonLogger';
|
|
2
2
|
import type { Promisable } from '../typeFest';
|
|
3
|
-
import type { Reviver, UnixTimestampMillisNumber } from '../types';
|
|
3
|
+
import type { AnyObject, NumberOfMilliseconds, Reviver, UnixTimestampMillisNumber } from '../types';
|
|
4
4
|
import type { HttpMethod, HttpStatusFamily } from './http.model';
|
|
5
|
-
export interface FetcherNormalizedCfg extends Required<FetcherCfg>, Omit<FetcherRequest, 'started' | 'fullUrl' | 'logRequest' | 'logRequestBody' | 'logResponse' | 'logResponseBody' | 'redirect' | 'credentials'> {
|
|
5
|
+
export interface FetcherNormalizedCfg extends Required<FetcherCfg>, Omit<FetcherRequest, 'started' | 'fullUrl' | 'logRequest' | 'logRequestBody' | 'logResponse' | 'logResponseBody' | 'debug' | 'redirect' | 'credentials'> {
|
|
6
6
|
logger: CommonLogger;
|
|
7
7
|
searchParams: Record<string, any>;
|
|
8
8
|
}
|
|
@@ -68,13 +68,13 @@ export interface FetcherCfg {
|
|
|
68
68
|
}
|
|
69
69
|
export interface FetcherRetryStatus {
|
|
70
70
|
retryAttempt: number;
|
|
71
|
-
retryTimeout:
|
|
71
|
+
retryTimeout: NumberOfMilliseconds;
|
|
72
72
|
retryStopped: boolean;
|
|
73
73
|
}
|
|
74
74
|
export interface FetcherRetryOptions {
|
|
75
75
|
count: number;
|
|
76
|
-
timeout:
|
|
77
|
-
timeoutMax:
|
|
76
|
+
timeout: NumberOfMilliseconds;
|
|
77
|
+
timeoutMax: NumberOfMilliseconds;
|
|
78
78
|
timeoutMultiplier: number;
|
|
79
79
|
}
|
|
80
80
|
export interface FetcherRequest extends Omit<FetcherOptions, 'method' | 'headers' | 'baseUrl' | 'url'> {
|
|
@@ -88,7 +88,7 @@ export interface FetcherRequest extends Omit<FetcherOptions, 'method' | 'headers
|
|
|
88
88
|
*/
|
|
89
89
|
fullUrl: string;
|
|
90
90
|
init: RequestInitNormalized;
|
|
91
|
-
|
|
91
|
+
responseType: FetcherResponseType;
|
|
92
92
|
timeoutSeconds: number;
|
|
93
93
|
retry: FetcherRetryOptions;
|
|
94
94
|
retryPost: boolean;
|
|
@@ -112,14 +112,29 @@ export interface FetcherOptions {
|
|
|
112
112
|
* so both should finish within this single timeout (not each).
|
|
113
113
|
*/
|
|
114
114
|
timeoutSeconds?: number;
|
|
115
|
-
json?: any;
|
|
116
|
-
text?: string;
|
|
117
115
|
/**
|
|
118
116
|
* Supports all the types that RequestInit.body supports.
|
|
119
117
|
*
|
|
120
118
|
* Useful when you want to e.g pass FormData.
|
|
121
119
|
*/
|
|
122
120
|
body?: Blob | BufferSource | FormData | URLSearchParams | string;
|
|
121
|
+
/**
|
|
122
|
+
* Same as `body`, but also conveniently sets the
|
|
123
|
+
* Content-Type header to `text/plain`
|
|
124
|
+
*/
|
|
125
|
+
text?: string;
|
|
126
|
+
/**
|
|
127
|
+
* Same as `body`, but:
|
|
128
|
+
* 1. JSON.stringifies the passed variable
|
|
129
|
+
* 2. Conveniently sets the Content-Type header to `application/json`
|
|
130
|
+
*/
|
|
131
|
+
json?: any;
|
|
132
|
+
/**
|
|
133
|
+
* Same as `body`, but:
|
|
134
|
+
* 1. Transforms the passed plain js object into URLSearchParams and passes it to `body`
|
|
135
|
+
* 2. Conveniently sets the Content-Type header to `application/x-www-form-urlencoded`
|
|
136
|
+
*/
|
|
137
|
+
form?: FormData | URLSearchParams | AnyObject;
|
|
123
138
|
credentials?: RequestCredentials;
|
|
124
139
|
/**
|
|
125
140
|
* Default to 'follow'.
|
|
@@ -128,7 +143,7 @@ export interface FetcherOptions {
|
|
|
128
143
|
*/
|
|
129
144
|
redirect?: RequestRedirect;
|
|
130
145
|
headers?: Record<string, any>;
|
|
131
|
-
|
|
146
|
+
responseType?: FetcherResponseType;
|
|
132
147
|
searchParams?: Record<string, any>;
|
|
133
148
|
/**
|
|
134
149
|
* Default is 2 retries (3 tries in total).
|
|
@@ -157,6 +172,10 @@ export interface FetcherOptions {
|
|
|
157
172
|
logRequestBody?: boolean;
|
|
158
173
|
logResponse?: boolean;
|
|
159
174
|
logResponseBody?: boolean;
|
|
175
|
+
/**
|
|
176
|
+
* If true - enables all possible logging.
|
|
177
|
+
*/
|
|
178
|
+
debug?: boolean;
|
|
160
179
|
}
|
|
161
180
|
export type RequestInitNormalized = Omit<RequestInit, 'method' | 'headers'> & {
|
|
162
181
|
method: HttpMethod;
|
|
@@ -185,4 +204,4 @@ export interface FetcherErrorResponse<BODY = unknown> {
|
|
|
185
204
|
signature: string;
|
|
186
205
|
}
|
|
187
206
|
export type FetcherResponse<BODY = unknown> = FetcherSuccessResponse<BODY> | FetcherErrorResponse<BODY>;
|
|
188
|
-
export type
|
|
207
|
+
export type FetcherResponseType = 'json' | 'text' | 'void' | 'arrayBuffer' | 'blob' | 'readableStream';
|
package/dist/index.d.ts
CHANGED
|
@@ -80,6 +80,7 @@ export * from './http/fetcher';
|
|
|
80
80
|
export * from './http/fetcher.model';
|
|
81
81
|
export * from './string/hash.util';
|
|
82
82
|
export * from './env/buildInfo';
|
|
83
|
+
export * from './form.util';
|
|
83
84
|
export * from './zod/zod.util';
|
|
84
85
|
export * from './zod/zod.shared.schemas';
|
|
85
86
|
import { z, ZodSchema, ZodError, ZodIssue } from 'zod';
|
package/dist/index.js
CHANGED
|
@@ -84,6 +84,7 @@ tslib_1.__exportStar(require("./http/fetcher"), exports);
|
|
|
84
84
|
tslib_1.__exportStar(require("./http/fetcher.model"), exports);
|
|
85
85
|
tslib_1.__exportStar(require("./string/hash.util"), exports);
|
|
86
86
|
tslib_1.__exportStar(require("./env/buildInfo"), exports);
|
|
87
|
+
tslib_1.__exportStar(require("./form.util"), exports);
|
|
87
88
|
tslib_1.__exportStar(require("./zod/zod.util"), exports);
|
|
88
89
|
tslib_1.__exportStar(require("./zod/zod.shared.schemas"), exports);
|
|
89
90
|
const zod_1 = require("zod");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AppError } from '../error/app.error';
|
|
2
2
|
import type { ErrorData, ErrorObject } from '../error/error.model';
|
|
3
|
-
import type { AnyAsyncFunction } from '../types';
|
|
3
|
+
import type { AnyAsyncFunction, NumberOfMilliseconds } from '../types';
|
|
4
4
|
export declare class TimeoutError extends AppError {
|
|
5
5
|
constructor(message: string, data?: {}, cause?: ErrorObject);
|
|
6
6
|
}
|
|
@@ -8,7 +8,7 @@ export interface PTimeoutOptions {
|
|
|
8
8
|
/**
|
|
9
9
|
* Timeout in milliseconds.
|
|
10
10
|
*/
|
|
11
|
-
timeout:
|
|
11
|
+
timeout: NumberOfMilliseconds;
|
|
12
12
|
/**
|
|
13
13
|
* If set - will be included in the error message.
|
|
14
14
|
* Can be used to identify the place in the code that failed.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert any object to FormData.
|
|
3
|
+
* Please note that every key and value of FormData is `string`.
|
|
4
|
+
* Even if you pass a number - it'll be converted to string.
|
|
5
|
+
* Think URLSearchParams.
|
|
6
|
+
*/
|
|
7
|
+
export function objectToFormData(obj = {}) {
|
|
8
|
+
const fd = new FormData();
|
|
9
|
+
Object.entries(obj).forEach(([k, v]) => fd.append(k, v));
|
|
10
|
+
return fd;
|
|
11
|
+
}
|
|
12
|
+
export function formDataToObject(formData) {
|
|
13
|
+
return Object.fromEntries(formData);
|
|
14
|
+
}
|