@naturalcycles/js-lib 14.122.0 → 14.123.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.
@@ -91,7 +91,7 @@ export declare class LocalDate {
91
91
  unix(): UnixTimestampNumber;
92
92
  unixMillis(): UnixTimestampMillisNumber;
93
93
  toJSON(): IsoDateString;
94
- format(fmt: LocalDateFormatter): string;
94
+ format(fmt: Intl.DateTimeFormat | LocalDateFormatter): string;
95
95
  }
96
96
  /**
97
97
  * Shortcut wrapper around `LocalDate.parse` / `LocalDate.today`
@@ -404,6 +404,9 @@ class LocalDate {
404
404
  return this.toString();
405
405
  }
406
406
  format(fmt) {
407
+ if (fmt instanceof Intl.DateTimeFormat) {
408
+ return fmt.format(this.toDate());
409
+ }
407
410
  return fmt(this);
408
411
  }
409
412
  }
@@ -120,7 +120,7 @@ export declare class LocalTime {
120
120
  toStringCompact(seconds?: boolean): string;
121
121
  toString(): string;
122
122
  toJSON(): UnixTimestampNumber;
123
- format(fmt: LocalTimeFormatter): string;
123
+ format(fmt: Intl.DateTimeFormat | LocalTimeFormatter): string;
124
124
  }
125
125
  /**
126
126
  * Shortcut wrapper around `LocalDate.parse` / `LocalDate.today`
@@ -479,6 +479,9 @@ class LocalTime {
479
479
  return this.unix();
480
480
  }
481
481
  format(fmt) {
482
+ if (fmt instanceof Intl.DateTimeFormat) {
483
+ return fmt.format(this.$date);
484
+ }
482
485
  return fmt(this);
483
486
  }
484
487
  }
@@ -119,9 +119,15 @@ class Fetcher {
119
119
  logger.log(req.init.body); // todo: check if we can _inspect it
120
120
  }
121
121
  }
122
- res.fetchResponse = await globalThis.fetch(req.url, req.init);
122
+ try {
123
+ res.fetchResponse = await globalThis.fetch(req.url, req.init);
124
+ }
125
+ catch (err) {
126
+ // For example, CORS error would result in "TypeError: failed to fetch" here
127
+ res.err = err;
128
+ }
123
129
  res.statusFamily = this.getStatusFamily(res);
124
- if (res.fetchResponse.ok) {
130
+ if (res.fetchResponse?.ok) {
125
131
  if (mode === 'json') {
126
132
  // if no body: set responseBody as {}
127
133
  // do not throw a "cannot parse null as Json" error
@@ -150,14 +156,28 @@ class Fetcher {
150
156
  }
151
157
  else {
152
158
  clearTimeout(timeout);
153
- const body = (0, json_util_1._jsonParseIfPossible)(await res.fetchResponse.text());
154
- const errObj = (0, error_util_1._anyToErrorObject)(body);
159
+ let errObj;
160
+ if (res.fetchResponse) {
161
+ const body = (0, json_util_1._jsonParseIfPossible)(await res.fetchResponse.text());
162
+ errObj = (0, error_util_1._anyToErrorObject)(body);
163
+ }
164
+ else if (res.err) {
165
+ errObj = (0, error_util_1._errorToErrorObject)(res.err);
166
+ }
167
+ else {
168
+ errObj = {};
169
+ }
155
170
  const originalMessage = errObj.message;
156
- errObj.message = [[res.fetchResponse.status, signature].join(' '), originalMessage].join('\n');
171
+ errObj.message = [
172
+ [res.fetchResponse?.status, signature].filter(Boolean).join(' '),
173
+ originalMessage,
174
+ ]
175
+ .filter(Boolean)
176
+ .join('\n');
157
177
  res.err = new http_error_1.HttpError(errObj.message, (0, object_util_1._filterNullishValues)({
158
178
  ...errObj.data,
159
179
  originalMessage,
160
- httpStatusCode: res.fetchResponse.status,
180
+ httpStatusCode: res.fetchResponse?.status || 0,
161
181
  // These properties are provided to be used in e.g custom Sentry error grouping
162
182
  // Actually, disabled now, to avoid unnecessary error printing when both msg and data are printed
163
183
  // Enabled, cause `data` is not printed by default when error is HttpError
@@ -201,8 +221,13 @@ class Fetcher {
201
221
  if (method === 'post' && !retryPost)
202
222
  return false;
203
223
  const { statusFamily } = res;
224
+ const statusCode = res.fetchResponse?.status || 0;
204
225
  if (statusFamily === 5 && !retry5xx)
205
226
  return false;
227
+ if ([408, 429].includes(statusCode)) {
228
+ // these codes are always retried
229
+ return true;
230
+ }
206
231
  if (statusFamily === 4 && !retry4xx)
207
232
  return false;
208
233
  return true; // default is true
@@ -254,11 +279,12 @@ class Fetcher {
254
279
  logResponseBody: debug,
255
280
  retry: { ...defRetryOptions },
256
281
  init: {
257
- method: 'get',
258
- headers: {},
282
+ method: cfg.method || 'get',
283
+ headers: cfg.headers || {},
284
+ credentials: cfg.credentials,
259
285
  },
260
286
  hooks: {},
261
- }, cfg);
287
+ }, (0, object_util_1._omit)(cfg, ['method', 'credentials', 'headers']));
262
288
  norm.init.headers = (0, object_util_1._mapKeys)(norm.init.headers, k => k.toLowerCase());
263
289
  return norm;
264
290
  }
@@ -271,18 +297,18 @@ class Fetcher {
271
297
  retryPost,
272
298
  retry4xx,
273
299
  retry5xx,
274
- ...(0, object_util_1._omit)(opt, ['method', 'headers']),
300
+ ...(0, object_util_1._omit)(opt, ['method', 'headers', 'credentials']),
275
301
  retry: {
276
302
  ...retry,
277
303
  ...(0, object_util_1._filterUndefinedValues)(opt.retry || {}),
278
304
  },
279
- init: (0, object_util_1._merge)({ ...this.cfg.init },
280
- // opt.init,
281
- (0, object_util_1._filterUndefinedValues)({
282
- method: opt.method,
283
- credentials: opt.credentials,
305
+ init: (0, object_util_1._merge)({
306
+ ...this.cfg.init,
307
+ method: opt.method || this.cfg.init.method,
308
+ credentials: opt.credentials || this.cfg.init.credentials,
309
+ }, {
284
310
  headers: (0, object_util_1._mapKeys)(opt.headers || {}, k => k.toLowerCase()),
285
- })),
311
+ }),
286
312
  };
287
313
  // setup url
288
314
  if (baseUrl) {
@@ -401,6 +401,9 @@ export class LocalDate {
401
401
  return this.toString();
402
402
  }
403
403
  format(fmt) {
404
+ if (fmt instanceof Intl.DateTimeFormat) {
405
+ return fmt.format(this.toDate());
406
+ }
404
407
  return fmt(this);
405
408
  }
406
409
  }
@@ -477,6 +477,9 @@ export class LocalTime {
477
477
  return this.unix();
478
478
  }
479
479
  format(fmt) {
480
+ if (fmt instanceof Intl.DateTimeFormat) {
481
+ return fmt.format(this.$date);
482
+ }
480
483
  return fmt(this);
481
484
  }
482
485
  }
@@ -1,6 +1,6 @@
1
1
  /// <reference lib="dom"/>
2
2
  import { __asyncValues } from "tslib";
3
- import { _anyToErrorObject } from '../error/error.util';
3
+ import { _anyToErrorObject, _errorToErrorObject } from '../error/error.util';
4
4
  import { HttpError } from '../error/http.error';
5
5
  import { _clamp } from '../number/number.util';
6
6
  import { _filterNullishValues, _filterUndefinedValues, _mapKeys, _merge, _omit, } from '../object/object.util';
@@ -73,6 +73,7 @@ export class Fetcher {
73
73
  }
74
74
  async rawFetch(url, rawOpt = {}) {
75
75
  var _a, e_1, _b, _c, _d, e_2, _e, _f;
76
+ var _g, _h, _j;
76
77
  const { logger } = this.cfg;
77
78
  const req = this.normalizeOptions(url, rawOpt);
78
79
  const { timeoutSeconds, mode, init: { method }, } = req;
@@ -86,22 +87,22 @@ export class Fetcher {
86
87
  }, timeoutSeconds * 1000);
87
88
  }
88
89
  try {
89
- for (var _g = true, _h = __asyncValues(this.cfg.hooks.beforeRequest || []), _j; _j = await _h.next(), _a = _j.done, !_a;) {
90
- _c = _j.value;
91
- _g = false;
90
+ for (var _k = true, _l = __asyncValues(this.cfg.hooks.beforeRequest || []), _m; _m = await _l.next(), _a = _m.done, !_a;) {
91
+ _c = _m.value;
92
+ _k = false;
92
93
  try {
93
94
  const hook = _c;
94
95
  await hook(req);
95
96
  }
96
97
  finally {
97
- _g = true;
98
+ _k = true;
98
99
  }
99
100
  }
100
101
  }
101
102
  catch (e_1_1) { e_1 = { error: e_1_1 }; }
102
103
  finally {
103
104
  try {
104
- if (!_g && !_a && (_b = _h.return)) await _b.call(_h);
105
+ if (!_k && !_a && (_b = _l.return)) await _b.call(_l);
105
106
  }
106
107
  finally { if (e_1) throw e_1.error; }
107
108
  }
@@ -127,9 +128,15 @@ export class Fetcher {
127
128
  logger.log(req.init.body); // todo: check if we can _inspect it
128
129
  }
129
130
  }
130
- res.fetchResponse = await globalThis.fetch(req.url, req.init);
131
+ try {
132
+ res.fetchResponse = await globalThis.fetch(req.url, req.init);
133
+ }
134
+ catch (err) {
135
+ // For example, CORS error would result in "TypeError: failed to fetch" here
136
+ res.err = err;
137
+ }
131
138
  res.statusFamily = this.getStatusFamily(res);
132
- if (res.fetchResponse.ok) {
139
+ if ((_g = res.fetchResponse) === null || _g === void 0 ? void 0 : _g.ok) {
133
140
  if (mode === 'json') {
134
141
  // if no body: set responseBody as {}
135
142
  // do not throw a "cannot parse null as Json" error
@@ -158,11 +165,25 @@ export class Fetcher {
158
165
  }
159
166
  else {
160
167
  clearTimeout(timeout);
161
- const body = _jsonParseIfPossible(await res.fetchResponse.text());
162
- const errObj = _anyToErrorObject(body);
168
+ let errObj;
169
+ if (res.fetchResponse) {
170
+ const body = _jsonParseIfPossible(await res.fetchResponse.text());
171
+ errObj = _anyToErrorObject(body);
172
+ }
173
+ else if (res.err) {
174
+ errObj = _errorToErrorObject(res.err);
175
+ }
176
+ else {
177
+ errObj = {};
178
+ }
163
179
  const originalMessage = errObj.message;
164
- errObj.message = [[res.fetchResponse.status, signature].join(' '), originalMessage].join('\n');
165
- res.err = new HttpError(errObj.message, _filterNullishValues(Object.assign(Object.assign({}, errObj.data), { originalMessage, httpStatusCode: res.fetchResponse.status,
180
+ errObj.message = [
181
+ [(_h = res.fetchResponse) === null || _h === void 0 ? void 0 : _h.status, signature].filter(Boolean).join(' '),
182
+ originalMessage,
183
+ ]
184
+ .filter(Boolean)
185
+ .join('\n');
186
+ 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,
166
187
  // These properties are provided to be used in e.g custom Sentry error grouping
167
188
  // Actually, disabled now, to avoid unnecessary error printing when both msg and data are printed
168
189
  // Enabled, cause `data` is not printed by default when error is HttpError
@@ -172,22 +193,22 @@ export class Fetcher {
172
193
  }
173
194
  }
174
195
  try {
175
- for (var _k = true, _l = __asyncValues(this.cfg.hooks.afterResponse || []), _m; _m = await _l.next(), _d = _m.done, !_d;) {
176
- _f = _m.value;
177
- _k = false;
196
+ for (var _o = true, _p = __asyncValues(this.cfg.hooks.afterResponse || []), _q; _q = await _p.next(), _d = _q.done, !_d;) {
197
+ _f = _q.value;
198
+ _o = false;
178
199
  try {
179
200
  const hook = _f;
180
201
  await hook(res);
181
202
  }
182
203
  finally {
183
- _k = true;
204
+ _o = true;
184
205
  }
185
206
  }
186
207
  }
187
208
  catch (e_2_1) { e_2 = { error: e_2_1 }; }
188
209
  finally {
189
210
  try {
190
- if (!_k && !_d && (_e = _l.return)) await _e.call(_l);
211
+ if (!_o && !_d && (_e = _p.return)) await _e.call(_p);
191
212
  }
192
213
  finally { if (e_2) throw e_2.error; }
193
214
  }
@@ -234,13 +255,19 @@ export class Fetcher {
234
255
  * unless there's reason not to (e.g method is POST).
235
256
  */
