@twin.org/core 0.0.1-next.5 → 0.0.1-next.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.cjs +1401 -736
- package/dist/esm/index.mjs +1398 -737
- package/dist/types/encoding/base58.d.ts +18 -0
- package/dist/types/factories/factory.d.ts +20 -1
- package/dist/types/helpers/envHelper.d.ts +16 -0
- package/dist/types/helpers/jsonHelper.d.ts +30 -0
- package/dist/types/helpers/objectHelper.d.ts +33 -0
- package/dist/types/helpers/uint8ArrayHelper.d.ts +11 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/models/IComponent.d.ts +12 -3
- package/dist/types/models/coerceType.d.ts +49 -0
- package/dist/types/models/compressionType.d.ts +1 -1
- package/dist/types/utils/asyncCache.d.ts +8 -0
- package/dist/types/utils/coerce.d.ts +22 -0
- package/dist/types/utils/converter.d.ts +12 -0
- package/dist/types/utils/guards.d.ts +16 -0
- package/dist/types/utils/is.d.ts +12 -0
- package/dist/types/utils/validation.d.ts +2 -0
- package/docs/changelog.md +8 -1
- package/docs/reference/classes/AlreadyExistsError.md +67 -25
- package/docs/reference/classes/ArrayHelper.md +6 -2
- package/docs/reference/classes/AsyncCache.md +56 -6
- package/docs/reference/classes/Base32.md +6 -2
- package/docs/reference/classes/Base58.md +61 -0
- package/docs/reference/classes/Base64.md +9 -3
- package/docs/reference/classes/Base64Url.md +6 -2
- package/docs/reference/classes/BaseError.md +65 -23
- package/docs/reference/classes/BitString.md +18 -6
- package/docs/reference/classes/Coerce.md +104 -8
- package/docs/reference/classes/Compression.md +16 -8
- package/docs/reference/classes/ConflictError.md +70 -26
- package/docs/reference/classes/Converter.md +104 -20
- package/docs/reference/classes/EnvHelper.md +43 -0
- package/docs/reference/classes/ErrorHelper.md +9 -3
- package/docs/reference/classes/Factory.md +78 -10
- package/docs/reference/classes/FilenameHelper.md +3 -1
- package/docs/reference/classes/GeneralError.md +65 -25
- package/docs/reference/classes/GuardError.md +70 -26
- package/docs/reference/classes/Guards.md +286 -74
- package/docs/reference/classes/HexHelper.md +15 -5
- package/docs/reference/classes/I18n.md +42 -16
- package/docs/reference/classes/Is.md +160 -44
- package/docs/reference/classes/JsonHelper.md +137 -5
- package/docs/reference/classes/NotFoundError.md +67 -25
- package/docs/reference/classes/NotImplementedError.md +61 -23
- package/docs/reference/classes/NotSupportedError.md +64 -24
- package/docs/reference/classes/ObjectHelper.md +188 -20
- package/docs/reference/classes/RandomHelper.md +3 -1
- package/docs/reference/classes/StringHelper.md +51 -17
- package/docs/reference/classes/Uint8ArrayHelper.md +35 -0
- package/docs/reference/classes/UnauthorizedError.md +64 -24
- package/docs/reference/classes/UnprocessableError.md +65 -25
- package/docs/reference/classes/Url.md +30 -10
- package/docs/reference/classes/Urn.md +54 -18
- package/docs/reference/classes/Validation.md +315 -109
- package/docs/reference/classes/ValidationError.md +64 -24
- package/docs/reference/index.md +5 -0
- package/docs/reference/interfaces/IComponent.md +30 -8
- package/docs/reference/interfaces/IError.md +1 -1
- package/docs/reference/interfaces/ILocaleDictionary.md +1 -1
- package/docs/reference/interfaces/IValidationFailure.md +1 -1
- package/docs/reference/type-aliases/CoerceType.md +5 -0
- package/docs/reference/variables/CoerceType.md +67 -0
- package/docs/reference/variables/CompressionType.md +1 -1
- package/locales/en.json +21 -1
- package/package.json +6 -6
package/dist/esm/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { IntlMessageFormat } from 'intl-messageformat';
|
|
2
1
|
import { createPatch, applyPatch } from 'rfc6902';
|
|
2
|
+
import { IntlMessageFormat } from 'intl-messageformat';
|
|
3
3
|
|
|
4
4
|
// Copyright 2024 IOTA Stiftung.
|
|
5
5
|
// SPDX-License-Identifier: Apache-2.0.
|
|
@@ -111,7 +111,12 @@ class Is {
|
|
|
111
111
|
}
|
|
112
112
|
try {
|
|
113
113
|
const json = JSON.parse(value);
|
|
114
|
-
return
|
|
114
|
+
return (Is.object(json) ||
|
|
115
|
+
Is.array(json) ||
|
|
116
|
+
Is.string(json) ||
|
|
117
|
+
Is.number(json) ||
|
|
118
|
+
Is.boolean(json) ||
|
|
119
|
+
Is.null(json));
|
|
115
120
|
}
|
|
116
121
|
catch {
|
|
117
122
|
return false;
|
|
@@ -135,7 +140,17 @@ class Is {
|
|
|
135
140
|
static stringBase64Url(value) {
|
|
136
141
|
return (Is.stringValue(value) &&
|
|
137
142
|
// eslint-disable-next-line unicorn/better-regex
|
|
138
|
-
/^(
|
|
143
|
+
/^([A-Za-z0-9-_])*$/.test(value));
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Is the value a base58 string.
|
|
147
|
+
* @param value The value to test.
|
|
148
|
+
* @returns True if the value is a base58 string.
|
|
149
|
+
*/
|
|
150
|
+
static stringBase58(value) {
|
|
151
|
+
return (Is.stringValue(value) &&
|
|
152
|
+
// eslint-disable-next-line unicorn/better-regex
|
|
153
|
+
/^[A-HJ-NP-Za-km-z1-9]*$/.test(value));
|
|
139
154
|
}
|
|
140
155
|
/**
|
|
141
156
|
* Is the value a hex string.
|
|
@@ -349,6 +364,14 @@ class Is {
|
|
|
349
364
|
static promise(value) {
|
|
350
365
|
return value instanceof Promise;
|
|
351
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* Is the value a regexp.
|
|
369
|
+
* @param value The value to test.
|
|
370
|
+
* @returns True if the value is a regexp.
|
|
371
|
+
*/
|
|
372
|
+
static regexp(value) {
|
|
373
|
+
return value instanceof RegExp;
|
|
374
|
+
}
|
|
352
375
|
}
|
|
353
376
|
|
|
354
377
|
// Copyright 2024 IOTA Stiftung.
|
|
@@ -898,6 +921,127 @@ class Base32 {
|
|
|
898
921
|
}
|
|
899
922
|
}
|
|
900
923
|
|
|
924
|
+
/**
|
|
925
|
+
* Class to help with base58 Encoding/Decoding.
|
|
926
|
+
*/
|
|
927
|
+
class Base58 {
|
|
928
|
+
/**
|
|
929
|
+
* Runtime name for the class.
|
|
930
|
+
* @internal
|
|
931
|
+
*/
|
|
932
|
+
static _CLASS_NAME = "Base58";
|
|
933
|
+
/**
|
|
934
|
+
* Alphabet table for encoding.
|
|
935
|
+
* @internal
|
|
936
|
+
*/
|
|
937
|
+
static _ALPHABET =
|
|
938
|
+
// cspell:disable-next-line
|
|
939
|
+
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
940
|
+
/**
|
|
941
|
+
* Reverse map for decoding.
|
|
942
|
+
* @internal
|
|
943
|
+
*/
|
|
944
|
+
static _ALPHABET_REVERSE = [
|
|
945
|
+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
946
|
+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
947
|
+
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, 9, 10, 11, 12, 13, 14, 15, 16, -1,
|
|
948
|
+
17, 18, 19, 20, 21, -1, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1, -1, 33,
|
|
949
|
+
34, 35, 36, 37, 38, 39, 40, 41, 42, 43, -1, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
|
|
950
|
+
57, -1, -1, -1, -1, -1
|
|
951
|
+
];
|
|
952
|
+
/**
|
|
953
|
+
* Convert the base 58 string to a byte array.
|
|
954
|
+
* @param base58 The base58 string to convert.
|
|
955
|
+
* @returns The byte array.
|
|
956
|
+
* @throws If the input string contains a character not in the Base58 alphabet.
|
|
957
|
+
*/
|
|
958
|
+
static decode(base58) {
|
|
959
|
+
let zeroes = 0;
|
|
960
|
+
for (let i = 0; i < base58.length; i++) {
|
|
961
|
+
if (base58[i] !== "1") {
|
|
962
|
+
break;
|
|
963
|
+
}
|
|
964
|
+
zeroes += 1;
|
|
965
|
+
}
|
|
966
|
+
const size = Math.trunc((base58.length * 733) / 1000) + 1;
|
|
967
|
+
const b256 = new Uint8Array(size).fill(0);
|
|
968
|
+
let length = 0;
|
|
969
|
+
for (let i = zeroes; i < base58.length; i++) {
|
|
970
|
+
const ch = base58.charCodeAt(i);
|
|
971
|
+
if (ch & 0xff80) {
|
|
972
|
+
throw new GeneralError(Base58._CLASS_NAME, "invalidCharacter", { invalidCharacter: ch });
|
|
973
|
+
}
|
|
974
|
+
const val = Base58._ALPHABET_REVERSE[ch];
|
|
975
|
+
if (val === -1) {
|
|
976
|
+
throw new GeneralError(Base58._CLASS_NAME, "invalidCharacter", { invalidCharacter: ch });
|
|
977
|
+
}
|
|
978
|
+
let carry = val;
|
|
979
|
+
let j = 0;
|
|
980
|
+
for (let k = size - 1; k >= 0; k--, j++) {
|
|
981
|
+
if (carry === 0 && j >= length) {
|
|
982
|
+
break;
|
|
983
|
+
}
|
|
984
|
+
carry += b256[k] * 58;
|
|
985
|
+
b256[k] = carry;
|
|
986
|
+
carry >>>= 8;
|
|
987
|
+
}
|
|
988
|
+
length = j;
|
|
989
|
+
}
|
|
990
|
+
const out = new Uint8Array(zeroes + length);
|
|
991
|
+
let j;
|
|
992
|
+
for (j = 0; j < zeroes; j++) {
|
|
993
|
+
out[j] = 0;
|
|
994
|
+
}
|
|
995
|
+
let i = size - length;
|
|
996
|
+
while (i < size) {
|
|
997
|
+
out[j++] = b256[i++];
|
|
998
|
+
}
|
|
999
|
+
return out;
|
|
1000
|
+
}
|
|
1001
|
+
/**
|
|
1002
|
+
* Convert a byte array to base 58.
|
|
1003
|
+
* @param bytes The byte array to encode.
|
|
1004
|
+
* @returns The data as base58 string.
|
|
1005
|
+
*/
|
|
1006
|
+
static encode(bytes) {
|
|
1007
|
+
let zeroes = 0;
|
|
1008
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
1009
|
+
if (bytes[i] !== 0) {
|
|
1010
|
+
break;
|
|
1011
|
+
}
|
|
1012
|
+
zeroes += 1;
|
|
1013
|
+
}
|
|
1014
|
+
const size = Math.trunc(((bytes.length - zeroes) * 138) / 100) + 1;
|
|
1015
|
+
const b58 = new Uint8Array(size).fill(0);
|
|
1016
|
+
let length = 0;
|
|
1017
|
+
for (let i = zeroes; i < bytes.length; i++) {
|
|
1018
|
+
let carry = bytes[i];
|
|
1019
|
+
let j = 0;
|
|
1020
|
+
for (let k = size - 1; k >= 0; k--, j++) {
|
|
1021
|
+
if (carry === 0 && j >= length) {
|
|
1022
|
+
break;
|
|
1023
|
+
}
|
|
1024
|
+
carry += b58[k] * 256;
|
|
1025
|
+
b58[k] = carry % 58;
|
|
1026
|
+
carry = Math.trunc(carry / 58);
|
|
1027
|
+
}
|
|
1028
|
+
length = j;
|
|
1029
|
+
}
|
|
1030
|
+
let i = size - length;
|
|
1031
|
+
while (i < size && b58[i] === 0) {
|
|
1032
|
+
i += 1;
|
|
1033
|
+
}
|
|
1034
|
+
let str = "";
|
|
1035
|
+
for (let j = 0; j < zeroes; j++) {
|
|
1036
|
+
str += "1";
|
|
1037
|
+
}
|
|
1038
|
+
while (i < size) {
|
|
1039
|
+
str += Base58._ALPHABET[b58[i++]];
|
|
1040
|
+
}
|
|
1041
|
+
return str;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
|
|
901
1045
|
// Copyright 2024 IOTA Stiftung.
|
|
902
1046
|
// SPDX-License-Identifier: Apache-2.0.
|
|
903
1047
|
/* eslint-disable no-bitwise */
|
|
@@ -1052,7 +1196,7 @@ class Base64 {
|
|
|
1052
1196
|
const maxChunkLength = 16383; // must be multiple of 3
|
|
1053
1197
|
// go through the array every three bytes, we'll deal with trailing stuff later
|
|
1054
1198
|
for (let i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
|
|
1055
|
-
parts.push(Base64.encodeChunk(bytes, i, i + maxChunkLength
|
|
1199
|
+
parts.push(Base64.encodeChunk(bytes, i, Math.min(i + maxChunkLength, len2)));
|
|
1056
1200
|
}
|
|
1057
1201
|
// pad the end with zeros, but make sure to not forget the extra bytes
|
|
1058
1202
|
if (extraBytes === 1) {
|
|
@@ -1393,6 +1537,18 @@ class Guards {
|
|
|
1393
1537
|
throw new GuardError(source, "guard.stringEmpty", property, value);
|
|
1394
1538
|
}
|
|
1395
1539
|
}
|
|
1540
|
+
/**
|
|
1541
|
+
* Is the property a JSON value.
|
|
1542
|
+
* @param source The source of the error.
|
|
1543
|
+
* @param property The name of the property.
|
|
1544
|
+
* @param value The value to test.
|
|
1545
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1546
|
+
*/
|
|
1547
|
+
static json(source, property, value) {
|
|
1548
|
+
if (!Is.json(value)) {
|
|
1549
|
+
throw new GuardError(source, "guard.stringJson", property, value);
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1396
1552
|
/**
|
|
1397
1553
|
* Is the property a base64 string.
|
|
1398
1554
|
* @param source The source of the error.
|
|
@@ -1417,6 +1573,18 @@ class Guards {
|
|
|
1417
1573
|
throw new GuardError(source, "guard.base64Url", property, value);
|
|
1418
1574
|
}
|
|
1419
1575
|
}
|
|
1576
|
+
/**
|
|
1577
|
+
* Is the property a base58 string.
|
|
1578
|
+
* @param source The source of the error.
|
|
1579
|
+
* @param property The name of the property.
|
|
1580
|
+
* @param value The value to test.
|
|
1581
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1582
|
+
*/
|
|
1583
|
+
static stringBase58(source, property, value) {
|
|
1584
|
+
if (!Is.stringBase58(value)) {
|
|
1585
|
+
throw new GuardError(source, "guard.base58", property, value);
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1420
1588
|
/**
|
|
1421
1589
|
* Is the property a string with a hex value.
|
|
1422
1590
|
* @param source The source of the error.
|
|
@@ -1718,6 +1886,29 @@ class Factory {
|
|
|
1718
1886
|
}
|
|
1719
1887
|
return Factory._factories[typeName];
|
|
1720
1888
|
}
|
|
1889
|
+
/**
|
|
1890
|
+
* Get all the factories.
|
|
1891
|
+
* @returns All the factories.
|
|
1892
|
+
*/
|
|
1893
|
+
static getFactories() {
|
|
1894
|
+
return Factory._factories;
|
|
1895
|
+
}
|
|
1896
|
+
/**
|
|
1897
|
+
* Reset all the factories, which removes any created instances, but not the registrations.
|
|
1898
|
+
*/
|
|
1899
|
+
static resetFactories() {
|
|
1900
|
+
for (const typeName in Factory._factories) {
|
|
1901
|
+
Factory._factories[typeName].reset();
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
/**
|
|
1905
|
+
* Clear all the factories, which removes anything registered with the factories.
|
|
1906
|
+
*/
|
|
1907
|
+
static clearFactories() {
|
|
1908
|
+
for (const typeName in Factory._factories) {
|
|
1909
|
+
Factory._factories[typeName].clear();
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1721
1912
|
/**
|
|
1722
1913
|
* Register a new generator.
|
|
1723
1914
|
* @param name The name of the generator.
|
|
@@ -1789,7 +1980,7 @@ class Factory {
|
|
|
1789
1980
|
}
|
|
1790
1981
|
}
|
|
1791
1982
|
/**
|
|
1792
|
-
*
|
|
1983
|
+
* Remove all the instances and leave the generators intact.
|
|
1793
1984
|
*/
|
|
1794
1985
|
reset() {
|
|
1795
1986
|
for (const name in this._generators) {
|
|
@@ -1797,6 +1988,14 @@ class Factory {
|
|
|
1797
1988
|
}
|
|
1798
1989
|
this._instances = {};
|
|
1799
1990
|
}
|
|
1991
|
+
/**
|
|
1992
|
+
* Remove all the instances and the generators.
|
|
1993
|
+
*/
|
|
1994
|
+
clear() {
|
|
1995
|
+
this._instances = {};
|
|
1996
|
+
this._generators = {};
|
|
1997
|
+
this._orderCounter = 0;
|
|
1998
|
+
}
|
|
1800
1999
|
/**
|
|
1801
2000
|
* Get all the instances as a map.
|
|
1802
2001
|
* @returns The instances as a map.
|
|
@@ -1902,952 +2101,1335 @@ class ArrayHelper {
|
|
|
1902
2101
|
|
|
1903
2102
|
// Copyright 2024 IOTA Stiftung.
|
|
1904
2103
|
// SPDX-License-Identifier: Apache-2.0.
|
|
2104
|
+
/* eslint-disable no-bitwise */
|
|
1905
2105
|
/**
|
|
1906
|
-
*
|
|
2106
|
+
* Convert arrays to and from different formats.
|
|
1907
2107
|
*/
|
|
1908
|
-
class
|
|
1909
|
-
/**
|
|
1910
|
-
* The default translation.
|
|
1911
|
-
*/
|
|
1912
|
-
static DEFAULT_LOCALE = "en";
|
|
1913
|
-
/**
|
|
1914
|
-
* Dictionaries for lookups.
|
|
1915
|
-
* @internal
|
|
1916
|
-
*/
|
|
1917
|
-
static _localeDictionaries = {};
|
|
1918
|
-
/**
|
|
1919
|
-
* The current locale.
|
|
1920
|
-
* @internal
|
|
1921
|
-
*/
|
|
1922
|
-
static _currentLocale = I18n.DEFAULT_LOCALE;
|
|
2108
|
+
class Converter {
|
|
1923
2109
|
/**
|
|
1924
|
-
*
|
|
2110
|
+
* Lookup table for encoding.
|
|
1925
2111
|
* @internal
|
|
1926
2112
|
*/
|
|
1927
|
-
static
|
|
2113
|
+
static _ENCODE_LOOKUP;
|
|
1928
2114
|
/**
|
|
1929
|
-
*
|
|
2115
|
+
* Lookup table for decoding.
|
|
1930
2116
|
* @internal
|
|
1931
2117
|
*/
|
|
1932
|
-
static
|
|
2118
|
+
static _DECODE_LOOKUP;
|
|
1933
2119
|
/**
|
|
1934
|
-
*
|
|
1935
|
-
* @param
|
|
2120
|
+
* Encode a raw array to UTF8 string.
|
|
2121
|
+
* @param array The bytes to encode.
|
|
2122
|
+
* @param startIndex The index to start in the bytes.
|
|
2123
|
+
* @param length The length of bytes to read.
|
|
2124
|
+
* @returns The array formatted as UTF8.
|
|
1936
2125
|
*/
|
|
1937
|
-
static
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
2126
|
+
static bytesToUtf8(array, startIndex, length) {
|
|
2127
|
+
const start = startIndex ?? 0;
|
|
2128
|
+
const len = length ?? array.length;
|
|
2129
|
+
let str = "";
|
|
2130
|
+
for (let i = start; i < start + len; i++) {
|
|
2131
|
+
const value = array[i];
|
|
2132
|
+
if (value < 0x80) {
|
|
2133
|
+
str += String.fromCharCode(value);
|
|
2134
|
+
}
|
|
2135
|
+
else if (value > 0xbf && value < 0xe0) {
|
|
2136
|
+
str += String.fromCharCode(((value & 0x1f) << 6) | (array[i + 1] & 0x3f));
|
|
2137
|
+
i += 1;
|
|
2138
|
+
}
|
|
2139
|
+
else if (value > 0xdf && value < 0xf0) {
|
|
2140
|
+
str += String.fromCharCode(((value & 0x0f) << 12) | ((array[i + 1] & 0x3f) << 6) | (array[i + 2] & 0x3f));
|
|
2141
|
+
i += 2;
|
|
2142
|
+
}
|
|
2143
|
+
else {
|
|
2144
|
+
// surrogate pair
|
|
2145
|
+
const charCode = (((value & 0x07) << 18) |
|
|
2146
|
+
((array[i + 1] & 0x3f) << 12) |
|
|
2147
|
+
((array[i + 2] & 0x3f) << 6) |
|
|
2148
|
+
(array[i + 3] & 0x3f)) -
|
|
2149
|
+
0x010000;
|
|
2150
|
+
str += String.fromCharCode((charCode >> 10) | 0xd800, (charCode & 0x03ff) | 0xdc00);
|
|
2151
|
+
i += 3;
|
|
2152
|
+
}
|
|
1941
2153
|
}
|
|
2154
|
+
return str;
|
|
1942
2155
|
}
|
|
1943
2156
|
/**
|
|
1944
|
-
*
|
|
1945
|
-
* @
|
|
2157
|
+
* Convert a UTF8 string to raw array.
|
|
2158
|
+
* @param utf8 The text to decode.
|
|
2159
|
+
* @returns The array.
|
|
1946
2160
|
*/
|
|
1947
|
-
static
|
|
1948
|
-
|
|
2161
|
+
static utf8ToBytes(utf8) {
|
|
2162
|
+
const bytes = [];
|
|
2163
|
+
for (let i = 0; i < utf8.length; i++) {
|
|
2164
|
+
let charCode = utf8.charCodeAt(i);
|
|
2165
|
+
if (charCode < 0x80) {
|
|
2166
|
+
bytes.push(charCode);
|
|
2167
|
+
}
|
|
2168
|
+
else if (charCode < 0x800) {
|
|
2169
|
+
bytes.push(0xc0 | (charCode >> 6), 0x80 | (charCode & 0x3f));
|
|
2170
|
+
}
|
|
2171
|
+
else if (charCode < 0xd800 || charCode >= 0xe000) {
|
|
2172
|
+
bytes.push(0xe0 | (charCode >> 12), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f));
|
|
2173
|
+
}
|
|
2174
|
+
else {
|
|
2175
|
+
// surrogate pair
|
|
2176
|
+
i++;
|
|
2177
|
+
// UTF-16 encodes 0x10000-0x10FFFF by
|
|
2178
|
+
// subtracting 0x10000 and splitting the
|
|
2179
|
+
// 20 bits of 0x0-0xFFFFF into two halves
|
|
2180
|
+
charCode = 0x10000 + (((charCode & 0x3ff) << 10) | (utf8.charCodeAt(i) & 0x3ff));
|
|
2181
|
+
bytes.push(0xf0 | (charCode >> 18), 0x80 | ((charCode >> 12) & 0x3f), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f));
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
return Uint8Array.from(bytes);
|
|
1949
2185
|
}
|
|
1950
2186
|
/**
|
|
1951
|
-
*
|
|
1952
|
-
* @param
|
|
1953
|
-
* @param
|
|
2187
|
+
* Encode a raw array to hex string.
|
|
2188
|
+
* @param array The bytes to encode.
|
|
2189
|
+
* @param includePrefix Include the 0x prefix on the returned hex.
|
|
2190
|
+
* @param startIndex The index to start in the bytes.
|
|
2191
|
+
* @param length The length of bytes to read.
|
|
2192
|
+
* @param reverse Reverse the combine direction.
|
|
2193
|
+
* @returns The array formatted as hex.
|
|
1954
2194
|
*/
|
|
1955
|
-
static
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
2195
|
+
static bytesToHex(array, includePrefix = false, startIndex, length, reverse) {
|
|
2196
|
+
let hex = "";
|
|
2197
|
+
this.buildHexLookups();
|
|
2198
|
+
if (Converter._ENCODE_LOOKUP) {
|
|
2199
|
+
const len = length ?? array.length;
|
|
2200
|
+
const start = startIndex ?? 0;
|
|
2201
|
+
if (reverse) {
|
|
2202
|
+
for (let i = 0; i < len; i++) {
|
|
2203
|
+
hex = Converter._ENCODE_LOOKUP[array[start + i]] + hex;
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
else {
|
|
2207
|
+
for (let i = 0; i < len; i++) {
|
|
2208
|
+
hex += Converter._ENCODE_LOOKUP[array[start + i]];
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
1961
2211
|
}
|
|
2212
|
+
return includePrefix ? HexHelper.addPrefix(hex) : hex;
|
|
1962
2213
|
}
|
|
1963
2214
|
/**
|
|
1964
|
-
*
|
|
1965
|
-
* @param
|
|
1966
|
-
* @
|
|
2215
|
+
* Decode a hex string to raw array.
|
|
2216
|
+
* @param hex The hex to decode.
|
|
2217
|
+
* @param reverse Store the characters in reverse.
|
|
2218
|
+
* @returns The array.
|
|
1967
2219
|
*/
|
|
1968
|
-
static
|
|
1969
|
-
|
|
2220
|
+
static hexToBytes(hex, reverse) {
|
|
2221
|
+
const strippedHex = HexHelper.stripPrefix(hex);
|
|
2222
|
+
const sizeof = strippedHex.length >> 1;
|
|
2223
|
+
const length = sizeof << 1;
|
|
2224
|
+
const array = new Uint8Array(sizeof);
|
|
2225
|
+
this.buildHexLookups();
|
|
2226
|
+
if (Converter._DECODE_LOOKUP) {
|
|
2227
|
+
let i = 0;
|
|
2228
|
+
let n = 0;
|
|
2229
|
+
while (i < length) {
|
|
2230
|
+
array[n++] =
|
|
2231
|
+
(Converter._DECODE_LOOKUP[strippedHex.charCodeAt(i++)] << 4) |
|
|
2232
|
+
Converter._DECODE_LOOKUP[strippedHex.charCodeAt(i++)];
|
|
2233
|
+
}
|
|
2234
|
+
if (reverse) {
|
|
2235
|
+
array.reverse();
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
return array;
|
|
1970
2239
|
}
|
|
1971
2240
|
/**
|
|
1972
|
-
*
|
|
1973
|
-
* @
|
|
2241
|
+
* Convert the UTF8 to hex.
|
|
2242
|
+
* @param utf8 The text to convert.
|
|
2243
|
+
* @param includePrefix Include the 0x prefix on the returned hex.
|
|
2244
|
+
* @returns The hex version of the bytes.
|
|
1974
2245
|
*/
|
|
1975
|
-
static
|
|
1976
|
-
|
|
2246
|
+
static utf8ToHex(utf8, includePrefix = false) {
|
|
2247
|
+
const hex = Converter.bytesToHex(Converter.utf8ToBytes(utf8));
|
|
2248
|
+
return includePrefix ? HexHelper.addPrefix(hex) : hex;
|
|
1977
2249
|
}
|
|
1978
2250
|
/**
|
|
1979
|
-
*
|
|
1980
|
-
* @param
|
|
1981
|
-
* @
|
|
2251
|
+
* Convert the hex text to text.
|
|
2252
|
+
* @param hex The hex to convert.
|
|
2253
|
+
* @returns The UTF8 version of the bytes.
|
|
1982
2254
|
*/
|
|
1983
|
-
static
|
|
1984
|
-
|
|
2255
|
+
static hexToUtf8(hex) {
|
|
2256
|
+
return Converter.bytesToUtf8(Converter.hexToBytes(HexHelper.stripPrefix(hex)));
|
|
1985
2257
|
}
|
|
1986
2258
|
/**
|
|
1987
|
-
*
|
|
1988
|
-
* @param
|
|
2259
|
+
* Convert bytes to binary string.
|
|
2260
|
+
* @param bytes The bytes to convert.
|
|
2261
|
+
* @returns A binary string of the bytes.
|
|
1989
2262
|
*/
|
|
1990
|
-
static
|
|
1991
|
-
|
|
2263
|
+
static bytesToBinary(bytes) {
|
|
2264
|
+
const b = [];
|
|
2265
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2266
|
+
b.push(bytes[i].toString(2).padStart(8, "0"));
|
|
2267
|
+
}
|
|
2268
|
+
return b.join("");
|
|
1992
2269
|
}
|
|
1993
2270
|
/**
|
|
1994
|
-
*
|
|
1995
|
-
* @param
|
|
1996
|
-
* @
|
|
2271
|
+
* Convert a binary string to bytes.
|
|
2272
|
+
* @param binary The binary string.
|
|
2273
|
+
* @returns The bytes.
|
|
1997
2274
|
*/
|
|
1998
|
-
static
|
|
1999
|
-
|
|
2275
|
+
static binaryToBytes(binary) {
|
|
2276
|
+
const bytes = new Uint8Array(Math.ceil(binary.length / 8));
|
|
2277
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2278
|
+
bytes[i] = Number.parseInt(binary.slice(i * 8, (i + 1) * 8), 2);
|
|
2279
|
+
}
|
|
2280
|
+
return bytes;
|
|
2000
2281
|
}
|
|
2001
2282
|
/**
|
|
2002
|
-
*
|
|
2003
|
-
* @param
|
|
2283
|
+
* Convert bytes to base64 string.
|
|
2284
|
+
* @param bytes The bytes to convert.
|
|
2285
|
+
* @returns A base64 string of the bytes.
|
|
2004
2286
|
*/
|
|
2005
|
-
static
|
|
2006
|
-
|
|
2287
|
+
static bytesToBase64(bytes) {
|
|
2288
|
+
return Base64.encode(bytes);
|
|
2007
2289
|
}
|
|
2008
2290
|
/**
|
|
2009
|
-
*
|
|
2010
|
-
* @param
|
|
2011
|
-
* @
|
|
2012
|
-
* @param overrideLocale Override the locale.
|
|
2013
|
-
* @returns The formatted string.
|
|
2291
|
+
* Convert a base64 string to bytes.
|
|
2292
|
+
* @param base64 The base64 string.
|
|
2293
|
+
* @returns The bytes.
|
|
2014
2294
|
*/
|
|
2015
|
-
static
|
|
2016
|
-
|
|
2017
|
-
if (cl.startsWith("debug-")) {
|
|
2018
|
-
cl = I18n.DEFAULT_LOCALE;
|
|
2019
|
-
}
|
|
2020
|
-
if (!I18n._localeDictionaries[cl]) {
|
|
2021
|
-
return `!!Missing ${cl}`;
|
|
2022
|
-
}
|
|
2023
|
-
if (!I18n._localeDictionaries[cl][key]) {
|
|
2024
|
-
return `!!Missing ${cl}.${key}`;
|
|
2025
|
-
}
|
|
2026
|
-
if (I18n._currentLocale === "debug-k") {
|
|
2027
|
-
return key;
|
|
2028
|
-
}
|
|
2029
|
-
let ret = new IntlMessageFormat(I18n._localeDictionaries[cl][key], cl).format(values);
|
|
2030
|
-
if (I18n._currentLocale === "debug-x") {
|
|
2031
|
-
ret = ret.replace(/[a-z]/g, "x").replace(/[A-Z]/g, "x").replace(/\d/g, "n");
|
|
2032
|
-
}
|
|
2033
|
-
return ret;
|
|
2295
|
+
static base64ToBytes(base64) {
|
|
2296
|
+
return Base64.decode(base64);
|
|
2034
2297
|
}
|
|
2035
2298
|
/**
|
|
2036
|
-
*
|
|
2037
|
-
* @param
|
|
2038
|
-
* @returns
|
|
2299
|
+
* Convert bytes to base64 url string.
|
|
2300
|
+
* @param bytes The bytes to convert.
|
|
2301
|
+
* @returns A base64 url string of the bytes.
|
|
2039
2302
|
*/
|
|
2040
|
-
static
|
|
2041
|
-
return
|
|
2303
|
+
static bytesToBase64Url(bytes) {
|
|
2304
|
+
return Base64Url.encode(bytes);
|
|
2042
2305
|
}
|
|
2043
2306
|
/**
|
|
2044
|
-
*
|
|
2045
|
-
* @param
|
|
2046
|
-
* @
|
|
2047
|
-
* @param mergedKeys The merged keys dictionary to populate.
|
|
2048
|
-
* @internal
|
|
2307
|
+
* Convert a base64 url string to bytes.
|
|
2308
|
+
* @param base64Url The base64 url string.
|
|
2309
|
+
* @returns The bytes.
|
|
2049
2310
|
*/
|
|
2050
|
-
static
|
|
2051
|
-
|
|
2052
|
-
const val = translation[key];
|
|
2053
|
-
const mergedPath = propertyPath.length > 0 ? `${propertyPath}.${key}` : key;
|
|
2054
|
-
if (Is.string(val)) {
|
|
2055
|
-
mergedKeys[mergedPath] = val;
|
|
2056
|
-
}
|
|
2057
|
-
else if (Is.object(val)) {
|
|
2058
|
-
I18n.flattenTranslationKeys(val, mergedPath, mergedKeys);
|
|
2059
|
-
}
|
|
2060
|
-
}
|
|
2311
|
+
static base64UrlToBytes(base64Url) {
|
|
2312
|
+
return Base64Url.decode(base64Url);
|
|
2061
2313
|
}
|
|
2062
|
-
}
|
|
2063
|
-
|
|
2064
|
-
// Copyright 2024 IOTA Stiftung.
|
|
2065
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
2066
|
-
/**
|
|
2067
|
-
* Error helper functions.
|
|
2068
|
-
*/
|
|
2069
|
-
class ErrorHelper {
|
|
2070
2314
|
/**
|
|
2071
|
-
*
|
|
2072
|
-
* @param
|
|
2073
|
-
* @returns
|
|
2315
|
+
* Convert bytes to base58 string.
|
|
2316
|
+
* @param bytes The bytes to convert.
|
|
2317
|
+
* @returns A base58 string of the bytes.
|
|
2074
2318
|
*/
|
|
2075
|
-
static
|
|
2076
|
-
return
|
|
2319
|
+
static bytesToBase58(bytes) {
|
|
2320
|
+
return Base58.encode(bytes);
|
|
2077
2321
|
}
|
|
2078
2322
|
/**
|
|
2079
|
-
*
|
|
2080
|
-
* @param
|
|
2081
|
-
* @returns The
|
|
2323
|
+
* Convert a base58 string to bytes.
|
|
2324
|
+
* @param base58 The base58 string.
|
|
2325
|
+
* @returns The bytes.
|
|
2082
2326
|
*/
|
|
2083
|
-
static
|
|
2084
|
-
|
|
2085
|
-
if (Is.notEmpty(error)) {
|
|
2086
|
-
const errors = BaseError.flatten(error);
|
|
2087
|
-
for (const err of errors) {
|
|
2088
|
-
const errorNameKey = `errorNames.${StringHelper.camelCase(err.name)}`;
|
|
2089
|
-
const errorMessageKey = `error.${err.message}`;
|
|
2090
|
-
// If there is no error message then it is probably
|
|
2091
|
-
// from a 3rd party lib, so don't format it just display
|
|
2092
|
-
const hasErrorName = I18n.hasMessage(errorNameKey);
|
|
2093
|
-
const hasErrorMessage = I18n.hasMessage(errorMessageKey);
|
|
2094
|
-
const localizedError = {
|
|
2095
|
-
name: I18n.formatMessage(hasErrorName ? errorNameKey : "errorNames.error"),
|
|
2096
|
-
message: hasErrorMessage
|
|
2097
|
-
? I18n.formatMessage(errorMessageKey, err.properties)
|
|
2098
|
-
: err.message
|
|
2099
|
-
};
|
|
2100
|
-
if (Is.stringValue(err.source)) {
|
|
2101
|
-
localizedError.source = err.source;
|
|
2102
|
-
}
|
|
2103
|
-
if (Is.stringValue(err.stack)) {
|
|
2104
|
-
// Remove the first line from the stack traces as they
|
|
2105
|
-
// just have the error type and message duplicated
|
|
2106
|
-
const lines = err.stack.split("\n");
|
|
2107
|
-
lines.shift();
|
|
2108
|
-
localizedError.stack = lines.join("\n");
|
|
2109
|
-
}
|
|
2110
|
-
const additional = ErrorHelper.formatValidationErrors(err);
|
|
2111
|
-
if (Is.stringValue(additional)) {
|
|
2112
|
-
localizedError.additional = additional;
|
|
2113
|
-
}
|
|
2114
|
-
formattedErrors.push(localizedError);
|
|
2115
|
-
}
|
|
2116
|
-
}
|
|
2117
|
-
return formattedErrors;
|
|
2327
|
+
static base58ToBytes(base58) {
|
|
2328
|
+
return Base58.decode(base58);
|
|
2118
2329
|
}
|
|
2119
2330
|
/**
|
|
2120
|
-
*
|
|
2121
|
-
* @
|
|
2122
|
-
* @returns The localized version of the errors flattened.
|
|
2331
|
+
* Build the static lookup tables.
|
|
2332
|
+
* @internal
|
|
2123
2333
|
*/
|
|
2124
|
-
static
|
|
2125
|
-
if (
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
v += ` = ${JSON.stringify(validationFailure.properties.value)}`;
|
|
2334
|
+
static buildHexLookups() {
|
|
2335
|
+
if (!Converter._ENCODE_LOOKUP || !Converter._DECODE_LOOKUP) {
|
|
2336
|
+
const alphabet = "0123456789abcdef";
|
|
2337
|
+
Converter._ENCODE_LOOKUP = [];
|
|
2338
|
+
Converter._DECODE_LOOKUP = [];
|
|
2339
|
+
for (let i = 0; i < 256; i++) {
|
|
2340
|
+
Converter._ENCODE_LOOKUP[i] = alphabet[(i >> 4) & 0xf] + alphabet[i & 0xf];
|
|
2341
|
+
if (i < 16) {
|
|
2342
|
+
if (i < 10) {
|
|
2343
|
+
Converter._DECODE_LOOKUP[0x30 + i] = i;
|
|
2344
|
+
}
|
|
2345
|
+
else {
|
|
2346
|
+
Converter._DECODE_LOOKUP[0x61 - 10 + i] = i;
|
|
2347
|
+
}
|
|
2139
2348
|
}
|
|
2140
|
-
validationErrors.push(v);
|
|
2141
2349
|
}
|
|
2142
|
-
return validationErrors.join("\n");
|
|
2143
2350
|
}
|
|
2144
2351
|
}
|
|
2145
2352
|
}
|
|
2146
2353
|
|
|
2147
|
-
// Copyright 2024 IOTA Stiftung.
|
|
2148
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
2149
2354
|
/**
|
|
2150
|
-
*
|
|
2355
|
+
* Helpers methods for JSON objects.
|
|
2151
2356
|
*/
|
|
2152
|
-
class
|
|
2357
|
+
class JsonHelper {
|
|
2153
2358
|
/**
|
|
2154
|
-
*
|
|
2155
|
-
* @
|
|
2156
|
-
* @throws TypeError If the value can not be coerced.
|
|
2157
|
-
* @returns The value if it can be coerced.
|
|
2359
|
+
* Runtime name for the class.
|
|
2360
|
+
* @internal
|
|
2158
2361
|
*/
|
|
2159
|
-
static
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2362
|
+
static _CLASS_NAME = "JsonHelper";
|
|
2363
|
+
/**
|
|
2364
|
+
* Serializes in canonical format.
|
|
2365
|
+
* Based on https://www.rfc-editor.org/rfc/rfc8785.
|
|
2366
|
+
* @param object The object to be serialized.
|
|
2367
|
+
* @returns The serialized object.
|
|
2368
|
+
*/
|
|
2369
|
+
static canonicalize(object) {
|
|
2370
|
+
const buffer = [];
|
|
2371
|
+
if (object === null ||
|
|
2372
|
+
typeof object !== "object" ||
|
|
2373
|
+
("toJSON" in object && object.toJSON instanceof Function)) {
|
|
2374
|
+
// Primitive data type
|
|
2375
|
+
buffer.push(JSON.stringify(object));
|
|
2168
2376
|
}
|
|
2169
|
-
if (
|
|
2170
|
-
|
|
2377
|
+
else if (Array.isArray(object)) {
|
|
2378
|
+
// Array maintain element order
|
|
2379
|
+
const parts = [];
|
|
2380
|
+
for (const element of object) {
|
|
2381
|
+
if (element === undefined) {
|
|
2382
|
+
parts.push("null");
|
|
2383
|
+
}
|
|
2384
|
+
else {
|
|
2385
|
+
parts.push(JsonHelper.canonicalize(element));
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
buffer.push(`[${parts.join(",")}]`);
|
|
2171
2389
|
}
|
|
2172
|
-
|
|
2173
|
-
|
|
2390
|
+
else {
|
|
2391
|
+
// Object sort properties
|
|
2392
|
+
const props = [];
|
|
2393
|
+
const keys = Object.keys(object).sort();
|
|
2394
|
+
const o = object;
|
|
2395
|
+
for (const key of keys) {
|
|
2396
|
+
if (o[key] !== undefined) {
|
|
2397
|
+
props.push(`${JSON.stringify(key)}:${JsonHelper.canonicalize(o[key])}`);
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
buffer.push(`{${props.join(",")}}`);
|
|
2174
2401
|
}
|
|
2402
|
+
return buffer.join("");
|
|
2175
2403
|
}
|
|
2176
2404
|
/**
|
|
2177
|
-
*
|
|
2178
|
-
*
|
|
2179
|
-
* @
|
|
2180
|
-
* @
|
|
2405
|
+
* Creates a RFC 6902 diff set.
|
|
2406
|
+
* Based on https://www.rfc-editor.org/rfc/rfc6902.
|
|
2407
|
+
* @param object1 The first object.
|
|
2408
|
+
* @param object2 The second object.
|
|
2409
|
+
* @returns The list of patches.
|
|
2181
2410
|
*/
|
|
2182
|
-
static
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
}
|
|
2186
|
-
if (Is.number(value)) {
|
|
2187
|
-
return value;
|
|
2188
|
-
}
|
|
2189
|
-
if (Is.string(value)) {
|
|
2190
|
-
const parsed = Number.parseFloat(value);
|
|
2191
|
-
if (Is.number(parsed)) {
|
|
2192
|
-
return parsed;
|
|
2193
|
-
}
|
|
2194
|
-
}
|
|
2195
|
-
if (Is.boolean(value)) {
|
|
2196
|
-
return value ? 1 : 0;
|
|
2197
|
-
}
|
|
2198
|
-
if (Is.date(value)) {
|
|
2199
|
-
return value.getTime();
|
|
2200
|
-
}
|
|
2411
|
+
static diff(object1, object2) {
|
|
2412
|
+
const operations = createPatch(object1, object2);
|
|
2413
|
+
return operations;
|
|
2201
2414
|
}
|
|
2202
2415
|
/**
|
|
2203
|
-
*
|
|
2204
|
-
*
|
|
2205
|
-
* @
|
|
2206
|
-
* @
|
|
2416
|
+
* Applies a RFC 6902 diff set to an object.
|
|
2417
|
+
* Based on https://www.rfc-editor.org/rfc/rfc6902.
|
|
2418
|
+
* @param object The object to patch.
|
|
2419
|
+
* @param patches The second object.
|
|
2420
|
+
* @returns The updated object.
|
|
2421
|
+
* @throws GeneralError if the patch fails.
|
|
2207
2422
|
*/
|
|
2208
|
-
static
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
}
|
|
2215
|
-
if (Is.number(value)) {
|
|
2216
|
-
return BigInt(value);
|
|
2217
|
-
}
|
|
2218
|
-
if (Is.string(value)) {
|
|
2219
|
-
const parsed = Number.parseFloat(value);
|
|
2220
|
-
if (Is.integer(parsed)) {
|
|
2221
|
-
return BigInt(parsed);
|
|
2423
|
+
static patch(object, patches) {
|
|
2424
|
+
const clone = ObjectHelper.clone(object);
|
|
2425
|
+
const result = applyPatch(clone, patches);
|
|
2426
|
+
for (let i = 0; i < result.length; i++) {
|
|
2427
|
+
if (!Is.empty(result[i])) {
|
|
2428
|
+
throw new GeneralError(JsonHelper._CLASS_NAME, "failedPatch", { index: i }, result[i]);
|
|
2222
2429
|
}
|
|
2223
2430
|
}
|
|
2224
|
-
|
|
2225
|
-
return value ? 1n : 0n;
|
|
2226
|
-
}
|
|
2431
|
+
return clone;
|
|
2227
2432
|
}
|
|
2228
2433
|
/**
|
|
2229
|
-
*
|
|
2230
|
-
* @param
|
|
2231
|
-
* @
|
|
2232
|
-
* @returns The
|
|
2434
|
+
* Stringify the JSON with support for extended data types date/bigint/uint8array.
|
|
2435
|
+
* @param object The object to stringify.
|
|
2436
|
+
* @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
|
|
2437
|
+
* @returns The stringified object.
|
|
2233
2438
|
*/
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2439
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2440
|
+
static stringifyEx(object, space) {
|
|
2441
|
+
// We want to keep the 'this' intact for the replacer
|
|
2442
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
2443
|
+
return JSON.stringify(object, JsonHelper.stringifyExReplacer, space);
|
|
2444
|
+
}
|
|
2445
|
+
/**
|
|
2446
|
+
* Parse the JSON string with support for extended data types date/bigint/uint8array.
|
|
2447
|
+
* @param json The object to pause.
|
|
2448
|
+
* @returns The object.
|
|
2449
|
+
*/
|
|
2450
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2451
|
+
static parseEx(json) {
|
|
2452
|
+
// We want to keep the 'this' intact for the reviver
|
|
2453
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
2454
|
+
return JSON.parse(json, JsonHelper.parseExReviver);
|
|
2455
|
+
}
|
|
2456
|
+
/**
|
|
2457
|
+
* Replacer function to handle extended data types.
|
|
2458
|
+
* @param this The object.
|
|
2459
|
+
* @param key The key.
|
|
2460
|
+
* @param value The value.
|
|
2461
|
+
* @returns The value.
|
|
2462
|
+
*/
|
|
2463
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2464
|
+
static stringifyExReplacer(key, value) {
|
|
2465
|
+
const rawValue = this[key];
|
|
2466
|
+
if (Is.bigint(rawValue)) {
|
|
2467
|
+
return {
|
|
2468
|
+
"@ext": "bigint",
|
|
2469
|
+
value: rawValue.toString()
|
|
2470
|
+
};
|
|
2471
|
+
}
|
|
2472
|
+
else if (Is.date(rawValue)) {
|
|
2473
|
+
return {
|
|
2474
|
+
"@ext": "date",
|
|
2475
|
+
value: rawValue.getTime()
|
|
2476
|
+
};
|
|
2477
|
+
}
|
|
2478
|
+
else if (Is.uint8Array(rawValue)) {
|
|
2479
|
+
return {
|
|
2480
|
+
"@ext": "uint8array",
|
|
2481
|
+
value: Converter.bytesToBase64(rawValue)
|
|
2482
|
+
};
|
|
2483
|
+
}
|
|
2484
|
+
return value;
|
|
2485
|
+
}
|
|
2486
|
+
/**
|
|
2487
|
+
* Reviver function to handle extended data types.
|
|
2488
|
+
* @param this The object.
|
|
2489
|
+
* @param key The key.
|
|
2490
|
+
* @param value The value.
|
|
2491
|
+
* @returns The value.
|
|
2492
|
+
*/
|
|
2493
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2494
|
+
static parseExReviver(key, value) {
|
|
2495
|
+
if (Is.object(value)) {
|
|
2496
|
+
if (value["@ext"] === "bigint") {
|
|
2497
|
+
return BigInt(value.value);
|
|
2248
2498
|
}
|
|
2249
|
-
if (
|
|
2250
|
-
return
|
|
2499
|
+
else if (value["@ext"] === "date") {
|
|
2500
|
+
return new Date(value.value);
|
|
2501
|
+
}
|
|
2502
|
+
else if (value["@ext"] === "uint8array") {
|
|
2503
|
+
return Converter.base64ToBytes(value.value);
|
|
2251
2504
|
}
|
|
2252
2505
|
}
|
|
2506
|
+
return value;
|
|
2253
2507
|
}
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2510
|
+
/**
|
|
2511
|
+
* Class to help with objects.
|
|
2512
|
+
*/
|
|
2513
|
+
class ObjectHelper {
|
|
2254
2514
|
/**
|
|
2255
|
-
*
|
|
2256
|
-
* @
|
|
2257
|
-
* @throws TypeError If the value can not be coerced.
|
|
2258
|
-
* @returns The value if it can be coerced.
|
|
2515
|
+
* Runtime name for the class.
|
|
2516
|
+
* @internal
|
|
2259
2517
|
*/
|
|
2260
|
-
static
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
if (Is.string(value)) {
|
|
2271
|
-
const dt = new Date(value);
|
|
2272
|
-
if (!Number.isNaN(dt.getTime())) {
|
|
2273
|
-
const utc = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate());
|
|
2274
|
-
return new Date(utc);
|
|
2275
|
-
}
|
|
2518
|
+
static _CLASS_NAME = "ObjectHelper";
|
|
2519
|
+
/**
|
|
2520
|
+
* Convert an object to bytes.
|
|
2521
|
+
* @param obj The object to convert.
|
|
2522
|
+
* @param format Format the JSON content.
|
|
2523
|
+
* @returns The object as bytes.
|
|
2524
|
+
*/
|
|
2525
|
+
static toBytes(obj, format = false) {
|
|
2526
|
+
if (obj === undefined) {
|
|
2527
|
+
return new Uint8Array();
|
|
2276
2528
|
}
|
|
2529
|
+
const json = format ? JSON.stringify(obj, undefined, "\t") : JSON.stringify(obj);
|
|
2530
|
+
return Converter.utf8ToBytes(json);
|
|
2277
2531
|
}
|
|
2278
2532
|
/**
|
|
2279
|
-
*
|
|
2280
|
-
* @param
|
|
2281
|
-
* @
|
|
2282
|
-
* @
|
|
2533
|
+
* Convert a bytes to an object.
|
|
2534
|
+
* @param bytes The bytes to convert to an object.
|
|
2535
|
+
* @returns The object.
|
|
2536
|
+
* @throws GeneralError if there was an error parsing the JSON.
|
|
2283
2537
|
*/
|
|
2284
|
-
static
|
|
2285
|
-
if (Is.
|
|
2286
|
-
return
|
|
2287
|
-
}
|
|
2288
|
-
if (Is.date(value)) {
|
|
2289
|
-
return value;
|
|
2538
|
+
static fromBytes(bytes) {
|
|
2539
|
+
if (Is.empty(bytes) || bytes.length === 0) {
|
|
2540
|
+
return undefined;
|
|
2290
2541
|
}
|
|
2291
|
-
|
|
2292
|
-
|
|
2542
|
+
try {
|
|
2543
|
+
const utf8 = Converter.bytesToUtf8(bytes);
|
|
2544
|
+
return JSON.parse(utf8);
|
|
2293
2545
|
}
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
if (!Number.isNaN(dt.getTime())) {
|
|
2297
|
-
const utc = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate(), dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
|
|
2298
|
-
return new Date(utc);
|
|
2299
|
-
}
|
|
2546
|
+
catch (err) {
|
|
2547
|
+
throw new GeneralError(ObjectHelper._CLASS_NAME, "failedBytesToJSON", undefined, err);
|
|
2300
2548
|
}
|
|
2301
2549
|
}
|
|
2302
2550
|
/**
|
|
2303
|
-
*
|
|
2304
|
-
* @param
|
|
2305
|
-
* @
|
|
2306
|
-
* @returns The value if it can be coerced.
|
|
2551
|
+
* Make a deep clone of an object.
|
|
2552
|
+
* @param obj The object to clone.
|
|
2553
|
+
* @returns The objects clone.
|
|
2307
2554
|
*/
|
|
2308
|
-
static
|
|
2309
|
-
if (Is.undefined(
|
|
2310
|
-
return
|
|
2555
|
+
static clone(obj) {
|
|
2556
|
+
if (Is.undefined(obj)) {
|
|
2557
|
+
return undefined;
|
|
2311
2558
|
}
|
|
2312
|
-
|
|
2313
|
-
|
|
2559
|
+
return structuredClone(obj);
|
|
2560
|
+
}
|
|
2561
|
+
/**
|
|
2562
|
+
* Deep merge objects.
|
|
2563
|
+
* @param obj1 The first object to merge.
|
|
2564
|
+
* @param obj2 The second object to merge.
|
|
2565
|
+
* @returns The combined deep merge of the objects.
|
|
2566
|
+
*/
|
|
2567
|
+
static merge(obj1, obj2) {
|
|
2568
|
+
if (Is.empty(obj1)) {
|
|
2569
|
+
return ObjectHelper.clone(obj2);
|
|
2314
2570
|
}
|
|
2315
|
-
if (Is.
|
|
2316
|
-
|
|
2317
|
-
dt.setFullYear(1970, 0, 1);
|
|
2318
|
-
return dt;
|
|
2571
|
+
if (Is.empty(obj2)) {
|
|
2572
|
+
return ObjectHelper.clone(obj1);
|
|
2319
2573
|
}
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2574
|
+
const obj1Clone = ObjectHelper.clone(obj1);
|
|
2575
|
+
if (Is.object(obj1Clone) && Is.object(obj2)) {
|
|
2576
|
+
const keys = Object.keys(obj2);
|
|
2577
|
+
for (const key of keys) {
|
|
2578
|
+
if (Is.object(obj1Clone[key]) && Is.object(obj2[key])) {
|
|
2579
|
+
ObjectHelper.propertySet(obj1Clone, key, ObjectHelper.merge(obj1Clone[key], obj2[key]));
|
|
2580
|
+
}
|
|
2581
|
+
else {
|
|
2582
|
+
ObjectHelper.propertySet(obj1Clone, key, obj2[key]);
|
|
2583
|
+
}
|
|
2325
2584
|
}
|
|
2326
2585
|
}
|
|
2586
|
+
return obj1Clone;
|
|
2327
2587
|
}
|
|
2328
2588
|
/**
|
|
2329
|
-
*
|
|
2330
|
-
* @param
|
|
2331
|
-
* @
|
|
2332
|
-
* @
|
|
2589
|
+
* Does one object equal another.
|
|
2590
|
+
* @param obj1 The first object to compare.
|
|
2591
|
+
* @param obj2 The second object to compare.
|
|
2592
|
+
* @param strictPropertyOrder Should the properties be in the same order, defaults to true.
|
|
2593
|
+
* @returns True is the objects are equal.
|
|
2333
2594
|
*/
|
|
2334
|
-
static
|
|
2335
|
-
if (
|
|
2336
|
-
return
|
|
2337
|
-
}
|
|
2338
|
-
if (Is.object(value)) {
|
|
2339
|
-
return value;
|
|
2595
|
+
static equal(obj1, obj2, strictPropertyOrder) {
|
|
2596
|
+
if (strictPropertyOrder ?? true) {
|
|
2597
|
+
return JSON.stringify(obj1) === JSON.stringify(obj2);
|
|
2340
2598
|
}
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2599
|
+
return JsonHelper.canonicalize(obj1) === JsonHelper.canonicalize(obj2);
|
|
2600
|
+
}
|
|
2601
|
+
/**
|
|
2602
|
+
* Get the property of an unknown object.
|
|
2603
|
+
* @param obj The object to get the property from.
|
|
2604
|
+
* @param property The property to get, can be separated by dots for nested path.
|
|
2605
|
+
* @returns The property.
|
|
2606
|
+
*/
|
|
2607
|
+
static propertyGet(obj, property) {
|
|
2608
|
+
const pathParts = property.split(".");
|
|
2609
|
+
let pathValue = obj;
|
|
2610
|
+
for (const pathPart of pathParts) {
|
|
2611
|
+
// Is the path part numeric i.e. an array index.
|
|
2612
|
+
const arrayMatch = /^(\d+)$/.exec(pathPart);
|
|
2613
|
+
if (arrayMatch) {
|
|
2614
|
+
const arrayIndex = Number.parseInt(arrayMatch[1], 10);
|
|
2615
|
+
if (Is.arrayValue(pathValue) && arrayIndex < pathValue.length) {
|
|
2616
|
+
// There is no prop name so this is a direct array index on the current object
|
|
2617
|
+
pathValue = pathValue[arrayIndex];
|
|
2618
|
+
}
|
|
2619
|
+
else {
|
|
2620
|
+
// Array index for non array object so return
|
|
2621
|
+
return undefined;
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
else if (Is.object(pathValue)) {
|
|
2625
|
+
// No array part in path so assume object sub property
|
|
2626
|
+
pathValue = pathValue[pathPart];
|
|
2627
|
+
}
|
|
2628
|
+
else {
|
|
2629
|
+
return undefined;
|
|
2344
2630
|
}
|
|
2345
|
-
catch { }
|
|
2346
2631
|
}
|
|
2632
|
+
return pathValue;
|
|
2347
2633
|
}
|
|
2348
|
-
}
|
|
2349
|
-
|
|
2350
|
-
// Copyright 2024 IOTA Stiftung.
|
|
2351
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
2352
|
-
/**
|
|
2353
|
-
* Class to help with filenames.
|
|
2354
|
-
*/
|
|
2355
|
-
class FilenameHelper {
|
|
2356
2634
|
/**
|
|
2357
|
-
*
|
|
2358
|
-
* @param
|
|
2359
|
-
* @
|
|
2635
|
+
* Set the property of an unknown object.
|
|
2636
|
+
* @param obj The object to set the property from.
|
|
2637
|
+
* @param property The property to set.
|
|
2638
|
+
* @param value The value to set.
|
|
2639
|
+
* @throws GeneralError if the property target is not an object.
|
|
2360
2640
|
*/
|
|
2361
|
-
static
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2641
|
+
static propertySet(obj, property, value) {
|
|
2642
|
+
const pathParts = property.split(".");
|
|
2643
|
+
let pathValue = obj;
|
|
2644
|
+
let parentObj;
|
|
2645
|
+
for (let i = 0; i < pathParts.length; i++) {
|
|
2646
|
+
const pathPart = pathParts[i];
|
|
2647
|
+
// Is the path part numeric i.e. an array index.
|
|
2648
|
+
const arrayMatch = /^(\d+)$/.exec(pathPart);
|
|
2649
|
+
const arrayIndex = arrayMatch ? Number.parseInt(arrayMatch[1], 10) : -1;
|
|
2650
|
+
if (i === pathParts.length - 1) {
|
|
2651
|
+
// Last part of path so set the value
|
|
2652
|
+
if (arrayIndex >= 0) {
|
|
2653
|
+
if (Is.array(pathValue)) {
|
|
2654
|
+
pathValue[arrayIndex] = value;
|
|
2655
|
+
}
|
|
2656
|
+
else {
|
|
2657
|
+
throw new GeneralError(ObjectHelper._CLASS_NAME, "cannotSetArrayIndex", {
|
|
2658
|
+
property,
|
|
2659
|
+
index: arrayIndex
|
|
2660
|
+
});
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
else if (Is.object(pathValue)) {
|
|
2664
|
+
pathValue[pathPart] = value;
|
|
2665
|
+
}
|
|
2666
|
+
else {
|
|
2667
|
+
throw new GeneralError(ObjectHelper._CLASS_NAME, "cannotSetProperty", { property });
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
else {
|
|
2671
|
+
parentObj = pathValue;
|
|
2672
|
+
if (Is.object(pathValue)) {
|
|
2673
|
+
pathValue = pathValue[pathPart];
|
|
2674
|
+
}
|
|
2675
|
+
else if (Is.array(pathValue)) {
|
|
2676
|
+
pathValue = pathValue[arrayIndex];
|
|
2677
|
+
}
|
|
2678
|
+
if (Is.empty(pathValue)) {
|
|
2679
|
+
const nextArrayMatch = /^(\d+)$/.exec(pathParts[i + 1]);
|
|
2680
|
+
const nextArrayIndex = nextArrayMatch ? Number.parseInt(nextArrayMatch[1], 10) : -1;
|
|
2681
|
+
if (nextArrayIndex >= 0) {
|
|
2682
|
+
pathValue = [];
|
|
2683
|
+
}
|
|
2684
|
+
else {
|
|
2685
|
+
pathValue = {};
|
|
2686
|
+
}
|
|
2687
|
+
if (Is.object(parentObj)) {
|
|
2688
|
+
parentObj[pathPart] = pathValue;
|
|
2689
|
+
}
|
|
2690
|
+
else if (Is.array(parentObj)) {
|
|
2691
|
+
parentObj[arrayIndex] = pathValue;
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2365
2695
|
}
|
|
2366
|
-
// Common non filename characters
|
|
2367
|
-
safe = safe.replace(/["*/:<>?\\|]/g, "_");
|
|
2368
|
-
// Windows non filename characters
|
|
2369
|
-
safe = safe.replace(/^(con|prn|aux|nul|com\d|lpt\d)$/i, "_");
|
|
2370
|
-
// Control characters
|
|
2371
|
-
safe = safe.replace(/[\u0000-\u001F\u0080-\u009F]/g, "_");
|
|
2372
|
-
// Relative paths
|
|
2373
|
-
safe = safe.replace(/^\.+/, "_");
|
|
2374
|
-
// Trailing periods
|
|
2375
|
-
safe = safe.replace(/\.+$/, "");
|
|
2376
|
-
return safe;
|
|
2377
2696
|
}
|
|
2378
|
-
}
|
|
2379
|
-
|
|
2380
|
-
// Copyright 2024 IOTA Stiftung.
|
|
2381
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
2382
|
-
/**
|
|
2383
|
-
* Helpers methods for JSON objects.
|
|
2384
|
-
*/
|
|
2385
|
-
class JsonHelper {
|
|
2386
2697
|
/**
|
|
2387
|
-
*
|
|
2388
|
-
*
|
|
2389
|
-
* @param
|
|
2390
|
-
* @returns The serialized object.
|
|
2698
|
+
* Delete the property of an unknown object.
|
|
2699
|
+
* @param obj The object to set the property from.
|
|
2700
|
+
* @param property The property to set
|
|
2391
2701
|
*/
|
|
2392
|
-
static
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
typeof object !== "object" ||
|
|
2396
|
-
("toJSON" in object && object.toJSON instanceof Function)) {
|
|
2397
|
-
// Primitive data type
|
|
2398
|
-
buffer.push(JSON.stringify(object));
|
|
2702
|
+
static propertyDelete(obj, property) {
|
|
2703
|
+
if (Is.object(obj)) {
|
|
2704
|
+
delete obj[property];
|
|
2399
2705
|
}
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2706
|
+
}
|
|
2707
|
+
/**
|
|
2708
|
+
* Extract a property from the object, providing alternative names.
|
|
2709
|
+
* @param obj The object to extract from.
|
|
2710
|
+
* @param propertyNames The possible names for the property.
|
|
2711
|
+
* @param removeProperties Remove the properties from the object, defaults to true.
|
|
2712
|
+
* @returns The property if available.
|
|
2713
|
+
*/
|
|
2714
|
+
static extractProperty(obj, propertyNames, removeProperties = true) {
|
|
2715
|
+
let retVal;
|
|
2716
|
+
if (Is.object(obj)) {
|
|
2717
|
+
const names = Is.string(propertyNames) ? [propertyNames] : propertyNames;
|
|
2718
|
+
for (const prop of names) {
|
|
2719
|
+
retVal ??= ObjectHelper.propertyGet(obj, prop);
|
|
2720
|
+
if (removeProperties) {
|
|
2721
|
+
ObjectHelper.propertyDelete(obj, prop);
|
|
2409
2722
|
}
|
|
2410
2723
|
}
|
|
2411
|
-
buffer.push(`[${parts.join(",")}]`);
|
|
2412
2724
|
}
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2725
|
+
return retVal;
|
|
2726
|
+
}
|
|
2727
|
+
/**
|
|
2728
|
+
* Pick a subset of properties from an object.
|
|
2729
|
+
* @param obj The object to pick the properties from.
|
|
2730
|
+
* @param keys The property keys to pick.
|
|
2731
|
+
* @returns The partial object.
|
|
2732
|
+
*/
|
|
2733
|
+
static pick(obj, keys) {
|
|
2734
|
+
if (Is.object(obj) && Is.arrayValue(keys)) {
|
|
2735
|
+
const result = {};
|
|
2418
2736
|
for (const key of keys) {
|
|
2419
|
-
|
|
2420
|
-
props.push(`${JSON.stringify(key)}:${JsonHelper.canonicalize(o[key])}`);
|
|
2421
|
-
}
|
|
2737
|
+
result[key] = obj[key];
|
|
2422
2738
|
}
|
|
2423
|
-
|
|
2739
|
+
return result;
|
|
2424
2740
|
}
|
|
2425
|
-
return
|
|
2741
|
+
return obj;
|
|
2426
2742
|
}
|
|
2427
2743
|
/**
|
|
2428
|
-
*
|
|
2429
|
-
*
|
|
2430
|
-
* @param
|
|
2431
|
-
* @
|
|
2432
|
-
* @returns The list of patches.
|
|
2744
|
+
* Omit a subset of properties from an object.
|
|
2745
|
+
* @param obj The object to omit the properties from.
|
|
2746
|
+
* @param keys The property keys to omit.
|
|
2747
|
+
* @returns The partial object.
|
|
2433
2748
|
*/
|
|
2434
|
-
static
|
|
2435
|
-
|
|
2436
|
-
|
|
2749
|
+
static omit(obj, keys) {
|
|
2750
|
+
if (Is.object(obj) && Is.arrayValue(keys)) {
|
|
2751
|
+
const result = { ...obj };
|
|
2752
|
+
for (const key of keys) {
|
|
2753
|
+
delete result[key];
|
|
2754
|
+
}
|
|
2755
|
+
return result;
|
|
2756
|
+
}
|
|
2757
|
+
return obj;
|
|
2437
2758
|
}
|
|
2438
2759
|
/**
|
|
2439
|
-
*
|
|
2440
|
-
*
|
|
2441
|
-
* @
|
|
2442
|
-
* @param patches The second object.
|
|
2443
|
-
* @returns The updated object.
|
|
2760
|
+
* Converter the non JSON primitives to extended types.
|
|
2761
|
+
* @param obj The object to convert.
|
|
2762
|
+
* @returns The object with extended properties.
|
|
2444
2763
|
*/
|
|
2445
|
-
|
|
2446
|
-
|
|
2764
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2765
|
+
static toExtended(obj) {
|
|
2766
|
+
const jsonExtended = JsonHelper.stringifyEx(obj);
|
|
2767
|
+
return JSON.parse(jsonExtended);
|
|
2768
|
+
}
|
|
2769
|
+
/**
|
|
2770
|
+
* Converter the extended types to non JSON primitives.
|
|
2771
|
+
* @param obj The object to convert.
|
|
2772
|
+
* @returns The object with regular properties.
|
|
2773
|
+
*/
|
|
2774
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2775
|
+
static fromExtended(obj) {
|
|
2776
|
+
const jsonExtended = JsonHelper.stringifyEx(obj);
|
|
2777
|
+
return JsonHelper.parseEx(jsonExtended);
|
|
2778
|
+
}
|
|
2779
|
+
/**
|
|
2780
|
+
* Remove empty properties from an object.
|
|
2781
|
+
* @param obj The object to remove the empty properties from.
|
|
2782
|
+
* @param options The options for the removal.
|
|
2783
|
+
* @param options.removeUndefined Remove undefined properties, defaults to true.
|
|
2784
|
+
* @param options.removeNull Remove null properties, defaults to false.
|
|
2785
|
+
* @returns The object with empty properties removed.
|
|
2786
|
+
*/
|
|
2787
|
+
static removeEmptyProperties(obj, options) {
|
|
2788
|
+
if (Is.object(obj)) {
|
|
2789
|
+
const removeUndefined = options?.removeUndefined ?? true;
|
|
2790
|
+
const removeNull = options?.removeNull ?? false;
|
|
2791
|
+
const newObj = {};
|
|
2792
|
+
const keys = Object.keys(obj);
|
|
2793
|
+
for (const key of keys) {
|
|
2794
|
+
if (!((removeUndefined && Is.undefined(obj[key])) || (removeNull && Is.null(obj[key])))) {
|
|
2795
|
+
newObj[key] = ObjectHelper.removeEmptyProperties(obj[key], options);
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
return newObj;
|
|
2799
|
+
}
|
|
2800
|
+
else if (Is.array(obj)) {
|
|
2801
|
+
const arr = [];
|
|
2802
|
+
for (const element of obj) {
|
|
2803
|
+
arr.push(ObjectHelper.removeEmptyProperties(element, options));
|
|
2804
|
+
}
|
|
2805
|
+
return arr;
|
|
2806
|
+
}
|
|
2807
|
+
return obj;
|
|
2447
2808
|
}
|
|
2448
2809
|
}
|
|
2449
2810
|
|
|
2450
2811
|
// Copyright 2024 IOTA Stiftung.
|
|
2451
2812
|
// SPDX-License-Identifier: Apache-2.0.
|
|
2452
|
-
/* eslint-disable no-bitwise */
|
|
2453
2813
|
/**
|
|
2454
|
-
*
|
|
2814
|
+
* Environment variable helper.
|
|
2455
2815
|
*/
|
|
2456
|
-
class
|
|
2816
|
+
class EnvHelper {
|
|
2817
|
+
/**
|
|
2818
|
+
* Get the environment variable as an object with camel cased names.
|
|
2819
|
+
* @param envVars The environment variables.
|
|
2820
|
+
* @param prefix The prefix of the environment variables, if not provided gets all.
|
|
2821
|
+
* @returns The object with camel cased names.
|
|
2822
|
+
*/
|
|
2823
|
+
static envToJson(envVars, prefix) {
|
|
2824
|
+
const result = {};
|
|
2825
|
+
if (!Is.empty(envVars)) {
|
|
2826
|
+
if (Is.empty(prefix)) {
|
|
2827
|
+
for (const envVar in envVars) {
|
|
2828
|
+
if (Is.stringValue(envVars[envVar])) {
|
|
2829
|
+
const camelCaseName = StringHelper.camelCase(envVar.toLowerCase());
|
|
2830
|
+
ObjectHelper.propertySet(result, camelCaseName, envVars[envVar]);
|
|
2831
|
+
}
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
else {
|
|
2835
|
+
for (const envVar in envVars) {
|
|
2836
|
+
if (envVar.startsWith(prefix) && Is.stringValue(envVars[envVar])) {
|
|
2837
|
+
const camelCaseName = StringHelper.camelCase(envVar.replace(prefix, "").toLowerCase());
|
|
2838
|
+
ObjectHelper.propertySet(result, camelCaseName, envVars[envVar]);
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
return result;
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
|
|
2847
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2848
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
2849
|
+
/**
|
|
2850
|
+
* Class to perform internationalization.
|
|
2851
|
+
*/
|
|
2852
|
+
class I18n {
|
|
2457
2853
|
/**
|
|
2458
|
-
*
|
|
2854
|
+
* The default translation.
|
|
2855
|
+
*/
|
|
2856
|
+
static DEFAULT_LOCALE = "en";
|
|
2857
|
+
/**
|
|
2858
|
+
* Dictionaries for lookups.
|
|
2459
2859
|
* @internal
|
|
2460
2860
|
*/
|
|
2461
|
-
static
|
|
2861
|
+
static _localeDictionaries = {};
|
|
2462
2862
|
/**
|
|
2463
|
-
*
|
|
2863
|
+
* The current locale.
|
|
2464
2864
|
* @internal
|
|
2465
2865
|
*/
|
|
2466
|
-
static
|
|
2866
|
+
static _currentLocale = I18n.DEFAULT_LOCALE;
|
|
2467
2867
|
/**
|
|
2468
|
-
*
|
|
2469
|
-
* @
|
|
2470
|
-
* @param startIndex The index to start in the bytes.
|
|
2471
|
-
* @param length The length of bytes to read.
|
|
2472
|
-
* @returns The array formatted as UTF8.
|
|
2868
|
+
* Change handler for the locale being updated.
|
|
2869
|
+
* @internal
|
|
2473
2870
|
*/
|
|
2474
|
-
static
|
|
2475
|
-
const start = startIndex ?? 0;
|
|
2476
|
-
const len = length ?? array.length;
|
|
2477
|
-
let str = "";
|
|
2478
|
-
for (let i = start; i < start + len; i++) {
|
|
2479
|
-
const value = array[i];
|
|
2480
|
-
if (value < 0x80) {
|
|
2481
|
-
str += String.fromCharCode(value);
|
|
2482
|
-
}
|
|
2483
|
-
else if (value > 0xbf && value < 0xe0) {
|
|
2484
|
-
str += String.fromCharCode(((value & 0x1f) << 6) | (array[i + 1] & 0x3f));
|
|
2485
|
-
i += 1;
|
|
2486
|
-
}
|
|
2487
|
-
else if (value > 0xdf && value < 0xf0) {
|
|
2488
|
-
str += String.fromCharCode(((value & 0x0f) << 12) | ((array[i + 1] & 0x3f) << 6) | (array[i + 2] & 0x3f));
|
|
2489
|
-
i += 2;
|
|
2490
|
-
}
|
|
2491
|
-
else {
|
|
2492
|
-
// surrogate pair
|
|
2493
|
-
const charCode = (((value & 0x07) << 18) |
|
|
2494
|
-
((array[i + 1] & 0x3f) << 12) |
|
|
2495
|
-
((array[i + 2] & 0x3f) << 6) |
|
|
2496
|
-
(array[i + 3] & 0x3f)) -
|
|
2497
|
-
0x010000;
|
|
2498
|
-
str += String.fromCharCode((charCode >> 10) | 0xd800, (charCode & 0x03ff) | 0xdc00);
|
|
2499
|
-
i += 3;
|
|
2500
|
-
}
|
|
2501
|
-
}
|
|
2502
|
-
return str;
|
|
2503
|
-
}
|
|
2871
|
+
static _localeChangedHandlers = {};
|
|
2504
2872
|
/**
|
|
2505
|
-
*
|
|
2506
|
-
* @
|
|
2507
|
-
* @returns The array.
|
|
2873
|
+
* Change handler for the dictionaries being updated.
|
|
2874
|
+
* @internal
|
|
2508
2875
|
*/
|
|
2509
|
-
static
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
}
|
|
2519
|
-
else if (charCode < 0xd800 || charCode >= 0xe000) {
|
|
2520
|
-
bytes.push(0xe0 | (charCode >> 12), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f));
|
|
2521
|
-
}
|
|
2522
|
-
else {
|
|
2523
|
-
// surrogate pair
|
|
2524
|
-
i++;
|
|
2525
|
-
// UTF-16 encodes 0x10000-0x10FFFF by
|
|
2526
|
-
// subtracting 0x10000 and splitting the
|
|
2527
|
-
// 20 bits of 0x0-0xFFFFF into two halves
|
|
2528
|
-
charCode = 0x10000 + (((charCode & 0x3ff) << 10) | (utf8.charCodeAt(i) & 0x3ff));
|
|
2529
|
-
bytes.push(0xf0 | (charCode >> 18), 0x80 | ((charCode >> 12) & 0x3f), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f));
|
|
2530
|
-
}
|
|
2876
|
+
static _dictionaryChangedHandlers = {};
|
|
2877
|
+
/**
|
|
2878
|
+
* Set the locale.
|
|
2879
|
+
* @param locale The new locale.
|
|
2880
|
+
*/
|
|
2881
|
+
static setLocale(locale) {
|
|
2882
|
+
I18n._currentLocale = locale;
|
|
2883
|
+
for (const callback in I18n._localeChangedHandlers) {
|
|
2884
|
+
I18n._localeChangedHandlers[callback](I18n._currentLocale);
|
|
2531
2885
|
}
|
|
2532
|
-
return Uint8Array.from(bytes);
|
|
2533
2886
|
}
|
|
2534
2887
|
/**
|
|
2535
|
-
*
|
|
2536
|
-
* @
|
|
2537
|
-
* @param includePrefix Include the 0x prefix on the returned hex.
|
|
2538
|
-
* @param startIndex The index to start in the bytes.
|
|
2539
|
-
* @param length The length of bytes to read.
|
|
2540
|
-
* @param reverse Reverse the combine direction.
|
|
2541
|
-
* @returns The array formatted as hex.
|
|
2888
|
+
* Get the locale.
|
|
2889
|
+
* @returns The current locale.
|
|
2542
2890
|
*/
|
|
2543
|
-
static
|
|
2544
|
-
|
|
2545
|
-
this.buildHexLookups();
|
|
2546
|
-
if (Converter._ENCODE_LOOKUP) {
|
|
2547
|
-
const len = length ?? array.length;
|
|
2548
|
-
const start = startIndex ?? 0;
|
|
2549
|
-
if (reverse) {
|
|
2550
|
-
for (let i = 0; i < len; i++) {
|
|
2551
|
-
hex = Converter._ENCODE_LOOKUP[array[start + i]] + hex;
|
|
2552
|
-
}
|
|
2553
|
-
}
|
|
2554
|
-
else {
|
|
2555
|
-
for (let i = 0; i < len; i++) {
|
|
2556
|
-
hex += Converter._ENCODE_LOOKUP[array[start + i]];
|
|
2557
|
-
}
|
|
2558
|
-
}
|
|
2559
|
-
}
|
|
2560
|
-
return includePrefix ? HexHelper.addPrefix(hex) : hex;
|
|
2891
|
+
static getLocale() {
|
|
2892
|
+
return I18n._currentLocale;
|
|
2561
2893
|
}
|
|
2562
2894
|
/**
|
|
2563
|
-
*
|
|
2564
|
-
* @param
|
|
2565
|
-
* @param
|
|
2566
|
-
* @returns The array.
|
|
2895
|
+
* Add a locale dictionary.
|
|
2896
|
+
* @param locale The locale.
|
|
2897
|
+
* @param dictionary The dictionary to add.
|
|
2567
2898
|
*/
|
|
2568
|
-
static
|
|
2569
|
-
const
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
const
|
|
2573
|
-
|
|
2574
|
-
if (Converter._DECODE_LOOKUP) {
|
|
2575
|
-
let i = 0;
|
|
2576
|
-
let n = 0;
|
|
2577
|
-
while (i < length) {
|
|
2578
|
-
array[n++] =
|
|
2579
|
-
(Converter._DECODE_LOOKUP[strippedHex.charCodeAt(i++)] << 4) |
|
|
2580
|
-
Converter._DECODE_LOOKUP[strippedHex.charCodeAt(i++)];
|
|
2581
|
-
}
|
|
2582
|
-
if (reverse) {
|
|
2583
|
-
array.reverse();
|
|
2584
|
-
}
|
|
2899
|
+
static addDictionary(locale, dictionary) {
|
|
2900
|
+
const mergedKeys = {};
|
|
2901
|
+
I18n.flattenTranslationKeys(dictionary, "", mergedKeys);
|
|
2902
|
+
I18n._localeDictionaries[locale] = mergedKeys;
|
|
2903
|
+
for (const callback in I18n._dictionaryChangedHandlers) {
|
|
2904
|
+
I18n._dictionaryChangedHandlers[callback](I18n._currentLocale);
|
|
2585
2905
|
}
|
|
2586
|
-
return array;
|
|
2587
2906
|
}
|
|
2588
2907
|
/**
|
|
2589
|
-
*
|
|
2590
|
-
* @param
|
|
2591
|
-
* @
|
|
2592
|
-
* @returns The hex version of the bytes.
|
|
2908
|
+
* Get a locale dictionary.
|
|
2909
|
+
* @param locale The locale.
|
|
2910
|
+
* @returns The dictionary of undefined if it does not exist.
|
|
2593
2911
|
*/
|
|
2594
|
-
static
|
|
2595
|
-
|
|
2596
|
-
return includePrefix ? HexHelper.addPrefix(hex) : hex;
|
|
2912
|
+
static getDictionary(locale) {
|
|
2913
|
+
return I18n._localeDictionaries[locale];
|
|
2597
2914
|
}
|
|
2598
2915
|
/**
|
|
2599
|
-
*
|
|
2600
|
-
* @
|
|
2601
|
-
* @returns The UTF8 version of the bytes.
|
|
2916
|
+
* Get all the locale dictionaries.
|
|
2917
|
+
* @returns The dictionaries.
|
|
2602
2918
|
*/
|
|
2603
|
-
static
|
|
2604
|
-
return
|
|
2919
|
+
static getAllDictionaries() {
|
|
2920
|
+
return I18n._localeDictionaries;
|
|
2605
2921
|
}
|
|
2606
2922
|
/**
|
|
2607
|
-
*
|
|
2608
|
-
* @param
|
|
2609
|
-
* @
|
|
2923
|
+
* Add a locale changed handler.
|
|
2924
|
+
* @param id The id of the handler.
|
|
2925
|
+
* @param handler The handler to add.
|
|
2610
2926
|
*/
|
|
2611
|
-
static
|
|
2612
|
-
|
|
2613
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
2614
|
-
b.push(bytes[i].toString(2).padStart(8, "0"));
|
|
2615
|
-
}
|
|
2616
|
-
return b.join("");
|
|
2927
|
+
static addLocaleHandler(id, handler) {
|
|
2928
|
+
I18n._localeChangedHandlers[id] = handler;
|
|
2617
2929
|
}
|
|
2618
2930
|
/**
|
|
2619
|
-
*
|
|
2620
|
-
* @param
|
|
2621
|
-
* @returns The bytes.
|
|
2931
|
+
* Remove a locale changed handler.
|
|
2932
|
+
* @param id The id of the handler.
|
|
2622
2933
|
*/
|
|
2623
|
-
static
|
|
2624
|
-
|
|
2625
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
2626
|
-
bytes[i] = Number.parseInt(binary.slice(i * 8, (i + 1) * 8), 2);
|
|
2627
|
-
}
|
|
2628
|
-
return bytes;
|
|
2934
|
+
static removeLocaleHandler(id) {
|
|
2935
|
+
delete I18n._localeChangedHandlers[id];
|
|
2629
2936
|
}
|
|
2630
2937
|
/**
|
|
2631
|
-
*
|
|
2632
|
-
* @param
|
|
2633
|
-
* @
|
|
2938
|
+
* Add a dictionary changed handler.
|
|
2939
|
+
* @param id The id of the handler.
|
|
2940
|
+
* @param handler The handler to add.
|
|
2634
2941
|
*/
|
|
2635
|
-
static
|
|
2636
|
-
|
|
2942
|
+
static addDictionaryHandler(id, handler) {
|
|
2943
|
+
I18n._dictionaryChangedHandlers[id] = handler;
|
|
2637
2944
|
}
|
|
2638
2945
|
/**
|
|
2639
|
-
*
|
|
2640
|
-
* @param
|
|
2641
|
-
* @returns The bytes.
|
|
2946
|
+
* Remove a dictionary changed handler.
|
|
2947
|
+
* @param id The id of the handler.
|
|
2642
2948
|
*/
|
|
2643
|
-
static
|
|
2644
|
-
|
|
2949
|
+
static removeDictionaryHandler(id) {
|
|
2950
|
+
delete I18n._dictionaryChangedHandlers[id];
|
|
2645
2951
|
}
|
|
2646
2952
|
/**
|
|
2647
|
-
*
|
|
2648
|
-
* @param
|
|
2649
|
-
* @
|
|
2953
|
+
* Format a message.
|
|
2954
|
+
* @param key The key of the message to format.
|
|
2955
|
+
* @param values The values to substitute into the message.
|
|
2956
|
+
* @param overrideLocale Override the locale.
|
|
2957
|
+
* @returns The formatted string.
|
|
2650
2958
|
*/
|
|
2651
|
-
static
|
|
2652
|
-
|
|
2959
|
+
static formatMessage(key, values, overrideLocale) {
|
|
2960
|
+
let cl = overrideLocale ?? I18n._currentLocale;
|
|
2961
|
+
if (cl.startsWith("debug-")) {
|
|
2962
|
+
cl = I18n.DEFAULT_LOCALE;
|
|
2963
|
+
}
|
|
2964
|
+
if (!I18n._localeDictionaries[cl]) {
|
|
2965
|
+
return `!!Missing ${cl}`;
|
|
2966
|
+
}
|
|
2967
|
+
if (!I18n._localeDictionaries[cl][key]) {
|
|
2968
|
+
return `!!Missing ${cl}.${key}`;
|
|
2969
|
+
}
|
|
2970
|
+
if (I18n._currentLocale === "debug-k") {
|
|
2971
|
+
return key;
|
|
2972
|
+
}
|
|
2973
|
+
let ret = new IntlMessageFormat(I18n._localeDictionaries[cl][key], cl).format(values);
|
|
2974
|
+
if (I18n._currentLocale === "debug-x") {
|
|
2975
|
+
ret = ret.replace(/[a-z]/g, "x").replace(/[A-Z]/g, "x").replace(/\d/g, "n");
|
|
2976
|
+
}
|
|
2977
|
+
return ret;
|
|
2653
2978
|
}
|
|
2654
2979
|
/**
|
|
2655
|
-
*
|
|
2656
|
-
* @param
|
|
2657
|
-
* @returns
|
|
2980
|
+
* Check if the dictionaries have a message for the given key.
|
|
2981
|
+
* @param key The key to check for existence.
|
|
2982
|
+
* @returns True if the key exists.
|
|
2658
2983
|
*/
|
|
2659
|
-
static
|
|
2660
|
-
return
|
|
2984
|
+
static hasMessage(key) {
|
|
2985
|
+
return Is.string(I18n._localeDictionaries[I18n._currentLocale]?.[key]);
|
|
2661
2986
|
}
|
|
2662
2987
|
/**
|
|
2663
|
-
*
|
|
2988
|
+
* Flatten the translation property paths for faster lookup.
|
|
2989
|
+
* @param translation The translation to merge.
|
|
2990
|
+
* @param propertyPath The current root path.
|
|
2991
|
+
* @param mergedKeys The merged keys dictionary to populate.
|
|
2664
2992
|
* @internal
|
|
2665
2993
|
*/
|
|
2666
|
-
static
|
|
2667
|
-
|
|
2668
|
-
const
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2994
|
+
static flattenTranslationKeys(translation, propertyPath, mergedKeys) {
|
|
2995
|
+
for (const key in translation) {
|
|
2996
|
+
const val = translation[key];
|
|
2997
|
+
const mergedPath = propertyPath.length > 0 ? `${propertyPath}.${key}` : key;
|
|
2998
|
+
if (Is.string(val)) {
|
|
2999
|
+
mergedKeys[mergedPath] = val;
|
|
3000
|
+
}
|
|
3001
|
+
else if (Is.object(val)) {
|
|
3002
|
+
I18n.flattenTranslationKeys(val, mergedPath, mergedKeys);
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
3007
|
+
|
|
3008
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3009
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3010
|
+
/**
|
|
3011
|
+
* Error helper functions.
|
|
3012
|
+
*/
|
|
3013
|
+
class ErrorHelper {
|
|
3014
|
+
/**
|
|
3015
|
+
* Format Errors and returns just their messages.
|
|
3016
|
+
* @param error The error to format.
|
|
3017
|
+
* @returns The error formatted including any inner errors.
|
|
3018
|
+
*/
|
|
3019
|
+
static formatErrors(error) {
|
|
3020
|
+
return ErrorHelper.localizeErrors(error).map(e => e.message);
|
|
3021
|
+
}
|
|
3022
|
+
/**
|
|
3023
|
+
* Localize the content of an error and any inner errors.
|
|
3024
|
+
* @param error The error to format.
|
|
3025
|
+
* @returns The localized version of the errors flattened.
|
|
3026
|
+
*/
|
|
3027
|
+
static localizeErrors(error) {
|
|
3028
|
+
const formattedErrors = [];
|
|
3029
|
+
if (Is.notEmpty(error)) {
|
|
3030
|
+
const errors = BaseError.flatten(error);
|
|
3031
|
+
for (const err of errors) {
|
|
3032
|
+
const errorNameKey = `errorNames.${StringHelper.camelCase(err.name)}`;
|
|
3033
|
+
const errorMessageKey = `error.${err.message}`;
|
|
3034
|
+
// If there is no error message then it is probably
|
|
3035
|
+
// from a 3rd party lib, so don't format it just display
|
|
3036
|
+
const hasErrorName = I18n.hasMessage(errorNameKey);
|
|
3037
|
+
const hasErrorMessage = I18n.hasMessage(errorMessageKey);
|
|
3038
|
+
const localizedError = {
|
|
3039
|
+
name: I18n.formatMessage(hasErrorName ? errorNameKey : "errorNames.error"),
|
|
3040
|
+
message: hasErrorMessage
|
|
3041
|
+
? I18n.formatMessage(errorMessageKey, err.properties)
|
|
3042
|
+
: err.message
|
|
3043
|
+
};
|
|
3044
|
+
if (Is.stringValue(err.source)) {
|
|
3045
|
+
localizedError.source = err.source;
|
|
3046
|
+
}
|
|
3047
|
+
if (Is.stringValue(err.stack)) {
|
|
3048
|
+
// Remove the first line from the stack traces as they
|
|
3049
|
+
// just have the error type and message duplicated
|
|
3050
|
+
const lines = err.stack.split("\n");
|
|
3051
|
+
lines.shift();
|
|
3052
|
+
localizedError.stack = lines.join("\n");
|
|
3053
|
+
}
|
|
3054
|
+
const additional = ErrorHelper.formatValidationErrors(err);
|
|
3055
|
+
if (Is.stringValue(additional)) {
|
|
3056
|
+
localizedError.additional = additional;
|
|
3057
|
+
}
|
|
3058
|
+
formattedErrors.push(localizedError);
|
|
3059
|
+
}
|
|
3060
|
+
}
|
|
3061
|
+
return formattedErrors;
|
|
3062
|
+
}
|
|
3063
|
+
/**
|
|
3064
|
+
* Localize the content of an error and any inner errors.
|
|
3065
|
+
* @param error The error to format.
|
|
3066
|
+
* @returns The localized version of the errors flattened.
|
|
3067
|
+
*/
|
|
3068
|
+
static formatValidationErrors(error) {
|
|
3069
|
+
if (Is.object(error.properties) &&
|
|
3070
|
+
Object.keys(error.properties).length > 0 &&
|
|
3071
|
+
Is.object(error.properties) &&
|
|
3072
|
+
Is.arrayValue(error.properties.validationFailures)) {
|
|
3073
|
+
const validationErrors = [];
|
|
3074
|
+
for (const validationFailure of error.properties.validationFailures) {
|
|
3075
|
+
const errorI18n = `error.${validationFailure.reason}`;
|
|
3076
|
+
const errorMessage = I18n.hasMessage(errorI18n)
|
|
3077
|
+
? I18n.formatMessage(errorI18n, validationFailure.properties)
|
|
3078
|
+
: errorI18n;
|
|
3079
|
+
let v = `${validationFailure.property}: ${errorMessage}`;
|
|
3080
|
+
if (Is.object(validationFailure.properties) &&
|
|
3081
|
+
Is.notEmpty(validationFailure.properties.value)) {
|
|
3082
|
+
v += ` = ${JSON.stringify(validationFailure.properties.value)}`;
|
|
2680
3083
|
}
|
|
3084
|
+
validationErrors.push(v);
|
|
2681
3085
|
}
|
|
3086
|
+
return validationErrors.join("\n");
|
|
2682
3087
|
}
|
|
2683
3088
|
}
|
|
2684
3089
|
}
|
|
2685
3090
|
|
|
3091
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3092
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
2686
3093
|
/**
|
|
2687
|
-
*
|
|
3094
|
+
* The types the extracted data can be coerced to.
|
|
2688
3095
|
*/
|
|
2689
|
-
|
|
3096
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
3097
|
+
const CoerceType = {
|
|
2690
3098
|
/**
|
|
2691
|
-
*
|
|
2692
|
-
* @internal
|
|
3099
|
+
* String.
|
|
2693
3100
|
*/
|
|
2694
|
-
|
|
3101
|
+
String: "string",
|
|
2695
3102
|
/**
|
|
2696
|
-
*
|
|
2697
|
-
* @param obj The object to convert.
|
|
2698
|
-
* @param format Format the JSON content.
|
|
2699
|
-
* @returns The object as bytes.
|
|
3103
|
+
* Number.
|
|
2700
3104
|
*/
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
3105
|
+
Number: "number",
|
|
3106
|
+
/**
|
|
3107
|
+
* Integer.
|
|
3108
|
+
*/
|
|
3109
|
+
Integer: "integer",
|
|
3110
|
+
/**
|
|
3111
|
+
* Boolean.
|
|
3112
|
+
*/
|
|
3113
|
+
Boolean: "boolean",
|
|
3114
|
+
/**
|
|
3115
|
+
* Big Integer.
|
|
3116
|
+
*/
|
|
3117
|
+
BigInt: "bigint",
|
|
3118
|
+
/**
|
|
3119
|
+
* Date.
|
|
3120
|
+
*/
|
|
3121
|
+
Date: "date",
|
|
3122
|
+
/**
|
|
3123
|
+
* Date Time.
|
|
3124
|
+
*/
|
|
3125
|
+
DateTime: "datetime",
|
|
3126
|
+
/**
|
|
3127
|
+
* Time.
|
|
3128
|
+
*/
|
|
3129
|
+
Time: "time",
|
|
3130
|
+
/**
|
|
3131
|
+
* Object.
|
|
3132
|
+
*/
|
|
3133
|
+
Object: "object",
|
|
3134
|
+
/**
|
|
3135
|
+
* Uint8Array.
|
|
3136
|
+
*/
|
|
3137
|
+
Uint8Array: "uint8array"
|
|
3138
|
+
};
|
|
3139
|
+
|
|
3140
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3141
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3142
|
+
/**
|
|
3143
|
+
* Coerce an object from one type to another.
|
|
3144
|
+
*/
|
|
3145
|
+
class Coerce {
|
|
3146
|
+
/**
|
|
3147
|
+
* Coerce the value to a string.
|
|
3148
|
+
* @param value The value to coerce.
|
|
3149
|
+
* @throws TypeError If the value can not be coerced.
|
|
3150
|
+
* @returns The value if it can be coerced.
|
|
3151
|
+
*/
|
|
3152
|
+
static string(value) {
|
|
3153
|
+
if (Is.undefined(value)) {
|
|
3154
|
+
return value;
|
|
3155
|
+
}
|
|
3156
|
+
if (Is.string(value)) {
|
|
3157
|
+
return value;
|
|
3158
|
+
}
|
|
3159
|
+
if (Is.number(value)) {
|
|
3160
|
+
return value.toString();
|
|
3161
|
+
}
|
|
3162
|
+
if (Is.boolean(value)) {
|
|
3163
|
+
return value ? "true" : "false";
|
|
3164
|
+
}
|
|
3165
|
+
if (Is.date(value)) {
|
|
3166
|
+
return value.toISOString();
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
/**
|
|
3170
|
+
* Coerce the value to a number.
|
|
3171
|
+
* @param value The value to coerce.
|
|
3172
|
+
* @throws TypeError If the value can not be coerced.
|
|
3173
|
+
* @returns The value if it can be coerced.
|
|
3174
|
+
*/
|
|
3175
|
+
static number(value) {
|
|
3176
|
+
if (Is.undefined(value)) {
|
|
3177
|
+
return value;
|
|
3178
|
+
}
|
|
3179
|
+
if (Is.number(value)) {
|
|
3180
|
+
return value;
|
|
3181
|
+
}
|
|
3182
|
+
if (Is.string(value)) {
|
|
3183
|
+
const parsed = Number.parseFloat(value);
|
|
3184
|
+
if (Is.number(parsed)) {
|
|
3185
|
+
return parsed;
|
|
3186
|
+
}
|
|
3187
|
+
}
|
|
3188
|
+
if (Is.boolean(value)) {
|
|
3189
|
+
return value ? 1 : 0;
|
|
3190
|
+
}
|
|
3191
|
+
if (Is.date(value)) {
|
|
3192
|
+
return value.getTime();
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
/**
|
|
3196
|
+
* Coerce the value to an integer.
|
|
3197
|
+
* @param value The value to coerce.
|
|
3198
|
+
* @throws TypeError If the value can not be coerced.
|
|
3199
|
+
* @returns The value if it can be coerced.
|
|
3200
|
+
*/
|
|
3201
|
+
static integer(value) {
|
|
3202
|
+
const num = Coerce.number(value);
|
|
3203
|
+
if (!Is.undefined(num)) {
|
|
3204
|
+
return Math.trunc(num);
|
|
3205
|
+
}
|
|
3206
|
+
}
|
|
3207
|
+
/**
|
|
3208
|
+
* Coerce the value to a bigint.
|
|
3209
|
+
* @param value The value to coerce.
|
|
3210
|
+
* @throws TypeError If the value can not be coerced.
|
|
3211
|
+
* @returns The value if it can be coerced.
|
|
3212
|
+
*/
|
|
3213
|
+
static bigint(value) {
|
|
3214
|
+
if (Is.undefined(value)) {
|
|
3215
|
+
return value;
|
|
3216
|
+
}
|
|
3217
|
+
if (Is.bigint(value)) {
|
|
3218
|
+
return value;
|
|
3219
|
+
}
|
|
3220
|
+
if (Is.number(value)) {
|
|
3221
|
+
return BigInt(value);
|
|
3222
|
+
}
|
|
3223
|
+
if (Is.string(value)) {
|
|
3224
|
+
const parsed = Number.parseFloat(value);
|
|
3225
|
+
if (Is.integer(parsed)) {
|
|
3226
|
+
return BigInt(parsed);
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
if (Is.boolean(value)) {
|
|
3230
|
+
return value ? 1n : 0n;
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
/**
|
|
3234
|
+
* Coerce the value to a boolean.
|
|
3235
|
+
* @param value The value to coerce.
|
|
3236
|
+
* @throws TypeError If the value can not be coerced.
|
|
3237
|
+
* @returns The value if it can be coerced.
|
|
3238
|
+
*/
|
|
3239
|
+
static boolean(value) {
|
|
3240
|
+
if (Is.undefined(value)) {
|
|
3241
|
+
return value;
|
|
3242
|
+
}
|
|
3243
|
+
if (Is.boolean(value)) {
|
|
3244
|
+
return value;
|
|
3245
|
+
}
|
|
3246
|
+
if (Is.number(value)) {
|
|
3247
|
+
// eslint-disable-next-line no-unneeded-ternary
|
|
3248
|
+
return value ? true : false;
|
|
3249
|
+
}
|
|
3250
|
+
if (Is.string(value)) {
|
|
3251
|
+
if (/true/i.test(value)) {
|
|
3252
|
+
return true;
|
|
3253
|
+
}
|
|
3254
|
+
if (/false/i.test(value)) {
|
|
3255
|
+
return false;
|
|
3256
|
+
}
|
|
2704
3257
|
}
|
|
2705
|
-
const json = format ? JSON.stringify(obj, undefined, "\t") : JSON.stringify(obj);
|
|
2706
|
-
return Converter.utf8ToBytes(json);
|
|
2707
3258
|
}
|
|
2708
3259
|
/**
|
|
2709
|
-
*
|
|
2710
|
-
* @param
|
|
2711
|
-
* @
|
|
2712
|
-
* @
|
|
3260
|
+
* Coerce the value to a date.
|
|
3261
|
+
* @param value The value to coerce.
|
|
3262
|
+
* @throws TypeError If the value can not be coerced.
|
|
3263
|
+
* @returns The value if it can be coerced.
|
|
2713
3264
|
*/
|
|
2714
|
-
static
|
|
2715
|
-
if (Is.
|
|
2716
|
-
return
|
|
3265
|
+
static date(value) {
|
|
3266
|
+
if (Is.undefined(value)) {
|
|
3267
|
+
return value;
|
|
2717
3268
|
}
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
return JSON.parse(utf8);
|
|
3269
|
+
if (Is.date(value)) {
|
|
3270
|
+
return value;
|
|
2721
3271
|
}
|
|
2722
|
-
|
|
2723
|
-
|
|
3272
|
+
if (Is.number(value)) {
|
|
3273
|
+
return new Date(value);
|
|
2724
3274
|
}
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
static clone(obj) {
|
|
2732
|
-
if (Is.undefined(obj)) {
|
|
2733
|
-
return undefined;
|
|
3275
|
+
if (Is.string(value)) {
|
|
3276
|
+
const dt = new Date(value);
|
|
3277
|
+
if (!Number.isNaN(dt.getTime())) {
|
|
3278
|
+
const utc = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate());
|
|
3279
|
+
return new Date(utc);
|
|
3280
|
+
}
|
|
2734
3281
|
}
|
|
2735
|
-
return structuredClone(obj);
|
|
2736
3282
|
}
|
|
2737
3283
|
/**
|
|
2738
|
-
*
|
|
2739
|
-
* @param
|
|
2740
|
-
* @
|
|
2741
|
-
* @returns The
|
|
3284
|
+
* Coerce the value to a date/time.
|
|
3285
|
+
* @param value The value to coerce.
|
|
3286
|
+
* @throws TypeError If the value can not be coerced.
|
|
3287
|
+
* @returns The value if it can be coerced.
|
|
2742
3288
|
*/
|
|
2743
|
-
static
|
|
2744
|
-
if (Is.
|
|
2745
|
-
return
|
|
3289
|
+
static dateTime(value) {
|
|
3290
|
+
if (Is.undefined(value)) {
|
|
3291
|
+
return value;
|
|
2746
3292
|
}
|
|
2747
|
-
if (Is.
|
|
2748
|
-
return
|
|
3293
|
+
if (Is.date(value)) {
|
|
3294
|
+
return value;
|
|
2749
3295
|
}
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
ObjectHelper.propertySet(obj1Clone, key, obj2[key]);
|
|
2759
|
-
}
|
|
3296
|
+
if (Is.number(value)) {
|
|
3297
|
+
return new Date(value);
|
|
3298
|
+
}
|
|
3299
|
+
if (Is.string(value)) {
|
|
3300
|
+
const dt = new Date(value);
|
|
3301
|
+
if (!Number.isNaN(dt.getTime())) {
|
|
3302
|
+
const utc = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate(), dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
|
|
3303
|
+
return new Date(utc);
|
|
2760
3304
|
}
|
|
2761
3305
|
}
|
|
2762
|
-
return obj1Clone;
|
|
2763
3306
|
}
|
|
2764
3307
|
/**
|
|
2765
|
-
*
|
|
2766
|
-
* @param
|
|
2767
|
-
* @
|
|
2768
|
-
* @
|
|
2769
|
-
* @returns True is the objects are equal.
|
|
3308
|
+
* Coerce the value to a time.
|
|
3309
|
+
* @param value The value to coerce.
|
|
3310
|
+
* @throws TypeError If the value can not be coerced.
|
|
3311
|
+
* @returns The value if it can be coerced.
|
|
2770
3312
|
*/
|
|
2771
|
-
static
|
|
2772
|
-
if (
|
|
2773
|
-
return
|
|
3313
|
+
static time(value) {
|
|
3314
|
+
if (Is.undefined(value)) {
|
|
3315
|
+
return value;
|
|
2774
3316
|
}
|
|
2775
|
-
|
|
2776
|
-
}
|
|
2777
|
-
/**
|
|
2778
|
-
* Get the property of an unknown object.
|
|
2779
|
-
* @param obj The object to get the property from.
|
|
2780
|
-
* @param property The property to get, can be separated by dots for nested path.
|
|
2781
|
-
* @returns The property.
|
|
2782
|
-
*/
|
|
2783
|
-
static propertyGet(obj, property) {
|
|
2784
|
-
if (property.includes(".")) {
|
|
2785
|
-
const parts = property.split(".");
|
|
2786
|
-
let value = obj;
|
|
2787
|
-
for (const part of parts) {
|
|
2788
|
-
if (Is.object(value)) {
|
|
2789
|
-
value = value[part];
|
|
2790
|
-
}
|
|
2791
|
-
else {
|
|
2792
|
-
return undefined;
|
|
2793
|
-
}
|
|
2794
|
-
}
|
|
3317
|
+
if (Is.date(value)) {
|
|
2795
3318
|
return value;
|
|
2796
3319
|
}
|
|
2797
|
-
|
|
3320
|
+
if (Is.number(value)) {
|
|
3321
|
+
const dt = new Date(value);
|
|
3322
|
+
dt.setFullYear(1970, 0, 1);
|
|
3323
|
+
return dt;
|
|
3324
|
+
}
|
|
3325
|
+
if (Is.string(value)) {
|
|
3326
|
+
const dt = new Date(value);
|
|
3327
|
+
if (!Number.isNaN(dt.getTime())) {
|
|
3328
|
+
const utc = Date.UTC(1970, 0, 1, dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
|
|
3329
|
+
return new Date(utc);
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
2798
3332
|
}
|
|
2799
3333
|
/**
|
|
2800
|
-
*
|
|
2801
|
-
* @param
|
|
2802
|
-
* @
|
|
2803
|
-
* @
|
|
3334
|
+
* Coerce the value to an object.
|
|
3335
|
+
* @param value The value to coerce.
|
|
3336
|
+
* @throws TypeError If the value can not be coerced.
|
|
3337
|
+
* @returns The value if it can be coerced.
|
|
2804
3338
|
*/
|
|
2805
|
-
static
|
|
2806
|
-
if (Is.
|
|
2807
|
-
|
|
3339
|
+
static object(value) {
|
|
3340
|
+
if (Is.undefined(value)) {
|
|
3341
|
+
return value;
|
|
3342
|
+
}
|
|
3343
|
+
if (Is.object(value)) {
|
|
3344
|
+
return value;
|
|
3345
|
+
}
|
|
3346
|
+
if (Is.stringValue(value)) {
|
|
3347
|
+
try {
|
|
3348
|
+
return JSON.parse(value);
|
|
3349
|
+
}
|
|
3350
|
+
catch { }
|
|
2808
3351
|
}
|
|
2809
3352
|
}
|
|
2810
3353
|
/**
|
|
2811
|
-
*
|
|
2812
|
-
* @param
|
|
2813
|
-
* @
|
|
3354
|
+
* Coerce the value to a Uint8Array.
|
|
3355
|
+
* @param value The value to coerce.
|
|
3356
|
+
* @throws TypeError If the value can not be coerced.
|
|
3357
|
+
* @returns The value if it can be coerced.
|
|
2814
3358
|
*/
|
|
2815
|
-
static
|
|
2816
|
-
if (Is.
|
|
2817
|
-
|
|
3359
|
+
static uint8Array(value) {
|
|
3360
|
+
if (Is.undefined(value)) {
|
|
3361
|
+
return value;
|
|
3362
|
+
}
|
|
3363
|
+
if (Is.string(value)) {
|
|
3364
|
+
if (Is.stringHex(value.toLowerCase(), true)) {
|
|
3365
|
+
return Converter.hexToBytes(value.toLowerCase());
|
|
3366
|
+
}
|
|
3367
|
+
if (Is.stringBase64(value)) {
|
|
3368
|
+
return Converter.base64ToBytes(value);
|
|
3369
|
+
}
|
|
2818
3370
|
}
|
|
2819
3371
|
}
|
|
2820
3372
|
/**
|
|
2821
|
-
*
|
|
2822
|
-
* @param
|
|
2823
|
-
* @param
|
|
2824
|
-
* @returns The
|
|
2825
|
-
*/
|
|
2826
|
-
static
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
3373
|
+
* Coerces a value based on the coercion type.
|
|
3374
|
+
* @param value The value to coerce.
|
|
3375
|
+
* @param type The coercion type to perform.
|
|
3376
|
+
* @returns The coerced value.
|
|
3377
|
+
*/
|
|
3378
|
+
static byType(value, type) {
|
|
3379
|
+
switch (type) {
|
|
3380
|
+
case CoerceType.String:
|
|
3381
|
+
return Coerce.string(value);
|
|
3382
|
+
case CoerceType.Number:
|
|
3383
|
+
return Coerce.number(value);
|
|
3384
|
+
case CoerceType.Integer:
|
|
3385
|
+
return Coerce.integer(value);
|
|
3386
|
+
case CoerceType.BigInt:
|
|
3387
|
+
return Coerce.bigint(value);
|
|
3388
|
+
case CoerceType.Boolean:
|
|
3389
|
+
return Coerce.boolean(value);
|
|
3390
|
+
case CoerceType.Date:
|
|
3391
|
+
return Coerce.date(value);
|
|
3392
|
+
case CoerceType.DateTime:
|
|
3393
|
+
return Coerce.dateTime(value);
|
|
3394
|
+
case CoerceType.Time:
|
|
3395
|
+
return Coerce.time(value);
|
|
3396
|
+
case CoerceType.Object:
|
|
3397
|
+
return Coerce.object(value);
|
|
3398
|
+
case CoerceType.Uint8Array:
|
|
3399
|
+
return Coerce.uint8Array(value);
|
|
3400
|
+
default:
|
|
3401
|
+
return value;
|
|
2833
3402
|
}
|
|
2834
|
-
return obj;
|
|
2835
3403
|
}
|
|
3404
|
+
}
|
|
3405
|
+
|
|
3406
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3407
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3408
|
+
/**
|
|
3409
|
+
* Class to help with filenames.
|
|
3410
|
+
*/
|
|
3411
|
+
class FilenameHelper {
|
|
2836
3412
|
/**
|
|
2837
|
-
*
|
|
2838
|
-
* @param
|
|
2839
|
-
* @
|
|
2840
|
-
* @returns The partial object.
|
|
3413
|
+
* Replaces any unsafe characters in the filename.
|
|
3414
|
+
* @param filename The filename to make safe.
|
|
3415
|
+
* @returns The safe filename.
|
|
2841
3416
|
*/
|
|
2842
|
-
static
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
delete result[key];
|
|
2847
|
-
}
|
|
2848
|
-
return result;
|
|
3417
|
+
static safeFilename(filename) {
|
|
3418
|
+
let safe = Coerce.string(filename);
|
|
3419
|
+
if (Is.empty(safe)) {
|
|
3420
|
+
return "";
|
|
2849
3421
|
}
|
|
2850
|
-
|
|
3422
|
+
// Common non filename characters
|
|
3423
|
+
safe = safe.replace(/["*/:<>?\\|]/g, "_");
|
|
3424
|
+
// Windows non filename characters
|
|
3425
|
+
safe = safe.replace(/^(con|prn|aux|nul|com\d|lpt\d)$/i, "_");
|
|
3426
|
+
// Control characters
|
|
3427
|
+
safe = safe.replace(/[\u0000-\u001F\u0080-\u009F]/g, "_");
|
|
3428
|
+
// Relative paths
|
|
3429
|
+
safe = safe.replace(/^\.+/, "_");
|
|
3430
|
+
// Trailing periods
|
|
3431
|
+
safe = safe.replace(/\.+$/, "");
|
|
3432
|
+
return safe;
|
|
2851
3433
|
}
|
|
2852
3434
|
}
|
|
2853
3435
|
|
|
@@ -2869,6 +3451,32 @@ class RandomHelper {
|
|
|
2869
3451
|
}
|
|
2870
3452
|
}
|
|
2871
3453
|
|
|
3454
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3455
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3456
|
+
/**
|
|
3457
|
+
* Class to help with uint8 arrays.
|
|
3458
|
+
*/
|
|
3459
|
+
class Uint8ArrayHelper {
|
|
3460
|
+
/**
|
|
3461
|
+
* Concatenate multiple arrays.
|
|
3462
|
+
* @param arrays The array to concatenate.
|
|
3463
|
+
* @returns The combined array.
|
|
3464
|
+
*/
|
|
3465
|
+
static concat(arrays) {
|
|
3466
|
+
let totalLength = 0;
|
|
3467
|
+
for (const array of arrays) {
|
|
3468
|
+
totalLength += array.length;
|
|
3469
|
+
}
|
|
3470
|
+
const concatBytes = new Uint8Array(totalLength);
|
|
3471
|
+
let offset = 0;
|
|
3472
|
+
for (const array of arrays) {
|
|
3473
|
+
concatBytes.set(array, offset);
|
|
3474
|
+
offset += array.length;
|
|
3475
|
+
}
|
|
3476
|
+
return concatBytes;
|
|
3477
|
+
}
|
|
3478
|
+
}
|
|
3479
|
+
|
|
2872
3480
|
// Copyright 2024 IOTA Stiftung.
|
|
2873
3481
|
// SPDX-License-Identifier: Apache-2.0.
|
|
2874
3482
|
/**
|
|
@@ -2881,7 +3489,7 @@ const CompressionType = {
|
|
|
2881
3489
|
*/
|
|
2882
3490
|
Gzip: "gzip",
|
|
2883
3491
|
/**
|
|
2884
|
-
*
|
|
3492
|
+
* Deflate.
|
|
2885
3493
|
*/
|
|
2886
3494
|
Deflate: "deflate"
|
|
2887
3495
|
};
|
|
@@ -3365,6 +3973,19 @@ class AsyncCache {
|
|
|
3365
3973
|
static async get(key) {
|
|
3366
3974
|
return AsyncCache._cache[key]?.response;
|
|
3367
3975
|
}
|
|
3976
|
+
/**
|
|
3977
|
+
* Set an entry into the cache.
|
|
3978
|
+
* @param key The key to set in the cache.
|
|
3979
|
+
* @param value The value to set in the cache.
|
|
3980
|
+
* @param ttlMs The TTL of the entry in the cache in ms, defaults to 1s.
|
|
3981
|
+
* @returns Nothing.
|
|
3982
|
+
*/
|
|
3983
|
+
static async set(key, value, ttlMs) {
|
|
3984
|
+
AsyncCache._cache[key] = {
|
|
3985
|
+
response: Promise.resolve(value),
|
|
3986
|
+
expires: ttlMs === 0 ? 0 : Date.now() + (ttlMs ?? 1000)
|
|
3987
|
+
};
|
|
3988
|
+
}
|
|
3368
3989
|
/**
|
|
3369
3990
|
* Remove an entry from the cache.
|
|
3370
3991
|
* @param key The key to remove from the cache.
|
|
@@ -3419,11 +4040,10 @@ class Compression {
|
|
|
3419
4040
|
Guards.uint8Array(Compression._CLASS_NAME, "bytes", bytes);
|
|
3420
4041
|
Guards.arrayOneOf(Compression._CLASS_NAME, "type", type, Object.values(CompressionType));
|
|
3421
4042
|
const blob = new Blob([bytes]);
|
|
3422
|
-
const
|
|
3423
|
-
const
|
|
3424
|
-
const compressedBlob = await new Response(
|
|
3425
|
-
const
|
|
3426
|
-
const compressedBytes = new Uint8Array(ab);
|
|
4043
|
+
const compressionStream = new CompressionStream(type);
|
|
4044
|
+
const compressionPipe = blob.stream().pipeThrough(compressionStream);
|
|
4045
|
+
const compressedBlob = await new Response(compressionPipe).blob();
|
|
4046
|
+
const compressedBytes = new Uint8Array(await compressedBlob.arrayBuffer());
|
|
3427
4047
|
// GZIP header contains a byte which specifies the OS the
|
|
3428
4048
|
// compression was performed on. We set this to 3 (Unix) to ensure
|
|
3429
4049
|
// that we produce consistent results.
|
|
@@ -3442,11 +4062,10 @@ class Compression {
|
|
|
3442
4062
|
Guards.uint8Array(Compression._CLASS_NAME, "compressedBytes", compressedBytes);
|
|
3443
4063
|
Guards.arrayOneOf(Compression._CLASS_NAME, "type", type, Object.values(CompressionType));
|
|
3444
4064
|
const blob = new Blob([compressedBytes]);
|
|
3445
|
-
const
|
|
3446
|
-
const
|
|
3447
|
-
const decompressedBlob = await new Response(
|
|
3448
|
-
|
|
3449
|
-
return new Uint8Array(ab);
|
|
4065
|
+
const decompressionStream = new DecompressionStream(type);
|
|
4066
|
+
const decompressionPipe = blob.stream().pipeThrough(decompressionStream);
|
|
4067
|
+
const decompressedBlob = await new Response(decompressionPipe).blob();
|
|
4068
|
+
return new Uint8Array(await decompressedBlob.bytes());
|
|
3450
4069
|
}
|
|
3451
4070
|
}
|
|
3452
4071
|
|
|
@@ -3504,6 +4123,7 @@ class Validation {
|
|
|
3504
4123
|
* @param options Additional options for the validation.
|
|
3505
4124
|
* @param options.minLength The minimum length of the string.
|
|
3506
4125
|
* @param options.maxLength The maximum length of the string.
|
|
4126
|
+
* @param options.format Specific format to check.
|
|
3507
4127
|
* @returns True if the value is a valid string.
|
|
3508
4128
|
*/
|
|
3509
4129
|
static string(property, value, failures, fieldNameResource, options) {
|
|
@@ -3522,6 +4142,47 @@ class Validation {
|
|
|
3522
4142
|
const maxLimitDefined = Is.integer(maxLength);
|
|
3523
4143
|
const belowMin = minLimitDefined && value.length < minLength;
|
|
3524
4144
|
const aboveMax = maxLimitDefined && value.length > maxLength;
|
|
4145
|
+
if (options?.format === "base58" && !Is.stringBase58(value)) {
|
|
4146
|
+
failures.push({
|
|
4147
|
+
property,
|
|
4148
|
+
reason: "validation.beTextBase58",
|
|
4149
|
+
properties: {
|
|
4150
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
4151
|
+
value
|
|
4152
|
+
}
|
|
4153
|
+
});
|
|
4154
|
+
}
|
|
4155
|
+
else if (options?.format === "base64" && !Is.stringBase64(value)) {
|
|
4156
|
+
failures.push({
|
|
4157
|
+
property,
|
|
4158
|
+
reason: "validation.beTextBase64",
|
|
4159
|
+
properties: {
|
|
4160
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
4161
|
+
value
|
|
4162
|
+
}
|
|
4163
|
+
});
|
|
4164
|
+
}
|
|
4165
|
+
else if (options?.format === "hex" && !Is.stringHex(value)) {
|
|
4166
|
+
failures.push({
|
|
4167
|
+
property,
|
|
4168
|
+
reason: "validation.beTextHex",
|
|
4169
|
+
properties: {
|
|
4170
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
4171
|
+
value
|
|
4172
|
+
}
|
|
4173
|
+
});
|
|
4174
|
+
}
|
|
4175
|
+
else if (Is.regexp(options?.format) && !options.format.test(value)) {
|
|
4176
|
+
failures.push({
|
|
4177
|
+
property,
|
|
4178
|
+
reason: "validation.beTextRegExp",
|
|
4179
|
+
properties: {
|
|
4180
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
4181
|
+
value,
|
|
4182
|
+
format: options?.format
|
|
4183
|
+
}
|
|
4184
|
+
});
|
|
4185
|
+
}
|
|
3525
4186
|
if (minLimitDefined && maxLimitDefined && (belowMin || aboveMax)) {
|
|
3526
4187
|
failures.push({
|
|
3527
4188
|
property,
|
|
@@ -4181,4 +4842,4 @@ class Validation {
|
|
|
4181
4842
|
}
|
|
4182
4843
|
}
|
|
4183
4844
|
|
|
4184
|
-
export { AlreadyExistsError, ArrayHelper, AsyncCache, Base32, Base64, Base64Url, BaseError, BitString, Coerce, ComponentFactory, Compression, CompressionType, ConflictError, Converter, ErrorHelper, Factory, FilenameHelper, GeneralError, GuardError, Guards, HexHelper, I18n, Is, JsonHelper, NotFoundError, NotImplementedError, NotSupportedError, ObjectHelper, RandomHelper, StringHelper, UnauthorizedError, UnprocessableError, Url, Urn, Validation, ValidationError };
|
|
4845
|
+
export { AlreadyExistsError, ArrayHelper, AsyncCache, Base32, Base58, Base64, Base64Url, BaseError, BitString, Coerce, CoerceType, ComponentFactory, Compression, CompressionType, ConflictError, Converter, EnvHelper, ErrorHelper, Factory, FilenameHelper, GeneralError, GuardError, Guards, HexHelper, I18n, Is, JsonHelper, NotFoundError, NotImplementedError, NotSupportedError, ObjectHelper, RandomHelper, StringHelper, Uint8ArrayHelper, UnauthorizedError, UnprocessableError, Url, Urn, Validation, ValidationError };
|