@naturalcycles/js-lib 14.162.0 → 14.164.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 +19 -0
- package/dist/array/array.util.js +38 -1
- package/dist/datetime/localDate.js +4 -6
- package/dist/datetime/localTime.js +10 -11
- package/dist/error/error.model.d.ts +5 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/semver.d.ts +44 -0
- package/dist/semver.js +83 -0
- package/dist-esm/array/array.util.js +35 -0
- package/dist-esm/datetime/localDate.js +4 -6
- package/dist-esm/datetime/localTime.js +10 -11
- package/dist-esm/index.js +1 -0
- package/dist-esm/semver.js +78 -0
- package/package.json +1 -1
- package/src/array/array.util.ts +36 -0
- package/src/datetime/localDate.ts +4 -6
- package/src/datetime/localTime.ts +10 -10
- package/src/error/error.model.ts +5 -1
- package/src/index.ts +1 -0
- package/src/semver.ts +87 -0
|
@@ -16,6 +16,25 @@ export declare function _chunk<T>(array: readonly T[], size?: number): T[][];
|
|
|
16
16
|
* Removes duplicates from given array.
|
|
17
17
|
*/
|
|
18
18
|
export declare function _uniq<T>(a: readonly T[]): T[];
|
|
19
|
+
/**
|
|
20
|
+
* Pushes an item to an array if it's not already there.
|
|
21
|
+
* Mutates the array (same as normal `push`) and also returns it for chaining convenience.
|
|
22
|
+
*
|
|
23
|
+
* _pushUniq([1, 2, 3], 2) // => [1, 2, 3]
|
|
24
|
+
*
|
|
25
|
+
* Shortcut for:
|
|
26
|
+
* if (!a.includes(item)) a.push(item)
|
|
27
|
+
* // or
|
|
28
|
+
* a = [...new Set(a).add(item)]
|
|
29
|
+
* // or
|
|
30
|
+
* a = _uniq([...a, item])
|
|
31
|
+
*/
|
|
32
|
+
export declare function _pushUniq<T>(a: T[], ...items: T[]): T[];
|
|
33
|
+
/**
|
|
34
|
+
* Like _pushUniq but uses a mapper to determine uniqueness (like _uniqBy).
|
|
35
|
+
* Mutates the array (same as normal `push`).
|
|
36
|
+
*/
|
|
37
|
+
export declare function _pushUniqBy<T>(a: T[], mapper: Mapper<T, any>, ...items: T[]): T[];
|
|
19
38
|
/**
|
|
20
39
|
* This method is like `_.uniq` except that it accepts `iteratee` which is
|
|
21
40
|
* invoked for each element in `array` to generate the criterion by which
|
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._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._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._pushUniqBy = exports._pushUniq = exports._uniq = 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
|
|
@@ -27,6 +27,43 @@ function _uniq(a) {
|
|
|
27
27
|
return [...new Set(a)];
|
|
28
28
|
}
|
|
29
29
|
exports._uniq = _uniq;
|
|
30
|
+
/**
|
|
31
|
+
* Pushes an item to an array if it's not already there.
|
|
32
|
+
* Mutates the array (same as normal `push`) and also returns it for chaining convenience.
|
|
33
|
+
*
|
|
34
|
+
* _pushUniq([1, 2, 3], 2) // => [1, 2, 3]
|
|
35
|
+
*
|
|
36
|
+
* Shortcut for:
|
|
37
|
+
* if (!a.includes(item)) a.push(item)
|
|
38
|
+
* // or
|
|
39
|
+
* a = [...new Set(a).add(item)]
|
|
40
|
+
* // or
|
|
41
|
+
* a = _uniq([...a, item])
|
|
42
|
+
*/
|
|
43
|
+
function _pushUniq(a, ...items) {
|
|
44
|
+
items.forEach(item => {
|
|
45
|
+
if (!a.includes(item))
|
|
46
|
+
a.push(item);
|
|
47
|
+
});
|
|
48
|
+
return a;
|
|
49
|
+
}
|
|
50
|
+
exports._pushUniq = _pushUniq;
|
|
51
|
+
/**
|
|
52
|
+
* Like _pushUniq but uses a mapper to determine uniqueness (like _uniqBy).
|
|
53
|
+
* Mutates the array (same as normal `push`).
|
|
54
|
+
*/
|
|
55
|
+
function _pushUniqBy(a, mapper, ...items) {
|
|
56
|
+
const mappedSet = new Set(a.map((item, i) => mapper(item, i)));
|
|
57
|
+
items.forEach((item, i) => {
|
|
58
|
+
const mapped = mapper(item, i);
|
|
59
|
+
if (!mappedSet.has(mapped)) {
|
|
60
|
+
a.push(item);
|
|
61
|
+
mappedSet.add(mapped);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
return a;
|
|
65
|
+
}
|
|
66
|
+
exports._pushUniqBy = _pushUniqBy;
|
|
30
67
|
/**
|
|
31
68
|
* This method is like `_.uniq` except that it accepts `iteratee` which is
|
|
32
69
|
* invoked for each element in `array` to generate the criterion by which
|
|
@@ -23,16 +23,14 @@ class LocalDate {
|
|
|
23
23
|
*/
|
|
24
24
|
static of(d) {
|
|
25
25
|
const t = this.parseOrNull(d);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
26
|
+
(0, assert_1._assert)(t !== null, `Cannot parse "${d}" into LocalDate`, {
|
|
27
|
+
input: d,
|
|
28
|
+
});
|
|
29
29
|
return t;
|
|
30
30
|
}
|
|
31
31
|
static parseCompact(d) {
|
|
32
32
|
const [year, month, day] = [d.slice(0, 4), d.slice(4, 2), d.slice(6, 2)].map(Number);
|
|
33
|
-
|
|
34
|
-
throw new Error(`Cannot parse "${d}" into LocalDate`);
|
|
35
|
-
}
|
|
33
|
+
(0, assert_1._assert)(day && month && year, `Cannot parse "${d}" into LocalDate`);
|
|
36
34
|
return new LocalDate(year, month, day);
|
|
37
35
|
}
|
|
38
36
|
static fromDate(d) {
|
|
@@ -33,9 +33,9 @@ class LocalTime {
|
|
|
33
33
|
*/
|
|
34
34
|
static of(d) {
|
|
35
35
|
const t = this.parseOrNull(d);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
36
|
+
(0, assert_1._assert)(t !== null, `Cannot parse "${d}" into LocalTime`, {
|
|
37
|
+
input: d,
|
|
38
|
+
});
|
|
39
39
|
return t;
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
@@ -82,9 +82,9 @@ class LocalTime {
|
|
|
82
82
|
if (d instanceof Date)
|
|
83
83
|
return d;
|
|
84
84
|
const date = typeof d === 'number' ? new Date(d * 1000) : new Date(d);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
85
|
+
(0, assert_1._assert)(!isNaN(date.getDate()), `Cannot parse "${d}" to Date`, {
|
|
86
|
+
input: d,
|
|
87
|
+
});
|
|
88
88
|
return date;
|
|
89
89
|
}
|
|
90
90
|
static parseToUnixTimestamp(d) {
|
|
@@ -93,9 +93,9 @@ class LocalTime {
|
|
|
93
93
|
if (d instanceof LocalTime)
|
|
94
94
|
return d.unix();
|
|
95
95
|
const date = d instanceof Date ? d : new Date(d);
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
96
|
+
(0, assert_1._assert)(!isNaN(date.getDate()), `Cannot parse "${d}" to UnixTimestamp`, {
|
|
97
|
+
input: d,
|
|
98
|
+
});
|
|
99
99
|
return date.valueOf() / 1000;
|
|
100
100
|
}
|
|
101
101
|
static isValid(d) {
|
|
@@ -171,8 +171,7 @@ class LocalTime {
|
|
|
171
171
|
if (v === undefined) {
|
|
172
172
|
return dow;
|
|
173
173
|
}
|
|
174
|
-
|
|
175
|
-
throw new Error(`Invalid dayOfWeek: ${v}`);
|
|
174
|
+
(0, assert_1._assert)(VALID_DAYS_OF_WEEK.has(v), `Invalid dayOfWeek: ${v}`);
|
|
176
175
|
return this.add(v - dow, 'day');
|
|
177
176
|
}
|
|
178
177
|
hour(v) {
|
|
@@ -44,12 +44,16 @@ export interface ErrorData {
|
|
|
44
44
|
* Can be used by error-reporting tools (e.g Sentry).
|
|
45
45
|
* If fingerprint is defined - it'll be used INSTEAD of default fingerprint of a tool.
|
|
46
46
|
* Can be used to force-group errors that are NOT needed to be split by endpoint or calling function.
|
|
47
|
+
*
|
|
48
|
+
* Sentry takes string[], but for convenience we allow to pass a singe string.
|
|
47
49
|
*/
|
|
48
|
-
fingerprint?: string[];
|
|
50
|
+
fingerprint?: string | string[];
|
|
49
51
|
/**
|
|
50
52
|
* If set to true - it'll use error.message as fingerprint,
|
|
51
53
|
* so, all errors with the same message will be grouped together, even if they occurred in different places.
|
|
52
54
|
* Defaults to false.
|
|
55
|
+
*
|
|
56
|
+
* @experimental
|
|
53
57
|
*/
|
|
54
58
|
fingerprintByMessage?: boolean;
|
|
55
59
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -81,6 +81,7 @@ export * from './http/fetcher.model';
|
|
|
81
81
|
export * from './string/hash.util';
|
|
82
82
|
export * from './env/buildInfo';
|
|
83
83
|
export * from './form.util';
|
|
84
|
+
export * from './semver';
|
|
84
85
|
export * from './zod/zod.util';
|
|
85
86
|
export * from './zod/zod.shared.schemas';
|
|
86
87
|
import { z, ZodSchema, ZodError, ZodIssue } from 'zod';
|
package/dist/index.js
CHANGED
|
@@ -85,6 +85,7 @@ 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
87
|
tslib_1.__exportStar(require("./form.util"), exports);
|
|
88
|
+
tslib_1.__exportStar(require("./semver"), exports);
|
|
88
89
|
tslib_1.__exportStar(require("./zod/zod.util"), exports);
|
|
89
90
|
tslib_1.__exportStar(require("./zod/zod.shared.schemas"), exports);
|
|
90
91
|
const zod_1 = require("zod");
|
package/dist/semver.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export type SemverInput = string | Semver;
|
|
2
|
+
export type SemverTokens = [major: number, minor: number, patch: number];
|
|
3
|
+
/**
|
|
4
|
+
* Simple Semver implementation.
|
|
5
|
+
*
|
|
6
|
+
* Suitable for Browser usage, unlike npm `semver` which is Node-targeted and simply too big for the Browser.
|
|
7
|
+
*
|
|
8
|
+
* Parsing algorithm is simple:
|
|
9
|
+
* 1. Split by `.`
|
|
10
|
+
* 2. parseInt each of 3 tokens, set to 0 if falsy
|
|
11
|
+
*
|
|
12
|
+
* toString returns `major.minor.patch`
|
|
13
|
+
* Missing tokens are replaced with 0.
|
|
14
|
+
*
|
|
15
|
+
* _semver('1').toString() === '1.0.0'
|
|
16
|
+
*
|
|
17
|
+
* @experimental
|
|
18
|
+
*/
|
|
19
|
+
export declare class Semver {
|
|
20
|
+
tokens: SemverTokens;
|
|
21
|
+
private constructor();
|
|
22
|
+
get major(): number;
|
|
23
|
+
get minor(): number;
|
|
24
|
+
get patch(): number;
|
|
25
|
+
static of(input: SemverInput): Semver;
|
|
26
|
+
static parseOrNull(input: SemverInput | undefined | null): Semver | null;
|
|
27
|
+
isAfter: (other: SemverInput) => boolean;
|
|
28
|
+
isSameOrAfter: (other: SemverInput) => boolean;
|
|
29
|
+
isBefore: (other: SemverInput) => boolean;
|
|
30
|
+
isSameOrBefore: (other: SemverInput) => boolean;
|
|
31
|
+
isSame: (other: SemverInput) => boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Returns 1 if this > other
|
|
34
|
+
* returns 0 if they are equal
|
|
35
|
+
* returns -1 if this < other
|
|
36
|
+
*/
|
|
37
|
+
cmp(other: SemverInput): -1 | 0 | 1;
|
|
38
|
+
toJSON: () => string;
|
|
39
|
+
toString(): string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Shortcut for Semver.of(input)
|
|
43
|
+
*/
|
|
44
|
+
export declare function _semver(input: SemverInput): Semver;
|
package/dist/semver.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports._semver = exports.Semver = void 0;
|
|
4
|
+
const range_1 = require("./array/range");
|
|
5
|
+
const assert_1 = require("./error/assert");
|
|
6
|
+
/**
|
|
7
|
+
* Simple Semver implementation.
|
|
8
|
+
*
|
|
9
|
+
* Suitable for Browser usage, unlike npm `semver` which is Node-targeted and simply too big for the Browser.
|
|
10
|
+
*
|
|
11
|
+
* Parsing algorithm is simple:
|
|
12
|
+
* 1. Split by `.`
|
|
13
|
+
* 2. parseInt each of 3 tokens, set to 0 if falsy
|
|
14
|
+
*
|
|
15
|
+
* toString returns `major.minor.patch`
|
|
16
|
+
* Missing tokens are replaced with 0.
|
|
17
|
+
*
|
|
18
|
+
* _semver('1').toString() === '1.0.0'
|
|
19
|
+
*
|
|
20
|
+
* @experimental
|
|
21
|
+
*/
|
|
22
|
+
class Semver {
|
|
23
|
+
constructor(tokens) {
|
|
24
|
+
this.tokens = tokens;
|
|
25
|
+
this.isAfter = (other) => this.cmp(other) > 0;
|
|
26
|
+
this.isSameOrAfter = (other) => this.cmp(other) >= 0;
|
|
27
|
+
this.isBefore = (other) => this.cmp(other) < 0;
|
|
28
|
+
this.isSameOrBefore = (other) => this.cmp(other) <= 0;
|
|
29
|
+
this.isSame = (other) => this.cmp(other) === 0;
|
|
30
|
+
this.toJSON = () => this.toString();
|
|
31
|
+
}
|
|
32
|
+
get major() {
|
|
33
|
+
return this.tokens[0];
|
|
34
|
+
}
|
|
35
|
+
get minor() {
|
|
36
|
+
return this.tokens[1];
|
|
37
|
+
}
|
|
38
|
+
get patch() {
|
|
39
|
+
return this.tokens[2];
|
|
40
|
+
}
|
|
41
|
+
static of(input) {
|
|
42
|
+
const s = this.parseOrNull(input);
|
|
43
|
+
(0, assert_1._assert)(s !== null, `Cannot parse "${input}" into Semver`, {
|
|
44
|
+
userFriendly: true,
|
|
45
|
+
input,
|
|
46
|
+
});
|
|
47
|
+
return s;
|
|
48
|
+
}
|
|
49
|
+
static parseOrNull(input) {
|
|
50
|
+
if (!input)
|
|
51
|
+
return null;
|
|
52
|
+
if (input instanceof Semver)
|
|
53
|
+
return input;
|
|
54
|
+
const t = input.split('.');
|
|
55
|
+
return new Semver((0, range_1._range)(3).map(i => parseInt(t[i]) || 0));
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Returns 1 if this > other
|
|
59
|
+
* returns 0 if they are equal
|
|
60
|
+
* returns -1 if this < other
|
|
61
|
+
*/
|
|
62
|
+
cmp(other) {
|
|
63
|
+
const { tokens } = Semver.of(other);
|
|
64
|
+
for (let i = 0; i < 3; i++) {
|
|
65
|
+
if (this.tokens[i] < tokens[i])
|
|
66
|
+
return -1;
|
|
67
|
+
if (this.tokens[i] > tokens[i])
|
|
68
|
+
return 1;
|
|
69
|
+
}
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
toString() {
|
|
73
|
+
return this.tokens.join('.');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.Semver = Semver;
|
|
77
|
+
/**
|
|
78
|
+
* Shortcut for Semver.of(input)
|
|
79
|
+
*/
|
|
80
|
+
function _semver(input) {
|
|
81
|
+
return Semver.of(input);
|
|
82
|
+
}
|
|
83
|
+
exports._semver = _semver;
|
|
@@ -22,6 +22,41 @@ export function _chunk(array, size = 1) {
|
|
|
22
22
|
export function _uniq(a) {
|
|
23
23
|
return [...new Set(a)];
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Pushes an item to an array if it's not already there.
|
|
27
|
+
* Mutates the array (same as normal `push`) and also returns it for chaining convenience.
|
|
28
|
+
*
|
|
29
|
+
* _pushUniq([1, 2, 3], 2) // => [1, 2, 3]
|
|
30
|
+
*
|
|
31
|
+
* Shortcut for:
|
|
32
|
+
* if (!a.includes(item)) a.push(item)
|
|
33
|
+
* // or
|
|
34
|
+
* a = [...new Set(a).add(item)]
|
|
35
|
+
* // or
|
|
36
|
+
* a = _uniq([...a, item])
|
|
37
|
+
*/
|
|
38
|
+
export function _pushUniq(a, ...items) {
|
|
39
|
+
items.forEach(item => {
|
|
40
|
+
if (!a.includes(item))
|
|
41
|
+
a.push(item);
|
|
42
|
+
});
|
|
43
|
+
return a;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Like _pushUniq but uses a mapper to determine uniqueness (like _uniqBy).
|
|
47
|
+
* Mutates the array (same as normal `push`).
|
|
48
|
+
*/
|
|
49
|
+
export function _pushUniqBy(a, mapper, ...items) {
|
|
50
|
+
const mappedSet = new Set(a.map((item, i) => mapper(item, i)));
|
|
51
|
+
items.forEach((item, i) => {
|
|
52
|
+
const mapped = mapper(item, i);
|
|
53
|
+
if (!mappedSet.has(mapped)) {
|
|
54
|
+
a.push(item);
|
|
55
|
+
mappedSet.add(mapped);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
return a;
|
|
59
|
+
}
|
|
25
60
|
/**
|
|
26
61
|
* This method is like `_.uniq` except that it accepts `iteratee` which is
|
|
27
62
|
* invoked for each element in `array` to generate the criterion by which
|
|
@@ -20,16 +20,14 @@ export class LocalDate {
|
|
|
20
20
|
*/
|
|
21
21
|
static of(d) {
|
|
22
22
|
const t = this.parseOrNull(d);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
23
|
+
_assert(t !== null, `Cannot parse "${d}" into LocalDate`, {
|
|
24
|
+
input: d,
|
|
25
|
+
});
|
|
26
26
|
return t;
|
|
27
27
|
}
|
|
28
28
|
static parseCompact(d) {
|
|
29
29
|
const [year, month, day] = [d.slice(0, 4), d.slice(4, 2), d.slice(6, 2)].map(Number);
|
|
30
|
-
|
|
31
|
-
throw new Error(`Cannot parse "${d}" into LocalDate`);
|
|
32
|
-
}
|
|
30
|
+
_assert(day && month && year, `Cannot parse "${d}" into LocalDate`);
|
|
33
31
|
return new LocalDate(year, month, day);
|
|
34
32
|
}
|
|
35
33
|
static fromDate(d) {
|
|
@@ -30,9 +30,9 @@ export class LocalTime {
|
|
|
30
30
|
*/
|
|
31
31
|
static of(d) {
|
|
32
32
|
const t = this.parseOrNull(d);
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
33
|
+
_assert(t !== null, `Cannot parse "${d}" into LocalTime`, {
|
|
34
|
+
input: d,
|
|
35
|
+
});
|
|
36
36
|
return t;
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
@@ -79,9 +79,9 @@ export class LocalTime {
|
|
|
79
79
|
if (d instanceof Date)
|
|
80
80
|
return d;
|
|
81
81
|
const date = typeof d === 'number' ? new Date(d * 1000) : new Date(d);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
82
|
+
_assert(!isNaN(date.getDate()), `Cannot parse "${d}" to Date`, {
|
|
83
|
+
input: d,
|
|
84
|
+
});
|
|
85
85
|
return date;
|
|
86
86
|
}
|
|
87
87
|
static parseToUnixTimestamp(d) {
|
|
@@ -90,9 +90,9 @@ export class LocalTime {
|
|
|
90
90
|
if (d instanceof LocalTime)
|
|
91
91
|
return d.unix();
|
|
92
92
|
const date = d instanceof Date ? d : new Date(d);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
93
|
+
_assert(!isNaN(date.getDate()), `Cannot parse "${d}" to UnixTimestamp`, {
|
|
94
|
+
input: d,
|
|
95
|
+
});
|
|
96
96
|
return date.valueOf() / 1000;
|
|
97
97
|
}
|
|
98
98
|
static isValid(d) {
|
|
@@ -168,8 +168,7 @@ export class LocalTime {
|
|
|
168
168
|
if (v === undefined) {
|
|
169
169
|
return dow;
|
|
170
170
|
}
|
|
171
|
-
|
|
172
|
-
throw new Error(`Invalid dayOfWeek: ${v}`);
|
|
171
|
+
_assert(VALID_DAYS_OF_WEEK.has(v), `Invalid dayOfWeek: ${v}`);
|
|
173
172
|
return this.add(v - dow, 'day');
|
|
174
173
|
}
|
|
175
174
|
hour(v) {
|
package/dist-esm/index.js
CHANGED
|
@@ -81,6 +81,7 @@ export * from './http/fetcher.model';
|
|
|
81
81
|
export * from './string/hash.util';
|
|
82
82
|
export * from './env/buildInfo';
|
|
83
83
|
export * from './form.util';
|
|
84
|
+
export * from './semver';
|
|
84
85
|
export * from './zod/zod.util';
|
|
85
86
|
export * from './zod/zod.shared.schemas';
|
|
86
87
|
import { z, ZodSchema, ZodError } from 'zod';
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { _range } from './array/range';
|
|
2
|
+
import { _assert } from './error/assert';
|
|
3
|
+
/**
|
|
4
|
+
* Simple Semver implementation.
|
|
5
|
+
*
|
|
6
|
+
* Suitable for Browser usage, unlike npm `semver` which is Node-targeted and simply too big for the Browser.
|
|
7
|
+
*
|
|
8
|
+
* Parsing algorithm is simple:
|
|
9
|
+
* 1. Split by `.`
|
|
10
|
+
* 2. parseInt each of 3 tokens, set to 0 if falsy
|
|
11
|
+
*
|
|
12
|
+
* toString returns `major.minor.patch`
|
|
13
|
+
* Missing tokens are replaced with 0.
|
|
14
|
+
*
|
|
15
|
+
* _semver('1').toString() === '1.0.0'
|
|
16
|
+
*
|
|
17
|
+
* @experimental
|
|
18
|
+
*/
|
|
19
|
+
export class Semver {
|
|
20
|
+
constructor(tokens) {
|
|
21
|
+
this.tokens = tokens;
|
|
22
|
+
this.isAfter = (other) => this.cmp(other) > 0;
|
|
23
|
+
this.isSameOrAfter = (other) => this.cmp(other) >= 0;
|
|
24
|
+
this.isBefore = (other) => this.cmp(other) < 0;
|
|
25
|
+
this.isSameOrBefore = (other) => this.cmp(other) <= 0;
|
|
26
|
+
this.isSame = (other) => this.cmp(other) === 0;
|
|
27
|
+
this.toJSON = () => this.toString();
|
|
28
|
+
}
|
|
29
|
+
get major() {
|
|
30
|
+
return this.tokens[0];
|
|
31
|
+
}
|
|
32
|
+
get minor() {
|
|
33
|
+
return this.tokens[1];
|
|
34
|
+
}
|
|
35
|
+
get patch() {
|
|
36
|
+
return this.tokens[2];
|
|
37
|
+
}
|
|
38
|
+
static of(input) {
|
|
39
|
+
const s = this.parseOrNull(input);
|
|
40
|
+
_assert(s !== null, `Cannot parse "${input}" into Semver`, {
|
|
41
|
+
userFriendly: true,
|
|
42
|
+
input,
|
|
43
|
+
});
|
|
44
|
+
return s;
|
|
45
|
+
}
|
|
46
|
+
static parseOrNull(input) {
|
|
47
|
+
if (!input)
|
|
48
|
+
return null;
|
|
49
|
+
if (input instanceof Semver)
|
|
50
|
+
return input;
|
|
51
|
+
const t = input.split('.');
|
|
52
|
+
return new Semver(_range(3).map(i => parseInt(t[i]) || 0));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Returns 1 if this > other
|
|
56
|
+
* returns 0 if they are equal
|
|
57
|
+
* returns -1 if this < other
|
|
58
|
+
*/
|
|
59
|
+
cmp(other) {
|
|
60
|
+
const { tokens } = Semver.of(other);
|
|
61
|
+
for (let i = 0; i < 3; i++) {
|
|
62
|
+
if (this.tokens[i] < tokens[i])
|
|
63
|
+
return -1;
|
|
64
|
+
if (this.tokens[i] > tokens[i])
|
|
65
|
+
return 1;
|
|
66
|
+
}
|
|
67
|
+
return 0;
|
|
68
|
+
}
|
|
69
|
+
toString() {
|
|
70
|
+
return this.tokens.join('.');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Shortcut for Semver.of(input)
|
|
75
|
+
*/
|
|
76
|
+
export function _semver(input) {
|
|
77
|
+
return Semver.of(input);
|
|
78
|
+
}
|
package/package.json
CHANGED
package/src/array/array.util.ts
CHANGED
|
@@ -26,6 +26,42 @@ export function _uniq<T>(a: readonly T[]): T[] {
|
|
|
26
26
|
return [...new Set(a)]
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Pushes an item to an array if it's not already there.
|
|
31
|
+
* Mutates the array (same as normal `push`) and also returns it for chaining convenience.
|
|
32
|
+
*
|
|
33
|
+
* _pushUniq([1, 2, 3], 2) // => [1, 2, 3]
|
|
34
|
+
*
|
|
35
|
+
* Shortcut for:
|
|
36
|
+
* if (!a.includes(item)) a.push(item)
|
|
37
|
+
* // or
|
|
38
|
+
* a = [...new Set(a).add(item)]
|
|
39
|
+
* // or
|
|
40
|
+
* a = _uniq([...a, item])
|
|
41
|
+
*/
|
|
42
|
+
export function _pushUniq<T>(a: T[], ...items: T[]): T[] {
|
|
43
|
+
items.forEach(item => {
|
|
44
|
+
if (!a.includes(item)) a.push(item)
|
|
45
|
+
})
|
|
46
|
+
return a
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Like _pushUniq but uses a mapper to determine uniqueness (like _uniqBy).
|
|
51
|
+
* Mutates the array (same as normal `push`).
|
|
52
|
+
*/
|
|
53
|
+
export function _pushUniqBy<T>(a: T[], mapper: Mapper<T, any>, ...items: T[]): T[] {
|
|
54
|
+
const mappedSet = new Set(a.map((item, i) => mapper(item, i)))
|
|
55
|
+
items.forEach((item, i) => {
|
|
56
|
+
const mapped = mapper(item, i)
|
|
57
|
+
if (!mappedSet.has(mapped)) {
|
|
58
|
+
a.push(item)
|
|
59
|
+
mappedSet.add(mapped)
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
return a
|
|
63
|
+
}
|
|
64
|
+
|
|
29
65
|
/**
|
|
30
66
|
* This method is like `_.uniq` except that it accepts `iteratee` which is
|
|
31
67
|
* invoked for each element in `array` to generate the criterion by which
|
|
@@ -38,9 +38,9 @@ export class LocalDate {
|
|
|
38
38
|
static of(d: LocalDateInput): LocalDate {
|
|
39
39
|
const t = this.parseOrNull(d)
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
41
|
+
_assert(t !== null, `Cannot parse "${d}" into LocalDate`, {
|
|
42
|
+
input: d,
|
|
43
|
+
})
|
|
44
44
|
|
|
45
45
|
return t
|
|
46
46
|
}
|
|
@@ -48,9 +48,7 @@ export class LocalDate {
|
|
|
48
48
|
static parseCompact(d: string): LocalDate {
|
|
49
49
|
const [year, month, day] = [d.slice(0, 4), d.slice(4, 2), d.slice(6, 2)].map(Number)
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
throw new Error(`Cannot parse "${d}" into LocalDate`)
|
|
53
|
-
}
|
|
51
|
+
_assert(day && month && year, `Cannot parse "${d}" into LocalDate`)
|
|
54
52
|
|
|
55
53
|
return new LocalDate(year, month, day)
|
|
56
54
|
}
|
|
@@ -53,9 +53,9 @@ export class LocalTime {
|
|
|
53
53
|
static of(d: LocalTimeInput): LocalTime {
|
|
54
54
|
const t = this.parseOrNull(d)
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
56
|
+
_assert(t !== null, `Cannot parse "${d}" into LocalTime`, {
|
|
57
|
+
input: d,
|
|
58
|
+
})
|
|
59
59
|
|
|
60
60
|
return t
|
|
61
61
|
}
|
|
@@ -106,9 +106,9 @@ export class LocalTime {
|
|
|
106
106
|
|
|
107
107
|
const date = typeof d === 'number' ? new Date(d * 1000) : new Date(d)
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
109
|
+
_assert(!isNaN(date.getDate()), `Cannot parse "${d}" to Date`, {
|
|
110
|
+
input: d,
|
|
111
|
+
})
|
|
112
112
|
|
|
113
113
|
return date
|
|
114
114
|
}
|
|
@@ -119,9 +119,9 @@ export class LocalTime {
|
|
|
119
119
|
|
|
120
120
|
const date = d instanceof Date ? d : new Date(d)
|
|
121
121
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
122
|
+
_assert(!isNaN(date.getDate()), `Cannot parse "${d}" to UnixTimestamp`, {
|
|
123
|
+
input: d,
|
|
124
|
+
})
|
|
125
125
|
|
|
126
126
|
return date.valueOf() / 1000
|
|
127
127
|
}
|
|
@@ -220,7 +220,7 @@ export class LocalTime {
|
|
|
220
220
|
return dow
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
|
|
223
|
+
_assert(VALID_DAYS_OF_WEEK.has(v), `Invalid dayOfWeek: ${v}`)
|
|
224
224
|
|
|
225
225
|
return this.add(v - dow, 'day')
|
|
226
226
|
}
|
package/src/error/error.model.ts
CHANGED
|
@@ -54,13 +54,17 @@ export interface ErrorData {
|
|
|
54
54
|
* Can be used by error-reporting tools (e.g Sentry).
|
|
55
55
|
* If fingerprint is defined - it'll be used INSTEAD of default fingerprint of a tool.
|
|
56
56
|
* Can be used to force-group errors that are NOT needed to be split by endpoint or calling function.
|
|
57
|
+
*
|
|
58
|
+
* Sentry takes string[], but for convenience we allow to pass a singe string.
|
|
57
59
|
*/
|
|
58
|
-
fingerprint?: string[]
|
|
60
|
+
fingerprint?: string | string[]
|
|
59
61
|
|
|
60
62
|
/**
|
|
61
63
|
* If set to true - it'll use error.message as fingerprint,
|
|
62
64
|
* so, all errors with the same message will be grouped together, even if they occurred in different places.
|
|
63
65
|
* Defaults to false.
|
|
66
|
+
*
|
|
67
|
+
* @experimental
|
|
64
68
|
*/
|
|
65
69
|
fingerprintByMessage?: boolean
|
|
66
70
|
|
package/src/index.ts
CHANGED
|
@@ -81,6 +81,7 @@ export * from './http/fetcher.model'
|
|
|
81
81
|
export * from './string/hash.util'
|
|
82
82
|
export * from './env/buildInfo'
|
|
83
83
|
export * from './form.util'
|
|
84
|
+
export * from './semver'
|
|
84
85
|
export * from './zod/zod.util'
|
|
85
86
|
export * from './zod/zod.shared.schemas'
|
|
86
87
|
import { z, ZodSchema, ZodError, ZodIssue } from 'zod'
|
package/src/semver.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { _range } from './array/range'
|
|
2
|
+
import { _assert } from './error/assert'
|
|
3
|
+
|
|
4
|
+
export type SemverInput = string | Semver
|
|
5
|
+
export type SemverTokens = [major: number, minor: number, patch: number]
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Simple Semver implementation.
|
|
9
|
+
*
|
|
10
|
+
* Suitable for Browser usage, unlike npm `semver` which is Node-targeted and simply too big for the Browser.
|
|
11
|
+
*
|
|
12
|
+
* Parsing algorithm is simple:
|
|
13
|
+
* 1. Split by `.`
|
|
14
|
+
* 2. parseInt each of 3 tokens, set to 0 if falsy
|
|
15
|
+
*
|
|
16
|
+
* toString returns `major.minor.patch`
|
|
17
|
+
* Missing tokens are replaced with 0.
|
|
18
|
+
*
|
|
19
|
+
* _semver('1').toString() === '1.0.0'
|
|
20
|
+
*
|
|
21
|
+
* @experimental
|
|
22
|
+
*/
|
|
23
|
+
export class Semver {
|
|
24
|
+
private constructor(public tokens: SemverTokens) {}
|
|
25
|
+
|
|
26
|
+
get major(): number {
|
|
27
|
+
return this.tokens[0]
|
|
28
|
+
}
|
|
29
|
+
get minor(): number {
|
|
30
|
+
return this.tokens[1]
|
|
31
|
+
}
|
|
32
|
+
get patch(): number {
|
|
33
|
+
return this.tokens[2]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static of(input: SemverInput): Semver {
|
|
37
|
+
const s = this.parseOrNull(input)
|
|
38
|
+
|
|
39
|
+
_assert(s !== null, `Cannot parse "${input}" into Semver`, {
|
|
40
|
+
userFriendly: true,
|
|
41
|
+
input,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
return s
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
static parseOrNull(input: SemverInput | undefined | null): Semver | null {
|
|
48
|
+
if (!input) return null
|
|
49
|
+
if (input instanceof Semver) return input
|
|
50
|
+
|
|
51
|
+
const t = input.split('.')
|
|
52
|
+
return new Semver(_range(3).map(i => parseInt(t[i]!) || 0) as SemverTokens)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
isAfter = (other: SemverInput): boolean => this.cmp(other) > 0
|
|
56
|
+
isSameOrAfter = (other: SemverInput): boolean => this.cmp(other) >= 0
|
|
57
|
+
isBefore = (other: SemverInput): boolean => this.cmp(other) < 0
|
|
58
|
+
isSameOrBefore = (other: SemverInput): boolean => this.cmp(other) <= 0
|
|
59
|
+
isSame = (other: SemverInput): boolean => this.cmp(other) === 0
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Returns 1 if this > other
|
|
63
|
+
* returns 0 if they are equal
|
|
64
|
+
* returns -1 if this < other
|
|
65
|
+
*/
|
|
66
|
+
cmp(other: SemverInput): -1 | 0 | 1 {
|
|
67
|
+
const { tokens } = Semver.of(other)
|
|
68
|
+
for (let i = 0; i < 3; i++) {
|
|
69
|
+
if (this.tokens[i]! < tokens[i]!) return -1
|
|
70
|
+
if (this.tokens[i]! > tokens[i]!) return 1
|
|
71
|
+
}
|
|
72
|
+
return 0
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
toJSON = (): string => this.toString()
|
|
76
|
+
|
|
77
|
+
toString(): string {
|
|
78
|
+
return this.tokens.join('.')
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Shortcut for Semver.of(input)
|
|
84
|
+
*/
|
|
85
|
+
export function _semver(input: SemverInput): Semver {
|
|
86
|
+
return Semver.of(input)
|
|
87
|
+
}
|