236
257
  shouldRetry(res) {
258
+ var _a;
237
259
  const { retryPost, retry4xx, retry5xx } = res.req;
238
260
  const { method } = res.req.init;
239
261
  if (method === 'post' && !retryPost)
240
262
  return false;
241
263
  const { statusFamily } = res;
264
+ const statusCode = ((_a = res.fetchResponse) === null || _a === void 0 ? void 0 : _a.status) || 0;
242
265
  if (statusFamily === 5 && !retry5xx)
243
266
  return false;
267
+ if ([408, 429].includes(statusCode)) {
268
+ // these codes are always retried
269
+ return true;
270
+ }
244
271
  if (statusFamily === 4 && !retry4xx)
245
272
  return false;
246
273
  return true; // default is true
@@ -294,11 +321,12 @@ export class Fetcher {
294
321
  logResponseBody: debug,
295
322
  retry: Object.assign({}, defRetryOptions),
296
323
  init: {
297
- method: 'get',
298
- headers: {},
324
+ method: cfg.method || 'get',
325
+ headers: cfg.headers || {},
326
+ credentials: cfg.credentials,
299
327
  },
300
328
  hooks: {},
301
- }, cfg);
329
+ }, _omit(cfg, ['method', 'credentials', 'headers']));
302
330
  norm.init.headers = _mapKeys(norm.init.headers, k => k.toLowerCase());
