@valkyriestudios/utils 12.19.0 → 12.20.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/README.md +51 -17
- package/date/format.js +82 -48
- package/date/index.d.ts +2 -1
- package/date/index.js +4 -1
- package/date/isFormat.d.ts +12 -0
- package/date/isFormat.js +110 -0
- package/hash/fnv1A.js +2 -2
- package/index.d.ts +6 -1
- package/object/pick.js +2 -3
- package/package.json +1 -1
- package/string/humanizeBytes.js +5 -11
- package/string/humanizeNumber.js +11 -13
- package/string/shorten.js +0 -3
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# @valkyriestudios/utils
|
|
2
2
|
|
|
3
|
-
[](https://codecov.io/gh/ValkyrieStudios/utils)
|
|
4
|
+
[](https://github.com/ValkyrieStudios/utils/actions/workflows/test.yml)
|
|
5
|
+
[](https://github.com/ValkyrieStudios/utils/actions/workflows/lint.yml)
|
|
6
|
+
[](https://github.com/ValkyrieStudios/utils/actions/workflows/github-code-scanning/codeql)
|
|
7
7
|
[](https://www.npmjs.com/package/@valkyriestudios/utils)
|
|
8
8
|
[](https://www.npmjs.com/package/@valkyriestudios/utils)
|
|
9
9
|
|
|
@@ -15,14 +15,14 @@ Zero-dependency collection of single-function utilities for common tasks
|
|
|
15
15
|
## Available Functions
|
|
16
16
|
|
|
17
17
|
### array
|
|
18
|
-
- **isArray(val:
|
|
18
|
+
- **isArray(val:unknown)**
|
|
19
19
|
Check if a variable is of type Array
|
|
20
20
|
```typescript
|
|
21
21
|
isArray({a:1}); // FALSE
|
|
22
22
|
isArray([]); // TRUE
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
- **isNotEmptyArray(val:
|
|
25
|
+
- **isNotEmptyArray(val:unknown)**
|
|
26
26
|
Check if a variable a non-empty array
|
|
27
27
|
```typescript
|
|
28
28
|
isNotEmptyArray({a:1}); // FALSE
|
|
@@ -365,13 +365,44 @@ await memoized('123456'); /* Original function will be called and re-cached */
|
|
|
365
365
|
```
|
|
366
366
|
|
|
367
367
|
### date
|
|
368
|
-
- **isDate(val:
|
|
368
|
+
- **isDate(val:unknown)**
|
|
369
369
|
Check if a variable is of type Date
|
|
370
370
|
```typescript
|
|
371
371
|
isDate(new Date('December 17, 1995 03:24:00')); // TRUE
|
|
372
372
|
isDate('December 17, 1995 03:24:00'); // FALSE
|
|
373
373
|
```
|
|
374
374
|
|
|
375
|
+
- **isFormat(val:unknown, spec:string)**
|
|
376
|
+
Check if a variable is a string in a particular date format
|
|
377
|
+
```typescript
|
|
378
|
+
isFormat('2024-02-07', 'YYYY-MM-DD'); // TRUE
|
|
379
|
+
isFormat('2024-2-07', 'YYYY-MM-DD'); // FALSE
|
|
380
|
+
isFormat('12:30 AM', 'HH:mm A'); // TRUE
|
|
381
|
+
isFormat('2024-Q4', 'YYYY-[Q]Q'); // TRUE
|
|
382
|
+
isFormat('2024-Q5', 'YYYY-[Q]Q'); // FALSE (there is no such thing as a fifth quarter)
|
|
383
|
+
isFormat('2024-02-29T12:30:00.000Z', 'ISO'); // TRUE
|
|
384
|
+
isFormat('2023-02-29T12:30:00.000Z', 'ISO'); // FALSE (leap year)
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
Available tokens for usage in spec:
|
|
388
|
+
| Token | Description | Example |
|
|
389
|
+
|:---------|:--------------------------|:---------------|
|
|
390
|
+
| `YYYY` | Full Year | 2021 |
|
|
391
|
+
| `Q` | Quarters of the year | 1 2 3 4 |
|
|
392
|
+
| `MM` | Month as 2 char | 01 02 .. 11 12 |
|
|
393
|
+
| `DD` | Day of month as 2 char | 01 02 .. 30 31 |
|
|
394
|
+
| `HH` | Hours as 2-char | 00 01 .. 22 23 |
|
|
395
|
+
| `mm` | Minutes as 2-char | 00 01 .. 58 59 |
|
|
396
|
+
| `ss` | Seconds as 2-char | 00 01 .. 58 59 |
|
|
397
|
+
| `SSS` | Milliseconds as 3-digit | 000 001 ... 998 999 |
|
|
398
|
+
| `A` | Uppercase AM/PM | AM ... PM |
|
|
399
|
+
| `a` | Lowercase AM/PM | am ... pm |
|
|
400
|
+
| `Z` | Zone, does not allow full zone names, only Z or offsets | `Z` `+02:00` |
|
|
401
|
+
| `ISO` | Check for full iso date format, take note this enforces milliseconds as a requirement | 2024-02-03T10:28:30.000Z |
|
|
402
|
+
|
|
403
|
+
Note: The `ISO` token is a shorthand for `YYYY-MM-DDTHH:mm:ss.SSSZ`
|
|
404
|
+
Note: You can escape characters by surrounding them with `[...]` in your spec, eg: `YYYY-[Q]Q` would check for example `2024-Q1`
|
|
405
|
+
|
|
375
406
|
- **diff(val_a:Date, val_b:Date, key:string)**
|
|
376
407
|
Take two incoming dates and return the difference between them in a certain unit. Possible key options(week,day,hour,minute,second,millisecond).
|
|
377
408
|
|
|
@@ -385,6 +416,7 @@ diff(new Date("2022-10-05T13:12:11+02:00"), new Date("2022-10-05T17:43:09.344+06
|
|
|
385
416
|
diff(new Date("2022-10-05T13:12:11+02:00"), new Date("2022-10-05T17:43:09.344+06:00"), 'millisecond'); // -1858344
|
|
386
417
|
diff(new Date("2022-11-05T13:12:11+06:00"), new Date("2022-10-05T13:25:43.898+02:00")); // 2663187102
|
|
387
418
|
```
|
|
419
|
+
|
|
388
420
|
- **format(val:Date, spec:string, locale?:string, zone?:string):string**
|
|
389
421
|
Format a date according to a spec/locale and zone
|
|
390
422
|
|
|
@@ -392,6 +424,8 @@ Note: The locale is by default set to 'en-US'
|
|
|
392
424
|
|
|
393
425
|
Note: The zone is by default detected as the zone of the client
|
|
394
426
|
|
|
427
|
+
Note: You can escape characters by surrounding them with `[...]` in your spec, eg: `YYYY-[Q]Q` would for example become `2024-Q1`
|
|
428
|
+
|
|
395
429
|
Available tokens for usage in spec:
|
|
396
430
|
| Token | Description | Example |
|
|
397
431
|
|:---------|:--------------------------|:---------------|
|
|
@@ -670,7 +704,7 @@ Generate a unique identifier (guid) according to RFC4122
|
|
|
670
704
|
guid(); // 245caf1a-86af-11e7-bb31-be2e44b06b34
|
|
671
705
|
```
|
|
672
706
|
|
|
673
|
-
- **fnv1A(val:
|
|
707
|
+
- **fnv1A(val:unknown)**
|
|
674
708
|
Generate a fnv1A hash from an object, using a 32-bit prime/offset
|
|
675
709
|
```typescript
|
|
676
710
|
fnv1A('hello world'); // -2023343616
|
|
@@ -681,7 +715,7 @@ fnv1A(new Date('2012-02-02')); // 1655579136
|
|
|
681
715
|
```
|
|
682
716
|
|
|
683
717
|
### number
|
|
684
|
-
- **isNumber(val:
|
|
718
|
+
- **isNumber(val:unknown)**
|
|
685
719
|
Check if a variable is a number
|
|
686
720
|
```typescript
|
|
687
721
|
isNumber('foo'); // FALSE
|
|
@@ -735,7 +769,7 @@ isNumberBetween(0, 0, 1); // TRUE
|
|
|
735
769
|
isNumberBetween(-1, 0, 1); // FALSE
|
|
736
770
|
```
|
|
737
771
|
|
|
738
|
-
- **isInteger(val:
|
|
772
|
+
- **isInteger(val:unknown)**
|
|
739
773
|
Check if a variable is an integer
|
|
740
774
|
```typescript
|
|
741
775
|
isInteger('foo'); // FALSE
|
|
@@ -791,7 +825,7 @@ isIntegerBetween(0, 0, 1); // TRUE
|
|
|
791
825
|
isIntegerBetween(-1, 0, 1); // FALSE
|
|
792
826
|
```
|
|
793
827
|
|
|
794
|
-
- **isNumericalNaN(val:
|
|
828
|
+
- **isNumericalNaN(val:unknown)**
|
|
795
829
|
Check if a variable is a numerical nan ( a number that is a NaN, this distinguishment is made since both a string or a number can be NaN)
|
|
796
830
|
```typescript
|
|
797
831
|
isNumericalNaN('foo'); // FALSE
|
|
@@ -829,14 +863,14 @@ randomIntBetween(25, 100); // Will generate a random between 25 and 100 (100 not
|
|
|
829
863
|
```
|
|
830
864
|
|
|
831
865
|
### object
|
|
832
|
-
- **isObject(val:
|
|
866
|
+
- **isObject(val:unknown)**
|
|
833
867
|
Check if a variable is of type Object
|
|
834
868
|
```typescript
|
|
835
869
|
isObject({a: 1}); // TRUE
|
|
836
870
|
isObject(1); // FALSE
|
|
837
871
|
```
|
|
838
872
|
|
|
839
|
-
- **isNotEmptyObject(val:
|
|
873
|
+
- **isNotEmptyObject(val:unknown)**
|
|
840
874
|
Check if a variable a non-empty object
|
|
841
875
|
```typescript
|
|
842
876
|
isNotEmptyObject({a:1}); // TRUE
|
|
@@ -887,7 +921,7 @@ define({
|
|
|
887
921
|
```
|
|
888
922
|
|
|
889
923
|
### regexp
|
|
890
|
-
- **isRegExp(val:
|
|
924
|
+
- **isRegExp(val:unknown)**
|
|
891
925
|
Check if a variable is an instance of RegExp
|
|
892
926
|
```typescript
|
|
893
927
|
isRegExp('foo'); // FALSE
|
|
@@ -903,7 +937,7 @@ sanitizeRegExp('contact@valkyriestudios.be'); // contact@valkyriestudios\\.be
|
|
|
903
937
|
```
|
|
904
938
|
|
|
905
939
|
### string
|
|
906
|
-
- **isString(val:
|
|
940
|
+
- **isString(val:unknown)**
|
|
907
941
|
Check if a variable is a string
|
|
908
942
|
```typescript
|
|
909
943
|
isString('foo'); // TRUE
|
|
@@ -921,7 +955,7 @@ isStringBetween(' Joe', 1, 3); // TRUE
|
|
|
921
955
|
isStringBetween(' Joe', 1, 3, false); // FALSE
|
|
922
956
|
```
|
|
923
957
|
|
|
924
|
-
- **isNotEmptyString(val:
|
|
958
|
+
- **isNotEmptyString(val:unknown, trimmed:boolean=true)**
|
|
925
959
|
Check if a variable a non-empty string
|
|
926
960
|
```typescript
|
|
927
961
|
isNotEmptyString({a:1}); // FALSE
|
|
@@ -931,7 +965,7 @@ isNotEmptyString(' ', false); // TRUE
|
|
|
931
965
|
isNotEmptyString('Hi'); // TRUE
|
|
932
966
|
```
|
|
933
967
|
|
|
934
|
-
- **shorten(val:
|
|
968
|
+
- **shorten(val:string, length:integer, postfix:string=..., truncate_words=true)**
|
|
935
969
|
Shorten a string and add a postfix if string went over length
|
|
936
970
|
```typescript
|
|
937
971
|
shorten('To the moon and beyond', 11, '..'); // 'To the moon..'
|
package/date/format.js
CHANGED
|
@@ -15,36 +15,35 @@ finally {
|
|
|
15
15
|
DEFAULT_TZ = 'UTC';
|
|
16
16
|
}
|
|
17
17
|
const escape_rgx = /\[[\s\S]+?]/g;
|
|
18
|
-
const intl_formatters =
|
|
19
|
-
const spec_cache =
|
|
20
|
-
const zone_offset_cache =
|
|
18
|
+
const intl_formatters = Object.create(null);
|
|
19
|
+
const spec_cache = Object.create(null);
|
|
20
|
+
const zone_offset_cache = Object.create(null);
|
|
21
21
|
function DOY(d) {
|
|
22
22
|
return ((d - new Date(d.getFullYear(), 0, 0)) / 86400000) | 0;
|
|
23
23
|
}
|
|
24
24
|
function toZone(date, zone) {
|
|
25
25
|
const ckey = `${zone}:${date.getUTCFullYear()}${DOY(date)}`;
|
|
26
|
-
if (zone_offset_cache
|
|
27
|
-
return new Date(date.getTime() + zone_offset_cache
|
|
26
|
+
if (zone_offset_cache[ckey] !== undefined)
|
|
27
|
+
return new Date(date.getTime() + zone_offset_cache[ckey]);
|
|
28
28
|
const client_time = date.getTime();
|
|
29
|
-
let zone_time =
|
|
29
|
+
let zone_time = null;
|
|
30
30
|
try {
|
|
31
31
|
zone_time = new Date(date.toLocaleString(DEFAULT_LOCALE, { timeZone: zone })).getTime();
|
|
32
32
|
}
|
|
33
33
|
catch (err) {
|
|
34
|
-
}
|
|
35
|
-
if (!Number.isInteger(zone_time))
|
|
36
34
|
throw new Error(`format: Invalid zone passed - ${zone}`);
|
|
35
|
+
}
|
|
37
36
|
const offset = zone_time - client_time;
|
|
38
|
-
zone_offset_cache
|
|
37
|
+
zone_offset_cache[ckey] = offset;
|
|
39
38
|
return new Date(client_time + offset);
|
|
40
39
|
}
|
|
41
40
|
function runIntl(loc, token, props, val) {
|
|
42
|
-
const hash =
|
|
43
|
-
let formatter = intl_formatters
|
|
41
|
+
const hash = loc + ':' + token;
|
|
42
|
+
let formatter = intl_formatters[hash];
|
|
44
43
|
if (!formatter) {
|
|
45
44
|
try {
|
|
46
45
|
formatter = new Intl.DateTimeFormat(loc, props);
|
|
47
|
-
intl_formatters
|
|
46
|
+
intl_formatters[hash] = formatter;
|
|
48
47
|
}
|
|
49
48
|
catch (err) {
|
|
50
49
|
throw new Error(`format: Failed to run conversion for ${token} with locale ${loc}`);
|
|
@@ -57,21 +56,46 @@ const Tokens = [
|
|
|
57
56
|
['Q', d => ((d.getMonth() + 3) / 3) | 0],
|
|
58
57
|
['MMMM', (d, loc) => runIntl(loc, 'MMMM', { month: 'long' }, d)],
|
|
59
58
|
['MMM', (d, loc) => runIntl(loc, 'MMM', { month: 'short' }, d)],
|
|
60
|
-
['MM', d =>
|
|
59
|
+
['MM', d => {
|
|
60
|
+
const val = d.getMonth() + 1;
|
|
61
|
+
return (val < 10 ? '0' : '') + val;
|
|
62
|
+
}],
|
|
61
63
|
['M', d => d.getMonth() + 1],
|
|
62
|
-
['DD', d =>
|
|
64
|
+
['DD', d => {
|
|
65
|
+
const val = d.getDate();
|
|
66
|
+
return (val < 10 ? '0' : '') + val;
|
|
67
|
+
}],
|
|
63
68
|
['D', d => d.getDate()],
|
|
64
69
|
['dddd', (d, loc) => runIntl(loc, 'dddd', { weekday: 'long' }, d)],
|
|
65
70
|
['ddd', (d, loc) => runIntl(loc, 'ddd', { weekday: 'short' }, d)],
|
|
66
|
-
['HH', d =>
|
|
71
|
+
['HH', d => {
|
|
72
|
+
const val = d.getHours();
|
|
73
|
+
return (val < 10 ? '0' : '') + val;
|
|
74
|
+
}],
|
|
67
75
|
['H', d => d.getHours()],
|
|
68
|
-
['hh', d =>
|
|
76
|
+
['hh', d => {
|
|
77
|
+
const val = ((d.getHours() + 11) % 12) + 1;
|
|
78
|
+
return (val < 10 ? '0' : '') + val;
|
|
79
|
+
}],
|
|
69
80
|
['h', d => ((d.getHours() + 11) % 12) + 1],
|
|
70
|
-
['mm', d =>
|
|
81
|
+
['mm', d => {
|
|
82
|
+
const val = d.getMinutes();
|
|
83
|
+
return (val < 10 ? '0' : '') + val;
|
|
84
|
+
}],
|
|
71
85
|
['m', d => d.getMinutes()],
|
|
72
|
-
['ss', d =>
|
|
86
|
+
['ss', d => {
|
|
87
|
+
const val = d.getSeconds();
|
|
88
|
+
return (val < 10 ? '0' : '') + val;
|
|
89
|
+
}],
|
|
73
90
|
['s', d => d.getSeconds()],
|
|
74
|
-
['SSS', d =>
|
|
91
|
+
['SSS', d => {
|
|
92
|
+
const val = d.getMilliseconds();
|
|
93
|
+
return val < 10
|
|
94
|
+
? '00' + val
|
|
95
|
+
: val < 100
|
|
96
|
+
? '0' + val
|
|
97
|
+
: val;
|
|
98
|
+
}],
|
|
75
99
|
['A', d => d.getHours() < 12 ? 'AM' : 'PM'],
|
|
76
100
|
['a', d => d.getHours() < 12 ? 'am' : 'pm'],
|
|
77
101
|
['l', (d, loc) => runIntl(loc, 'l', { dateStyle: 'short' }, d)],
|
|
@@ -79,42 +103,46 @@ const Tokens = [
|
|
|
79
103
|
['t', (d, loc) => runIntl(loc, 't', { timeStyle: 'short' }, d)],
|
|
80
104
|
['T', (d, loc) => runIntl(loc, 'T', { timeStyle: 'medium' }, d)],
|
|
81
105
|
]
|
|
82
|
-
.sort((a, b) => a[0].length > b[0].length ? -1 : 1)
|
|
83
|
-
.map((el) => [el[0], new RegExp(el[0], 'g'), el[1]]);
|
|
106
|
+
.sort((a, b) => a[0].length > b[0].length ? -1 : 1);
|
|
84
107
|
function getSpecChain(spec) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
let cursor;
|
|
90
|
-
let spec_cursor = spec;
|
|
108
|
+
if (spec_cache[spec] !== undefined)
|
|
109
|
+
return spec_cache[spec];
|
|
110
|
+
const spec_chain = [];
|
|
111
|
+
const matched_positions = new Set();
|
|
91
112
|
for (let i = 0; i < Tokens.length; i++) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
113
|
+
const [token] = Tokens[i];
|
|
114
|
+
let pos = spec.indexOf(token);
|
|
115
|
+
const token_len = token.length;
|
|
116
|
+
while (pos !== -1) {
|
|
117
|
+
if (!matched_positions.has(pos)) {
|
|
118
|
+
spec_chain.push(Tokens[i]);
|
|
119
|
+
for (let j = 0; j < token_len; j++) {
|
|
120
|
+
matched_positions.add(pos + j);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
pos = spec.indexOf(token, pos + 1);
|
|
124
|
+
}
|
|
97
125
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
return spec_chain;
|
|
126
|
+
const result = spec_chain.length ? spec_chain : null;
|
|
127
|
+
spec_cache[spec] = result;
|
|
128
|
+
return result;
|
|
102
129
|
}
|
|
103
130
|
function format(val, spec, locale = DEFAULT_LOCALE, zone = DEFAULT_TZ) {
|
|
104
131
|
if (!(0, is_1.isDate)(val))
|
|
105
132
|
throw new TypeError('format: val must be a Date');
|
|
106
|
-
if (typeof spec !== 'string'
|
|
107
|
-
throw new TypeError('format: spec must be a
|
|
108
|
-
if (typeof locale !== 'string'
|
|
109
|
-
throw new TypeError('format: locale must be a
|
|
110
|
-
if (typeof zone !== 'string'
|
|
111
|
-
throw new TypeError('format: zone must be a
|
|
133
|
+
if (typeof spec !== 'string')
|
|
134
|
+
throw new TypeError('format: spec must be a string');
|
|
135
|
+
if (typeof locale !== 'string')
|
|
136
|
+
throw new TypeError('format: locale must be a string');
|
|
137
|
+
if (typeof zone !== 'string')
|
|
138
|
+
throw new TypeError('format: zone must be a string');
|
|
112
139
|
let formatted_string = spec;
|
|
113
140
|
const escaped_acc = [];
|
|
141
|
+
let escaped_count = 0;
|
|
114
142
|
if (formatted_string.indexOf('[') >= 0) {
|
|
115
143
|
formatted_string = formatted_string.replace(escape_rgx, match => {
|
|
116
|
-
const escape_token =
|
|
117
|
-
escaped_acc.push([escape_token, match.
|
|
144
|
+
const escape_token = '$R' + escaped_count++ + '$';
|
|
145
|
+
escaped_acc.push([escape_token, match.slice(1, -1)]);
|
|
118
146
|
return escape_token;
|
|
119
147
|
});
|
|
120
148
|
}
|
|
@@ -123,11 +151,17 @@ function format(val, spec, locale = DEFAULT_LOCALE, zone = DEFAULT_TZ) {
|
|
|
123
151
|
return val.toISOString();
|
|
124
152
|
const d = toZone(val, zone);
|
|
125
153
|
for (let i = 0; i < spec_chain.length; i++) {
|
|
126
|
-
const
|
|
127
|
-
|
|
154
|
+
const [token, formatter] = spec_chain[i];
|
|
155
|
+
let pos = formatted_string.indexOf(token);
|
|
156
|
+
while (pos !== -1) {
|
|
157
|
+
formatted_string = formatted_string.slice(0, pos) +
|
|
158
|
+
formatter(d, locale) +
|
|
159
|
+
formatted_string.slice(pos + token.length);
|
|
160
|
+
pos = formatted_string.indexOf(token, pos + token.length);
|
|
161
|
+
}
|
|
128
162
|
}
|
|
129
|
-
if (
|
|
130
|
-
for (let i = 0; i <
|
|
163
|
+
if (escaped_count) {
|
|
164
|
+
for (let i = 0; i < escaped_count; i++) {
|
|
131
165
|
const escape_token = escaped_acc[i];
|
|
132
166
|
formatted_string = formatted_string.replace(escape_token[0], escape_token[1]);
|
|
133
167
|
}
|
package/date/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { addUTC } from './addUTC';
|
|
|
2
2
|
import { diff } from './diff';
|
|
3
3
|
import { endOfUTC } from './endOfUTC';
|
|
4
4
|
import { format } from './format';
|
|
5
|
+
import { isDateFormat } from './isFormat';
|
|
5
6
|
import { isDate } from './is';
|
|
6
7
|
import { nowUnix } from './nowUnix';
|
|
7
8
|
import { nowUnixMs } from './nowUnixMs';
|
|
@@ -9,4 +10,4 @@ import { setTimeUTC } from './setTimeUTC';
|
|
|
9
10
|
import { startOfUTC } from './startOfUTC';
|
|
10
11
|
import { toUnix } from './toUnix';
|
|
11
12
|
import { toUTC } from './toUTC';
|
|
12
|
-
export { addUTC, diff, endOfUTC, format, isDate, isDate as is, nowUnix, nowUnixMs, setTimeUTC, startOfUTC, toUnix, toUTC };
|
|
13
|
+
export { addUTC, diff, endOfUTC, format, isDateFormat as isFormat, isDateFormat, isDate, isDate as is, nowUnix, nowUnixMs, setTimeUTC, startOfUTC, toUnix, toUTC };
|
package/date/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.toUTC = exports.toUnix = exports.startOfUTC = exports.setTimeUTC = exports.nowUnixMs = exports.nowUnix = exports.is = exports.isDate = exports.format = exports.endOfUTC = exports.diff = exports.addUTC = void 0;
|
|
3
|
+
exports.toUTC = exports.toUnix = exports.startOfUTC = exports.setTimeUTC = exports.nowUnixMs = exports.nowUnix = exports.is = exports.isDate = exports.isDateFormat = exports.isFormat = exports.format = exports.endOfUTC = exports.diff = exports.addUTC = void 0;
|
|
4
4
|
const addUTC_1 = require("./addUTC");
|
|
5
5
|
Object.defineProperty(exports, "addUTC", { enumerable: true, get: function () { return addUTC_1.addUTC; } });
|
|
6
6
|
const diff_1 = require("./diff");
|
|
@@ -9,6 +9,9 @@ const endOfUTC_1 = require("./endOfUTC");
|
|
|
9
9
|
Object.defineProperty(exports, "endOfUTC", { enumerable: true, get: function () { return endOfUTC_1.endOfUTC; } });
|
|
10
10
|
const format_1 = require("./format");
|
|
11
11
|
Object.defineProperty(exports, "format", { enumerable: true, get: function () { return format_1.format; } });
|
|
12
|
+
const isFormat_1 = require("./isFormat");
|
|
13
|
+
Object.defineProperty(exports, "isFormat", { enumerable: true, get: function () { return isFormat_1.isDateFormat; } });
|
|
14
|
+
Object.defineProperty(exports, "isDateFormat", { enumerable: true, get: function () { return isFormat_1.isDateFormat; } });
|
|
12
15
|
const is_1 = require("./is");
|
|
13
16
|
Object.defineProperty(exports, "isDate", { enumerable: true, get: function () { return is_1.isDate; } });
|
|
14
17
|
Object.defineProperty(exports, "is", { enumerable: true, get: function () { return is_1.isDate; } });
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if a given string is in a particular format
|
|
3
|
+
* Eg:
|
|
4
|
+
* isDateFormat('2024-02-09', 'YYYY-MM-DD'); // true
|
|
5
|
+
* isDateFormat('2024-02-09T14:30', 'YYYY-MM-DD'); // false
|
|
6
|
+
*
|
|
7
|
+
* @param {unknown} input - String to format (eg: '2024-08-01')
|
|
8
|
+
* @param {string} spec - Spec to validate (Eg: 'YYYY-MM-DD')
|
|
9
|
+
* @returns {boolean} Whether or not the input is valid according to the spec
|
|
10
|
+
*/
|
|
11
|
+
declare function isDateFormat(input: unknown, spec: string): input is string;
|
|
12
|
+
export { isDateFormat, isDateFormat as default };
|
package/date/isFormat.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isDateFormat = isDateFormat;
|
|
4
|
+
exports.default = isDateFormat;
|
|
5
|
+
const SPECIAL_CHARS = /[.*+?^${}()|[\]\\]/g;
|
|
6
|
+
const TOKENS = [
|
|
7
|
+
['YYYY', /\d{4}/.source, (raw, context) => {
|
|
8
|
+
context.year = parseInt(raw, 10);
|
|
9
|
+
return context.year > 0;
|
|
10
|
+
}],
|
|
11
|
+
['MM', /(?:0[1-9]|1[0-2])/.source, (raw, context) => {
|
|
12
|
+
context.month = parseInt(raw, 10);
|
|
13
|
+
return context.month >= 1 && context.month <= 12;
|
|
14
|
+
}],
|
|
15
|
+
['DD', /(?:0[1-9]|[12][0-9]|3[01])/.source, (raw, context) => {
|
|
16
|
+
context.day = parseInt(raw, 10);
|
|
17
|
+
return context.day >= 1 && context.day <= 31;
|
|
18
|
+
}],
|
|
19
|
+
['HH', /(?:[01][0-9]|2[0-3])/.source, (raw, context) => {
|
|
20
|
+
context.hour = parseInt(raw, 10);
|
|
21
|
+
return context.hour >= 0 && context.hour <= 23;
|
|
22
|
+
}],
|
|
23
|
+
['mm', /[0-5][0-9]/.source, () => true],
|
|
24
|
+
['ss', /[0-5][0-9]/.source, () => true],
|
|
25
|
+
['SSS', /\d{3}/.source, () => true],
|
|
26
|
+
['Q', /[1-4]/.source, () => true],
|
|
27
|
+
['A', /(?:AM|PM)/.source, (raw, context) => {
|
|
28
|
+
context.is12 = 1;
|
|
29
|
+
return raw === 'AM' || raw === 'PM';
|
|
30
|
+
}],
|
|
31
|
+
['a', /(?:am|pm)/.source, (raw, context) => {
|
|
32
|
+
context.is12 = 1;
|
|
33
|
+
return raw === 'am' || raw === 'pm';
|
|
34
|
+
}],
|
|
35
|
+
['Z', /Z|[+-](?:0[0-9]|1[0-4]):[0-5][0-9]/.source, raw => {
|
|
36
|
+
if (raw === 'Z')
|
|
37
|
+
return true;
|
|
38
|
+
let hour = parseInt(raw[1] + raw[2], 10);
|
|
39
|
+
if (raw[0] === '-')
|
|
40
|
+
hour = -hour;
|
|
41
|
+
const minutes = parseInt(raw[4] + raw[5], 10);
|
|
42
|
+
if (hour === 14 || hour === -12)
|
|
43
|
+
return minutes === 0;
|
|
44
|
+
return hour >= -11 && hour < 14 && [0, 15, 30, 45].indexOf(minutes) >= 0;
|
|
45
|
+
}],
|
|
46
|
+
];
|
|
47
|
+
const SPEC_ALIASES = {
|
|
48
|
+
ISO: 'YYYY-MM-DDTHH:mm:ss.SSSZ',
|
|
49
|
+
};
|
|
50
|
+
const spec_pat_cache = {};
|
|
51
|
+
function compileSpec(spec) {
|
|
52
|
+
if (spec in spec_pat_cache)
|
|
53
|
+
return spec_pat_cache[spec];
|
|
54
|
+
const tokens = [];
|
|
55
|
+
let pat = '';
|
|
56
|
+
let cursor = 0;
|
|
57
|
+
while (cursor < spec.length) {
|
|
58
|
+
if (spec[cursor] === '[') {
|
|
59
|
+
const end_idx = spec.indexOf(']', cursor);
|
|
60
|
+
if (end_idx === -1)
|
|
61
|
+
throw new Error('isDateFormat: Unmatched [ in format string');
|
|
62
|
+
pat += spec.slice(cursor + 1, end_idx).replace(SPECIAL_CHARS, '\\$&');
|
|
63
|
+
cursor = end_idx + 1;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
const token_idx = TOKENS.findIndex(([token_key]) => spec.startsWith(token_key, cursor));
|
|
67
|
+
if (token_idx >= 0) {
|
|
68
|
+
const [token_key, token_rgx] = TOKENS[token_idx];
|
|
69
|
+
pat += '(' + token_rgx + ')';
|
|
70
|
+
tokens.push(token_idx);
|
|
71
|
+
cursor += token_key.length;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
pat += spec[cursor].replace(SPECIAL_CHARS, '\\$&');
|
|
75
|
+
cursor++;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
spec_pat_cache[spec] = { rgx: RegExp('^' + pat + '$'), tokens };
|
|
80
|
+
return spec_pat_cache[spec];
|
|
81
|
+
}
|
|
82
|
+
function isDateFormat(input, spec) {
|
|
83
|
+
if (typeof input !== 'string' || input.trim().length === 0) {
|
|
84
|
+
throw new TypeError('isDateFormat: input must be a non-empty string');
|
|
85
|
+
}
|
|
86
|
+
if (typeof spec !== 'string') {
|
|
87
|
+
throw new TypeError('isDateFormat: spec must be a string');
|
|
88
|
+
}
|
|
89
|
+
const { tokens, rgx } = compileSpec(SPEC_ALIASES[spec] || spec);
|
|
90
|
+
if (!tokens.length)
|
|
91
|
+
return false;
|
|
92
|
+
const patMatch = rgx.exec(input);
|
|
93
|
+
if (!patMatch)
|
|
94
|
+
return false;
|
|
95
|
+
const matches = patMatch.slice(1);
|
|
96
|
+
const context = {};
|
|
97
|
+
for (let i = 0; i < matches.length; i++) {
|
|
98
|
+
if (!TOKENS[tokens[i]][2](matches[i], context))
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
const { is12, day, month, year } = context;
|
|
102
|
+
if (day && month) {
|
|
103
|
+
const date = new Date(year || 2024, month - 1, day);
|
|
104
|
+
if (date.getDate() !== day || date.getMonth() !== month - 1)
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
if (is12 && 'hour' in context && context.hour > 11)
|
|
108
|
+
return false;
|
|
109
|
+
return true;
|
|
110
|
+
}
|
package/hash/fnv1A.js
CHANGED
|
@@ -28,11 +28,11 @@ function fnv1A(data, offset = FNV_32) {
|
|
|
28
28
|
if (data === null) {
|
|
29
29
|
sanitized = REPL_NULL;
|
|
30
30
|
}
|
|
31
|
-
else if (Array.isArray(data) ||
|
|
31
|
+
else if (Array.isArray(data) || Object.prototype.toString.call(data) === '[object Object]') {
|
|
32
32
|
sanitized = JSON.stringify(data);
|
|
33
33
|
}
|
|
34
34
|
else if (data instanceof RegExp) {
|
|
35
|
-
sanitized = data
|
|
35
|
+
sanitized = String(data);
|
|
36
36
|
}
|
|
37
37
|
else if (data instanceof Date) {
|
|
38
38
|
sanitized = String(data.getTime());
|
package/index.d.ts
CHANGED
|
@@ -147,6 +147,10 @@ declare module "date/format" {
|
|
|
147
147
|
function format(val: Date, spec: string, locale?: string, zone?: string): string;
|
|
148
148
|
export { format, format as default };
|
|
149
149
|
}
|
|
150
|
+
declare module "date/isFormat" {
|
|
151
|
+
function isDateFormat(input: unknown, spec: string): input is string;
|
|
152
|
+
export { isDateFormat, isDateFormat as default };
|
|
153
|
+
}
|
|
150
154
|
declare module "date/nowUnix" {
|
|
151
155
|
function nowUnix(): number;
|
|
152
156
|
export { nowUnix, nowUnix as default };
|
|
@@ -186,6 +190,7 @@ declare module "date/index" {
|
|
|
186
190
|
import { diff } from "date/diff";
|
|
187
191
|
import { endOfUTC } from "date/endOfUTC";
|
|
188
192
|
import { format } from "date/format";
|
|
193
|
+
import { isDateFormat } from "date/isFormat";
|
|
189
194
|
import { isDate } from "date/is";
|
|
190
195
|
import { nowUnix } from "date/nowUnix";
|
|
191
196
|
import { nowUnixMs } from "date/nowUnixMs";
|
|
@@ -193,7 +198,7 @@ declare module "date/index" {
|
|
|
193
198
|
import { startOfUTC } from "date/startOfUTC";
|
|
194
199
|
import { toUnix } from "date/toUnix";
|
|
195
200
|
import { toUTC } from "date/toUTC";
|
|
196
|
-
export { addUTC, diff, endOfUTC, format, isDate, isDate as is, nowUnix, nowUnixMs, setTimeUTC, startOfUTC, toUnix, toUTC };
|
|
201
|
+
export { addUTC, diff, endOfUTC, format, isDateFormat as isFormat, isDateFormat, isDate, isDate as is, nowUnix, nowUnixMs, setTimeUTC, startOfUTC, toUnix, toUTC };
|
|
197
202
|
}
|
|
198
203
|
declare module "formdata/is" {
|
|
199
204
|
function isFormData(val: unknown): val is FormData;
|
package/object/pick.js
CHANGED
|
@@ -18,7 +18,7 @@ function pick(obj, keys) {
|
|
|
18
18
|
sanitized = key.trim();
|
|
19
19
|
if (!sanitized.length)
|
|
20
20
|
continue;
|
|
21
|
-
if (sanitized.
|
|
21
|
+
if (sanitized.indexOf('.') >= 0) {
|
|
22
22
|
val = (0, get_1.deepGet)(obj, sanitized);
|
|
23
23
|
if (val === undefined)
|
|
24
24
|
continue;
|
|
@@ -27,9 +27,8 @@ function pick(obj, keys) {
|
|
|
27
27
|
let cursor = map;
|
|
28
28
|
for (let y = 0; y < parts_len - 1; y++) {
|
|
29
29
|
const part = parts[y].trim();
|
|
30
|
-
if (!cursor[part])
|
|
30
|
+
if (!cursor[part])
|
|
31
31
|
cursor[part] = {};
|
|
32
|
-
}
|
|
33
32
|
cursor = cursor[part];
|
|
34
33
|
}
|
|
35
34
|
cursor[parts[parts_len - 1].trim()] = val;
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{ "name": "@valkyriestudios/utils", "version": "12.
|
|
1
|
+
{ "name": "@valkyriestudios/utils", "version": "12.20.0", "description": "A collection of single-function utilities for common tasks", "author": { "name": "Peter Vermeulen", "url": "https://www.linkedin.com/in/petervermeulen1/" }, "keywords": [ "utility", "library", "javascript", "js", "node", "bun" ], "license": "MIT", "repository": { "type": "git", "url": "git+https://github.com/ValkyrieStudios/utils.git" }, "bugs": { "url": "https://github.com/ValkyrieStudios/utils/issues" }, "homepage": "https://github.com/ValkyrieStudios/utils#readme", "types": "index.d.ts" }
|
package/string/humanizeBytes.js
CHANGED
|
@@ -3,21 +3,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.humanizeBytes = humanizeBytes;
|
|
4
4
|
exports.default = humanizeBytes;
|
|
5
5
|
const humanizeNumber_1 = require("./humanizeNumber");
|
|
6
|
-
const
|
|
6
|
+
const DEFAULT_UNITS = [' bytes', ' KB', ' MB', ' GB', ' TB', ' PB', ' EB', ' ZB', ' YB'];
|
|
7
7
|
function humanizeBytes(val, options = {}) {
|
|
8
8
|
return (0, humanizeNumber_1.humanizeNumber)(val, {
|
|
9
|
-
delim:
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
separator: typeof options?.separator === 'string' && options.separator.trim().length
|
|
13
|
-
? options.separator
|
|
14
|
-
: '.',
|
|
15
|
-
precision: (0, isIntegerAboveOrEqual_1.isIntegerAboveOrEqual)(options?.precision, 0)
|
|
16
|
-
? options.precision
|
|
17
|
-
: 2,
|
|
9
|
+
delim: 'delim' in options ? options.delim : ',',
|
|
10
|
+
separator: 'separator' in options ? options.separator : '.',
|
|
11
|
+
precision: 'precision' in options ? options.precision : 2,
|
|
18
12
|
units: Array.isArray(options?.units) && options.units.length
|
|
19
13
|
? options.units
|
|
20
|
-
:
|
|
14
|
+
: DEFAULT_UNITS,
|
|
21
15
|
divider: 1024,
|
|
22
16
|
real: true,
|
|
23
17
|
});
|
package/string/humanizeNumber.js
CHANGED
|
@@ -16,31 +16,29 @@ function humanizeNumber(val, options = {}) {
|
|
|
16
16
|
: options?.units === false
|
|
17
17
|
? false
|
|
18
18
|
: DEFAULT_UNITS;
|
|
19
|
-
let normalized =
|
|
20
|
-
if (typeof val === 'string') {
|
|
21
|
-
normalized = REAL ? parseInt(val.trim(), 10) : parseFloat(val);
|
|
22
|
-
}
|
|
23
|
-
else if (Number.isFinite(val)) {
|
|
24
|
-
normalized = REAL ? Math.round(val) : val;
|
|
25
|
-
}
|
|
19
|
+
let normalized = typeof val === 'string' ? parseFloat(val.trim()) : val;
|
|
26
20
|
if (!Number.isFinite(normalized) || normalized === 0)
|
|
27
21
|
return UNITS ? `0${UNITS[0]}` : '0';
|
|
22
|
+
if (REAL)
|
|
23
|
+
normalized = Math.round(normalized);
|
|
28
24
|
const sign = normalized < 0 ? '-' : '';
|
|
29
25
|
normalized = Math.abs(normalized);
|
|
30
26
|
let unit_ix = 0;
|
|
31
27
|
if (UNITS) {
|
|
32
|
-
|
|
28
|
+
while (normalized >= DIVIDER && unit_ix < UNITS.length - 1) {
|
|
33
29
|
normalized /= DIVIDER;
|
|
30
|
+
unit_ix++;
|
|
34
31
|
}
|
|
35
32
|
}
|
|
36
33
|
const humanized = `${(0, round_1.round)(normalized, PRECISION)}`.split('.', 2);
|
|
37
|
-
const
|
|
34
|
+
const integer_part = humanized[0];
|
|
35
|
+
const integer_part_len = integer_part.length;
|
|
38
36
|
let formattedIntegerPart = '';
|
|
39
|
-
for (let i = 0; i <
|
|
40
|
-
if (i > 0 && (
|
|
37
|
+
for (let i = 0; i < integer_part_len; i++) {
|
|
38
|
+
if (i > 0 && (integer_part_len - i) % 3 === 0) {
|
|
41
39
|
formattedIntegerPart += DELIM;
|
|
42
40
|
}
|
|
43
|
-
formattedIntegerPart +=
|
|
41
|
+
formattedIntegerPart += integer_part[i];
|
|
44
42
|
}
|
|
45
|
-
return
|
|
43
|
+
return sign + formattedIntegerPart + (humanized[1] ? SEPARATOR + humanized[1] : '') + (UNITS ? UNITS[unit_ix] : '');
|
|
46
44
|
}
|
package/string/shorten.js
CHANGED
|
@@ -17,8 +17,5 @@ function shorten(val, length, postfix = '...', truncate_words = true) {
|
|
|
17
17
|
while (end > 0 && sanitized[end] !== ' ' && sanitized[end - 1] !== ' ') {
|
|
18
18
|
end--;
|
|
19
19
|
}
|
|
20
|
-
if (end === 0) {
|
|
21
|
-
return sanitized.substring(0, length) + postfix;
|
|
22
|
-
}
|
|
23
20
|
return sanitized.substring(0, end).trim() + postfix;
|
|
24
21
|
}
|