@valkyriestudios/utils 12.24.0 → 12.25.1
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 +59 -17
- package/array/mapFn.d.ts +6 -6
- package/array/mapFn.js +2 -1
- package/array/mapKey.d.ts +8 -4
- package/array/mapKey.js +8 -4
- package/array/mapPrimitive.d.ts +8 -4
- package/array/mapPrimitive.js +8 -5
- package/date/format.js +13 -11
- package/date/isFormat.js +14 -4
- package/deep/set.js +0 -2
- package/equal.js +49 -21
- package/formdata/toObject.d.ts +15 -3
- package/formdata/toObject.js +38 -25
- package/index.d.ts +28 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ isNotEmptyArray([]); // FALSE
|
|
|
30
30
|
isNotEmptyArray([0, 1, 2]); // TRUE
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
### array/mapKey(val:Record[], key:string, opts:
|
|
33
|
+
### array/mapKey(val:Record[], key:string, opts?:{merge?:boolean;filter_fn?:(el:T) => boolean})
|
|
34
34
|
Map a non-primitive object array into an object map by key
|
|
35
35
|
```typescript
|
|
36
36
|
import mapKey from '@valkyriestudios/utils/array/mapKey';
|
|
@@ -39,9 +39,7 @@ mapKey([
|
|
|
39
39
|
{uid: 15, name: 'Jonas'},
|
|
40
40
|
{uid: 87, name: 'Josh'},
|
|
41
41
|
], 'uid');
|
|
42
|
-
|
|
43
|
-
output:
|
|
44
|
-
|
|
42
|
+
/* Expected output: */
|
|
45
43
|
{
|
|
46
44
|
12: {uid: 12, name: 'Peter'},
|
|
47
45
|
15: {uid: 15, name: 'Jonas'},
|
|
@@ -64,9 +62,7 @@ mapKey([
|
|
|
64
62
|
new Date(),
|
|
65
63
|
{uid: 87, name: 'Josh'},
|
|
66
64
|
], 'uid');
|
|
67
|
-
|
|
68
|
-
output:
|
|
69
|
-
|
|
65
|
+
/* Expected output: */
|
|
70
66
|
{
|
|
71
67
|
12: {uid: 12, name: 'Peter'},
|
|
72
68
|
15: {uid: 15, name: 'Jonas'},
|
|
@@ -92,9 +88,7 @@ mapKey([
|
|
|
92
88
|
{uid: 87, name: 'Josh'},
|
|
93
89
|
{uid: 12, name: 'Farah'},
|
|
94
90
|
], 'uid', {merge: true})
|
|
95
|
-
|
|
96
|
-
output:
|
|
97
|
-
|
|
91
|
+
/* Expected output: */
|
|
98
92
|
{
|
|
99
93
|
12: {uid: 12, name: 'Farah'},
|
|
100
94
|
15: {uid: 15, name: 'Bob', dob: '2022-02-07'},
|
|
@@ -102,6 +96,24 @@ output:
|
|
|
102
96
|
}
|
|
103
97
|
```
|
|
104
98
|
|
|
99
|
+
allows filtering out objects with a custom filter_fn:
|
|
100
|
+
```typescript
|
|
101
|
+
import mapKey from '@valkyriestudios/utils/array/mapKey';
|
|
102
|
+
mapKey([
|
|
103
|
+
{uid: 12, name: 'Peter', isActive: true},
|
|
104
|
+
{uid: 15, name: 'Jonas', dob: '2022-02-07', isActive: true},
|
|
105
|
+
{uid: 15, name: 'Bob', isActive: false},
|
|
106
|
+
{name: 'Alana', isActive: true},
|
|
107
|
+
{uid: 87, name: 'Josh', isActive: false},
|
|
108
|
+
{uid: 12, name: 'Farah', isActive: false},
|
|
109
|
+
], 'uid', {merge: true})
|
|
110
|
+
/* Expected output: */
|
|
111
|
+
{
|
|
112
|
+
12: {uid: 12, name: 'Peter', isActive: true},
|
|
113
|
+
15: {uid: 15, name: 'Jonas', dob: '2022-02-07'},
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
105
117
|
### array/mapFn(val:Record[], key:Function, opts:object={})
|
|
106
118
|
Same behavior as mapKey but instead of a key, a function is passed to generate your own key. Eg:
|
|
107
119
|
|
|
@@ -112,9 +124,7 @@ mapFn([
|
|
|
112
124
|
{uid: 15, name: 'Jonas'},
|
|
113
125
|
{uid: 87, name: 'Josh'},
|
|
114
126
|
], el => el.uid)
|
|
115
|
-
|
|
116
|
-
output:
|
|
117
|
-
|
|
127
|
+
/* Expected output: */
|
|
118
128
|
{
|
|
119
129
|
12: {uid: 12, name: 'Peter'},
|
|
120
130
|
15: {uid: 15, name: 'Jonas'},
|
|
@@ -124,7 +134,7 @@ output:
|
|
|
124
134
|
|
|
125
135
|
options are the same as the mapKey function
|
|
126
136
|
|
|
127
|
-
### array/mapPrimitive(val:any[], opts
|
|
137
|
+
### array/mapPrimitive(val:any[], opts?:{valtrim:false;keyround:false;valround:false;filter_fn:(el)=>boolean})
|
|
128
138
|
Map an array of primitives (number/string)
|
|
129
139
|
```typescript
|
|
130
140
|
import mapPrimitive from '@valkyriestudios/utils/array/mapPrimitive';
|
|
@@ -133,6 +143,12 @@ mapPrimitive(['hello', 'hello', 'foo', 'bar']); // {hello: 'hello', foo: 'foo',
|
|
|
133
143
|
mapPrimitive(['hello', ' hello', 'foo', ' foo'], {valtrim: true}); // {hello: 'hello', foo: 'foo'}
|
|
134
144
|
```
|
|
135
145
|
|
|
146
|
+
Allows filtering out unwanted values:
|
|
147
|
+
```typescript
|
|
148
|
+
import mapPrimitive from '@valkyriestudios/utils/array/mapPrimitive';
|
|
149
|
+
mapPrimitive([1,2,'bla', 3, false], {filter_fn: isNumber}); // {1: 1, 2: 2, 3: 3}
|
|
150
|
+
```
|
|
151
|
+
|
|
136
152
|
### array/groupBy(val:Record[], handler:Function|string)
|
|
137
153
|
Return a grouped object from an array. This function **will automatically filter out any non/empty objects**.
|
|
138
154
|
|
|
@@ -428,7 +444,15 @@ Available tokens for usage in spec:
|
|
|
428
444
|
| `Z` | Zone, does not allow full zone names, only Z or offsets | `Z` `+02:00` |
|
|
429
445
|
| `ISO` | Check for full iso date format, take note this enforces milliseconds as a requirement | 2024-02-03T10:28:30.000Z |
|
|
430
446
|
|
|
431
|
-
|
|
447
|
+
Allows for marking certain portions as optional by wrapping in `{...}`:
|
|
448
|
+
```typescript
|
|
449
|
+
import isFormat from '@valkyriestudios/utils/date/isFormat';
|
|
450
|
+
isFormat('2024-02-07', 'YYYY-MM-DD{THH:mm:ss}'); // true
|
|
451
|
+
isFormat('2024-02-07T14:50', 'YYYY-MM-DD{THH:mm:ss}'); // false, optional part passed but invalid
|
|
452
|
+
isFormat('2024-02-07T14:50:30', 'YYYY-MM-DD{THH:mm:ss}'); // true
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
Note: The `ISO` token is a shorthand for `YYYY-MM-DDTHH:mm:ss{.SSS}Z`
|
|
432
456
|
Note: You can escape characters by surrounding them with `[...]` in your spec, eg: `YYYY-[Q]Q` would check for example `2024-Q1`
|
|
433
457
|
|
|
434
458
|
### date/diff(val_a:Date, val_b:Date, key:string)
|
|
@@ -787,7 +811,7 @@ isFormData(new FormData()); // TRUE
|
|
|
787
811
|
isFormData({hi: 'there'}); // FALSE
|
|
788
812
|
```
|
|
789
813
|
|
|
790
|
-
### formdata/toObject(val:FormData, {raw?:string[]|true;single?:string[]} = {})
|
|
814
|
+
### formdata/toObject(val:FormData, {raw?:string[]|true;single?:string[];normalize_bool?:boolean;normalize_date?:bool;normalize_number?:bool} = {})
|
|
791
815
|
Converts an instance of FormData to an object
|
|
792
816
|
```typescript
|
|
793
817
|
import toObject from '@valkyriestudios/utils/formdata/toObject';
|
|
@@ -800,7 +824,7 @@ form.append('emptyField', '');
|
|
|
800
824
|
toObject(form); // {name: 'Alice', hobbies: ['reading', 'writing'], emptyField: ''}
|
|
801
825
|
```
|
|
802
826
|
|
|
803
|
-
Automatically converts strings to numbers
|
|
827
|
+
Automatically converts strings to numbers, dates, booleans and nests objects/arrays based on key structures:
|
|
804
828
|
```typescript
|
|
805
829
|
const form = new FormData();
|
|
806
830
|
form.append('user[0].name', 'Alice');
|
|
@@ -853,6 +877,24 @@ formData.append('action', 'reset');
|
|
|
853
877
|
toObject(formData, { single: ['status', 'action'] }) /* {status: 'inactive', action: 'reset'} */
|
|
854
878
|
```
|
|
855
879
|
|
|
880
|
+
Allows configuring what types of data to normalize:
|
|
881
|
+
```typescript
|
|
882
|
+
const form = new FormData();
|
|
883
|
+
form.append('pincode', '0123');
|
|
884
|
+
form.append('enabled', 'false');
|
|
885
|
+
form.append('config.isGood', 'true');
|
|
886
|
+
form.append('config.amount', ' 50 ');
|
|
887
|
+
|
|
888
|
+
toObject(form, {raw: ['pincode'], normalize_bool: false}); /* {
|
|
889
|
+
pincode: '0123',
|
|
890
|
+
enabled: 'false',
|
|
891
|
+
config: {
|
|
892
|
+
isGood: 'true',
|
|
893
|
+
amount: 50,
|
|
894
|
+
},
|
|
895
|
+
} */
|
|
896
|
+
```
|
|
897
|
+
|
|
856
898
|
### hash/guid()
|
|
857
899
|
Generate a unique identifier (guid) according to RFC4122
|
|
858
900
|
```typescript
|
package/array/mapFn.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
type MapOptions = {
|
|
2
2
|
/**
|
|
3
3
|
* Allow merging existing keys or not, if not keys will be overriden if they exist
|
|
4
4
|
* (default=false)
|
|
@@ -11,8 +11,8 @@ interface mapOptions {
|
|
|
11
11
|
* {12: {uid: 12, b: 'ho'}}
|
|
12
12
|
*/
|
|
13
13
|
merge?: boolean;
|
|
14
|
-
}
|
|
15
|
-
type
|
|
14
|
+
};
|
|
15
|
+
type MapFn<T extends Record<string, any>> = (entry: T) => (string | number | boolean);
|
|
16
16
|
/**
|
|
17
17
|
* Map an object array into a kv-object through a function that generates a key. Returning a non-string,
|
|
18
18
|
* non-numeric value from the function (eg: false) will filter out the object.
|
|
@@ -23,10 +23,10 @@ type mapFn<T extends Record<string, any>> = (entry: T) => (string | number | boo
|
|
|
23
23
|
* {12: {uid: 12, name: 'Peter'}, 15: {uid: 15, name: 'Jonas'}}
|
|
24
24
|
*
|
|
25
25
|
* @param {Record<string, any>[]} val - Array to map
|
|
26
|
-
* @param {
|
|
27
|
-
* @param {
|
|
26
|
+
* @param {MapFn} fn - Handler function which is run for each of the objects and should return a string or number
|
|
27
|
+
* @param {MapOptions?} opts - Options object to override built-in defaults
|
|
28
28
|
*
|
|
29
29
|
* @returns {Record<string, T>} KV-Map object
|
|
30
30
|
*/
|
|
31
|
-
declare function mapFn<T extends Record<string, any>>(arr: T[], fn:
|
|
31
|
+
declare function mapFn<T extends Record<string, any>>(arr: T[], fn: MapFn<T>, opts?: MapOptions): Record<string, T>;
|
|
32
32
|
export { mapFn, mapFn as default };
|
package/array/mapFn.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.mapFn = mapFn;
|
|
4
4
|
exports.default = mapFn;
|
|
5
|
+
const merge_1 = require("../object/merge");
|
|
5
6
|
function mapFn(arr, fn, opts) {
|
|
6
7
|
if ((!Array.isArray(arr) || !arr.length) ||
|
|
7
8
|
typeof fn !== 'function')
|
|
@@ -17,7 +18,7 @@ function mapFn(arr, fn, opts) {
|
|
|
17
18
|
if (!Number.isFinite(hash) && !(typeof hash === 'string' && hash.trim().length))
|
|
18
19
|
continue;
|
|
19
20
|
hash = hash + '';
|
|
20
|
-
map[hash] = MERGE &&
|
|
21
|
+
map[hash] = (MERGE && hash in map ? (0, merge_1.merge)(map[hash], el, { union: true }) : el);
|
|
21
22
|
}
|
|
22
23
|
return map;
|
|
23
24
|
}
|
package/array/mapKey.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
type MapOptions<T> = {
|
|
2
2
|
/**
|
|
3
3
|
* Allow merging existing keys or not, if not keys will be overriden if they exist
|
|
4
4
|
* (default=false)
|
|
@@ -11,7 +11,11 @@ interface mapOptions {
|
|
|
11
11
|
* {12: {uid: 12, b: 'ho'}}
|
|
12
12
|
*/
|
|
13
13
|
merge?: boolean;
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Pass a custom filter function which will be run in O(n) while iterating
|
|
16
|
+
*/
|
|
17
|
+
filter_fn?: (el: T) => boolean;
|
|
18
|
+
};
|
|
15
19
|
/**
|
|
16
20
|
* Map an object array into a kv-object by passing a common key that exists on the objects. Objects for
|
|
17
21
|
* which the key doesn't exist will be filtered out automatically
|
|
@@ -23,9 +27,9 @@ interface mapOptions {
|
|
|
23
27
|
*
|
|
24
28
|
* @param {Record<string,any>[]} val - Array to map
|
|
25
29
|
* @param {string} key - Key to map by
|
|
26
|
-
* @param {
|
|
30
|
+
* @param {MapOptions?} opts - Options object to override built-in defaults
|
|
27
31
|
*
|
|
28
32
|
* @returns {Record<string, T>} KV-Map object
|
|
29
33
|
*/
|
|
30
|
-
declare function mapKey<T extends Record<string, any>>(arr: T[], key: string, opts?:
|
|
34
|
+
declare function mapKey<T extends Record<string, any>>(arr: T[], key: string, opts?: MapOptions<T>): Record<string, T>;
|
|
31
35
|
export { mapKey, mapKey as default };
|
package/array/mapKey.js
CHANGED
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.mapKey = mapKey;
|
|
4
4
|
exports.default = mapKey;
|
|
5
|
+
const merge_1 = require("../object/merge");
|
|
5
6
|
function mapKey(arr, key, opts) {
|
|
6
7
|
if (!Array.isArray(arr) || typeof key !== 'string')
|
|
7
8
|
return {};
|
|
8
|
-
const key_s = key.trim();
|
|
9
|
-
if (!key_s.length)
|
|
10
|
-
return {};
|
|
11
9
|
const len = arr.length;
|
|
12
10
|
if (!len)
|
|
13
11
|
return {};
|
|
12
|
+
const key_s = key.trim();
|
|
13
|
+
if (!key_s.length)
|
|
14
|
+
return {};
|
|
15
|
+
const FILTER_FN = opts?.filter_fn;
|
|
14
16
|
const MERGE = opts?.merge === true;
|
|
15
17
|
const map = {};
|
|
16
18
|
for (let i = 0; i < len; i++) {
|
|
@@ -18,7 +20,9 @@ function mapKey(arr, key, opts) {
|
|
|
18
20
|
const el_key = el?.[key_s];
|
|
19
21
|
if (el_key === undefined)
|
|
20
22
|
continue;
|
|
21
|
-
|
|
23
|
+
if (FILTER_FN && !FILTER_FN(el))
|
|
24
|
+
continue;
|
|
25
|
+
map[el_key] = (MERGE && el_key in map ? (0, merge_1.merge)(map[el_key], el, { union: true }) : el);
|
|
22
26
|
}
|
|
23
27
|
return map;
|
|
24
28
|
}
|
package/array/mapPrimitive.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
type MapOptions = {
|
|
2
2
|
/**
|
|
3
3
|
* Automatically trim all string values
|
|
4
4
|
* (default=false)
|
|
@@ -18,7 +18,11 @@ interface mapOptions {
|
|
|
18
18
|
* eg: mapPrimitive([5.432, 5.4, 5.43, 4.2, 4.1], {keyround: true}) -> {5: 5.43, 4: 4.1}
|
|
19
19
|
*/
|
|
20
20
|
keyround?: boolean;
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Pass a custom filter function which will be run in O(n) while iterating
|
|
23
|
+
*/
|
|
24
|
+
filter_fn?: (el: unknown) => boolean;
|
|
25
|
+
};
|
|
22
26
|
type mapReturn = Record<string, string | number>;
|
|
23
27
|
/**
|
|
24
28
|
* Map an array of primitive values (numbers/strings) into a kv-object
|
|
@@ -30,7 +34,7 @@ type mapReturn = Record<string, string | number>;
|
|
|
30
34
|
* {hello: 'hello', foo: 'foo', bar: 'bar'}
|
|
31
35
|
*
|
|
32
36
|
* @param {unknown[]} val - Array to map
|
|
33
|
-
* @param {
|
|
37
|
+
* @param {MapOptions?} opts - Options object to override built-in defaults
|
|
34
38
|
*/
|
|
35
|
-
declare function mapPrimitive(arr: unknown[], opts?:
|
|
39
|
+
declare function mapPrimitive(arr: unknown[], opts?: MapOptions): mapReturn;
|
|
36
40
|
export { mapPrimitive, mapPrimitive as default };
|
package/array/mapPrimitive.js
CHANGED
|
@@ -10,11 +10,16 @@ function mapPrimitive(arr, opts = {}) {
|
|
|
10
10
|
const VALTRIM = opts?.valtrim === true;
|
|
11
11
|
const VALROUND = (0, isIntegerAboveOrEqual_1.isIntegerAboveOrEqual)(opts?.valround, 0)
|
|
12
12
|
? opts?.valround
|
|
13
|
-
: opts?.valround === true
|
|
13
|
+
: opts?.valround === true
|
|
14
|
+
? 0
|
|
15
|
+
: null;
|
|
14
16
|
const KEYROUND = opts?.keyround === true;
|
|
17
|
+
const FILTER_FN = opts?.filter_fn;
|
|
15
18
|
const map = {};
|
|
16
19
|
for (let i = 0; i < arr.length; i++) {
|
|
17
20
|
const el = arr[i];
|
|
21
|
+
if (FILTER_FN && !FILTER_FN(el))
|
|
22
|
+
continue;
|
|
18
23
|
if (typeof el === 'string') {
|
|
19
24
|
const trimmed = el.trim();
|
|
20
25
|
if (!trimmed)
|
|
@@ -22,11 +27,9 @@ function mapPrimitive(arr, opts = {}) {
|
|
|
22
27
|
map[trimmed] = VALTRIM ? trimmed : el;
|
|
23
28
|
}
|
|
24
29
|
else if (Number.isFinite(el)) {
|
|
25
|
-
map[`${KEYROUND ? Math.round(el) : el}`] = VALROUND ===
|
|
30
|
+
map[`${KEYROUND ? Math.round(el) : el}`] = VALROUND === null
|
|
26
31
|
? el
|
|
27
|
-
:
|
|
28
|
-
? Math.round(el)
|
|
29
|
-
: (0, round_1.round)(el, VALROUND);
|
|
32
|
+
: (0, round_1.round)(el, VALROUND);
|
|
30
33
|
}
|
|
31
34
|
}
|
|
32
35
|
return map;
|
package/date/format.js
CHANGED
|
@@ -59,7 +59,7 @@ function toZone(d, zone) {
|
|
|
59
59
|
return new Date(time + zone_offset_cache[ckey]);
|
|
60
60
|
let zone_time = null;
|
|
61
61
|
try {
|
|
62
|
-
zone_time = new Date(d.toLocaleString(DEFAULT_LOCALE, { timeZone: zone })).getTime();
|
|
62
|
+
zone_time = new Date(d.toLocaleString(DEFAULT_LOCALE, { timeZone: zone })).getTime() + d.getMilliseconds();
|
|
63
63
|
}
|
|
64
64
|
catch {
|
|
65
65
|
throw new Error(`format: Invalid zone passed - ${zone}`);
|
|
@@ -149,7 +149,7 @@ function getSpecChain(spec) {
|
|
|
149
149
|
let repl_len = 0;
|
|
150
150
|
if (base.indexOf('[') >= 0) {
|
|
151
151
|
base = base.replace(ESCAPE_RGX, match => {
|
|
152
|
-
const escape_token = '$
|
|
152
|
+
const escape_token = '$' + repl_len++ + '$';
|
|
153
153
|
repl.push([escape_token, match.slice(1, -1)]);
|
|
154
154
|
return escape_token;
|
|
155
155
|
});
|
|
@@ -171,7 +171,7 @@ function getSpecChain(spec) {
|
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
173
|
const chain_len = chain.length;
|
|
174
|
-
const result = chain_len ? { base, chain, chain_len, repl
|
|
174
|
+
const result = chain_len ? { base, chain, chain_len, repl } : null;
|
|
175
175
|
spec_cache[spec] = result;
|
|
176
176
|
return result;
|
|
177
177
|
}
|
|
@@ -190,15 +190,17 @@ function format(val, spec, locale = DEFAULT_LOCALE, zone = DEFAULT_TZ, sow = DEF
|
|
|
190
190
|
return n_val.toISOString();
|
|
191
191
|
const d = toZone(n_val, zone);
|
|
192
192
|
let base = n_spec.base;
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const
|
|
193
|
+
const repl = [...n_spec.repl];
|
|
194
|
+
let repl_len = n_spec.repl.length;
|
|
195
|
+
for (let i = 0; i < n_spec.chain_len; i++) {
|
|
196
|
+
const el = n_spec.chain[i];
|
|
197
|
+
let pos = base.indexOf(el[0]);
|
|
198
|
+
const token_val = el[1](d, locale, sow);
|
|
197
199
|
while (pos !== -1) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
pos = base.indexOf(
|
|
200
|
+
const key = '$' + repl_len++ + '$';
|
|
201
|
+
repl.push([key, token_val]);
|
|
202
|
+
base = base.slice(0, pos) + key + base.slice(pos + el[2]);
|
|
203
|
+
pos = base.indexOf(el[0], pos + el[2]);
|
|
202
204
|
}
|
|
203
205
|
}
|
|
204
206
|
for (let i = 0; i < repl_len; i++) {
|
package/date/isFormat.js
CHANGED
|
@@ -45,10 +45,10 @@ const TOKENS = [
|
|
|
45
45
|
}],
|
|
46
46
|
];
|
|
47
47
|
const SPEC_ALIASES = {
|
|
48
|
-
ISO: 'YYYY-MM-DDTHH:mm:ss.
|
|
48
|
+
ISO: 'YYYY-MM-DDTHH:mm:ss{.SSS}Z',
|
|
49
49
|
};
|
|
50
50
|
const spec_pat_cache = {};
|
|
51
|
-
function compileSpec(spec) {
|
|
51
|
+
function compileSpec(spec, is_chunk = false) {
|
|
52
52
|
if (spec in spec_pat_cache)
|
|
53
53
|
return spec_pat_cache[spec];
|
|
54
54
|
const tokens = [];
|
|
@@ -62,6 +62,15 @@ function compileSpec(spec) {
|
|
|
62
62
|
pat += spec.slice(cursor + 1, end_idx).replace(SPECIAL_CHARS, '\\$&');
|
|
63
63
|
cursor = end_idx + 1;
|
|
64
64
|
}
|
|
65
|
+
else if (spec[cursor] === '{') {
|
|
66
|
+
const end_idx = spec.indexOf('}', cursor);
|
|
67
|
+
if (end_idx === -1)
|
|
68
|
+
throw new Error('isDateFormat: Unmatched { in format string');
|
|
69
|
+
const compiled = compileSpec(spec.slice(cursor + 1, end_idx), true);
|
|
70
|
+
pat += '(?:' + compiled.rgx.source + ')?';
|
|
71
|
+
tokens.push(...compiled.tokens);
|
|
72
|
+
cursor = end_idx + 1;
|
|
73
|
+
}
|
|
65
74
|
else {
|
|
66
75
|
const token_idx = TOKENS.findIndex(([token_key]) => spec.startsWith(token_key, cursor));
|
|
67
76
|
if (token_idx >= 0) {
|
|
@@ -76,7 +85,7 @@ function compileSpec(spec) {
|
|
|
76
85
|
}
|
|
77
86
|
}
|
|
78
87
|
}
|
|
79
|
-
spec_pat_cache[spec] = { rgx: RegExp('^' + pat + '$'), tokens };
|
|
88
|
+
spec_pat_cache[spec] = { rgx: is_chunk ? RegExp(pat) : RegExp('^' + pat + '$'), tokens };
|
|
80
89
|
return spec_pat_cache[spec];
|
|
81
90
|
}
|
|
82
91
|
function isDateFormat(input, spec) {
|
|
@@ -95,7 +104,8 @@ function isDateFormat(input, spec) {
|
|
|
95
104
|
const matches = patMatch.slice(1);
|
|
96
105
|
const context = {};
|
|
97
106
|
for (let i = 0; i < matches.length; i++) {
|
|
98
|
-
|
|
107
|
+
const match = matches[i];
|
|
108
|
+
if (match !== undefined && !TOKENS[tokens[i]][2](match, context))
|
|
99
109
|
return false;
|
|
100
110
|
}
|
|
101
111
|
const { is12, day, month, year } = context;
|
package/deep/set.js
CHANGED
|
@@ -21,8 +21,6 @@ function deepSet(obj, path, value, define = false) {
|
|
|
21
21
|
.split('.');
|
|
22
22
|
const last_part_ix = parts.length - 1;
|
|
23
23
|
for (let i = 0; i < last_part_ix; i++) {
|
|
24
|
-
if (parts[i] === '')
|
|
25
|
-
continue;
|
|
26
24
|
if (Array.isArray(obj)) {
|
|
27
25
|
const idx = parseInt(parts[i]);
|
|
28
26
|
if (!Number.isInteger(idx) || idx < 0)
|
package/equal.js
CHANGED
|
@@ -2,41 +2,69 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.equal = equal;
|
|
4
4
|
exports.default = equal;
|
|
5
|
-
const isNumericalNaN_1 = require("./number/isNumericalNaN");
|
|
6
5
|
function isArrayEqual(a, b) {
|
|
7
6
|
const a_len = a.length;
|
|
8
7
|
if (a_len !== b.length)
|
|
9
8
|
return false;
|
|
10
9
|
for (let i = a_len - 1; i >= 0; i--) {
|
|
11
|
-
if (equal(a[i], b[i]))
|
|
12
|
-
|
|
13
|
-
return false;
|
|
10
|
+
if (!equal(a[i], b[i]))
|
|
11
|
+
return false;
|
|
14
12
|
}
|
|
15
13
|
return true;
|
|
16
14
|
}
|
|
17
15
|
function isObjectEqual(a, b) {
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
16
|
+
const a_keys = Object.keys(a);
|
|
17
|
+
const b_keys = Object.keys(b);
|
|
18
|
+
const a_len = a_keys.length;
|
|
19
|
+
if (a_len !== b_keys.length)
|
|
21
20
|
return false;
|
|
22
|
-
for (let i =
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
for (let i = a_len - 1; i >= 0; i--) {
|
|
22
|
+
const key = a_keys[i];
|
|
23
|
+
if (!equal(a[key], b[key]))
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
function isMapEqual(a, b) {
|
|
29
|
+
if (a.size !== b.size)
|
|
25
30
|
return false;
|
|
31
|
+
for (const [key, value] of a) {
|
|
32
|
+
if (!b.has(key) || !equal(value, b.get(key)))
|
|
33
|
+
return false;
|
|
26
34
|
}
|
|
27
35
|
return true;
|
|
28
36
|
}
|
|
29
37
|
function equal(a, b) {
|
|
30
|
-
if (a
|
|
31
|
-
return
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
if (a === b)
|
|
39
|
+
return true;
|
|
40
|
+
switch (typeof a) {
|
|
41
|
+
case 'number':
|
|
42
|
+
return Number.isNaN(b) && Number.isNaN(a);
|
|
43
|
+
case 'object': {
|
|
44
|
+
if (a === null || b === null)
|
|
45
|
+
return false;
|
|
46
|
+
if (Array.isArray(a))
|
|
47
|
+
return Array.isArray(b) && isArrayEqual(a, b);
|
|
48
|
+
const proto_a = Object.prototype.toString.call(a);
|
|
49
|
+
const proto_b = Object.prototype.toString.call(b);
|
|
50
|
+
if (proto_a !== proto_b)
|
|
51
|
+
return false;
|
|
52
|
+
switch (proto_a) {
|
|
53
|
+
case '[object Date]':
|
|
54
|
+
return a.valueOf() === b.valueOf();
|
|
55
|
+
case '[object Object]':
|
|
56
|
+
return isObjectEqual(a, b);
|
|
57
|
+
case '[object Error]':
|
|
58
|
+
return a.name === b.name && a.message === b.message;
|
|
59
|
+
case '[object RegExp]':
|
|
60
|
+
return String(a) === String(b);
|
|
61
|
+
case '[object Map]':
|
|
62
|
+
return isMapEqual(a, b);
|
|
63
|
+
default:
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
default:
|
|
68
|
+
return false;
|
|
38
69
|
}
|
|
39
|
-
if ((0, isNumericalNaN_1.isNumericalNaN)(a))
|
|
40
|
-
return (0, isNumericalNaN_1.isNumericalNaN)(b);
|
|
41
|
-
return a === b;
|
|
42
70
|
}
|
package/formdata/toObject.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
type
|
|
1
|
+
type ToObjectConfig = {
|
|
2
2
|
/**
|
|
3
3
|
* Pass array of keys that should not be normalized into number/bool when seen
|
|
4
4
|
*/
|
|
@@ -7,6 +7,18 @@ type toObjectConfig = {
|
|
|
7
7
|
* Pass array of keys that should only have a single value (e.g., 'action')
|
|
8
8
|
*/
|
|
9
9
|
single?: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Whether or not we should normalize booleans, defaults to true if not set
|
|
12
|
+
*/
|
|
13
|
+
normalize_bool?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Whether or not we should normalize dates, defaults to true if not set
|
|
16
|
+
*/
|
|
17
|
+
normalize_date?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Whether or not we should normalize numbers, defaults to true if not set
|
|
20
|
+
*/
|
|
21
|
+
normalize_number?: boolean;
|
|
10
22
|
};
|
|
11
23
|
/**
|
|
12
24
|
* Converts a FormData instance to a json object
|
|
@@ -21,7 +33,7 @@ type toObjectConfig = {
|
|
|
21
33
|
* {name: 'Alice', hobbies: ['reading', 'writing'], emptyField: ''}
|
|
22
34
|
*
|
|
23
35
|
* @param {FormData} val - FormData instance to convert to an object
|
|
24
|
-
* @param {}
|
|
36
|
+
* @param {ToObjectConfig?} config - Config for conversion
|
|
25
37
|
*/
|
|
26
|
-
declare function toObject<T extends Record<string, unknown>>(form: FormData, config?:
|
|
38
|
+
declare function toObject<T extends Record<string, unknown>>(form: FormData, config?: ToObjectConfig): T;
|
|
27
39
|
export { toObject, toObject as default };
|
package/formdata/toObject.js
CHANGED
|
@@ -2,36 +2,33 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.toObject = toObject;
|
|
4
4
|
exports.default = toObject;
|
|
5
|
+
const isFormat_1 = require("../date/isFormat");
|
|
5
6
|
const RGX_CLOSE = /\]/g;
|
|
6
7
|
function assignValue(acc, rawkey, value, single) {
|
|
7
8
|
let cursor = acc;
|
|
8
9
|
const keys = rawkey.replace(RGX_CLOSE, '').split(/\[|\./);
|
|
9
|
-
|
|
10
|
+
const keys_len = keys.length;
|
|
11
|
+
for (let i = 0; i < keys_len; i++) {
|
|
10
12
|
const key = keys[i];
|
|
11
|
-
if (i ===
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
if (i === keys_len - 1) {
|
|
14
|
+
const cursor_val = cursor[key];
|
|
15
|
+
if (cursor_val !== undefined && (!single || !single.has(key))) {
|
|
16
|
+
if (Array.isArray(cursor_val)) {
|
|
14
17
|
cursor[key].push(value);
|
|
15
18
|
}
|
|
16
19
|
else {
|
|
17
|
-
cursor[key] = [
|
|
20
|
+
cursor[key] = [cursor_val, value];
|
|
18
21
|
}
|
|
19
22
|
}
|
|
20
23
|
else {
|
|
21
24
|
cursor[key] = value;
|
|
22
25
|
}
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
if (Array.isArray(cursor)) {
|
|
26
|
-
const index = Number(key);
|
|
27
|
-
if (!cursor[index])
|
|
28
|
-
cursor[index] = isNaN(Number(keys[i + 1])) ? {} : [];
|
|
29
|
-
cursor = cursor[index];
|
|
30
26
|
}
|
|
31
27
|
else {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
const n_key = Array.isArray(cursor) ? Number(key) : key;
|
|
29
|
+
if (!cursor[n_key])
|
|
30
|
+
cursor[n_key] = isNaN(Number(keys[i + 1])) ? {} : [];
|
|
31
|
+
cursor = cursor[n_key];
|
|
35
32
|
}
|
|
36
33
|
}
|
|
37
34
|
}
|
|
@@ -40,20 +37,36 @@ function toObject(form, config) {
|
|
|
40
37
|
throw new Error('formdata/toObject: Value is not an instance of FormData');
|
|
41
38
|
const set = config?.raw === true ? true : new Set(Array.isArray(config?.raw) ? config?.raw : []);
|
|
42
39
|
const single = Array.isArray(config?.single) && config?.single.length ? new Set(config.single) : null;
|
|
40
|
+
const nBool = config?.normalize_bool !== false;
|
|
41
|
+
const nDate = config?.normalize_date !== false;
|
|
42
|
+
const nNumber = config?.normalize_number !== false;
|
|
43
43
|
const acc = {};
|
|
44
44
|
form.forEach((value, key) => {
|
|
45
|
-
let normalizedValue = value;
|
|
46
45
|
if (set !== true && typeof value === 'string' && !set.has(key)) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
46
|
+
if (nBool) {
|
|
47
|
+
const lower = value.toLowerCase();
|
|
48
|
+
if (lower === 'true') {
|
|
49
|
+
assignValue(acc, key, true, single);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
else if (lower === 'false') {
|
|
53
|
+
assignValue(acc, key, false, single);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const trimmed = value.trim();
|
|
58
|
+
if (trimmed.length) {
|
|
59
|
+
if (nNumber && !isNaN(Number(value))) {
|
|
60
|
+
assignValue(acc, key, Number(value), single);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (nDate && (0, isFormat_1.isDateFormat)(value, 'ISO')) {
|
|
64
|
+
assignValue(acc, key, new Date(value), single);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
55
68
|
}
|
|
56
|
-
assignValue(acc, key,
|
|
69
|
+
assignValue(acc, key, value, single);
|
|
57
70
|
});
|
|
58
71
|
return acc;
|
|
59
72
|
}
|
package/index.d.ts
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
declare module "number/isNumericalNaN" {
|
|
2
|
-
function isNumericalNaN(val: unknown): boolean;
|
|
3
|
-
export { isNumericalNaN, isNumericalNaN as default };
|
|
4
|
-
}
|
|
5
1
|
declare module "equal" {
|
|
6
2
|
function equal(a: any, b: any): boolean;
|
|
7
3
|
export { equal, equal as default };
|
|
@@ -40,29 +36,38 @@ declare module "array/join" {
|
|
|
40
36
|
function join(val: unknown[], opts?: joinOptions): string;
|
|
41
37
|
export { join, join as default };
|
|
42
38
|
}
|
|
39
|
+
declare module "object/merge" {
|
|
40
|
+
type MergeOptions = {
|
|
41
|
+
union?: boolean;
|
|
42
|
+
};
|
|
43
|
+
function merge(target: Record<string, any>, source?: Record<string, any> | Record<string, any>[], opts?: MergeOptions): Record<string, any>;
|
|
44
|
+
export { merge, merge as default };
|
|
45
|
+
}
|
|
43
46
|
declare module "array/mapFn" {
|
|
44
|
-
|
|
47
|
+
type MapOptions = {
|
|
45
48
|
merge?: boolean;
|
|
46
|
-
}
|
|
47
|
-
type
|
|
48
|
-
function mapFn<T extends Record<string, any>>(arr: T[], fn:
|
|
49
|
+
};
|
|
50
|
+
type MapFn<T extends Record<string, any>> = (entry: T) => (string | number | boolean);
|
|
51
|
+
function mapFn<T extends Record<string, any>>(arr: T[], fn: MapFn<T>, opts?: MapOptions): Record<string, T>;
|
|
49
52
|
export { mapFn, mapFn as default };
|
|
50
53
|
}
|
|
51
54
|
declare module "array/mapKey" {
|
|
52
|
-
|
|
55
|
+
type MapOptions<T> = {
|
|
53
56
|
merge?: boolean;
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
filter_fn?: (el: T) => boolean;
|
|
58
|
+
};
|
|
59
|
+
function mapKey<T extends Record<string, any>>(arr: T[], key: string, opts?: MapOptions<T>): Record<string, T>;
|
|
56
60
|
export { mapKey, mapKey as default };
|
|
57
61
|
}
|
|
58
62
|
declare module "array/mapPrimitive" {
|
|
59
|
-
|
|
63
|
+
type MapOptions = {
|
|
60
64
|
valtrim?: boolean;
|
|
61
65
|
valround?: boolean | number;
|
|
62
66
|
keyround?: boolean;
|
|
63
|
-
|
|
67
|
+
filter_fn?: (el: unknown) => boolean;
|
|
68
|
+
};
|
|
64
69
|
type mapReturn = Record<string, string | number>;
|
|
65
|
-
function mapPrimitive(arr: unknown[], opts?:
|
|
70
|
+
function mapPrimitive(arr: unknown[], opts?: MapOptions): mapReturn;
|
|
66
71
|
export { mapPrimitive, mapPrimitive as default };
|
|
67
72
|
}
|
|
68
73
|
declare module "object/isNotEmpty" {
|
|
@@ -229,11 +234,14 @@ declare module "formdata/is" {
|
|
|
229
234
|
export { isFormData, isFormData as default };
|
|
230
235
|
}
|
|
231
236
|
declare module "formdata/toObject" {
|
|
232
|
-
type
|
|
237
|
+
type ToObjectConfig = {
|
|
233
238
|
raw?: string[] | true;
|
|
234
239
|
single?: string[];
|
|
240
|
+
normalize_bool?: boolean;
|
|
241
|
+
normalize_date?: boolean;
|
|
242
|
+
normalize_number?: boolean;
|
|
235
243
|
};
|
|
236
|
-
function toObject<T extends Record<string, unknown>>(form: FormData, config?:
|
|
244
|
+
function toObject<T extends Record<string, unknown>>(form: FormData, config?: ToObjectConfig): T;
|
|
237
245
|
export { toObject, toObject as default };
|
|
238
246
|
}
|
|
239
247
|
declare module "formdata/index" {
|
|
@@ -309,13 +317,6 @@ declare module "object/define" {
|
|
|
309
317
|
};
|
|
310
318
|
export { define, define as default };
|
|
311
319
|
}
|
|
312
|
-
declare module "object/merge" {
|
|
313
|
-
type MergeOptions = {
|
|
314
|
-
union?: boolean;
|
|
315
|
-
};
|
|
316
|
-
function merge(target: Record<string, any>, source?: Record<string, any> | Record<string, any>[], opts?: MergeOptions): Record<string, any>;
|
|
317
|
-
export { merge, merge as default };
|
|
318
|
-
}
|
|
319
320
|
declare module "deep/get" {
|
|
320
321
|
type ObjectType = {
|
|
321
322
|
[key: string]: any;
|
|
@@ -385,6 +386,10 @@ declare module "number/isIntegerBelowOrEqual" {
|
|
|
385
386
|
function isIntegerBelowOrEqual(val: unknown, ref: number): val is number;
|
|
386
387
|
export { isIntegerBelowOrEqual, isIntegerBelowOrEqual as default };
|
|
387
388
|
}
|
|
389
|
+
declare module "number/isNumericalNaN" {
|
|
390
|
+
function isNumericalNaN(val: unknown): boolean;
|
|
391
|
+
export { isNumericalNaN, isNumericalNaN as default };
|
|
392
|
+
}
|
|
388
393
|
declare module "number/randomBetween" {
|
|
389
394
|
function randomBetween(min?: number, max?: number): number;
|
|
390
395
|
export { randomBetween, randomBetween as default };
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{ "name": "@valkyriestudios/utils", "version": "12.
|
|
1
|
+
{ "name": "@valkyriestudios/utils", "version": "12.25.1", "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" }
|