@naturalcycles/js-lib 14.175.0 → 14.177.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.
@@ -204,7 +204,8 @@ class Fetcher {
204
204
  }
205
205
  }
206
206
  try {
207
- res.fetchResponse = await Fetcher.callNativeFetch(req.fullUrl, req.init);
207
+ // Calls cfg.fetchFn if set, otherwise Fetcher.callNativeFetch
208
+ res.fetchResponse = await (this.cfg.fetchFn || Fetcher.callNativeFetch)(req.fullUrl, req.init);
208
209
  res.ok = res.fetchResponse.ok;
209
210
  // important to set it to undefined, otherwise it can keep the previous value (from previous try)
210
211
  res.err = undefined;
@@ -223,7 +224,7 @@ class Fetcher {
223
224
  }
224
225
  res.statusFamily = this.getStatusFamily(res);
225
226
  res.statusCode = res.fetchResponse?.status;
226
- if (res.fetchResponse?.ok) {
227
+ if (res.fetchResponse?.ok || !req.throwHttpErrors) {
227
228
  try {
228
229
  // We are applying a separate Timeout (as long as original Timeout for now) to "download and parse the body"
229
230
  await (0, pTimeout_1.pTimeout)(async () => await this.onOkResponse(res), {
@@ -292,7 +293,7 @@ class Fetcher {
292
293
  }
293
294
  res.retryStatus.retryStopped = true;
294
295
  // res.err can happen on `failed to fetch` type of error, e.g JSON.parse, CORS, unexpected redirect
295
- if (!res.err && this.cfg.logResponse) {
296
+ if ((!res.err || !req.throwHttpErrors) && this.cfg.logResponse) {
296
297
  const { retryAttempt } = res.retryStatus;
297
298
  const { logger } = this.cfg;
298
299
  logger.log([
@@ -530,6 +531,7 @@ class Fetcher {
530
531
  redirect: cfg.redirect,
531
532
  },
532
533
  hooks: {},
534
+ throwHttpErrors: true,
533
535
  }, (0, object_util_1._omit)(cfg, ['method', 'credentials', 'headers', 'redirect', 'logger']));
534
536
  norm.init.headers = (0, object_util_1._mapKeys)(norm.init.headers, k => k.toLowerCase());
535
537
  return norm;
@@ -548,6 +550,7 @@ class Fetcher {
548
550
  'logResponse',
549
551
  'logResponseBody',
550
552
  'debug',
553
+ 'throwHttpErrors',
551
554
  ]),
552
555
  started: Date.now(),
553
556
  ...(0, object_util_1._omit)(opt, ['method', 'headers', 'credentials']),
@@ -4,7 +4,7 @@ import type { CommonLogger } from '../log/commonLogger';
4
4
  import type { Promisable } from '../typeFest';
5
5
  import type { AnyObject, NumberOfMilliseconds, Reviver, UnixTimestampMillisNumber } from '../types';
6
6
  import type { HttpMethod, HttpStatusFamily } from './http.model';
7
- export interface FetcherNormalizedCfg extends Required<FetcherCfg>, Omit<FetcherRequest, 'started' | 'fullUrl' | 'logRequest' | 'logRequestBody' | 'logResponse' | 'logResponseBody' | 'debug' | 'redirect' | 'credentials'> {
7
+ export interface FetcherNormalizedCfg extends Required<FetcherCfg>, Omit<FetcherRequest, 'started' | 'fullUrl' | 'logRequest' | 'logRequestBody' | 'logResponse' | 'logResponseBody' | 'debug' | 'redirect' | 'credentials' | 'throwHttpErrors'> {
8
8
  logger: CommonLogger;
9
9
  searchParams: Record<string, any>;
10
10
  }
@@ -67,6 +67,7 @@ export interface FetcherCfg {
67
67
  * Defaults to `console`.
68
68
  */
69
69
  logger?: CommonLogger;
70
+ throwHttpErrors?: boolean;
70
71
  }
71
72
  export interface FetcherRetryStatus {
72
73
  retryAttempt: number;
@@ -178,6 +179,15 @@ export interface FetcherOptions {
178
179
  * If true - enables all possible logging.
179
180
  */
180
181
  debug?: boolean;
182
+ /**
183
+ * If provided - will be used instead of static `Fetcher.callNativeFetch`.
184
+ */
185
+ fetchFn?: FetchFunction;
186
+ /**
187
+ * Default to true.
188
+ * Set to false to not throw on `!Response.ok`, but simply return `Response.body` as-is (json parsed, etc).
189
+ */
190
+ throwHttpErrors?: boolean;
181
191
  }
182
192
  export type RequestInitNormalized = Omit<RequestInit, 'method' | 'headers'> & {
183
193
  method: HttpMethod;
@@ -207,3 +217,9 @@ export interface FetcherErrorResponse<BODY = unknown> {
207
217
  }
208
218
  export type FetcherResponse<BODY = unknown> = FetcherSuccessResponse<BODY> | FetcherErrorResponse<BODY>;
209
219
  export type FetcherResponseType = 'json' | 'text' | 'void' | 'arrayBuffer' | 'blob' | 'readableStream';
220
+ /**
221
+ * Signature for the `fetch` function.
222
+ * Used to be able to override and provide a different implementation,
223
+ * e.g when mocking.
224
+ */
225
+ export type FetchFunction = (url: string, init: RequestInitNormalized) => Promise<Response>;
@@ -182,7 +182,8 @@ export class Fetcher {
182
182
  }
183
183
  }
184
184
  try {
185
- res.fetchResponse = await Fetcher.callNativeFetch(req.fullUrl, req.init);
185
+ // Calls cfg.fetchFn if set, otherwise Fetcher.callNativeFetch
186
+ res.fetchResponse = await (this.cfg.fetchFn || Fetcher.callNativeFetch)(req.fullUrl, req.init);
186
187
  res.ok = res.fetchResponse.ok;
187
188
  // important to set it to undefined, otherwise it can keep the previous value (from previous try)
188
189
  res.err = undefined;
@@ -201,7 +202,7 @@ export class Fetcher {
201
202
  }
202
203
  res.statusFamily = this.getStatusFamily(res);
203
204
  res.statusCode = (_b = res.fetchResponse) === null || _b === void 0 ? void 0 : _b.status;
204
- if ((_c = res.fetchResponse) === null || _c === void 0 ? void 0 : _c.ok) {
205
+ if (((_c = res.fetchResponse) === null || _c === void 0 ? void 0 : _c.ok) || !req.throwHttpErrors) {
205
206
  try {
206
207
  // We are applying a separate Timeout (as long as original Timeout for now) to "download and parse the body"
207
208
  await pTimeout(async () => await this.onOkResponse(res), {
@@ -270,7 +271,7 @@ export class Fetcher {
270
271
  }
271
272
  res.retryStatus.retryStopped = true;
272
273
  // res.err can happen on `failed to fetch` type of error, e.g JSON.parse, CORS, unexpected redirect
273
- if (!res.err && this.cfg.logResponse) {
274
+ if ((!res.err || !req.throwHttpErrors) && this.cfg.logResponse) {
274
275
  const { retryAttempt } = res.retryStatus;
275
276
  const { logger } = this.cfg;
276
277
  logger.log([
@@ -510,6 +511,7 @@ export class Fetcher {
510
511
  redirect: cfg.redirect,
511
512
  },
512
513
  hooks: {},
514
+ throwHttpErrors: true,
513
515
  }, _omit(cfg, ['method', 'credentials', 'headers', 'redirect', 'logger']));
514
516
  norm.init.headers = _mapKeys(norm.init.headers, k => k.toLowerCase());
515
517
  return norm;
@@ -528,6 +530,7 @@ export class Fetcher {
528
530
  'logResponse',
529
531
  'logResponseBody',
530
532
  'debug',
533
+ 'throwHttpErrors',
531
534
  ])), { 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), { headers: Object.assign({}, this.cfg.init.headers), method: opt.method || this.cfg.init.method, credentials: opt.credentials || this.cfg.init.credentials, redirect: opt.redirect || this.cfg.init.redirect || 'follow' }), {
532
535
  headers: _mapKeys(opt.headers || {}, k => k.toLowerCase()),
533
536
  }) });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naturalcycles/js-lib",
3
- "version": "14.175.0",
3
+ "version": "14.177.0",
4
4
  "scripts": {
5
5
  "prepare": "husky install",
6
6
  "build-prod": "build-prod-esm-cjs",
@@ -19,6 +19,7 @@ export interface FetcherNormalizedCfg
19
19
  | 'debug'
20
20
  | 'redirect'
21
21
  | 'credentials'
22
+ | 'throwHttpErrors'
22
23
  > {
23
24
  logger: CommonLogger
24
25
  searchParams: Record<string, any>
@@ -93,6 +94,8 @@ export interface FetcherCfg {
93
94
  * Defaults to `console`.
94
95
  */
95
96
  logger?: CommonLogger
97
+
98
+ throwHttpErrors?: boolean
96
99
  }
97
100
 
98
101
  export interface FetcherRetryStatus {
@@ -227,6 +230,17 @@ export interface FetcherOptions {
227
230
  * If true - enables all possible logging.
228
231
  */
229
232
  debug?: boolean
233
+
234
+ /**
235
+ * If provided - will be used instead of static `Fetcher.callNativeFetch`.
236
+ */
237
+ fetchFn?: FetchFunction
238
+
239
+ /**
240
+ * Default to true.
241
+ * Set to false to not throw on `!Response.ok`, but simply return `Response.body` as-is (json parsed, etc).
242
+ */
243
+ throwHttpErrors?: boolean
230
244
  }
231
245
 
232
246
  export type RequestInitNormalized = Omit<RequestInit, 'method' | 'headers'> & {
@@ -269,3 +283,10 @@ export type FetcherResponseType =
269
283
  | 'arrayBuffer'
270
284
  | 'blob'
271
285
  | 'readableStream'
286
+
287
+ /**
288
+ * Signature for the `fetch` function.
289
+ * Used to be able to override and provide a different implementation,
290
+ * e.g when mocking.
291
+ */
292
+ export type FetchFunction = (url: string, init: RequestInitNormalized) => Promise<Response>
@@ -286,7 +286,11 @@ export class Fetcher {
286
286
  }
287
287
 
288
288
  try {
289
- res.fetchResponse = await Fetcher.callNativeFetch(req.fullUrl, req.init)
289
+ // Calls cfg.fetchFn if set, otherwise Fetcher.callNativeFetch
290
+ res.fetchResponse = await (this.cfg.fetchFn || Fetcher.callNativeFetch)(
291
+ req.fullUrl,
292
+ req.init,
293
+ )
290
294
  res.ok = res.fetchResponse.ok
291
295
  // important to set it to undefined, otherwise it can keep the previous value (from previous try)
292
296
  res.err = undefined
@@ -304,7 +308,7 @@ export class Fetcher {
304
308
  res.statusFamily = this.getStatusFamily(res)
305
309
  res.statusCode = res.fetchResponse?.status
306
310
 
307
- if (res.fetchResponse?.ok) {
311
+ if (res.fetchResponse?.ok || !req.throwHttpErrors) {
308
312
  try {
309
313
  // We are applying a separate Timeout (as long as original Timeout for now) to "download and parse the body"
310
314
  await pTimeout(
@@ -380,7 +384,7 @@ export class Fetcher {
380
384
  res.retryStatus.retryStopped = true
381
385
 
382
386
  // res.err can happen on `failed to fetch` type of error, e.g JSON.parse, CORS, unexpected redirect
383
- if (!res.err && this.cfg.logResponse) {
387
+ if ((!res.err || !req.throwHttpErrors) && this.cfg.logResponse) {
384
388
  const { retryAttempt } = res.retryStatus
385
389
  const { logger } = this.cfg
386
390
  logger.log(
@@ -650,6 +654,7 @@ export class Fetcher {
650
654
  redirect: cfg.redirect,
651
655
  },
652
656
  hooks: {},
657
+ throwHttpErrors: true,
653
658
  },
654
659
  _omit(cfg, ['method', 'credentials', 'headers', 'redirect', 'logger']),
655
660
  )
@@ -673,6 +678,7 @@ export class Fetcher {
673
678
  'logResponse',
674
679
  'logResponseBody',
675
680
  'debug',
681
+ 'throwHttpErrors',
676
682
  ]),
677
683
  started: Date.now(),
678
684
  ..._omit(opt, ['method', 'headers', 'credentials']),