303
331
  return norm;
304
332
  }
@@ -309,13 +337,9 @@ export class Fetcher {
309
337
  throwHttpErrors,
310
338
  retryPost,
311
339
  retry4xx,
312
- retry5xx }, _omit(opt, ['method', 'headers'])), { retry: Object.assign(Object.assign({}, retry), _filterUndefinedValues(opt.retry || {})), init: _merge(Object.assign({}, this.cfg.init),
313
- // opt.init,
314
- _filterUndefinedValues({
315
- method: opt.method,
316
- credentials: opt.credentials,
340
+ retry5xx }, _omit(opt, ['method', 'headers', 'credentials'])), { retry: Object.assign(Object.assign({}, 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 }), {
317
341
  headers: _mapKeys(opt.headers || {}, k => k.toLowerCase()),
318
- })) });
342
+ }) });
319
343
  // setup url
320
344
  if (baseUrl) {
321
345
  if (url.startsWith('/')) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naturalcycles/js-lib",
3
- "version": "14.122.0",
3
+ "version": "14.123.0",
4
4
  "scripts": {
5
5
  "prepare": "husky install",
6
6
  "build-prod": "build-prod-esm-cjs",
@@ -492,7 +492,11 @@ export class LocalDate {
492
492
  return this.toString()
493
493
  }
494
494
 
495
- format(fmt: LocalDateFormatter): string {
495
+ format(fmt: Intl.DateTimeFormat | LocalDateFormatter): string {
496
+ if (fmt instanceof Intl.DateTimeFormat) {
497
+ return fmt.format(this.toDate())
498
+ }
499
+
496
500
  return fmt(this)
497
501
  }
498
502
  }
@@ -590,7 +590,11 @@ export class LocalTime {
590
590
  return this.unix()
591
591
  }
592
592
 
593
- format(fmt: LocalTimeFormatter): string {
593
+ format(fmt: Intl.DateTimeFormat | LocalTimeFormatter): string {
594
+ if (fmt instanceof Intl.DateTimeFormat) {
595
+ return fmt.format(this.$date)
596
+ }
597
+
594
598
  return fmt(this)
595
599
  }
596
600
  }
@@ -1,6 +1,7 @@
1
1
  /// <reference lib="dom"/>
2
2
 
3
- import { _anyToErrorObject } from '../error/error.util'
3
+ import { ErrorObject } from '../error/error.model'
4
+ import { _anyToErrorObject, _errorToErrorObject } from '../error/error.util'
4
5
  import { HttpError } from '../error/http.error'
5
6
  import { CommonLogger } from '../log/commonLogger'
6
7
  import { _clamp } from '../number/number.util'
@@ -333,10 +334,15 @@ export class Fetcher {
333
334
  }
334
335
  }
335
336
 
336
- res.fetchResponse = await globalThis.fetch(req.url, req.init)
337
+ try {
338
+ res.fetchResponse = await globalThis.fetch(req.url, req.init)
339
+ } catch (err) {
340
+ // For example, CORS error would result in "TypeError: failed to fetch" here
341
+ res.err = err as Error
342
+ }
337
343
  res.statusFamily = this.getStatusFamily(res)
338
344
 
339
- if (res.fetchResponse.ok) {
345
+ if (res.fetchResponse?.ok) {
340
346
  if (mode === 'json') {
341
347
  // if no body: set responseBody as {}
342
348
  // do not throw a "cannot parse null as Json" error
@@ -369,12 +375,24 @@ export class Fetcher {
369
375
  } else {
370
376
  clearTimeout(timeout)
371
377
 
372
- const body = _jsonParseIfPossible(await res.fetchResponse.text())
373
- const errObj = _anyToErrorObject(body)
378
+ let errObj: ErrorObject
379
+
380
+ if (res.fetchResponse) {
381
+ const body = _jsonParseIfPossible(await res.fetchResponse.text())
382
+ errObj = _anyToErrorObject(body)
383
+ } else if (res.err) {
384
+ errObj = _errorToErrorObject(res.err)
385
+ } else {
386
+ errObj = {} as ErrorObject
387
+ }
388
+
374
389
  const originalMessage = errObj.message
375
- errObj.message = [[res.fetchResponse.status, signature].join(' '), originalMessage].join(
376
- '\n',
377
- )
390
+ errObj.message = [
391
+ [res.fetchResponse?.status, signature].filter(Boolean).join(' '),
392
+ originalMessage,
393
+ ]
394
+ .filter(Boolean)
395
+ .join('\n')
378
396
 
379
397
  res.err = new HttpError(
380
398
  errObj.message,
@@ -382,7 +400,7 @@ export class Fetcher {
382
400
  _filterNullishValues({
383
401
  ...errObj.data,
384
402
  originalMessage,
385
- httpStatusCode: res.fetchResponse.status,
403
+ httpStatusCode: res.fetchResponse?.status || 0,
386
404
  // These properties are provided to be used in e.g custom Sentry error grouping
387
405
  // Actually, disabled now, to avoid unnecessary error printing when both msg and data are printed
388
406
  // Enabled, cause `data` is not printed by default when error is HttpError
@@ -437,7 +455,12 @@ export class Fetcher {
437
455
  const { method } = res.req.init
438
456
  if (method === 'post' && !retryPost) return false
439
457
  const { statusFamily } = res
458
+ const statusCode = res.fetchResponse?.status || 0
440
459
  if (statusFamily === 5 && !retry5xx) return false
460
+ if ([408, 429].includes(statusCode)) {
461
+ // these codes are always retried
462
+ return true
463
+ }
441
464
  if (statusFamily === 4 && !retry4xx) return false
442
465
  return true // default is true
443
466
  }
@@ -487,12 +510,13 @@ export class Fetcher {
487
510
  logResponseBody: debug,
488
511
  retry: { ...defRetryOptions },
489
512
  init: {
490
- method: 'get',
491
- headers: {},
513
+ method: cfg.method || 'get',
514
+ headers: cfg.headers || {},
515
+ credentials: cfg.credentials,
492
516
  },
493
517
  hooks: {},
494
518
  },
495
- cfg,
519
+ _omit(cfg, ['method', 'credentials', 'headers']),
496
520
  )
497
521
 
498
522
  norm.init.headers = _mapKeys(norm.init.headers, k => k.toLowerCase())
@@ -511,19 +535,20 @@ export class Fetcher {
511
535
  retryPost,
512
536
  retry4xx,
513
537
  retry5xx,
514
- ..._omit(opt, ['method', 'headers']),
538
+ ..._omit(opt, ['method', 'headers', 'credentials']),
515
539
  retry: {
516
540
  ...retry,
517
541
  ..._filterUndefinedValues(opt.retry || {}),
518
542
  },
519
543
  init: _merge(
520
- { ...this.cfg.init },
521
- // opt.init,
522
- _filterUndefinedValues({
523
- method: opt.method,
524
- credentials: opt.credentials,
544
+ {
545
+ ...this.cfg.init,
546
+ method: opt.method || this.cfg.init.method,
547
+ credentials: opt.credentials || this.cfg.init.credentials,
548
+ },
549
+ {
525
550
  headers: _mapKeys(opt.headers || {}, k => k.toLowerCase()),
526
- } as RequestInit),
551
+ } as RequestInit,
527
552
  ),
528
553
  }
529
554