@naturalcycles/js-lib 14.139.3 → 14.141.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/array/array.util.d.ts +4 -0
- package/dist/array/array.util.js +46 -1
- package/dist/datetime/localDate.d.ts +1 -1
- package/dist/datetime/localDate.js +3 -0
- package/dist/datetime/localTime.js +1 -1
- package/dist/http/fetcher.d.ts +6 -1
- package/dist/http/fetcher.js +10 -3
- package/dist/string/stringifyAny.js +10 -14
- package/dist-esm/array/array.util.js +41 -0
- package/dist-esm/datetime/localDate.js +3 -0
- package/dist-esm/datetime/localTime.js +1 -1
- package/dist-esm/http/fetcher.js +10 -3
- package/dist-esm/string/stringifyAny.js +10 -14
- package/package.json +1 -1
- package/src/array/array.util.ts +50 -0
- package/src/datetime/localDate.ts +4 -1
- package/src/datetime/localTime.ts +1 -5
- package/src/http/fetcher.ts +11 -3
- package/src/string/stringifyAny.ts +12 -14
|
@@ -159,3 +159,7 @@ export declare function _maxOrUndefined<T>(array: T[]): NonNullable<T> | undefin
|
|
|
159
159
|
* Filters out nullish values (undefined and null).
|
|
160
160
|
*/
|
|
161
161
|
export declare function _max<T>(array: T[]): NonNullable<T>;
|
|
162
|
+
export declare function _maxBy<T>(array: T[], mapper: Mapper<T, number | undefined>): T;
|
|
163
|
+
export declare function _minBy<T>(array: T[], mapper: Mapper<T, number | undefined>): T;
|
|
164
|
+
export declare function _maxByOrUndefined<T>(array: T[], mapper: Mapper<T, number | undefined>): T | undefined;
|
|
165
|
+
export declare function _minByOrUndefined<T>(array: T[], mapper: Mapper<T, number | undefined>): T | undefined;
|
package/dist/array/array.util.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports._max = exports._maxOrUndefined = exports._min = exports._minOrUndefined = exports._lastOrUndefined = exports._last = exports._shuffle = exports._mapToObject = exports._sumBy = exports._sum = exports._difference = exports._intersection = exports._countBy = exports._dropRightWhile = exports._dropWhile = exports._takeRightWhile = exports._takeWhile = exports._findLast = exports._sortBy = exports._groupBy = exports._by = exports._uniqBy = exports._uniq = exports._flattenDeep = exports._flatten = exports._chunk = void 0;
|
|
3
|
+
exports._minByOrUndefined = exports._maxByOrUndefined = exports._minBy = exports._maxBy = exports._max = exports._maxOrUndefined = exports._min = exports._minOrUndefined = exports._lastOrUndefined = exports._last = exports._shuffle = exports._mapToObject = exports._sumBy = exports._sum = exports._difference = exports._intersection = exports._countBy = exports._dropRightWhile = exports._dropWhile = exports._takeRightWhile = exports._takeWhile = exports._findLast = exports._sortBy = exports._groupBy = exports._by = exports._uniqBy = exports._uniq = exports._flattenDeep = exports._flatten = exports._chunk = void 0;
|
|
4
4
|
const is_util_1 = require("../is.util");
|
|
5
5
|
/**
|
|
6
6
|
* Creates an array of elements split into groups the length of size. If collection can’t be split evenly, the
|
|
@@ -314,3 +314,48 @@ function _max(array) {
|
|
|
314
314
|
return a.reduce((max, item) => (max >= item ? max : item));
|
|
315
315
|
}
|
|
316
316
|
exports._max = _max;
|
|
317
|
+
function _maxBy(array, mapper) {
|
|
318
|
+
const max = _maxByOrUndefined(array, mapper);
|
|
319
|
+
if (max === undefined)
|
|
320
|
+
throw new Error(`_maxBy returned undefined`);
|
|
321
|
+
return max;
|
|
322
|
+
}
|
|
323
|
+
exports._maxBy = _maxBy;
|
|
324
|
+
function _minBy(array, mapper) {
|
|
325
|
+
const min = _minByOrUndefined(array, mapper);
|
|
326
|
+
if (min === undefined)
|
|
327
|
+
throw new Error(`_minBy returned undefined`);
|
|
328
|
+
return min;
|
|
329
|
+
}
|
|
330
|
+
exports._minBy = _minBy;
|
|
331
|
+
// todo: looks like it _maxByOrUndefined/_minByOrUndefined can be DRYer
|
|
332
|
+
function _maxByOrUndefined(array, mapper) {
|
|
333
|
+
if (!array.length)
|
|
334
|
+
return;
|
|
335
|
+
let maxItem;
|
|
336
|
+
let max;
|
|
337
|
+
array.forEach((item, i) => {
|
|
338
|
+
const v = mapper(item, i);
|
|
339
|
+
if (v !== undefined && (max === undefined || v > max)) {
|
|
340
|
+
maxItem = item;
|
|
341
|
+
max = v;
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
return maxItem;
|
|
345
|
+
}
|
|
346
|
+
exports._maxByOrUndefined = _maxByOrUndefined;
|
|
347
|
+
function _minByOrUndefined(array, mapper) {
|
|
348
|
+
if (!array.length)
|
|
349
|
+
return;
|
|
350
|
+
let minItem;
|
|
351
|
+
let min;
|
|
352
|
+
array.forEach((item, i) => {
|
|
353
|
+
const v = mapper(item, i);
|
|
354
|
+
if (v !== undefined && (min === undefined || v < min)) {
|
|
355
|
+
minItem = item;
|
|
356
|
+
min = v;
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
return minItem;
|
|
360
|
+
}
|
|
361
|
+
exports._minByOrUndefined = _minByOrUndefined;
|
|
@@ -3,7 +3,7 @@ import { LocalTime } from './localTime';
|
|
|
3
3
|
export type LocalDateUnit = LocalDateUnitStrict | 'week';
|
|
4
4
|
export type LocalDateUnitStrict = 'year' | 'month' | 'day';
|
|
5
5
|
export type Inclusiveness = '()' | '[]' | '[)' | '(]';
|
|
6
|
-
export type LocalDateConfig = LocalDate | IsoDateString;
|
|
6
|
+
export type LocalDateConfig = LocalDate | Date | IsoDateString;
|
|
7
7
|
export type LocalDateFormatter = (ld: LocalDate) => string;
|
|
8
8
|
/**
|
|
9
9
|
* @experimental
|
|
@@ -50,6 +50,9 @@ class LocalDate {
|
|
|
50
50
|
return null;
|
|
51
51
|
if (d instanceof LocalDate)
|
|
52
52
|
return d;
|
|
53
|
+
if (d instanceof Date) {
|
|
54
|
+
return this.fromDate(d);
|
|
55
|
+
}
|
|
53
56
|
// const [year, month, day] = d.slice(0, 10).split('-').map(Number)
|
|
54
57
|
const matches = typeof d === 'string' && DATE_REGEX.exec(d.slice(0, 10));
|
|
55
58
|
if (!matches)
|
|
@@ -406,7 +406,7 @@ class LocalTime {
|
|
|
406
406
|
return Math.floor(this.$date.valueOf() / 1000);
|
|
407
407
|
}
|
|
408
408
|
toLocalDate() {
|
|
409
|
-
return localDate_1.LocalDate.
|
|
409
|
+
return localDate_1.LocalDate.fromDate(this.$date);
|
|
410
410
|
}
|
|
411
411
|
toPretty(seconds = true) {
|
|
412
412
|
const { year, month, day, hour, minute, second } = this.components();
|
package/dist/http/fetcher.d.ts
CHANGED
|
@@ -34,9 +34,14 @@ export declare class Fetcher {
|
|
|
34
34
|
/**
|
|
35
35
|
* Returns FetcherResponse.
|
|
36
36
|
* Never throws, returns `err` property in the response instead.
|
|
37
|
+
* Use this method instead of `throwHttpErrors: false` or try-catching.
|
|
37
38
|
*/
|
|
38
|
-
|
|
39
|
+
doFetch<T = unknown>(url: string, rawOpt?: FetcherOptions): Promise<FetcherResponse<T>>;
|
|
39
40
|
private onOkResponse;
|
|
41
|
+
/**
|
|
42
|
+
* This method exists to be able to easily mock it.
|
|
43
|
+
*/
|
|
44
|
+
callNativeFetch(url: string, init: RequestInit): Promise<Response>;
|
|
40
45
|
private onNotOkResponse;
|
|
41
46
|
private processRetry;
|
|
42
47
|
/**
|
package/dist/http/fetcher.js
CHANGED
|
@@ -78,7 +78,7 @@ class Fetcher {
|
|
|
78
78
|
return new Fetcher(cfg);
|
|
79
79
|
}
|
|
80
80
|
async fetch(url, opt) {
|
|
81
|
-
const res = await this.
|
|
81
|
+
const res = await this.doFetch(url, opt);
|
|
82
82
|
if (res.err) {
|
|
83
83
|
if (res.req.throwHttpErrors)
|
|
84
84
|
throw res.err;
|
|
@@ -89,8 +89,9 @@ class Fetcher {
|
|
|
89
89
|
/**
|
|
90
90
|
* Returns FetcherResponse.
|
|
91
91
|
* Never throws, returns `err` property in the response instead.
|
|
92
|
+
* Use this method instead of `throwHttpErrors: false` or try-catching.
|
|
92
93
|
*/
|
|
93
|
-
async
|
|
94
|
+
async doFetch(url, rawOpt = {}) {
|
|
94
95
|
const { logger } = this.cfg;
|
|
95
96
|
const req = this.normalizeOptions(url, rawOpt);
|
|
96
97
|
const { timeoutSeconds, init: { method }, } = req;
|
|
@@ -131,7 +132,7 @@ class Fetcher {
|
|
|
131
132
|
}
|
|
132
133
|
}
|
|
133
134
|
try {
|
|
134
|
-
res.fetchResponse = await
|
|
135
|
+
res.fetchResponse = await this.callNativeFetch(req.url, req.init);
|
|
135
136
|
res.ok = res.fetchResponse.ok;
|
|
136
137
|
}
|
|
137
138
|
catch (err) {
|
|
@@ -215,6 +216,12 @@ class Fetcher {
|
|
|
215
216
|
}
|
|
216
217
|
}
|
|
217
218
|
}
|
|
219
|
+
/**
|
|
220
|
+
* This method exists to be able to easily mock it.
|
|
221
|
+
*/
|
|
222
|
+
async callNativeFetch(url, init) {
|
|
223
|
+
return await globalThis.fetch(url, init);
|
|
224
|
+
}
|
|
218
225
|
async onNotOkResponse(res, timeout) {
|
|
219
226
|
clearTimeout(timeout);
|
|
220
227
|
let errObj;
|
|
@@ -69,7 +69,15 @@ function _stringifyAny(obj, opt = {}) {
|
|
|
69
69
|
// if (obj?.name === 'Error') {
|
|
70
70
|
// s = obj.message
|
|
71
71
|
// }
|
|
72
|
-
|
|
72
|
+
if ((0, error_util_1._isErrorObject)(obj) && (0, error_util_1._isHttpErrorObject)(obj)) {
|
|
73
|
+
// Printing (0) to avoid ambiguity
|
|
74
|
+
s = `${obj.name}(${obj.data.httpStatusCode}): ${obj.message}`;
|
|
75
|
+
}
|
|
76
|
+
s ||= [obj.name, obj.message].filter(Boolean).join(': ');
|
|
77
|
+
if (typeof obj.code === 'string') {
|
|
78
|
+
// Error that has no `data`, but has `code` property
|
|
79
|
+
s += `\ncode: ${obj.code}`;
|
|
80
|
+
}
|
|
73
81
|
if (opt.includeErrorStack && obj.stack) {
|
|
74
82
|
// Here we're using the previously-generated "title line" (e.g "Error: some_message"),
|
|
75
83
|
// concatenating it with the Stack (but without the title line of the Stack)
|
|
@@ -79,18 +87,6 @@ function _stringifyAny(obj, opt = {}) {
|
|
|
79
87
|
const sLines = s.split('\n').length;
|
|
80
88
|
s = [s, ...obj.stack.split('\n').slice(sLines)].join('\n');
|
|
81
89
|
}
|
|
82
|
-
if ((0, error_util_1._isErrorObject)(obj)) {
|
|
83
|
-
if ((0, error_util_1._isHttpErrorObject)(obj)) {
|
|
84
|
-
// Only include (statusCode) if it's non-zero
|
|
85
|
-
// No: print (0), as it removes ambiguity
|
|
86
|
-
// `replace` here works ONCE, exactly as we need it
|
|
87
|
-
s = s.replace('HttpError', `HttpError(${obj.data.httpStatusCode})`);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
else if (typeof obj.code === 'string') {
|
|
91
|
-
// Error that has no `data`, but has `code` property
|
|
92
|
-
s = [s, `code: ${obj.code}`].join('\n');
|
|
93
|
-
}
|
|
94
90
|
if (supportsAggregateError && obj instanceof AggregateError && obj.errors.length) {
|
|
95
91
|
s = [
|
|
96
92
|
s,
|
|
@@ -99,7 +95,7 @@ function _stringifyAny(obj, opt = {}) {
|
|
|
99
95
|
].join('\n');
|
|
100
96
|
}
|
|
101
97
|
if (obj.cause && includeErrorCause) {
|
|
102
|
-
s = s + '\
|
|
98
|
+
s = s + '\nCaused by: ' + _stringifyAny(obj.cause, opt);
|
|
103
99
|
}
|
|
104
100
|
}
|
|
105
101
|
else if (typeof obj === 'string') {
|
|
@@ -285,3 +285,44 @@ export function _max(array) {
|
|
|
285
285
|
throw new Error('_max called on empty array');
|
|
286
286
|
return a.reduce((max, item) => (max >= item ? max : item));
|
|
287
287
|
}
|
|
288
|
+
export function _maxBy(array, mapper) {
|
|
289
|
+
const max = _maxByOrUndefined(array, mapper);
|
|
290
|
+
if (max === undefined)
|
|
291
|
+
throw new Error(`_maxBy returned undefined`);
|
|
292
|
+
return max;
|
|
293
|
+
}
|
|
294
|
+
export function _minBy(array, mapper) {
|
|
295
|
+
const min = _minByOrUndefined(array, mapper);
|
|
296
|
+
if (min === undefined)
|
|
297
|
+
throw new Error(`_minBy returned undefined`);
|
|
298
|
+
return min;
|
|
299
|
+
}
|
|
300
|
+
// todo: looks like it _maxByOrUndefined/_minByOrUndefined can be DRYer
|
|
301
|
+
export function _maxByOrUndefined(array, mapper) {
|
|
302
|
+
if (!array.length)
|
|
303
|
+
return;
|
|
304
|
+
let maxItem;
|
|
305
|
+
let max;
|
|
306
|
+
array.forEach((item, i) => {
|
|
307
|
+
const v = mapper(item, i);
|
|
308
|
+
if (v !== undefined && (max === undefined || v > max)) {
|
|
309
|
+
maxItem = item;
|
|
310
|
+
max = v;
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
return maxItem;
|
|
314
|
+
}
|
|
315
|
+
export function _minByOrUndefined(array, mapper) {
|
|
316
|
+
if (!array.length)
|
|
317
|
+
return;
|
|
318
|
+
let minItem;
|
|
319
|
+
let min;
|
|
320
|
+
array.forEach((item, i) => {
|
|
321
|
+
const v = mapper(item, i);
|
|
322
|
+
if (v !== undefined && (min === undefined || v < min)) {
|
|
323
|
+
minItem = item;
|
|
324
|
+
min = v;
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
return minItem;
|
|
328
|
+
}
|
|
@@ -47,6 +47,9 @@ export class LocalDate {
|
|
|
47
47
|
return null;
|
|
48
48
|
if (d instanceof LocalDate)
|
|
49
49
|
return d;
|
|
50
|
+
if (d instanceof Date) {
|
|
51
|
+
return this.fromDate(d);
|
|
52
|
+
}
|
|
50
53
|
// const [year, month, day] = d.slice(0, 10).split('-').map(Number)
|
|
51
54
|
const matches = typeof d === 'string' && DATE_REGEX.exec(d.slice(0, 10));
|
|
52
55
|
if (!matches)
|
|
@@ -404,7 +404,7 @@ export class LocalTime {
|
|
|
404
404
|
return Math.floor(this.$date.valueOf() / 1000);
|
|
405
405
|
}
|
|
406
406
|
toLocalDate() {
|
|
407
|
-
return LocalDate.
|
|
407
|
+
return LocalDate.fromDate(this.$date);
|
|
408
408
|
}
|
|
409
409
|
toPretty(seconds = true) {
|
|
410
410
|
const { year, month, day, hour, minute, second } = this.components();
|
package/dist-esm/http/fetcher.js
CHANGED
|
@@ -67,7 +67,7 @@ export class Fetcher {
|
|
|
67
67
|
return new Fetcher(cfg);
|
|
68
68
|
}
|
|
69
69
|
async fetch(url, opt) {
|
|
70
|
-
const res = await this.
|
|
70
|
+
const res = await this.doFetch(url, opt);
|
|
71
71
|
if (res.err) {
|
|
72
72
|
if (res.req.throwHttpErrors)
|
|
73
73
|
throw res.err;
|
|
@@ -78,8 +78,9 @@ export class Fetcher {
|
|
|
78
78
|
/**
|
|
79
79
|
* Returns FetcherResponse.
|
|
80
80
|
* Never throws, returns `err` property in the response instead.
|
|
81
|
+
* Use this method instead of `throwHttpErrors: false` or try-catching.
|
|
81
82
|
*/
|
|
82
|
-
async
|
|
83
|
+
async doFetch(url, rawOpt = {}) {
|
|
83
84
|
var _a, e_1, _b, _c, _d, e_2, _e, _f;
|
|
84
85
|
var _g;
|
|
85
86
|
const { logger } = this.cfg;
|
|
@@ -139,7 +140,7 @@ export class Fetcher {
|
|
|
139
140
|
}
|
|
140
141
|
}
|
|
141
142
|
try {
|
|
142
|
-
res.fetchResponse = await
|
|
143
|
+
res.fetchResponse = await this.callNativeFetch(req.url, req.init);
|
|
143
144
|
res.ok = res.fetchResponse.ok;
|
|
144
145
|
}
|
|
145
146
|
catch (err) {
|
|
@@ -240,6 +241,12 @@ export class Fetcher {
|
|
|
240
241
|
}
|
|
241
242
|
}
|
|
242
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* This method exists to be able to easily mock it.
|
|
246
|
+
*/
|
|
247
|
+
async callNativeFetch(url, init) {
|
|
248
|
+
return await globalThis.fetch(url, init);
|
|
249
|
+
}
|
|
243
250
|
async onNotOkResponse(res, timeout) {
|
|
244
251
|
var _a, _b;
|
|
245
252
|
clearTimeout(timeout);
|
|
@@ -65,7 +65,15 @@ export function _stringifyAny(obj, opt = {}) {
|
|
|
65
65
|
// if (obj?.name === 'Error') {
|
|
66
66
|
// s = obj.message
|
|
67
67
|
// }
|
|
68
|
-
|
|
68
|
+
if (_isErrorObject(obj) && _isHttpErrorObject(obj)) {
|
|
69
|
+
// Printing (0) to avoid ambiguity
|
|
70
|
+
s = `${obj.name}(${obj.data.httpStatusCode}): ${obj.message}`;
|
|
71
|
+
}
|
|
72
|
+
s || (s = [obj.name, obj.message].filter(Boolean).join(': '));
|
|
73
|
+
if (typeof obj.code === 'string') {
|
|
74
|
+
// Error that has no `data`, but has `code` property
|
|
75
|
+
s += `\ncode: ${obj.code}`;
|
|
76
|
+
}
|
|
69
77
|
if (opt.includeErrorStack && obj.stack) {
|
|
70
78
|
// Here we're using the previously-generated "title line" (e.g "Error: some_message"),
|
|
71
79
|
// concatenating it with the Stack (but without the title line of the Stack)
|
|
@@ -75,18 +83,6 @@ export function _stringifyAny(obj, opt = {}) {
|
|
|
75
83
|
const sLines = s.split('\n').length;
|
|
76
84
|
s = [s, ...obj.stack.split('\n').slice(sLines)].join('\n');
|
|
77
85
|
}
|
|
78
|
-
if (_isErrorObject(obj)) {
|
|
79
|
-
if (_isHttpErrorObject(obj)) {
|
|
80
|
-
// Only include (statusCode) if it's non-zero
|
|
81
|
-
// No: print (0), as it removes ambiguity
|
|
82
|
-
// `replace` here works ONCE, exactly as we need it
|
|
83
|
-
s = s.replace('HttpError', `HttpError(${obj.data.httpStatusCode})`);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
else if (typeof obj.code === 'string') {
|
|
87
|
-
// Error that has no `data`, but has `code` property
|
|
88
|
-
s = [s, `code: ${obj.code}`].join('\n');
|
|
89
|
-
}
|
|
90
86
|
if (supportsAggregateError && obj instanceof AggregateError && obj.errors.length) {
|
|
91
87
|
s = [
|
|
92
88
|
s,
|
|
@@ -95,7 +91,7 @@ export function _stringifyAny(obj, opt = {}) {
|
|
|
95
91
|
].join('\n');
|
|
96
92
|
}
|
|
97
93
|
if (obj.cause && includeErrorCause) {
|
|
98
|
-
s = s + '\
|
|
94
|
+
s = s + '\nCaused by: ' + _stringifyAny(obj.cause, opt);
|
|
99
95
|
}
|
|
100
96
|
}
|
|
101
97
|
else if (typeof obj === 'string') {
|
package/package.json
CHANGED
package/src/array/array.util.ts
CHANGED
|
@@ -321,3 +321,53 @@ export function _max<T>(array: T[]): NonNullable<T> {
|
|
|
321
321
|
if (!a.length) throw new Error('_max called on empty array')
|
|
322
322
|
return a.reduce((max, item) => (max >= item ? max : item))
|
|
323
323
|
}
|
|
324
|
+
|
|
325
|
+
export function _maxBy<T>(array: T[], mapper: Mapper<T, number | undefined>): T {
|
|
326
|
+
const max = _maxByOrUndefined(array, mapper)
|
|
327
|
+
if (max === undefined) throw new Error(`_maxBy returned undefined`)
|
|
328
|
+
return max
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export function _minBy<T>(array: T[], mapper: Mapper<T, number | undefined>): T {
|
|
332
|
+
const min = _minByOrUndefined(array, mapper)
|
|
333
|
+
if (min === undefined) throw new Error(`_minBy returned undefined`)
|
|
334
|
+
return min
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// todo: looks like it _maxByOrUndefined/_minByOrUndefined can be DRYer
|
|
338
|
+
|
|
339
|
+
export function _maxByOrUndefined<T>(
|
|
340
|
+
array: T[],
|
|
341
|
+
mapper: Mapper<T, number | undefined>,
|
|
342
|
+
): T | undefined {
|
|
343
|
+
if (!array.length) return
|
|
344
|
+
let maxItem: T | undefined
|
|
345
|
+
let max: number | undefined
|
|
346
|
+
array.forEach((item, i) => {
|
|
347
|
+
const v = mapper(item, i)
|
|
348
|
+
if (v !== undefined && (max === undefined || v > max)) {
|
|
349
|
+
maxItem = item
|
|
350
|
+
max = v
|
|
351
|
+
}
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
return maxItem
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
export function _minByOrUndefined<T>(
|
|
358
|
+
array: T[],
|
|
359
|
+
mapper: Mapper<T, number | undefined>,
|
|
360
|
+
): T | undefined {
|
|
361
|
+
if (!array.length) return
|
|
362
|
+
let minItem: T | undefined
|
|
363
|
+
let min: number | undefined
|
|
364
|
+
array.forEach((item, i) => {
|
|
365
|
+
const v = mapper(item, i)
|
|
366
|
+
if (v !== undefined && (min === undefined || v < min)) {
|
|
367
|
+
minItem = item
|
|
368
|
+
min = v
|
|
369
|
+
}
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
return minItem
|
|
373
|
+
}
|
|
@@ -14,7 +14,7 @@ export type Inclusiveness = '()' | '[]' | '[)' | '(]'
|
|
|
14
14
|
const MDAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
|
15
15
|
const DATE_REGEX = /^(\d\d\d\d)-(\d\d)-(\d\d)$/
|
|
16
16
|
|
|
17
|
-
export type LocalDateConfig = LocalDate | IsoDateString
|
|
17
|
+
export type LocalDateConfig = LocalDate | Date | IsoDateString
|
|
18
18
|
export type LocalDateFormatter = (ld: LocalDate) => string
|
|
19
19
|
|
|
20
20
|
/* eslint-disable no-dupe-class-members */
|
|
@@ -67,6 +67,9 @@ export class LocalDate {
|
|
|
67
67
|
static parseOrNull(d: LocalDateConfig | undefined | null): LocalDate | null {
|
|
68
68
|
if (!d) return null
|
|
69
69
|
if (d instanceof LocalDate) return d
|
|
70
|
+
if (d instanceof Date) {
|
|
71
|
+
return this.fromDate(d)
|
|
72
|
+
}
|
|
70
73
|
|
|
71
74
|
// const [year, month, day] = d.slice(0, 10).split('-').map(Number)
|
|
72
75
|
const matches = typeof (d as any) === 'string' && DATE_REGEX.exec(d.slice(0, 10))
|
|
@@ -497,11 +497,7 @@ export class LocalTime {
|
|
|
497
497
|
}
|
|
498
498
|
|
|
499
499
|
toLocalDate(): LocalDate {
|
|
500
|
-
return LocalDate.
|
|
501
|
-
this.$date.getFullYear(),
|
|
502
|
-
this.$date.getMonth() + 1,
|
|
503
|
-
this.$date.getDate(),
|
|
504
|
-
)
|
|
500
|
+
return LocalDate.fromDate(this.$date)
|
|
505
501
|
}
|
|
506
502
|
|
|
507
503
|
toPretty(seconds = true): IsoDateTimeString {
|
package/src/http/fetcher.ts
CHANGED
|
@@ -129,7 +129,7 @@ export class Fetcher {
|
|
|
129
129
|
headVoid!: (url: string, opt?: FetcherOptions) => Promise<void>
|
|
130
130
|
|
|
131
131
|
async fetch<T = unknown>(url: string, opt?: FetcherOptions): Promise<T> {
|
|
132
|
-
const res = await this.
|
|
132
|
+
const res = await this.doFetch<T>(url, opt)
|
|
133
133
|
if (res.err) {
|
|
134
134
|
if (res.req.throwHttpErrors) throw res.err
|
|
135
135
|
return res as any
|
|
@@ -140,8 +140,9 @@ export class Fetcher {
|
|
|
140
140
|
/**
|
|
141
141
|
* Returns FetcherResponse.
|
|
142
142
|
* Never throws, returns `err` property in the response instead.
|
|
143
|
+
* Use this method instead of `throwHttpErrors: false` or try-catching.
|
|
143
144
|
*/
|
|
144
|
-
async
|
|
145
|
+
async doFetch<T = unknown>(
|
|
145
146
|
url: string,
|
|
146
147
|
rawOpt: FetcherOptions = {},
|
|
147
148
|
): Promise<FetcherResponse<T>> {
|
|
@@ -198,7 +199,7 @@ export class Fetcher {
|
|
|
198
199
|
}
|
|
199
200
|
|
|
200
201
|
try {
|
|
201
|
-
res.fetchResponse = await
|
|
202
|
+
res.fetchResponse = await this.callNativeFetch(req.url, req.init)
|
|
202
203
|
res.ok = res.fetchResponse.ok
|
|
203
204
|
} catch (err) {
|
|
204
205
|
// For example, CORS error would result in "TypeError: failed to fetch" here
|
|
@@ -294,6 +295,13 @@ export class Fetcher {
|
|
|
294
295
|
}
|
|
295
296
|
}
|
|
296
297
|
|
|
298
|
+
/**
|
|
299
|
+
* This method exists to be able to easily mock it.
|
|
300
|
+
*/
|
|
301
|
+
async callNativeFetch(url: string, init: RequestInit): Promise<Response> {
|
|
302
|
+
return await globalThis.fetch(url, init)
|
|
303
|
+
}
|
|
304
|
+
|
|
297
305
|
private async onNotOkResponse(res: FetcherResponse, timeout?: number): Promise<void> {
|
|
298
306
|
clearTimeout(timeout)
|
|
299
307
|
|
|
@@ -103,7 +103,17 @@ export function _stringifyAny(obj: any, opt: StringifyAnyOptions = {}): string {
|
|
|
103
103
|
// if (obj?.name === 'Error') {
|
|
104
104
|
// s = obj.message
|
|
105
105
|
// }
|
|
106
|
-
|
|
106
|
+
if (_isErrorObject(obj) && _isHttpErrorObject(obj)) {
|
|
107
|
+
// Printing (0) to avoid ambiguity
|
|
108
|
+
s = `${obj.name}(${obj.data.httpStatusCode}): ${obj.message}`
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
s ||= [obj.name, obj.message].filter(Boolean).join(': ')
|
|
112
|
+
|
|
113
|
+
if (typeof (obj as any).code === 'string') {
|
|
114
|
+
// Error that has no `data`, but has `code` property
|
|
115
|
+
s += `\ncode: ${(obj as any).code}`
|
|
116
|
+
}
|
|
107
117
|
|
|
108
118
|
if (opt.includeErrorStack && obj.stack) {
|
|
109
119
|
// Here we're using the previously-generated "title line" (e.g "Error: some_message"),
|
|
@@ -116,18 +126,6 @@ export function _stringifyAny(obj: any, opt: StringifyAnyOptions = {}): string {
|
|
|
116
126
|
s = [s, ...obj.stack.split('\n').slice(sLines)].join('\n')
|
|
117
127
|
}
|
|
118
128
|
|
|
119
|
-
if (_isErrorObject(obj)) {
|
|
120
|
-
if (_isHttpErrorObject(obj)) {
|
|
121
|
-
// Only include (statusCode) if it's non-zero
|
|
122
|
-
// No: print (0), as it removes ambiguity
|
|
123
|
-
// `replace` here works ONCE, exactly as we need it
|
|
124
|
-
s = s.replace('HttpError', `HttpError(${obj.data.httpStatusCode})`)
|
|
125
|
-
}
|
|
126
|
-
} else if (typeof (obj as any).code === 'string') {
|
|
127
|
-
// Error that has no `data`, but has `code` property
|
|
128
|
-
s = [s, `code: ${(obj as any).code}`].join('\n')
|
|
129
|
-
}
|
|
130
|
-
|
|
131
129
|
if (supportsAggregateError && obj instanceof AggregateError && obj.errors.length) {
|
|
132
130
|
s = [
|
|
133
131
|
s,
|
|
@@ -137,7 +135,7 @@ export function _stringifyAny(obj: any, opt: StringifyAnyOptions = {}): string {
|
|
|
137
135
|
}
|
|
138
136
|
|
|
139
137
|
if (obj.cause && includeErrorCause) {
|
|
140
|
-
s = s + '\
|
|
138
|
+
s = s + '\nCaused by: ' + _stringifyAny(obj.cause, opt)
|
|
141
139
|
}
|
|
142
140
|
} else if (typeof obj === 'string') {
|
|
143
141
|
//
|