@naturalcycles/js-lib 14.119.1 → 14.120.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/http/fetcher.d.ts +23 -5
- package/dist/http/fetcher.js +35 -24
- package/dist-esm/http/fetcher.js +92 -26
- package/package.json +1 -1
- package/src/http/fetcher.ts +59 -31
package/dist/http/fetcher.d.ts
CHANGED
|
@@ -2,11 +2,17 @@
|
|
|
2
2
|
import { CommonLogger } from '../log/commonLogger';
|
|
3
3
|
import type { Promisable } from '../typeFest';
|
|
4
4
|
import type { HttpMethod, HttpStatusFamily } from './http.model';
|
|
5
|
-
export interface FetcherNormalizedCfg extends FetcherCfg
|
|
5
|
+
export interface FetcherNormalizedCfg extends Required<FetcherCfg>, FetcherRequest {
|
|
6
6
|
logger: CommonLogger;
|
|
7
7
|
searchParams: Record<string, any>;
|
|
8
8
|
}
|
|
9
|
+
export type FetcherBeforeRequestHook = (req: FetcherRequest) => Promisable<void>;
|
|
10
|
+
export type FetcherAfterResponseHook = (res: FetcherResponse) => Promisable<void>;
|
|
11
|
+
export type FetcherBeforeRetryHook = (res: FetcherResponse) => Promisable<void>;
|
|
9
12
|
export interface FetcherCfg {
|
|
13
|
+
/**
|
|
14
|
+
* Should **not** contain trailing slash.
|
|
15
|
+
*/
|
|
10
16
|
baseUrl?: string;
|
|
11
17
|
/**
|
|
12
18
|
* Default rule is that you **are allowed** to mutate req, res, res.retryStatus
|
|
@@ -17,22 +23,28 @@ export interface FetcherCfg {
|
|
|
17
23
|
/**
|
|
18
24
|
* Allows to mutate req.
|
|
19
25
|
*/
|
|
20
|
-
beforeRequest
|
|
26
|
+
beforeRequest?: FetcherBeforeRequestHook[];
|
|
21
27
|
/**
|
|
22
28
|
* Allows to mutate res.
|
|
23
29
|
* If you set `res.err` - it will be thrown.
|
|
24
30
|
*/
|
|
25
|
-
|
|
31
|
+
afterResponse?: FetcherAfterResponseHook[];
|
|
26
32
|
/**
|
|
27
33
|
* Allows to mutate res.retryStatus to override retry behavior.
|
|
28
34
|
*/
|
|
29
|
-
beforeRetry
|
|
35
|
+
beforeRetry?: FetcherBeforeRetryHook[];
|
|
30
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* If true - enables all possible logging.
|
|
39
|
+
*/
|
|
31
40
|
debug?: boolean;
|
|
32
41
|
logRequest?: boolean;
|
|
33
42
|
logRequestBody?: boolean;
|
|
34
43
|
logResponse?: boolean;
|
|
35
44
|
logResponseBody?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Defaults to `console`.
|
|
47
|
+
*/
|
|
36
48
|
logger?: CommonLogger;
|
|
37
49
|
}
|
|
38
50
|
export interface FetcherRetryStatus {
|
|
@@ -68,7 +80,7 @@ export interface FetcherOptions {
|
|
|
68
80
|
timeoutSeconds?: number;
|
|
69
81
|
json?: any;
|
|
70
82
|
text?: string;
|
|
71
|
-
|
|
83
|
+
credentials?: RequestCredentials;
|
|
72
84
|
headers?: Record<string, any>;
|
|
73
85
|
mode?: FetcherMode;
|
|
74
86
|
searchParams?: Record<string, any>;
|
|
@@ -120,6 +132,12 @@ export type FetcherMode = 'json' | 'text';
|
|
|
120
132
|
*/
|
|
121
133
|
export declare class Fetcher {
|
|
122
134
|
private constructor();
|
|
135
|
+
/**
|
|
136
|
+
* Add BeforeRequest hook at the end of the hooks list.
|
|
137
|
+
*/
|
|
138
|
+
onBeforeRequest(hook: FetcherBeforeRequestHook): this;
|
|
139
|
+
onAfterResponse(hook: FetcherAfterResponseHook): this;
|
|
140
|
+
onBeforeRetry(hook: FetcherBeforeRetryHook): this;
|
|
123
141
|
cfg: FetcherNormalizedCfg;
|
|
124
142
|
static create(cfg?: FetcherCfg & FetcherOptions): Fetcher;
|
|
125
143
|
getJson<T = unknown>(url: string, opt?: FetcherOptions): Promise<T>;
|
package/dist/http/fetcher.js
CHANGED
|
@@ -25,6 +25,24 @@ class Fetcher {
|
|
|
25
25
|
constructor(cfg = {}) {
|
|
26
26
|
this.cfg = this.normalizeCfg(cfg);
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Add BeforeRequest hook at the end of the hooks list.
|
|
30
|
+
*/
|
|
31
|
+
onBeforeRequest(hook) {
|
|
32
|
+
;
|
|
33
|
+
(this.cfg.hooks.beforeRequest ||= []).push(hook);
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
onAfterResponse(hook) {
|
|
37
|
+
;
|
|
38
|
+
(this.cfg.hooks.afterResponse ||= []).push(hook);
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
onBeforeRetry(hook) {
|
|
42
|
+
;
|
|
43
|
+
(this.cfg.hooks.beforeRetry ||= []).push(hook);
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
28
46
|
static create(cfg = {}) {
|
|
29
47
|
return new Fetcher(cfg);
|
|
30
48
|
}
|
|
@@ -90,7 +108,9 @@ class Fetcher {
|
|
|
90
108
|
abortController.abort(`timeout of ${timeoutSeconds} sec`);
|
|
91
109
|
}, timeoutSeconds * 1000);
|
|
92
110
|
}
|
|
93
|
-
await this.cfg.hooks
|
|
111
|
+
for await (const hook of this.cfg.hooks.beforeRequest || []) {
|
|
112
|
+
await hook(req);
|
|
113
|
+
}
|
|
94
114
|
const res = {
|
|
95
115
|
req,
|
|
96
116
|
retryStatus: {
|
|
@@ -159,29 +179,12 @@ class Fetcher {
|
|
|
159
179
|
url: req.url,
|
|
160
180
|
// tryCount: req.tryCount,
|
|
161
181
|
}));
|
|
162
|
-
// We don't log errors when they are also thrown,
|
|
163
|
-
// otherwise it gets logged twice: here, and upstream
|
|
164
|
-
// if (this.cfg.logResponse) {
|
|
165
|
-
// const { retryAttempt } = res.retryStatus
|
|
166
|
-
// logger.error(
|
|
167
|
-
// [
|
|
168
|
-
// [
|
|
169
|
-
// ' <<',
|
|
170
|
-
// res.fetchResponse.status,
|
|
171
|
-
// signature,
|
|
172
|
-
// retryAttempt && `try#${retryAttempt + 1}/${req.retry.count}`,
|
|
173
|
-
// _since(started),
|
|
174
|
-
// ]
|
|
175
|
-
// .filter(Boolean)
|
|
176
|
-
// .join(' '),
|
|
177
|
-
// _stringifyAny(body),
|
|
178
|
-
// ].join('\n'),
|
|
179
|
-
// )
|
|
180
|
-
// }
|
|
181
182
|
await this.processRetry(res);
|
|
182
183
|
}
|
|
183
184
|
}
|
|
184
|
-
await this.cfg.hooks
|
|
185
|
+
for await (const hook of this.cfg.hooks.afterResponse || []) {
|
|
186
|
+
await hook(res);
|
|
187
|
+
}
|
|
185
188
|
return res;
|
|
186
189
|
}
|
|
187
190
|
async processRetry(res) {
|
|
@@ -189,7 +192,9 @@ class Fetcher {
|
|
|
189
192
|
if (!this.shouldRetry(res)) {
|
|
190
193
|
retryStatus.retryStopped = true;
|
|
191
194
|
}
|
|
192
|
-
await this.cfg.hooks
|
|
195
|
+
for await (const hook of this.cfg.hooks.beforeRetry || []) {
|
|
196
|
+
await hook(res);
|
|
197
|
+
}
|
|
193
198
|
const { count, timeoutMultiplier, timeoutMax } = res.req.retry;
|
|
194
199
|
if (retryStatus.retryAttempt >= count) {
|
|
195
200
|
retryStatus.retryStopped = true;
|
|
@@ -245,8 +250,9 @@ class Fetcher {
|
|
|
245
250
|
console.warn(`Fetcher: baseUrl should not end with /`);
|
|
246
251
|
cfg.baseUrl = cfg.baseUrl.slice(0, cfg.baseUrl.length - 1);
|
|
247
252
|
}
|
|
248
|
-
const { debug } = cfg;
|
|
253
|
+
const { debug = false } = cfg;
|
|
249
254
|
const norm = (0, object_util_1._merge)({
|
|
255
|
+
baseUrl: '',
|
|
250
256
|
url: '',
|
|
251
257
|
searchParams: {},
|
|
252
258
|
timeoutSeconds: 30,
|
|
@@ -255,6 +261,7 @@ class Fetcher {
|
|
|
255
261
|
retry4xx: false,
|
|
256
262
|
retry5xx: true,
|
|
257
263
|
logger: console,
|
|
264
|
+
debug,
|
|
258
265
|
logRequest: debug,
|
|
259
266
|
logRequestBody: debug,
|
|
260
267
|
logResponse: debug,
|
|
@@ -264,6 +271,7 @@ class Fetcher {
|
|
|
264
271
|
method: 'get',
|
|
265
272
|
headers: {},
|
|
266
273
|
},
|
|
274
|
+
hooks: {},
|
|
267
275
|
}, cfg);
|
|
268
276
|
norm.init.headers = (0, object_util_1._mapKeys)(norm.init.headers, k => k.toLowerCase());
|
|
269
277
|
return norm;
|
|
@@ -282,8 +290,11 @@ class Fetcher {
|
|
|
282
290
|
...retry,
|
|
283
291
|
...(0, object_util_1._filterUndefinedValues)(opt.retry || {}),
|
|
284
292
|
},
|
|
285
|
-
init: (0, object_util_1._merge)({ ...this.cfg.init },
|
|
293
|
+
init: (0, object_util_1._merge)({ ...this.cfg.init },
|
|
294
|
+
// opt.init,
|
|
295
|
+
(0, object_util_1._filterUndefinedValues)({
|
|
286
296
|
method: opt.method,
|
|
297
|
+
credentials: opt.credentials,
|
|
287
298
|
headers: (0, object_util_1._mapKeys)(opt.headers || {}, k => k.toLowerCase()),
|
|
288
299
|
})),
|
|
289
300
|
};
|
package/dist-esm/http/fetcher.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference lib="dom"/>
|
|
2
|
+
import { __asyncValues } from "tslib";
|
|
2
3
|
import { _anyToErrorObject } from '../error/error.util';
|
|
3
4
|
import { HttpError } from '../error/http.error';
|
|
4
5
|
import { _clamp } from '../number/number.util';
|
|
@@ -22,6 +23,27 @@ export class Fetcher {
|
|
|
22
23
|
constructor(cfg = {}) {
|
|
23
24
|
this.cfg = this.normalizeCfg(cfg);
|
|
24
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Add BeforeRequest hook at the end of the hooks list.
|
|
28
|
+
*/
|
|
29
|
+
onBeforeRequest(hook) {
|
|
30
|
+
var _a;
|
|
31
|
+
;
|
|
32
|
+
((_a = this.cfg.hooks).beforeRequest || (_a.beforeRequest = [])).push(hook);
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
35
|
+
onAfterResponse(hook) {
|
|
36
|
+
var _a;
|
|
37
|
+
;
|
|
38
|
+
((_a = this.cfg.hooks).afterResponse || (_a.afterResponse = [])).push(hook);
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
onBeforeRetry(hook) {
|
|
42
|
+
var _a;
|
|
43
|
+
;
|
|
44
|
+
((_a = this.cfg.hooks).beforeRetry || (_a.beforeRetry = [])).push(hook);
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
25
47
|
static create(cfg = {}) {
|
|
26
48
|
return new Fetcher(cfg);
|
|
27
49
|
}
|
|
@@ -53,7 +75,7 @@ export class Fetcher {
|
|
|
53
75
|
return res.body;
|
|
54
76
|
}
|
|
55
77
|
async rawFetch(url, rawOpt = {}) {
|
|
56
|
-
var _a, _b, _c, _d;
|
|
78
|
+
var _a, e_1, _b, _c, _d, e_2, _e, _f;
|
|
57
79
|
const { logger } = this.cfg;
|
|
58
80
|
const req = this.normalizeOptions(url, rawOpt);
|
|
59
81
|
const { timeoutSeconds, mode, init: { method }, } = req;
|
|
@@ -66,7 +88,26 @@ export class Fetcher {
|
|
|
66
88
|
abortController.abort(`timeout of ${timeoutSeconds} sec`);
|
|
67
89
|
}, timeoutSeconds * 1000);
|
|
68
90
|
}
|
|
69
|
-
|
|
91
|
+
try {
|
|
92
|
+
for (var _g = true, _h = __asyncValues(this.cfg.hooks.beforeRequest || []), _j; _j = await _h.next(), _a = _j.done, !_a;) {
|
|
93
|
+
_c = _j.value;
|
|
94
|
+
_g = false;
|
|
95
|
+
try {
|
|
96
|
+
const hook = _c;
|
|
97
|
+
await hook(req);
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
_g = true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
105
|
+
finally {
|
|
106
|
+
try {
|
|
107
|
+
if (!_g && !_a && (_b = _h.return)) await _b.call(_h);
|
|
108
|
+
}
|
|
109
|
+
finally { if (e_1) throw e_1.error; }
|
|
110
|
+
}
|
|
70
111
|
const res = {
|
|
71
112
|
req,
|
|
72
113
|
retryStatus: {
|
|
@@ -130,38 +171,57 @@ export class Fetcher {
|
|
|
130
171
|
// Enabled, cause `data` is not printed by default when error is HttpError
|
|
131
172
|
// method: req.method,
|
|
132
173
|
url: req.url })));
|
|
133
|
-
// We don't log errors when they are also thrown,
|
|
134
|
-
// otherwise it gets logged twice: here, and upstream
|
|
135
|
-
// if (this.cfg.logResponse) {
|
|
136
|
-
// const { retryAttempt } = res.retryStatus
|
|
137
|
-
// logger.error(
|
|
138
|
-
// [
|
|
139
|
-
// [
|
|
140
|
-
// ' <<',
|
|
141
|
-
// res.fetchResponse.status,
|
|
142
|
-
// signature,
|
|
143
|
-
// retryAttempt && `try#${retryAttempt + 1}/${req.retry.count}`,
|
|
144
|
-
// _since(started),
|
|
145
|
-
// ]
|
|
146
|
-
// .filter(Boolean)
|
|
147
|
-
// .join(' '),
|
|
148
|
-
// _stringifyAny(body),
|
|
149
|
-
// ].join('\n'),
|
|
150
|
-
// )
|
|
151
|
-
// }
|
|
152
174
|
await this.processRetry(res);
|
|
153
175
|
}
|
|
154
176
|
}
|
|
155
|
-
|
|
177
|
+
try {
|
|
178
|
+
for (var _k = true, _l = __asyncValues(this.cfg.hooks.afterResponse || []), _m; _m = await _l.next(), _d = _m.done, !_d;) {
|
|
179
|
+
_f = _m.value;
|
|
180
|
+
_k = false;
|
|
181
|
+
try {
|
|
182
|
+
const hook = _f;
|
|
183
|
+
await hook(res);
|
|
184
|
+
}
|
|
185
|
+
finally {
|
|
186
|
+
_k = true;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
191
|
+
finally {
|
|
192
|
+
try {
|
|
193
|
+
if (!_k && !_d && (_e = _l.return)) await _e.call(_l);
|
|
194
|
+
}
|
|
195
|
+
finally { if (e_2) throw e_2.error; }
|
|
196
|
+
}
|
|
156
197
|
return res;
|
|
157
198
|
}
|
|
158
199
|
async processRetry(res) {
|
|
159
|
-
var _a, _b;
|
|
200
|
+
var _a, e_3, _b, _c;
|
|
160
201
|
const { retryStatus } = res;
|
|
161
202
|
if (!this.shouldRetry(res)) {
|
|
162
203
|
retryStatus.retryStopped = true;
|
|
163
204
|
}
|
|
164
|
-
|
|
205
|
+
try {
|
|
206
|
+
for (var _d = true, _e = __asyncValues(this.cfg.hooks.beforeRetry || []), _f; _f = await _e.next(), _a = _f.done, !_a;) {
|
|
207
|
+
_c = _f.value;
|
|
208
|
+
_d = false;
|
|
209
|
+
try {
|
|
210
|
+
const hook = _c;
|
|
211
|
+
await hook(res);
|
|
212
|
+
}
|
|
213
|
+
finally {
|
|
214
|
+
_d = true;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
219
|
+
finally {
|
|
220
|
+
try {
|
|
221
|
+
if (!_d && !_a && (_b = _e.return)) await _b.call(_e);
|
|
222
|
+
}
|
|
223
|
+
finally { if (e_3) throw e_3.error; }
|
|
224
|
+
}
|
|
165
225
|
const { count, timeoutMultiplier, timeoutMax } = res.req.retry;
|
|
166
226
|
if (retryStatus.retryAttempt >= count) {
|
|
167
227
|
retryStatus.retryStopped = true;
|
|
@@ -219,8 +279,9 @@ export class Fetcher {
|
|
|
219
279
|
console.warn(`Fetcher: baseUrl should not end with /`);
|
|
220
280
|
cfg.baseUrl = cfg.baseUrl.slice(0, cfg.baseUrl.length - 1);
|
|
221
281
|
}
|
|
222
|
-
const { debug } = cfg;
|
|
282
|
+
const { debug = false } = cfg;
|
|
223
283
|
const norm = _merge({
|
|
284
|
+
baseUrl: '',
|
|
224
285
|
url: '',
|
|
225
286
|
searchParams: {},
|
|
226
287
|
timeoutSeconds: 30,
|
|
@@ -229,6 +290,7 @@ export class Fetcher {
|
|
|
229
290
|
retry4xx: false,
|
|
230
291
|
retry5xx: true,
|
|
231
292
|
logger: console,
|
|
293
|
+
debug,
|
|
232
294
|
logRequest: debug,
|
|
233
295
|
logRequestBody: debug,
|
|
234
296
|
logResponse: debug,
|
|
@@ -238,6 +300,7 @@ export class Fetcher {
|
|
|
238
300
|
method: 'get',
|
|
239
301
|
headers: {},
|
|
240
302
|
},
|
|
303
|
+
hooks: {},
|
|
241
304
|
}, cfg);
|
|
242
305
|
norm.init.headers = _mapKeys(norm.init.headers, k => k.toLowerCase());
|
|
243
306
|
return norm;
|
|
@@ -249,8 +312,11 @@ export class Fetcher {
|
|
|
249
312
|
throwHttpErrors,
|
|
250
313
|
retryPost,
|
|
251
314
|
retry4xx,
|
|
252
|
-
retry5xx }, _omit(opt, ['method', 'headers'])), { retry: Object.assign(Object.assign({}, retry), _filterUndefinedValues(opt.retry || {})), init: _merge(Object.assign({}, this.cfg.init),
|
|
315
|
+
retry5xx }, _omit(opt, ['method', 'headers'])), { retry: Object.assign(Object.assign({}, retry), _filterUndefinedValues(opt.retry || {})), init: _merge(Object.assign({}, this.cfg.init),
|
|
316
|
+
// opt.init,
|
|
317
|
+
_filterUndefinedValues({
|
|
253
318
|
method: opt.method,
|
|
319
|
+
credentials: opt.credentials,
|
|
254
320
|
headers: _mapKeys(opt.headers || {}, k => k.toLowerCase()),
|
|
255
321
|
})) });
|
|
256
322
|
// setup url
|
package/package.json
CHANGED
package/src/http/fetcher.ts
CHANGED
|
@@ -17,12 +17,19 @@ import { _since } from '../time/time.util'
|
|
|
17
17
|
import type { Promisable } from '../typeFest'
|
|
18
18
|
import type { HttpMethod, HttpStatusFamily } from './http.model'
|
|
19
19
|
|
|
20
|
-
export interface FetcherNormalizedCfg extends FetcherCfg
|
|
20
|
+
export interface FetcherNormalizedCfg extends Required<FetcherCfg>, FetcherRequest {
|
|
21
21
|
logger: CommonLogger
|
|
22
22
|
searchParams: Record<string, any>
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
export type FetcherBeforeRequestHook = (req: FetcherRequest) => Promisable<void>
|
|
26
|
+
export type FetcherAfterResponseHook = (res: FetcherResponse) => Promisable<void>
|
|
27
|
+
export type FetcherBeforeRetryHook = (res: FetcherResponse) => Promisable<void>
|
|
28
|
+
|
|
25
29
|
export interface FetcherCfg {
|
|
30
|
+
/**
|
|
31
|
+
* Should **not** contain trailing slash.
|
|
32
|
+
*/
|
|
26
33
|
baseUrl?: string
|
|
27
34
|
|
|
28
35
|
/**
|
|
@@ -34,23 +41,30 @@ export interface FetcherCfg {
|
|
|
34
41
|
/**
|
|
35
42
|
* Allows to mutate req.
|
|
36
43
|
*/
|
|
37
|
-
beforeRequest
|
|
44
|
+
beforeRequest?: FetcherBeforeRequestHook[]
|
|
38
45
|
/**
|
|
39
46
|
* Allows to mutate res.
|
|
40
47
|
* If you set `res.err` - it will be thrown.
|
|
41
48
|
*/
|
|
42
|
-
|
|
49
|
+
afterResponse?: FetcherAfterResponseHook[]
|
|
43
50
|
/**
|
|
44
51
|
* Allows to mutate res.retryStatus to override retry behavior.
|
|
45
52
|
*/
|
|
46
|
-
beforeRetry
|
|
53
|
+
beforeRetry?: FetcherBeforeRetryHook[]
|
|
47
54
|
}
|
|
48
55
|
|
|
56
|
+
/**
|
|
57
|
+
* If true - enables all possible logging.
|
|
58
|
+
*/
|
|
49
59
|
debug?: boolean
|
|
50
60
|
logRequest?: boolean
|
|
51
61
|
logRequestBody?: boolean
|
|
52
62
|
logResponse?: boolean
|
|
53
63
|
logResponseBody?: boolean
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Defaults to `console`.
|
|
67
|
+
*/
|
|
54
68
|
logger?: CommonLogger
|
|
55
69
|
}
|
|
56
70
|
|
|
@@ -90,7 +104,13 @@ export interface FetcherOptions {
|
|
|
90
104
|
timeoutSeconds?: number
|
|
91
105
|
json?: any
|
|
92
106
|
text?: string
|
|
93
|
-
|
|
107
|
+
|
|
108
|
+
credentials?: RequestCredentials
|
|
109
|
+
|
|
110
|
+
// Removing RequestInit from options to simplify FetcherOptions interface.
|
|
111
|
+
// Will instead only add hand-picked useful options, such as `credentials`.
|
|
112
|
+
// init?: Partial<RequestInitNormalized>
|
|
113
|
+
|
|
94
114
|
headers?: Record<string, any>
|
|
95
115
|
mode?: FetcherMode // default to undefined (void response)
|
|
96
116
|
|
|
@@ -161,6 +181,24 @@ export class Fetcher {
|
|
|
161
181
|
this.cfg = this.normalizeCfg(cfg)
|
|
162
182
|
}
|
|
163
183
|
|
|
184
|
+
/**
|
|
185
|
+
* Add BeforeRequest hook at the end of the hooks list.
|
|
186
|
+
*/
|
|
187
|
+
onBeforeRequest(hook: FetcherBeforeRequestHook): this {
|
|
188
|
+
;(this.cfg.hooks.beforeRequest ||= []).push(hook)
|
|
189
|
+
return this
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
onAfterResponse(hook: FetcherAfterResponseHook): this {
|
|
193
|
+
;(this.cfg.hooks.afterResponse ||= []).push(hook)
|
|
194
|
+
return this
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
onBeforeRetry(hook: FetcherBeforeRetryHook): this {
|
|
198
|
+
;(this.cfg.hooks.beforeRetry ||= []).push(hook)
|
|
199
|
+
return this
|
|
200
|
+
}
|
|
201
|
+
|
|
164
202
|
public cfg: FetcherNormalizedCfg
|
|
165
203
|
|
|
166
204
|
static create(cfg: FetcherCfg & FetcherOptions = {}): Fetcher {
|
|
@@ -241,7 +279,9 @@ export class Fetcher {
|
|
|
241
279
|
}, timeoutSeconds * 1000) as any as number
|
|
242
280
|
}
|
|
243
281
|
|
|
244
|
-
await this.cfg.hooks
|
|
282
|
+
for await (const hook of this.cfg.hooks.beforeRequest || []) {
|
|
283
|
+
await hook(req)
|
|
284
|
+
}
|
|
245
285
|
|
|
246
286
|
const res: FetcherResponse<any> = {
|
|
247
287
|
req,
|
|
@@ -330,31 +370,13 @@ export class Fetcher {
|
|
|
330
370
|
}),
|
|
331
371
|
)
|
|
332
372
|
|
|
333
|
-
// We don't log errors when they are also thrown,
|
|
334
|
-
// otherwise it gets logged twice: here, and upstream
|
|
335
|
-
// if (this.cfg.logResponse) {
|
|
336
|
-
// const { retryAttempt } = res.retryStatus
|
|
337
|
-
// logger.error(
|
|
338
|
-
// [
|
|
339
|
-
// [
|
|
340
|
-
// ' <<',
|
|
341
|
-
// res.fetchResponse.status,
|
|
342
|
-
// signature,
|
|
343
|
-
// retryAttempt && `try#${retryAttempt + 1}/${req.retry.count}`,
|
|
344
|
-
// _since(started),
|
|
345
|
-
// ]
|
|
346
|
-
// .filter(Boolean)
|
|
347
|
-
// .join(' '),
|
|
348
|
-
// _stringifyAny(body),
|
|
349
|
-
// ].join('\n'),
|
|
350
|
-
// )
|
|
351
|
-
// }
|
|
352
|
-
|
|
353
373
|
await this.processRetry(res)
|
|
354
374
|
}
|
|
355
375
|
}
|
|
356
376
|
|
|
357
|
-
await this.cfg.hooks
|
|
377
|
+
for await (const hook of this.cfg.hooks.afterResponse || []) {
|
|
378
|
+
await hook(res)
|
|
379
|
+
}
|
|
358
380
|
|
|
359
381
|
return res
|
|
360
382
|
}
|
|
@@ -366,7 +388,9 @@ export class Fetcher {
|
|
|
366
388
|
retryStatus.retryStopped = true
|
|
367
389
|
}
|
|
368
390
|
|
|
369
|
-
await this.cfg.hooks
|
|
391
|
+
for await (const hook of this.cfg.hooks.beforeRetry || []) {
|
|
392
|
+
await hook(res)
|
|
393
|
+
}
|
|
370
394
|
|
|
371
395
|
const { count, timeoutMultiplier, timeoutMax } = res.req.retry
|
|
372
396
|
|
|
@@ -421,10 +445,11 @@ export class Fetcher {
|
|
|
421
445
|
console.warn(`Fetcher: baseUrl should not end with /`)
|
|
422
446
|
cfg.baseUrl = cfg.baseUrl.slice(0, cfg.baseUrl.length - 1)
|
|
423
447
|
}
|
|
424
|
-
const { debug } = cfg
|
|
448
|
+
const { debug = false } = cfg
|
|
425
449
|
|
|
426
450
|
const norm: FetcherNormalizedCfg = _merge(
|
|
427
451
|
{
|
|
452
|
+
baseUrl: '',
|
|
428
453
|
url: '',
|
|
429
454
|
searchParams: {},
|
|
430
455
|
timeoutSeconds: 30,
|
|
@@ -433,6 +458,7 @@ export class Fetcher {
|
|
|
433
458
|
retry4xx: false,
|
|
434
459
|
retry5xx: true,
|
|
435
460
|
logger: console,
|
|
461
|
+
debug,
|
|
436
462
|
logRequest: debug,
|
|
437
463
|
logRequestBody: debug,
|
|
438
464
|
logResponse: debug,
|
|
@@ -442,6 +468,7 @@ export class Fetcher {
|
|
|
442
468
|
method: 'get',
|
|
443
469
|
headers: {},
|
|
444
470
|
},
|
|
471
|
+
hooks: {},
|
|
445
472
|
},
|
|
446
473
|
cfg,
|
|
447
474
|
)
|
|
@@ -469,11 +496,12 @@ export class Fetcher {
|
|
|
469
496
|
},
|
|
470
497
|
init: _merge(
|
|
471
498
|
{ ...this.cfg.init },
|
|
472
|
-
opt.init,
|
|
499
|
+
// opt.init,
|
|
473
500
|
_filterUndefinedValues({
|
|
474
501
|
method: opt.method,
|
|
502
|
+
credentials: opt.credentials,
|
|
475
503
|
headers: _mapKeys(opt.headers || {}, k => k.toLowerCase()),
|
|
476
|
-
}),
|
|
504
|
+
} as RequestInit),
|
|
477
505
|
),
|
|
478
506
|
}
|
|
479
507
|
|