@trackunit/shared-utils 1.8.50 → 1.8.54
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/index.cjs.js +101 -57
- package/index.esm.js +100 -57
- package/package.json +3 -2
- package/src/DateTimeFormat.d.ts +8 -7
- package/src/DateUtils.d.ts +2 -2
- package/src/Maybe.d.ts +1 -1
- package/src/arrayUtils.d.ts +4 -2
- package/src/deepPartial.d.ts +4 -4
- package/src/enumUtils.d.ts +3 -3
- package/src/fastArrayOperations.d.ts +6 -4
- package/src/filter.d.ts +5 -5
- package/src/groupBy/groupBy.d.ts +6 -6
- package/src/idUtils.d.ts +2 -2
- package/src/imageTools.d.ts +1 -1
- package/src/objectUtils.d.ts +8 -7
- package/src/pictureUtils/pictureUtils.d.ts +2 -2
- package/src/sorting/sorting.d.ts +2 -2
- package/src/svgTools.d.ts +7 -5
- package/src/typeUtils.d.ts +1 -1
package/index.cjs.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var zod = require('zod');
|
|
3
4
|
var uuid = require('uuid');
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -19,7 +20,7 @@ const formatAddress = (address) => {
|
|
|
19
20
|
* @example formatCoordinates({ latitude: 1.234567, longitude: 2.345678 }) // "1.234567, 2.345678"
|
|
20
21
|
*/
|
|
21
22
|
const formatCoordinates = (coordinates, toFixed) => {
|
|
22
|
-
return toFixed
|
|
23
|
+
return toFixed !== undefined
|
|
23
24
|
? `${Number(coordinates.latitude.toFixed(toFixed))}, ${Number(coordinates.longitude.toFixed(toFixed))}`
|
|
24
25
|
: `${coordinates.latitude}, ${coordinates.longitude}`;
|
|
25
26
|
};
|
|
@@ -45,7 +46,7 @@ const nonNullable = (value) => {
|
|
|
45
46
|
*
|
|
46
47
|
*/
|
|
47
48
|
const truthy = (value) => {
|
|
48
|
-
return
|
|
49
|
+
return Boolean(value);
|
|
49
50
|
};
|
|
50
51
|
/**
|
|
51
52
|
* Converts an object into an array of properly typed key-value pairs.
|
|
@@ -59,7 +60,9 @@ const truthy = (value) => {
|
|
|
59
60
|
* @param {TObject} object - The object to convert.
|
|
60
61
|
* @template TObject - The type of the object.
|
|
61
62
|
*/
|
|
62
|
-
const objectEntries = (object) =>
|
|
63
|
+
const objectEntries = (object) =>
|
|
64
|
+
// eslint-disable-next-line local-rules/no-typescript-assertion
|
|
65
|
+
Object.entries(object);
|
|
63
66
|
/**
|
|
64
67
|
* Converts an array of key-value pairs into an object.
|
|
65
68
|
* This is a type-faithful alternative to Object.fromEntries().
|
|
@@ -68,6 +71,7 @@ const objectEntries = (object) => Object.entries(object);
|
|
|
68
71
|
* @template TEntries - The type of the entries array.
|
|
69
72
|
*/
|
|
70
73
|
const objectFromEntries = (entries) => {
|
|
74
|
+
// eslint-disable-next-line local-rules/no-typescript-assertion
|
|
71
75
|
return Object.fromEntries(entries);
|
|
72
76
|
};
|
|
73
77
|
/**
|
|
@@ -84,6 +88,7 @@ const objectFromEntries = (entries) => {
|
|
|
84
88
|
* @returns {(keyof TObject)[]} An array of the keys of the object.
|
|
85
89
|
*/
|
|
86
90
|
const objectKeys = (object) => {
|
|
91
|
+
// eslint-disable-next-line local-rules/no-typescript-assertion
|
|
87
92
|
return Object.keys(object).map(key => key);
|
|
88
93
|
};
|
|
89
94
|
/**
|
|
@@ -95,17 +100,20 @@ const objectKeys = (object) => {
|
|
|
95
100
|
*
|
|
96
101
|
* @template TObject - The type of the object.
|
|
97
102
|
* @param {TObject} object - The object to get the values from.
|
|
98
|
-
* @returns {
|
|
103
|
+
* @returns {Array} An array of the values of the object.
|
|
99
104
|
*/
|
|
100
|
-
const objectValues = (object) =>
|
|
105
|
+
const objectValues = (object) =>
|
|
106
|
+
// eslint-disable-next-line local-rules/no-typescript-assertion
|
|
107
|
+
Object.values(object);
|
|
101
108
|
|
|
102
109
|
/**
|
|
103
110
|
* Returns a new array with the items from the previous array and the new array, but only if the item's key is unique.
|
|
104
111
|
*
|
|
112
|
+
* @template TObject - the type of the items in the array
|
|
105
113
|
* @param key The key to use to determine uniqueness
|
|
106
114
|
* @param previous The previous array of items to merge with the new array
|
|
107
115
|
* @param newArray The new array of items to merge with the previous array
|
|
108
|
-
* @returns A new array with the items from the previous array and the new array, but only if the item's key is unique.
|
|
116
|
+
* @returns {Array<TObject>} A new array with the items from the previous array and the new array, but only if the item's key is unique.
|
|
109
117
|
*/
|
|
110
118
|
const unionArraysByKey = (key, previous, newArray) => {
|
|
111
119
|
const previousIds = previous?.map(edge => edge[key]) || [];
|
|
@@ -127,8 +135,9 @@ const isArrayEqual = (a, b) => a.length === b.length && a.every((v, i) => v ===
|
|
|
127
135
|
/**
|
|
128
136
|
* Checks if an array is not empty.
|
|
129
137
|
*
|
|
138
|
+
* @template TItem
|
|
130
139
|
* @param array - The array to check.
|
|
131
|
-
* @returns The array if not empty, otherwise undefined.
|
|
140
|
+
* @returns {Array<TItem> | undefined} The array if not empty, otherwise undefined.
|
|
132
141
|
*/
|
|
133
142
|
const arrayNotEmpty = (array) => array && array.length > 0 ? array : undefined;
|
|
134
143
|
|
|
@@ -160,14 +169,13 @@ const size = {
|
|
|
160
169
|
LARGE: "large",
|
|
161
170
|
};
|
|
162
171
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
})(exports.HoursAndMinutesFormat || (exports.HoursAndMinutesFormat = {}));
|
|
172
|
+
const HoursAndMinutesFormat = {
|
|
173
|
+
HOURS_MIN_SEC_LONG: "HOURS_MIN_SEC_LONG", // h [h] mm [min] ss [sec]
|
|
174
|
+
HOURS_MIN_SEC: "HOURS_MIN_SEC", // h[h] m[m] s[s]
|
|
175
|
+
HOURS_MIN_LONG: "HOURS_MIN_LONG", // h [hrs] mm [min]
|
|
176
|
+
HOURS_MIN: "HOURS_MIN", // h[h] m[m]
|
|
177
|
+
HOURS_LONG: "HOURS_LONG", // h [hrs]
|
|
178
|
+
};
|
|
171
179
|
const DateTimeFormat = {
|
|
172
180
|
DATE: {
|
|
173
181
|
selectFormat: "dateOnly",
|
|
@@ -207,7 +215,7 @@ const DateTimeFormat = {
|
|
|
207
215
|
*/
|
|
208
216
|
const getISOStringFromDate = (date) => {
|
|
209
217
|
try {
|
|
210
|
-
if (
|
|
218
|
+
if (date === null || date === undefined) {
|
|
211
219
|
return null;
|
|
212
220
|
}
|
|
213
221
|
return new Date(date).toISOString();
|
|
@@ -278,7 +286,7 @@ function doNothing() {
|
|
|
278
286
|
*/
|
|
279
287
|
const enumFromValue = (val, _enum) => {
|
|
280
288
|
const enumName = objectKeys(_enum).find(k => _enum[k] === val);
|
|
281
|
-
if (
|
|
289
|
+
if (enumName === undefined) {
|
|
282
290
|
throw Error(`Unknown enum value: ${val} expect one of: ${JSON.stringify(objectValues(_enum))}`);
|
|
283
291
|
}
|
|
284
292
|
return _enum[enumName];
|
|
@@ -308,7 +316,7 @@ const enumOrUndefinedFromValue = (_enum, val) => {
|
|
|
308
316
|
*/
|
|
309
317
|
const enumFromValueTypesafe = (val, _enum) => {
|
|
310
318
|
const enumName = objectKeys(_enum).find(k => _enum[k] === val);
|
|
311
|
-
if (
|
|
319
|
+
if (enumName === undefined) {
|
|
312
320
|
throw Error(`Unknown enum value: ${String(val)} expect one of: ${JSON.stringify(objectValues(_enum))}`);
|
|
313
321
|
}
|
|
314
322
|
return _enum[enumName];
|
|
@@ -333,9 +341,10 @@ const exhaustiveCheck = (value) => {
|
|
|
333
341
|
|
|
334
342
|
/**
|
|
335
343
|
*
|
|
344
|
+
* @template TItem
|
|
336
345
|
* @param arr1 An array of string or numbers
|
|
337
346
|
* @param arr2 An array of string or numbers
|
|
338
|
-
* @returns The elements shared between arr1 and arr2.
|
|
347
|
+
* @returns {Array<TItem>} The elements shared between arr1 and arr2.
|
|
339
348
|
*/
|
|
340
349
|
function intersection(arr1, arr2) {
|
|
341
350
|
const a = new Set(arr1);
|
|
@@ -345,9 +354,10 @@ function intersection(arr1, arr2) {
|
|
|
345
354
|
}
|
|
346
355
|
/**
|
|
347
356
|
*
|
|
357
|
+
* @template TItem
|
|
348
358
|
* @param arr1 An array of string or numbers
|
|
349
359
|
* @param arr2 An array of string or numbers
|
|
350
|
-
* @returns The elements present in arr1 but not arr2.
|
|
360
|
+
* @returns {Array<TItem>} The elements present in arr1 but not arr2.
|
|
351
361
|
*/
|
|
352
362
|
function difference(arr1, arr2) {
|
|
353
363
|
const a = new Set(arr1);
|
|
@@ -410,11 +420,11 @@ searchTerm) => {
|
|
|
410
420
|
/**
|
|
411
421
|
* Group an array of items by a key.
|
|
412
422
|
*
|
|
413
|
-
* @template
|
|
414
|
-
* @template
|
|
415
|
-
* @param {
|
|
416
|
-
* @param {(item:
|
|
417
|
-
* @returns {Map<
|
|
423
|
+
* @template TItem The type of items in the list.
|
|
424
|
+
* @template KeyGroup The type of the key.
|
|
425
|
+
* @param {TItem[]} list The array of items to group.
|
|
426
|
+
* @param {(item: TItem) => KeyGroup} getKey A function to get the key to group by.
|
|
427
|
+
* @returns {Map<KeyGroup, TItem[]>} A map of the items grouped by the key.
|
|
418
428
|
* @example
|
|
419
429
|
* groupBy([{ name: "John", surname: "Doe" }, { name: "Jane", surname: "Doe" }], p => p.surname)
|
|
420
430
|
* Returns: Map { "Doe" => [{ name: "John", surname: "Doe" }, { name: "Jane", surname: "Doe" }] }
|
|
@@ -496,7 +506,7 @@ const zeroPadToUUIDLength = (input, maxLength) => input.length < maxLength ? zer
|
|
|
496
506
|
*/
|
|
497
507
|
const toUUID = (value) => {
|
|
498
508
|
if (isUUID(value.toString())) {
|
|
499
|
-
return value;
|
|
509
|
+
return value.toString();
|
|
500
510
|
}
|
|
501
511
|
// A valid UUID length is 36 but we subtract 4 because we insert 4 hyphens.
|
|
502
512
|
const id = zeroPadToUUIDLength(value.toString(), 32);
|
|
@@ -519,7 +529,7 @@ const toID = (uuid) => Number(uuid.replace(/-/g, "").replace(/^0+/, ""));
|
|
|
519
529
|
* A helper function that converts a list of UUID to a list of valid machine IDs.
|
|
520
530
|
* NOTE that NaN will be removed so list cannot be expected to be of same size.
|
|
521
531
|
*
|
|
522
|
-
* @param
|
|
532
|
+
* @param ids The list of UUIDs that should be converted to a list of valid machine IDs
|
|
523
533
|
*/
|
|
524
534
|
const toIDs = (ids) => ids?.filter(id => !isNaN(toID(id))).map(id => toID(id)) ?? [];
|
|
525
535
|
|
|
@@ -532,7 +542,7 @@ const toIDs = (ids) => ids?.filter(id => !isNaN(toID(id))).map(id => toID(id)) ?
|
|
|
532
542
|
* @param {number} options.maxHeight - The maximum height of the converted PNG image.
|
|
533
543
|
* @param {number} options.maxWidth - The maximum width of the converted PNG image.
|
|
534
544
|
* @param {number} options.idealArea - The ideal area of the converted PNG image.
|
|
535
|
-
* @returns The converted PNG image as a data URL.
|
|
545
|
+
* @returns {string} The converted PNG image as a data URL.
|
|
536
546
|
* @throws Error if unable to get the canvas context.
|
|
537
547
|
*/
|
|
538
548
|
const toPNG = ({ image, dimensions, maxHeight, maxWidth, idealArea, }) => {
|
|
@@ -647,13 +657,15 @@ const fetchImageAsBase64 = async (url) => {
|
|
|
647
657
|
/**
|
|
648
658
|
* Deletes all undefined keys from an object.
|
|
649
659
|
*
|
|
650
|
-
* @
|
|
651
|
-
* @
|
|
660
|
+
* @template TObject
|
|
661
|
+
* @param {TObject} obj The object to delete undefined keys from.
|
|
652
662
|
* @example deleteUndefinedKeys({ a: 1, b: undefined }) // { a: 1 }
|
|
663
|
+
* @returns {TObject} The object without undefined keys.
|
|
653
664
|
*/
|
|
654
665
|
const deleteUndefinedKeys = (obj) => {
|
|
655
666
|
Object.keys(obj).forEach(key => {
|
|
656
667
|
if (typeof obj[key] === "object" && obj[key] !== null) {
|
|
668
|
+
// eslint-disable-next-line local-rules/no-typescript-assertion
|
|
657
669
|
deleteUndefinedKeys(obj[key]);
|
|
658
670
|
}
|
|
659
671
|
else if (obj[key] === undefined) {
|
|
@@ -665,8 +677,8 @@ const deleteUndefinedKeys = (obj) => {
|
|
|
665
677
|
/**
|
|
666
678
|
* Checks if an object is not empty.
|
|
667
679
|
*
|
|
668
|
-
* @param obj - The object to check.
|
|
669
|
-
* @returns The object if not empty, otherwise undefined.
|
|
680
|
+
* @template TObject * @param obj - The object to check.
|
|
681
|
+
* @returns {TObject | undefined} The object if not empty, otherwise undefined.
|
|
670
682
|
*/
|
|
671
683
|
const objNotEmpty = (obj) => obj && Object.keys(obj).length > 0 ? obj : undefined;
|
|
672
684
|
/**
|
|
@@ -676,14 +688,16 @@ const pick = (obj, ...keys) => {
|
|
|
676
688
|
return keys.reduce((acc, key) => {
|
|
677
689
|
acc[key] = obj[key];
|
|
678
690
|
return acc;
|
|
679
|
-
},
|
|
691
|
+
},
|
|
692
|
+
// eslint-disable-next-line local-rules/no-typescript-assertion
|
|
693
|
+
{});
|
|
680
694
|
};
|
|
681
695
|
/**
|
|
682
696
|
* Returns an object with only the differences between two input objects.
|
|
683
697
|
* Not recursive, only compares first level properties.
|
|
684
698
|
*/
|
|
685
699
|
const getFirstLevelObjectPropertyDifferences = (obj1, obj2) => {
|
|
686
|
-
return
|
|
700
|
+
return Object.entries(obj1).reduce((diff, [key, value]) => {
|
|
687
701
|
// Check if the property exists in obj2.
|
|
688
702
|
if (key in obj2) {
|
|
689
703
|
const val = obj2[key];
|
|
@@ -707,7 +721,8 @@ const getFirstLevelObjectPropertyDifferences = (obj1, obj2) => {
|
|
|
707
721
|
* @example replaceNullableNumbersWithZero({ a: 1, b: null, c: undefined }) // { a: 1, b: 0, c: 0 }
|
|
708
722
|
*/
|
|
709
723
|
const replaceNullableNumbersWithZero = (record) => {
|
|
710
|
-
|
|
724
|
+
// eslint-disable-next-line local-rules/no-typescript-assertion
|
|
725
|
+
return Object.fromEntries(Object.entries(record).map(([key, value]) => [key, value ?? 0]));
|
|
711
726
|
};
|
|
712
727
|
/**
|
|
713
728
|
* Removes a property from an object in a type-safe way.
|
|
@@ -720,6 +735,7 @@ const replaceNullableNumbersWithZero = (record) => {
|
|
|
720
735
|
const removeProperty = (obj, key) => {
|
|
721
736
|
const { [key]: _, ...rest } = obj;
|
|
722
737
|
// should be safe because we remove the key from the object, just no way to type it properly
|
|
738
|
+
// eslint-disable-next-line local-rules/no-typescript-assertion
|
|
723
739
|
return rest;
|
|
724
740
|
};
|
|
725
741
|
/**
|
|
@@ -735,7 +751,6 @@ const removeProperties = (obj, keys) => {
|
|
|
735
751
|
for (const key of keys) {
|
|
736
752
|
delete result[key];
|
|
737
753
|
}
|
|
738
|
-
// should be safe because we remove the keys from the object, just no way to type it properly
|
|
739
754
|
return result;
|
|
740
755
|
};
|
|
741
756
|
|
|
@@ -891,7 +906,7 @@ const convertBlobToBase64 = (blob) => {
|
|
|
891
906
|
const reader = new FileReader();
|
|
892
907
|
reader.readAsDataURL(blob);
|
|
893
908
|
reader.onload = () => {
|
|
894
|
-
if (
|
|
909
|
+
if (reader.result === null) {
|
|
895
910
|
no(new Error("Failed to read blob data"));
|
|
896
911
|
return;
|
|
897
912
|
}
|
|
@@ -982,7 +997,7 @@ const stringCompareFromKey = (key) => (a, b) => {
|
|
|
982
997
|
return collator.compare(aVal, bVal);
|
|
983
998
|
}
|
|
984
999
|
else {
|
|
985
|
-
throw new Error(`Cannot compare property '${key}': values are not strings`);
|
|
1000
|
+
throw new Error(`Cannot compare property '${String(key)}': values are not strings`);
|
|
986
1001
|
}
|
|
987
1002
|
};
|
|
988
1003
|
/**
|
|
@@ -1038,10 +1053,10 @@ const doNumberCompare = (a, b, options) => {
|
|
|
1038
1053
|
return 0;
|
|
1039
1054
|
}
|
|
1040
1055
|
if (!isDefined(a)) {
|
|
1041
|
-
return options
|
|
1056
|
+
return (options?.unknownAfterHighest === true) ? 1 : -1;
|
|
1042
1057
|
}
|
|
1043
1058
|
if (!isDefined(b)) {
|
|
1044
|
-
return options
|
|
1059
|
+
return (options?.unknownAfterHighest === true) ? -1 : 1;
|
|
1045
1060
|
}
|
|
1046
1061
|
return a - b;
|
|
1047
1062
|
};
|
|
@@ -1072,13 +1087,13 @@ function isDefined(value) {
|
|
|
1072
1087
|
* @example booleanCompare(false, true) // 1
|
|
1073
1088
|
*/
|
|
1074
1089
|
const booleanCompare = (a, b) => {
|
|
1075
|
-
if (
|
|
1090
|
+
if (a !== true && b !== true) {
|
|
1076
1091
|
return 0;
|
|
1077
1092
|
}
|
|
1078
|
-
if (
|
|
1093
|
+
if (a !== true) {
|
|
1079
1094
|
return 1;
|
|
1080
1095
|
}
|
|
1081
|
-
if (
|
|
1096
|
+
if (b !== true) {
|
|
1082
1097
|
return -1;
|
|
1083
1098
|
}
|
|
1084
1099
|
return 0;
|
|
@@ -1136,6 +1151,25 @@ const stripHiddenCharacters = (input) => {
|
|
|
1136
1151
|
return input.replace(hiddenRegex, "");
|
|
1137
1152
|
};
|
|
1138
1153
|
|
|
1154
|
+
const svgElementSchema = zod.z.object({
|
|
1155
|
+
nodeName: zod.z.string(),
|
|
1156
|
+
width: zod.z.object({
|
|
1157
|
+
baseVal: zod.z.object({
|
|
1158
|
+
value: zod.z.number(),
|
|
1159
|
+
}),
|
|
1160
|
+
}),
|
|
1161
|
+
height: zod.z.object({
|
|
1162
|
+
baseVal: zod.z.object({
|
|
1163
|
+
value: zod.z.number(),
|
|
1164
|
+
}),
|
|
1165
|
+
}),
|
|
1166
|
+
viewBox: zod.z.object({
|
|
1167
|
+
baseVal: zod.z.object({
|
|
1168
|
+
width: zod.z.number(),
|
|
1169
|
+
height: zod.z.number(),
|
|
1170
|
+
}),
|
|
1171
|
+
}),
|
|
1172
|
+
});
|
|
1139
1173
|
/**
|
|
1140
1174
|
* Convert the given value to HEX
|
|
1141
1175
|
* If the value is not rgb, returns back the same value
|
|
@@ -1158,7 +1192,7 @@ const colorsFromStyleDeclaration = (styles) => {
|
|
|
1158
1192
|
* Loads the dimensions of an SVG string.
|
|
1159
1193
|
*
|
|
1160
1194
|
* @param base64EncodedSVG The SVG string to load dimensions from.
|
|
1161
|
-
* @returns A promise that resolves to an object containing the width and height of the SVG.
|
|
1195
|
+
* @returns {Promise<ImageDimensions>} A promise that resolves to an object containing the width and height of the SVG.
|
|
1162
1196
|
* @throws {Error} If the SVG string is invalid or the dimensions cannot be determined.
|
|
1163
1197
|
*/
|
|
1164
1198
|
const loadSVGDimensions = (base64EncodedSVG) => {
|
|
@@ -1167,12 +1201,17 @@ const loadSVGDimensions = (base64EncodedSVG) => {
|
|
|
1167
1201
|
const svgAsString = atob(base64EncodedSVG.replace("data:image/svg+xml;base64,", ""));
|
|
1168
1202
|
const parser = new DOMParser();
|
|
1169
1203
|
const svgDoc = parser.parseFromString(svgAsString, "image/svg+xml");
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
if (svgElement.nodeName === "parsererror") {
|
|
1204
|
+
// Check if parsing was successful first
|
|
1205
|
+
if (svgDoc.documentElement.nodeName === "parsererror") {
|
|
1173
1206
|
reject(new Error("Invalid SVG string"));
|
|
1174
1207
|
return;
|
|
1175
1208
|
}
|
|
1209
|
+
const svgElementValidation = svgElementSchema.safeParse(svgDoc.documentElement);
|
|
1210
|
+
if (!svgElementValidation.success) {
|
|
1211
|
+
reject(new Error("Invalid SVG element structure"));
|
|
1212
|
+
return;
|
|
1213
|
+
}
|
|
1214
|
+
const svgElement = svgElementValidation.data;
|
|
1176
1215
|
// Get dimensions
|
|
1177
1216
|
const width = svgElement.width.baseVal.value;
|
|
1178
1217
|
const height = svgElement.height.baseVal.value;
|
|
@@ -1214,14 +1253,16 @@ const getAllColors = (base64EncodedSVG) => {
|
|
|
1214
1253
|
});
|
|
1215
1254
|
svgImage.querySelectorAll("style").forEach(element => {
|
|
1216
1255
|
const cssRules = element.sheet?.cssRules;
|
|
1217
|
-
if (cssRules
|
|
1256
|
+
if (cssRules && cssRules.length > 0) {
|
|
1218
1257
|
for (let i = 0; i < cssRules.length; i++) {
|
|
1219
1258
|
// Checking if the style attribute exists
|
|
1220
|
-
const
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1259
|
+
const cssRuleSchema = zod.z.object({
|
|
1260
|
+
style: zod.z.instanceof(CSSStyleDeclaration).optional(),
|
|
1261
|
+
});
|
|
1262
|
+
const cssRuleValidation = cssRuleSchema.safeParse(cssRules[i]);
|
|
1263
|
+
if (cssRuleValidation.success && cssRuleValidation.data.style) {
|
|
1264
|
+
const style = cssRuleValidation.data.style;
|
|
1265
|
+
colors.push(...colorsFromStyleDeclaration(style));
|
|
1225
1266
|
}
|
|
1226
1267
|
}
|
|
1227
1268
|
}
|
|
@@ -1252,10 +1293,12 @@ const getAllColors = (base64EncodedSVG) => {
|
|
|
1252
1293
|
/**
|
|
1253
1294
|
* Converts a Base64 encoded SVG to a Scaled Base64 PNG.
|
|
1254
1295
|
*
|
|
1255
|
-
* @param
|
|
1256
|
-
* @param
|
|
1257
|
-
* @param
|
|
1258
|
-
* @
|
|
1296
|
+
* @param params The parameters object
|
|
1297
|
+
* @param params.base64EncodedSVG The Base64 encoded svg string to convert
|
|
1298
|
+
* @param params.maxWidth The max width(px) of the output PNG
|
|
1299
|
+
* @param params.maxHeight The max height(px) of the output PNG
|
|
1300
|
+
* @param params.idealArea The ideal area for the image
|
|
1301
|
+
* @returns {Promise<string>} A Base64 encoded PNG, scaled to the given parameters
|
|
1259
1302
|
*/
|
|
1260
1303
|
const svgToPNG = async ({ base64EncodedSVG, maxWidth, maxHeight, idealArea, }) => {
|
|
1261
1304
|
const [image, dimensions] = await Promise.all([
|
|
@@ -1340,6 +1383,7 @@ const convertYardsToMeters = (value) => {
|
|
|
1340
1383
|
};
|
|
1341
1384
|
|
|
1342
1385
|
exports.DateTimeFormat = DateTimeFormat;
|
|
1386
|
+
exports.HoursAndMinutesFormat = HoursAndMinutesFormat;
|
|
1343
1387
|
exports.align = align;
|
|
1344
1388
|
exports.alphabeticallySort = alphabeticallySort;
|
|
1345
1389
|
exports.arrayLengthCompare = arrayLengthCompare;
|
package/index.esm.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
1
2
|
import { v3, v4, v5 } from 'uuid';
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -17,7 +18,7 @@ const formatAddress = (address) => {
|
|
|
17
18
|
* @example formatCoordinates({ latitude: 1.234567, longitude: 2.345678 }) // "1.234567, 2.345678"
|
|
18
19
|
*/
|
|
19
20
|
const formatCoordinates = (coordinates, toFixed) => {
|
|
20
|
-
return toFixed
|
|
21
|
+
return toFixed !== undefined
|
|
21
22
|
? `${Number(coordinates.latitude.toFixed(toFixed))}, ${Number(coordinates.longitude.toFixed(toFixed))}`
|
|
22
23
|
: `${coordinates.latitude}, ${coordinates.longitude}`;
|
|
23
24
|
};
|
|
@@ -43,7 +44,7 @@ const nonNullable = (value) => {
|
|
|
43
44
|
*
|
|
44
45
|
*/
|
|
45
46
|
const truthy = (value) => {
|
|
46
|
-
return
|
|
47
|
+
return Boolean(value);
|
|
47
48
|
};
|
|
48
49
|
/**
|
|
49
50
|
* Converts an object into an array of properly typed key-value pairs.
|
|
@@ -57,7 +58,9 @@ const truthy = (value) => {
|
|
|
57
58
|
* @param {TObject} object - The object to convert.
|
|
58
59
|
* @template TObject - The type of the object.
|
|
59
60
|
*/
|
|
60
|
-
const objectEntries = (object) =>
|
|
61
|
+
const objectEntries = (object) =>
|
|
62
|
+
// eslint-disable-next-line local-rules/no-typescript-assertion
|
|
63
|
+
Object.entries(object);
|
|
61
64
|
/**
|
|
62
65
|
* Converts an array of key-value pairs into an object.
|
|
63
66
|
* This is a type-faithful alternative to Object.fromEntries().
|
|
@@ -66,6 +69,7 @@ const objectEntries = (object) => Object.entries(object);
|
|
|
66
69
|
* @template TEntries - The type of the entries array.
|
|
67
70
|
*/
|
|
68
71
|
const objectFromEntries = (entries) => {
|
|
72
|
+
// eslint-disable-next-line local-rules/no-typescript-assertion
|
|
69
73
|
return Object.fromEntries(entries);
|
|
70
74
|
};
|
|
71
75
|
/**
|
|
@@ -82,6 +86,7 @@ const objectFromEntries = (entries) => {
|
|
|
82
86
|
* @returns {(keyof TObject)[]} An array of the keys of the object.
|
|
83
87
|
*/
|
|
84
88
|
const objectKeys = (object) => {
|
|
89
|
+
// eslint-disable-next-line local-rules/no-typescript-assertion
|
|
85
90
|
return Object.keys(object).map(key => key);
|
|
86
91
|
};
|
|
87
92
|
/**
|
|
@@ -93,17 +98,20 @@ const objectKeys = (object) => {
|
|
|
93
98
|
*
|
|
94
99
|
* @template TObject - The type of the object.
|
|
95
100
|
* @param {TObject} object - The object to get the values from.
|
|
96
|
-
* @returns {
|
|
101
|
+
* @returns {Array} An array of the values of the object.
|
|
97
102
|
*/
|
|
98
|
-
const objectValues = (object) =>
|
|
103
|
+
const objectValues = (object) =>
|
|
104
|
+
// eslint-disable-next-line local-rules/no-typescript-assertion
|
|
105
|
+
Object.values(object);
|
|
99
106
|
|
|
100
107
|
/**
|
|
101
108
|
* Returns a new array with the items from the previous array and the new array, but only if the item's key is unique.
|
|
102
109
|
*
|
|
110
|
+
* @template TObject - the type of the items in the array
|
|
103
111
|
* @param key The key to use to determine uniqueness
|
|
104
112
|
* @param previous The previous array of items to merge with the new array
|
|
105
113
|
* @param newArray The new array of items to merge with the previous array
|
|
106
|
-
* @returns A new array with the items from the previous array and the new array, but only if the item's key is unique.
|
|
114
|
+
* @returns {Array<TObject>} A new array with the items from the previous array and the new array, but only if the item's key is unique.
|
|
107
115
|
*/
|
|
108
116
|
const unionArraysByKey = (key, previous, newArray) => {
|
|
109
117
|
const previousIds = previous?.map(edge => edge[key]) || [];
|
|
@@ -125,8 +133,9 @@ const isArrayEqual = (a, b) => a.length === b.length && a.every((v, i) => v ===
|
|
|
125
133
|
/**
|
|
126
134
|
* Checks if an array is not empty.
|
|
127
135
|
*
|
|
136
|
+
* @template TItem
|
|
128
137
|
* @param array - The array to check.
|
|
129
|
-
* @returns The array if not empty, otherwise undefined.
|
|
138
|
+
* @returns {Array<TItem> | undefined} The array if not empty, otherwise undefined.
|
|
130
139
|
*/
|
|
131
140
|
const arrayNotEmpty = (array) => array && array.length > 0 ? array : undefined;
|
|
132
141
|
|
|
@@ -158,14 +167,13 @@ const size = {
|
|
|
158
167
|
LARGE: "large",
|
|
159
168
|
};
|
|
160
169
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
})(HoursAndMinutesFormat || (HoursAndMinutesFormat = {}));
|
|
170
|
+
const HoursAndMinutesFormat = {
|
|
171
|
+
HOURS_MIN_SEC_LONG: "HOURS_MIN_SEC_LONG", // h [h] mm [min] ss [sec]
|
|
172
|
+
HOURS_MIN_SEC: "HOURS_MIN_SEC", // h[h] m[m] s[s]
|
|
173
|
+
HOURS_MIN_LONG: "HOURS_MIN_LONG", // h [hrs] mm [min]
|
|
174
|
+
HOURS_MIN: "HOURS_MIN", // h[h] m[m]
|
|
175
|
+
HOURS_LONG: "HOURS_LONG", // h [hrs]
|
|
176
|
+
};
|
|
169
177
|
const DateTimeFormat = {
|
|
170
178
|
DATE: {
|
|
171
179
|
selectFormat: "dateOnly",
|
|
@@ -205,7 +213,7 @@ const DateTimeFormat = {
|
|
|
205
213
|
*/
|
|
206
214
|
const getISOStringFromDate = (date) => {
|
|
207
215
|
try {
|
|
208
|
-
if (
|
|
216
|
+
if (date === null || date === undefined) {
|
|
209
217
|
return null;
|
|
210
218
|
}
|
|
211
219
|
return new Date(date).toISOString();
|
|
@@ -276,7 +284,7 @@ function doNothing() {
|
|
|
276
284
|
*/
|
|
277
285
|
const enumFromValue = (val, _enum) => {
|
|
278
286
|
const enumName = objectKeys(_enum).find(k => _enum[k] === val);
|
|
279
|
-
if (
|
|
287
|
+
if (enumName === undefined) {
|
|
280
288
|
throw Error(`Unknown enum value: ${val} expect one of: ${JSON.stringify(objectValues(_enum))}`);
|
|
281
289
|
}
|
|
282
290
|
return _enum[enumName];
|
|
@@ -306,7 +314,7 @@ const enumOrUndefinedFromValue = (_enum, val) => {
|
|
|
306
314
|
*/
|
|
307
315
|
const enumFromValueTypesafe = (val, _enum) => {
|
|
308
316
|
const enumName = objectKeys(_enum).find(k => _enum[k] === val);
|
|
309
|
-
if (
|
|
317
|
+
if (enumName === undefined) {
|
|
310
318
|
throw Error(`Unknown enum value: ${String(val)} expect one of: ${JSON.stringify(objectValues(_enum))}`);
|
|
311
319
|
}
|
|
312
320
|
return _enum[enumName];
|
|
@@ -331,9 +339,10 @@ const exhaustiveCheck = (value) => {
|
|
|
331
339
|
|
|
332
340
|
/**
|
|
333
341
|
*
|
|
342
|
+
* @template TItem
|
|
334
343
|
* @param arr1 An array of string or numbers
|
|
335
344
|
* @param arr2 An array of string or numbers
|
|
336
|
-
* @returns The elements shared between arr1 and arr2.
|
|
345
|
+
* @returns {Array<TItem>} The elements shared between arr1 and arr2.
|
|
337
346
|
*/
|
|
338
347
|
function intersection(arr1, arr2) {
|
|
339
348
|
const a = new Set(arr1);
|
|
@@ -343,9 +352,10 @@ function intersection(arr1, arr2) {
|
|
|
343
352
|
}
|
|
344
353
|
/**
|
|
345
354
|
*
|
|
355
|
+
* @template TItem
|
|
346
356
|
* @param arr1 An array of string or numbers
|
|
347
357
|
* @param arr2 An array of string or numbers
|
|
348
|
-
* @returns The elements present in arr1 but not arr2.
|
|
358
|
+
* @returns {Array<TItem>} The elements present in arr1 but not arr2.
|
|
349
359
|
*/
|
|
350
360
|
function difference(arr1, arr2) {
|
|
351
361
|
const a = new Set(arr1);
|
|
@@ -408,11 +418,11 @@ searchTerm) => {
|
|
|
408
418
|
/**
|
|
409
419
|
* Group an array of items by a key.
|
|
410
420
|
*
|
|
411
|
-
* @template
|
|
412
|
-
* @template
|
|
413
|
-
* @param {
|
|
414
|
-
* @param {(item:
|
|
415
|
-
* @returns {Map<
|
|
421
|
+
* @template TItem The type of items in the list.
|
|
422
|
+
* @template KeyGroup The type of the key.
|
|
423
|
+
* @param {TItem[]} list The array of items to group.
|
|
424
|
+
* @param {(item: TItem) => KeyGroup} getKey A function to get the key to group by.
|
|
425
|
+
* @returns {Map<KeyGroup, TItem[]>} A map of the items grouped by the key.
|
|
416
426
|
* @example
|
|
417
427
|
* groupBy([{ name: "John", surname: "Doe" }, { name: "Jane", surname: "Doe" }], p => p.surname)
|
|
418
428
|
* Returns: Map { "Doe" => [{ name: "John", surname: "Doe" }, { name: "Jane", surname: "Doe" }] }
|
|
@@ -494,7 +504,7 @@ const zeroPadToUUIDLength = (input, maxLength) => input.length < maxLength ? zer
|
|
|
494
504
|
*/
|
|
495
505
|
const toUUID = (value) => {
|
|
496
506
|
if (isUUID(value.toString())) {
|
|
497
|
-
return value;
|
|
507
|
+
return value.toString();
|
|
498
508
|
}
|
|
499
509
|
// A valid UUID length is 36 but we subtract 4 because we insert 4 hyphens.
|
|
500
510
|
const id = zeroPadToUUIDLength(value.toString(), 32);
|
|
@@ -517,7 +527,7 @@ const toID = (uuid) => Number(uuid.replace(/-/g, "").replace(/^0+/, ""));
|
|
|
517
527
|
* A helper function that converts a list of UUID to a list of valid machine IDs.
|
|
518
528
|
* NOTE that NaN will be removed so list cannot be expected to be of same size.
|
|
519
529
|
*
|
|
520
|
-
* @param
|
|
530
|
+
* @param ids The list of UUIDs that should be converted to a list of valid machine IDs
|
|
521
531
|
*/
|
|
522
532
|
const toIDs = (ids) => ids?.filter(id => !isNaN(toID(id))).map(id => toID(id)) ?? [];
|
|
523
533
|
|
|
@@ -530,7 +540,7 @@ const toIDs = (ids) => ids?.filter(id => !isNaN(toID(id))).map(id => toID(id)) ?
|
|
|
530
540
|
* @param {number} options.maxHeight - The maximum height of the converted PNG image.
|
|
531
541
|
* @param {number} options.maxWidth - The maximum width of the converted PNG image.
|
|
532
542
|
* @param {number} options.idealArea - The ideal area of the converted PNG image.
|
|
533
|
-
* @returns The converted PNG image as a data URL.
|
|
543
|
+
* @returns {string} The converted PNG image as a data URL.
|
|
534
544
|
* @throws Error if unable to get the canvas context.
|
|
535
545
|
*/
|
|
536
546
|
const toPNG = ({ image, dimensions, maxHeight, maxWidth, idealArea, }) => {
|
|
@@ -645,13 +655,15 @@ const fetchImageAsBase64 = async (url) => {
|
|
|
645
655
|
/**
|
|
646
656
|
* Deletes all undefined keys from an object.
|
|
647
657
|
*
|
|
648
|
-
* @
|
|
649
|
-
* @
|
|
658
|
+
* @template TObject
|
|
659
|
+
* @param {TObject} obj The object to delete undefined keys from.
|
|
650
660
|
* @example deleteUndefinedKeys({ a: 1, b: undefined }) // { a: 1 }
|
|
661
|
+
* @returns {TObject} The object without undefined keys.
|
|
651
662
|
*/
|
|
652
663
|
const deleteUndefinedKeys = (obj) => {
|
|
653
664
|
Object.keys(obj).forEach(key => {
|
|
654
665
|
if (typeof obj[key] === "object" && obj[key] !== null) {
|
|
666
|
+
// eslint-disable-next-line local-rules/no-typescript-assertion
|
|
655
667
|
deleteUndefinedKeys(obj[key]);
|
|
656
668
|
}
|
|
657
669
|
else if (obj[key] === undefined) {
|
|
@@ -663,8 +675,8 @@ const deleteUndefinedKeys = (obj) => {
|
|
|
663
675
|
/**
|
|
664
676
|
* Checks if an object is not empty.
|
|
665
677
|
*
|
|
666
|
-
* @param obj - The object to check.
|
|
667
|
-
* @returns The object if not empty, otherwise undefined.
|
|
678
|
+
* @template TObject * @param obj - The object to check.
|
|
679
|
+
* @returns {TObject | undefined} The object if not empty, otherwise undefined.
|
|
668
680
|
*/
|
|
669
681
|
const objNotEmpty = (obj) => obj && Object.keys(obj).length > 0 ? obj : undefined;
|
|
670
682
|
/**
|
|
@@ -674,14 +686,16 @@ const pick = (obj, ...keys) => {
|
|
|
674
686
|
return keys.reduce((acc, key) => {
|
|
675
687
|
acc[key] = obj[key];
|
|
676
688
|
return acc;
|
|
677
|
-
},
|
|
689
|
+
},
|
|
690
|
+
// eslint-disable-next-line local-rules/no-typescript-assertion
|
|
691
|
+
{});
|
|
678
692
|
};
|
|
679
693
|
/**
|
|
680
694
|
* Returns an object with only the differences between two input objects.
|
|
681
695
|
* Not recursive, only compares first level properties.
|
|
682
696
|
*/
|
|
683
697
|
const getFirstLevelObjectPropertyDifferences = (obj1, obj2) => {
|
|
684
|
-
return
|
|
698
|
+
return Object.entries(obj1).reduce((diff, [key, value]) => {
|
|
685
699
|
// Check if the property exists in obj2.
|
|
686
700
|
if (key in obj2) {
|
|
687
701
|
const val = obj2[key];
|
|
@@ -705,7 +719,8 @@ const getFirstLevelObjectPropertyDifferences = (obj1, obj2) => {
|
|
|
705
719
|
* @example replaceNullableNumbersWithZero({ a: 1, b: null, c: undefined }) // { a: 1, b: 0, c: 0 }
|
|
706
720
|
*/
|
|
707
721
|
const replaceNullableNumbersWithZero = (record) => {
|
|
708
|
-
|
|
722
|
+
// eslint-disable-next-line local-rules/no-typescript-assertion
|
|
723
|
+
return Object.fromEntries(Object.entries(record).map(([key, value]) => [key, value ?? 0]));
|
|
709
724
|
};
|
|
710
725
|
/**
|
|
711
726
|
* Removes a property from an object in a type-safe way.
|
|
@@ -718,6 +733,7 @@ const replaceNullableNumbersWithZero = (record) => {
|
|
|
718
733
|
const removeProperty = (obj, key) => {
|
|
719
734
|
const { [key]: _, ...rest } = obj;
|
|
720
735
|
// should be safe because we remove the key from the object, just no way to type it properly
|
|
736
|
+
// eslint-disable-next-line local-rules/no-typescript-assertion
|
|
721
737
|
return rest;
|
|
722
738
|
};
|
|
723
739
|
/**
|
|
@@ -733,7 +749,6 @@ const removeProperties = (obj, keys) => {
|
|
|
733
749
|
for (const key of keys) {
|
|
734
750
|
delete result[key];
|
|
735
751
|
}
|
|
736
|
-
// should be safe because we remove the keys from the object, just no way to type it properly
|
|
737
752
|
return result;
|
|
738
753
|
};
|
|
739
754
|
|
|
@@ -889,7 +904,7 @@ const convertBlobToBase64 = (blob) => {
|
|
|
889
904
|
const reader = new FileReader();
|
|
890
905
|
reader.readAsDataURL(blob);
|
|
891
906
|
reader.onload = () => {
|
|
892
|
-
if (
|
|
907
|
+
if (reader.result === null) {
|
|
893
908
|
no(new Error("Failed to read blob data"));
|
|
894
909
|
return;
|
|
895
910
|
}
|
|
@@ -980,7 +995,7 @@ const stringCompareFromKey = (key) => (a, b) => {
|
|
|
980
995
|
return collator.compare(aVal, bVal);
|
|
981
996
|
}
|
|
982
997
|
else {
|
|
983
|
-
throw new Error(`Cannot compare property '${key}': values are not strings`);
|
|
998
|
+
throw new Error(`Cannot compare property '${String(key)}': values are not strings`);
|
|
984
999
|
}
|
|
985
1000
|
};
|
|
986
1001
|
/**
|
|
@@ -1036,10 +1051,10 @@ const doNumberCompare = (a, b, options) => {
|
|
|
1036
1051
|
return 0;
|
|
1037
1052
|
}
|
|
1038
1053
|
if (!isDefined(a)) {
|
|
1039
|
-
return options
|
|
1054
|
+
return (options?.unknownAfterHighest === true) ? 1 : -1;
|
|
1040
1055
|
}
|
|
1041
1056
|
if (!isDefined(b)) {
|
|
1042
|
-
return options
|
|
1057
|
+
return (options?.unknownAfterHighest === true) ? -1 : 1;
|
|
1043
1058
|
}
|
|
1044
1059
|
return a - b;
|
|
1045
1060
|
};
|
|
@@ -1070,13 +1085,13 @@ function isDefined(value) {
|
|
|
1070
1085
|
* @example booleanCompare(false, true) // 1
|
|
1071
1086
|
*/
|
|
1072
1087
|
const booleanCompare = (a, b) => {
|
|
1073
|
-
if (
|
|
1088
|
+
if (a !== true && b !== true) {
|
|
1074
1089
|
return 0;
|
|
1075
1090
|
}
|
|
1076
|
-
if (
|
|
1091
|
+
if (a !== true) {
|
|
1077
1092
|
return 1;
|
|
1078
1093
|
}
|
|
1079
|
-
if (
|
|
1094
|
+
if (b !== true) {
|
|
1080
1095
|
return -1;
|
|
1081
1096
|
}
|
|
1082
1097
|
return 0;
|
|
@@ -1134,6 +1149,25 @@ const stripHiddenCharacters = (input) => {
|
|
|
1134
1149
|
return input.replace(hiddenRegex, "");
|
|
1135
1150
|
};
|
|
1136
1151
|
|
|
1152
|
+
const svgElementSchema = z.object({
|
|
1153
|
+
nodeName: z.string(),
|
|
1154
|
+
width: z.object({
|
|
1155
|
+
baseVal: z.object({
|
|
1156
|
+
value: z.number(),
|
|
1157
|
+
}),
|
|
1158
|
+
}),
|
|
1159
|
+
height: z.object({
|
|
1160
|
+
baseVal: z.object({
|
|
1161
|
+
value: z.number(),
|
|
1162
|
+
}),
|
|
1163
|
+
}),
|
|
1164
|
+
viewBox: z.object({
|
|
1165
|
+
baseVal: z.object({
|
|
1166
|
+
width: z.number(),
|
|
1167
|
+
height: z.number(),
|
|
1168
|
+
}),
|
|
1169
|
+
}),
|
|
1170
|
+
});
|
|
1137
1171
|
/**
|
|
1138
1172
|
* Convert the given value to HEX
|
|
1139
1173
|
* If the value is not rgb, returns back the same value
|
|
@@ -1156,7 +1190,7 @@ const colorsFromStyleDeclaration = (styles) => {
|
|
|
1156
1190
|
* Loads the dimensions of an SVG string.
|
|
1157
1191
|
*
|
|
1158
1192
|
* @param base64EncodedSVG The SVG string to load dimensions from.
|
|
1159
|
-
* @returns A promise that resolves to an object containing the width and height of the SVG.
|
|
1193
|
+
* @returns {Promise<ImageDimensions>} A promise that resolves to an object containing the width and height of the SVG.
|
|
1160
1194
|
* @throws {Error} If the SVG string is invalid or the dimensions cannot be determined.
|
|
1161
1195
|
*/
|
|
1162
1196
|
const loadSVGDimensions = (base64EncodedSVG) => {
|
|
@@ -1165,12 +1199,17 @@ const loadSVGDimensions = (base64EncodedSVG) => {
|
|
|
1165
1199
|
const svgAsString = atob(base64EncodedSVG.replace("data:image/svg+xml;base64,", ""));
|
|
1166
1200
|
const parser = new DOMParser();
|
|
1167
1201
|
const svgDoc = parser.parseFromString(svgAsString, "image/svg+xml");
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
if (svgElement.nodeName === "parsererror") {
|
|
1202
|
+
// Check if parsing was successful first
|
|
1203
|
+
if (svgDoc.documentElement.nodeName === "parsererror") {
|
|
1171
1204
|
reject(new Error("Invalid SVG string"));
|
|
1172
1205
|
return;
|
|
1173
1206
|
}
|
|
1207
|
+
const svgElementValidation = svgElementSchema.safeParse(svgDoc.documentElement);
|
|
1208
|
+
if (!svgElementValidation.success) {
|
|
1209
|
+
reject(new Error("Invalid SVG element structure"));
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
const svgElement = svgElementValidation.data;
|
|
1174
1213
|
// Get dimensions
|
|
1175
1214
|
const width = svgElement.width.baseVal.value;
|
|
1176
1215
|
const height = svgElement.height.baseVal.value;
|
|
@@ -1212,14 +1251,16 @@ const getAllColors = (base64EncodedSVG) => {
|
|
|
1212
1251
|
});
|
|
1213
1252
|
svgImage.querySelectorAll("style").forEach(element => {
|
|
1214
1253
|
const cssRules = element.sheet?.cssRules;
|
|
1215
|
-
if (cssRules
|
|
1254
|
+
if (cssRules && cssRules.length > 0) {
|
|
1216
1255
|
for (let i = 0; i < cssRules.length; i++) {
|
|
1217
1256
|
// Checking if the style attribute exists
|
|
1218
|
-
const
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1257
|
+
const cssRuleSchema = z.object({
|
|
1258
|
+
style: z.instanceof(CSSStyleDeclaration).optional(),
|
|
1259
|
+
});
|
|
1260
|
+
const cssRuleValidation = cssRuleSchema.safeParse(cssRules[i]);
|
|
1261
|
+
if (cssRuleValidation.success && cssRuleValidation.data.style) {
|
|
1262
|
+
const style = cssRuleValidation.data.style;
|
|
1263
|
+
colors.push(...colorsFromStyleDeclaration(style));
|
|
1223
1264
|
}
|
|
1224
1265
|
}
|
|
1225
1266
|
}
|
|
@@ -1250,10 +1291,12 @@ const getAllColors = (base64EncodedSVG) => {
|
|
|
1250
1291
|
/**
|
|
1251
1292
|
* Converts a Base64 encoded SVG to a Scaled Base64 PNG.
|
|
1252
1293
|
*
|
|
1253
|
-
* @param
|
|
1254
|
-
* @param
|
|
1255
|
-
* @param
|
|
1256
|
-
* @
|
|
1294
|
+
* @param params The parameters object
|
|
1295
|
+
* @param params.base64EncodedSVG The Base64 encoded svg string to convert
|
|
1296
|
+
* @param params.maxWidth The max width(px) of the output PNG
|
|
1297
|
+
* @param params.maxHeight The max height(px) of the output PNG
|
|
1298
|
+
* @param params.idealArea The ideal area for the image
|
|
1299
|
+
* @returns {Promise<string>} A Base64 encoded PNG, scaled to the given parameters
|
|
1257
1300
|
*/
|
|
1258
1301
|
const svgToPNG = async ({ base64EncodedSVG, maxWidth, maxHeight, idealArea, }) => {
|
|
1259
1302
|
const [image, dimensions] = await Promise.all([
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/shared-utils",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.54",
|
|
4
4
|
"repository": "https://github.com/Trackunit/manager",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22.x"
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"uuid": "^11.1.0",
|
|
11
|
-
"@trackunit/react-test-setup": "1.3.
|
|
11
|
+
"@trackunit/react-test-setup": "1.3.53",
|
|
12
|
+
"zod": "^3.23.8"
|
|
12
13
|
},
|
|
13
14
|
"module": "./index.esm.js",
|
|
14
15
|
"main": "./index.cjs.js",
|
package/src/DateTimeFormat.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
export declare
|
|
2
|
-
HOURS_MIN_SEC_LONG
|
|
3
|
-
HOURS_MIN_SEC
|
|
4
|
-
HOURS_MIN_LONG
|
|
5
|
-
HOURS_MIN
|
|
6
|
-
HOURS_LONG
|
|
7
|
-
}
|
|
1
|
+
export declare const HoursAndMinutesFormat: {
|
|
2
|
+
readonly HOURS_MIN_SEC_LONG: "HOURS_MIN_SEC_LONG";
|
|
3
|
+
readonly HOURS_MIN_SEC: "HOURS_MIN_SEC";
|
|
4
|
+
readonly HOURS_MIN_LONG: "HOURS_MIN_LONG";
|
|
5
|
+
readonly HOURS_MIN: "HOURS_MIN";
|
|
6
|
+
readonly HOURS_LONG: "HOURS_LONG";
|
|
7
|
+
};
|
|
8
|
+
export type HoursAndMinutesFormatType = (typeof HoursAndMinutesFormat)[keyof typeof HoursAndMinutesFormat];
|
|
8
9
|
export declare const DateTimeFormat: {
|
|
9
10
|
readonly DATE: {
|
|
10
11
|
readonly selectFormat: "dateOnly";
|
package/src/DateUtils.d.ts
CHANGED
|
@@ -19,11 +19,11 @@ export declare const getDifferenceBetweenDates: (date1: Date, date2: Date, unit:
|
|
|
19
19
|
* @returns {Date} The start of the day for the given date.
|
|
20
20
|
* @example getStartOfDay(new Date("2021-05-19T12:34:56")) // "2021-05-19T00:00:00"
|
|
21
21
|
*/
|
|
22
|
-
export declare const getStartOfDay: <
|
|
22
|
+
export declare const getStartOfDay: <TDate extends string | number | Date>(date: TDate) => Date;
|
|
23
23
|
/**
|
|
24
24
|
* @description Returns the end of the day for a given date.
|
|
25
25
|
* @param date A date object or a date string.
|
|
26
26
|
* @returns {Date} The end of the day for the given date.
|
|
27
27
|
* @example getEndOfDay(new Date("2021-05-19T12:34:56")) // "2021-05-19T23:59:59"
|
|
28
28
|
*/
|
|
29
|
-
export declare const getEndOfDay: <
|
|
29
|
+
export declare const getEndOfDay: <TDate extends string | number | Date>(date: TDate) => Date;
|
package/src/Maybe.d.ts
CHANGED
package/src/arrayUtils.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Returns a new array with the items from the previous array and the new array, but only if the item's key is unique.
|
|
3
3
|
*
|
|
4
|
+
* @template TObject - the type of the items in the array
|
|
4
5
|
* @param key The key to use to determine uniqueness
|
|
5
6
|
* @param previous The previous array of items to merge with the new array
|
|
6
7
|
* @param newArray The new array of items to merge with the previous array
|
|
7
|
-
* @returns A new array with the items from the previous array and the new array, but only if the item's key is unique.
|
|
8
|
+
* @returns {Array<TObject>} A new array with the items from the previous array and the new array, but only if the item's key is unique.
|
|
8
9
|
*/
|
|
9
10
|
export declare const unionArraysByKey: <TObject extends object>(key: keyof TObject, previous: Array<TObject> | undefined | null, newArray: Array<TObject> | undefined | null) => Array<TObject>;
|
|
10
11
|
/**
|
|
@@ -14,7 +15,8 @@ export declare const isArrayEqual: <TItem extends string | number>(a: Array<TIte
|
|
|
14
15
|
/**
|
|
15
16
|
* Checks if an array is not empty.
|
|
16
17
|
*
|
|
18
|
+
* @template TItem
|
|
17
19
|
* @param array - The array to check.
|
|
18
|
-
* @returns The array if not empty, otherwise undefined.
|
|
20
|
+
* @returns {Array<TItem> | undefined} The array if not empty, otherwise undefined.
|
|
19
21
|
*/
|
|
20
22
|
export declare const arrayNotEmpty: <TItem>(array?: Array<TItem>) => Array<TItem> | undefined;
|
package/src/deepPartial.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type DeepPartialNullable<
|
|
2
|
-
[P in keyof
|
|
3
|
-
} :
|
|
4
|
-
export type DeepPartialNullableArray<
|
|
1
|
+
export type DeepPartialNullable<TObject> = TObject extends Array<unknown> ? DeepPartialNullableArray<TObject[number]> : TObject extends object ? {
|
|
2
|
+
[P in keyof TObject]?: DeepPartialNullable<TObject[P]> | null;
|
|
3
|
+
} : TObject | null;
|
|
4
|
+
export type DeepPartialNullableArray<TItem> = Array<DeepPartialNullable<TItem>>;
|
package/src/enumUtils.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @returns {Record<string, string>} The enum value.
|
|
7
7
|
* @example enumFromValue("MACHINE", AssetType) // AssetType.MACHINE
|
|
8
8
|
*/
|
|
9
|
-
export declare const enumFromValue: <
|
|
9
|
+
export declare const enumFromValue: <TEnum extends Record<string, string | number>>(val: string | number, _enum: TEnum) => TEnum[keyof TEnum];
|
|
10
10
|
/**
|
|
11
11
|
* Returns the enum value from a string value or undefined if the string value is undefined or null.
|
|
12
12
|
*
|
|
@@ -15,7 +15,7 @@ export declare const enumFromValue: <T extends Record<string, string | number>>(
|
|
|
15
15
|
* @returns {Record<string, string> | undefined} The enum value or undefined.
|
|
16
16
|
* @example enumFromValue("MACHINE", AssetType) // AssetType.MACHINE
|
|
17
17
|
*/
|
|
18
|
-
export declare const enumOrUndefinedFromValue: <
|
|
18
|
+
export declare const enumOrUndefinedFromValue: <TEnum extends Record<string, string>>(_enum: TEnum, val?: string | null) => TEnum[keyof TEnum] | undefined;
|
|
19
19
|
/**
|
|
20
20
|
* Returns the enum value from a string value or undefined if the string value is undefined or null.
|
|
21
21
|
* This is a typesafe version of enumOrUndefinedFromValue.
|
|
@@ -25,4 +25,4 @@ export declare const enumOrUndefinedFromValue: <T extends Record<string, string>
|
|
|
25
25
|
* @returns {Record<string, string> | undefined} The enum value or undefined.
|
|
26
26
|
* @example enumFromValue("MACHINE", AssetType) // AssetType.MACHINE
|
|
27
27
|
*/
|
|
28
|
-
export declare const enumFromValueTypesafe: <
|
|
28
|
+
export declare const enumFromValueTypesafe: <TEnum extends Record<string, string>>(val: keyof TEnum, _enum: TEnum) => TEnum[keyof TEnum];
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
*
|
|
3
|
+
* @template TItem
|
|
3
4
|
* @param arr1 An array of string or numbers
|
|
4
5
|
* @param arr2 An array of string or numbers
|
|
5
|
-
* @returns The elements shared between arr1 and arr2.
|
|
6
|
+
* @returns {Array<TItem>} The elements shared between arr1 and arr2.
|
|
6
7
|
*/
|
|
7
|
-
export declare function intersection<
|
|
8
|
+
export declare function intersection<TItem>(arr1: Array<TItem>, arr2: Array<TItem>): Array<TItem>;
|
|
8
9
|
/**
|
|
9
10
|
*
|
|
11
|
+
* @template TItem
|
|
10
12
|
* @param arr1 An array of string or numbers
|
|
11
13
|
* @param arr2 An array of string or numbers
|
|
12
|
-
* @returns The elements present in arr1 but not arr2.
|
|
14
|
+
* @returns {Array<TItem>} The elements present in arr1 but not arr2.
|
|
13
15
|
*/
|
|
14
|
-
export declare function difference<
|
|
16
|
+
export declare function difference<TItem>(arr1: Array<TItem>, arr2: Array<TItem>): Array<TItem>;
|
package/src/filter.d.ts
CHANGED
|
@@ -11,20 +11,20 @@ import { Maybe } from "./Maybe";
|
|
|
11
11
|
* @returns {[]} The filtered array
|
|
12
12
|
* @example filterByMultiple([{ name: "John", surname: "Doe" }, { name: "Jane", surname: "Doe" }], [p => p.name, p => p.surname], "John") // [{ name: "John", surname: "Doe" }]
|
|
13
13
|
*/
|
|
14
|
-
export declare function filterByMultiple<
|
|
14
|
+
export declare function filterByMultiple<TItem>(array: Array<TItem> | null | undefined, props: (p: TItem) => Array<Maybe<string> | undefined> | null | undefined, match: string): Array<TItem>;
|
|
15
15
|
/**
|
|
16
16
|
* Filter an array of items based on a list of properties and a search term.
|
|
17
17
|
* The provided properties will be matched based on the term split on space.
|
|
18
18
|
* All terms must have at least one match
|
|
19
19
|
* The terms can match the same property multiple times
|
|
20
20
|
*/
|
|
21
|
-
export declare const fuzzySearch: <
|
|
21
|
+
export declare const fuzzySearch: <TItem>(
|
|
22
22
|
/** The elements that should be filtered */
|
|
23
|
-
elementsToFilter: Array<
|
|
23
|
+
elementsToFilter: Array<TItem>,
|
|
24
24
|
/**
|
|
25
25
|
* A function to run on each element
|
|
26
26
|
* to get an array of strings which should be matched
|
|
27
27
|
*/
|
|
28
|
-
filterableProps: (element:
|
|
28
|
+
filterableProps: (element: TItem) => Array<string>,
|
|
29
29
|
/** A string of the search to match against the element props*/
|
|
30
|
-
searchTerm: string) => Array<
|
|
30
|
+
searchTerm: string) => Array<TItem>;
|
package/src/groupBy/groupBy.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Group an array of items by a key.
|
|
3
3
|
*
|
|
4
|
-
* @template
|
|
5
|
-
* @template
|
|
6
|
-
* @param {
|
|
7
|
-
* @param {(item:
|
|
8
|
-
* @returns {Map<
|
|
4
|
+
* @template TItem The type of items in the list.
|
|
5
|
+
* @template KeyGroup The type of the key.
|
|
6
|
+
* @param {TItem[]} list The array of items to group.
|
|
7
|
+
* @param {(item: TItem) => KeyGroup} getKey A function to get the key to group by.
|
|
8
|
+
* @returns {Map<KeyGroup, TItem[]>} A map of the items grouped by the key.
|
|
9
9
|
* @example
|
|
10
10
|
* groupBy([{ name: "John", surname: "Doe" }, { name: "Jane", surname: "Doe" }], p => p.surname)
|
|
11
11
|
* Returns: Map { "Doe" => [{ name: "John", surname: "Doe" }, { name: "Jane", surname: "Doe" }] }
|
|
12
12
|
*/
|
|
13
|
-
export declare function groupBy<
|
|
13
|
+
export declare function groupBy<TItem, KeyGroup>(list: Array<TItem>, getKey: (item: TItem) => KeyGroup): Map<KeyGroup, Array<TItem>>;
|
package/src/idUtils.d.ts
CHANGED
|
@@ -21,6 +21,6 @@ export declare const toID: (uuid: string) => number;
|
|
|
21
21
|
* A helper function that converts a list of UUID to a list of valid machine IDs.
|
|
22
22
|
* NOTE that NaN will be removed so list cannot be expected to be of same size.
|
|
23
23
|
*
|
|
24
|
-
* @param
|
|
24
|
+
* @param ids The list of UUIDs that should be converted to a list of valid machine IDs
|
|
25
25
|
*/
|
|
26
|
-
export declare const toIDs: (ids: Array<string> | null | undefined) => number
|
|
26
|
+
export declare const toIDs: (ids: Array<string> | null | undefined) => Array<number>;
|
package/src/imageTools.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export interface ImageDimensions {
|
|
|
11
11
|
* @param {number} options.maxHeight - The maximum height of the converted PNG image.
|
|
12
12
|
* @param {number} options.maxWidth - The maximum width of the converted PNG image.
|
|
13
13
|
* @param {number} options.idealArea - The ideal area of the converted PNG image.
|
|
14
|
-
* @returns The converted PNG image as a data URL.
|
|
14
|
+
* @returns {string} The converted PNG image as a data URL.
|
|
15
15
|
* @throws Error if unable to get the canvas context.
|
|
16
16
|
*/
|
|
17
17
|
export declare const toPNG: ({ image, dimensions, maxHeight, maxWidth, idealArea, }: {
|
package/src/objectUtils.d.ts
CHANGED
|
@@ -2,22 +2,23 @@ import { MappedOmit } from "./typeUtils";
|
|
|
2
2
|
/**
|
|
3
3
|
* Deletes all undefined keys from an object.
|
|
4
4
|
*
|
|
5
|
-
* @
|
|
6
|
-
* @
|
|
5
|
+
* @template TObject
|
|
6
|
+
* @param {TObject} obj The object to delete undefined keys from.
|
|
7
7
|
* @example deleteUndefinedKeys({ a: 1, b: undefined }) // { a: 1 }
|
|
8
|
+
* @returns {TObject} The object without undefined keys.
|
|
8
9
|
*/
|
|
9
|
-
export declare const deleteUndefinedKeys: <
|
|
10
|
+
export declare const deleteUndefinedKeys: <TObject extends Record<string, unknown>>(obj: TObject) => TObject;
|
|
10
11
|
/**
|
|
11
12
|
* Checks if an object is not empty.
|
|
12
13
|
*
|
|
13
|
-
* @param obj - The object to check.
|
|
14
|
-
* @returns The object if not empty, otherwise undefined.
|
|
14
|
+
* @template TObject * @param obj - The object to check.
|
|
15
|
+
* @returns {TObject | undefined} The object if not empty, otherwise undefined.
|
|
15
16
|
*/
|
|
16
|
-
export declare const objNotEmpty: <
|
|
17
|
+
export declare const objNotEmpty: <TObject extends object>(obj?: TObject) => TObject | undefined;
|
|
17
18
|
/**
|
|
18
19
|
* Picks the given keys from an object, typesafe.
|
|
19
20
|
*/
|
|
20
|
-
export declare const pick: <
|
|
21
|
+
export declare const pick: <TObject, KeyObject extends keyof TObject>(obj: TObject, ...keys: Array<KeyObject>) => Pick<TObject, KeyObject>;
|
|
21
22
|
/**
|
|
22
23
|
* Returns an object with only the differences between two input objects.
|
|
23
24
|
* Not recursive, only compares first level properties.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
interface
|
|
1
|
+
interface ResizeImageOptions {
|
|
2
2
|
maxSize: number;
|
|
3
3
|
file: File;
|
|
4
4
|
}
|
|
@@ -22,7 +22,7 @@ export declare const getResizedDimensions: (width: number, height: number, maxWi
|
|
|
22
22
|
/**
|
|
23
23
|
* Resize an image to be no larger than the maxSize on each dimension
|
|
24
24
|
*/
|
|
25
|
-
export declare const resizeImage: (settings:
|
|
25
|
+
export declare const resizeImage: (settings: ResizeImageOptions) => Promise<Blob>;
|
|
26
26
|
/**
|
|
27
27
|
* Convert a blob to a base64 string
|
|
28
28
|
*
|
package/src/sorting/sorting.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export declare const stringNaturalCompare: (a?: Maybe<string>, b?: Maybe<string>
|
|
|
26
26
|
* @example stringCompare("a", "b") // -1
|
|
27
27
|
* @example stringCompareFromKey("a", "b") // -1
|
|
28
28
|
*/
|
|
29
|
-
export declare const stringCompareFromKey: <
|
|
29
|
+
export declare const stringCompareFromKey: <TObject>(key: keyof TObject) => (a: TObject, b: TObject) => number;
|
|
30
30
|
/**
|
|
31
31
|
* Compare two dates
|
|
32
32
|
*
|
|
@@ -70,7 +70,7 @@ export declare const numberCompareUnknownAfterHighest: (a?: Maybe<number>, b?: M
|
|
|
70
70
|
* @example arrayCompare([1, 2], [1, 2], numberCompare) // 0
|
|
71
71
|
* @example arrayCompare([1, 3], [1, 2], numberCompare) // 1
|
|
72
72
|
*/
|
|
73
|
-
export declare const arrayLengthCompare: <
|
|
73
|
+
export declare const arrayLengthCompare: <TItem>(a?: Maybe<Array<TItem>>, b?: Maybe<Array<TItem>>) => number;
|
|
74
74
|
/**
|
|
75
75
|
* Compare two booleans
|
|
76
76
|
*
|
package/src/svgTools.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export declare const colorsFromStyleDeclaration: (styles: CSSStyleDeclaration) =
|
|
|
12
12
|
* Loads the dimensions of an SVG string.
|
|
13
13
|
*
|
|
14
14
|
* @param base64EncodedSVG The SVG string to load dimensions from.
|
|
15
|
-
* @returns A promise that resolves to an object containing the width and height of the SVG.
|
|
15
|
+
* @returns {Promise<ImageDimensions>} A promise that resolves to an object containing the width and height of the SVG.
|
|
16
16
|
* @throws {Error} If the SVG string is invalid or the dimensions cannot be determined.
|
|
17
17
|
*/
|
|
18
18
|
export declare const loadSVGDimensions: (base64EncodedSVG: string) => Promise<ImageDimensions>;
|
|
@@ -23,10 +23,12 @@ export declare const getAllColors: (base64EncodedSVG: string) => Array<string>;
|
|
|
23
23
|
/**
|
|
24
24
|
* Converts a Base64 encoded SVG to a Scaled Base64 PNG.
|
|
25
25
|
*
|
|
26
|
-
* @param
|
|
27
|
-
* @param
|
|
28
|
-
* @param
|
|
29
|
-
* @
|
|
26
|
+
* @param params The parameters object
|
|
27
|
+
* @param params.base64EncodedSVG The Base64 encoded svg string to convert
|
|
28
|
+
* @param params.maxWidth The max width(px) of the output PNG
|
|
29
|
+
* @param params.maxHeight The max height(px) of the output PNG
|
|
30
|
+
* @param params.idealArea The ideal area for the image
|
|
31
|
+
* @returns {Promise<string>} A Base64 encoded PNG, scaled to the given parameters
|
|
30
32
|
*/
|
|
31
33
|
export declare const svgToPNG: ({ base64EncodedSVG, maxWidth, maxHeight, idealArea, }: {
|
|
32
34
|
base64EncodedSVG: string;
|
package/src/typeUtils.d.ts
CHANGED
|
@@ -114,7 +114,7 @@ export declare const objectKeys: <TObject extends Record<PropertyKey, unknown>>(
|
|
|
114
114
|
*
|
|
115
115
|
* @template TObject - The type of the object.
|
|
116
116
|
* @param {TObject} object - The object to get the values from.
|
|
117
|
-
* @returns {
|
|
117
|
+
* @returns {Array} An array of the values of the object.
|
|
118
118
|
*/
|
|
119
119
|
export declare const objectValues: <TObject extends Record<PropertyKey, unknown>>(object: TObject) => Array<TObject[keyof TObject]>;
|
|
120
120
|
export {};
|