@twin.org/core 0.0.1-next.3 → 0.0.1-next.31

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