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

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 +1294 -730
  2. package/dist/esm/index.mjs +1292 -731
  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 +9 -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 +102 -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,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 typeof json === "object";
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;
@@ -137,6 +142,16 @@ class Is {
137
142
  // eslint-disable-next-line unicorn/better-regex
138
143
  /^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2}==|[A-Za-z0-9-_]{3}=)?$/.test(value));
139
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));
154
+ }
140
155
  /**
141
156
  * Is the value a hex string.
142
157
  * @param value The value to test.
@@ -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 > len2 ? len2 : 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
- * Reset all the instances.
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,244 +2101,252 @@ 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
- * Class to perform internationalization.
2106
+ * Convert arrays to and from different formats.
1907
2107
  */
1908
- class I18n {
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
- * Change handler for the locale being updated.
2110
+ * Lookup table for encoding.
1925
2111
  * @internal
1926
2112
  */
1927
- static _localeChangedHandlers = {};
2113
+ static _ENCODE_LOOKUP;
1928
2114
  /**
1929
- * Change handler for the dictionaries being updated.
2115
+ * Lookup table for decoding.
1930
2116
  * @internal
1931
2117
  */
1932
- static _dictionaryChangedHandlers = {};
2118
+ static _DECODE_LOOKUP;
1933
2119
  /**
1934
- * Set the locale.
1935
- * @param locale The new locale.
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 setLocale(locale) {
1938
- I18n._currentLocale = locale;
1939
- for (const callback in I18n._localeChangedHandlers) {
1940
- I18n._localeChangedHandlers[callback](I18n._currentLocale);
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
- * Get the locale.
1945
- * @returns The current locale.
2157
+ * Convert a UTF8 string to raw array.
2158
+ * @param utf8 The text to decode.
2159
+ * @returns The array.
1946
2160
  */
1947
- static getLocale() {
1948
- return I18n._currentLocale;
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
- * Add a locale dictionary.
1952
- * @param locale The locale.
1953
- * @param dictionary The dictionary to add.
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 addDictionary(locale, dictionary) {
1956
- const mergedKeys = {};
1957
- I18n.flattenTranslationKeys(dictionary, "", mergedKeys);
1958
- I18n._localeDictionaries[locale] = mergedKeys;
1959
- for (const callback in I18n._dictionaryChangedHandlers) {
1960
- I18n._dictionaryChangedHandlers[callback](I18n._currentLocale);
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
- * Get a locale dictionary.
1965
- * @param locale The locale.
1966
- * @returns The dictionary of undefined if it does not exist.
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 getDictionary(locale) {
1969
- return I18n._localeDictionaries[locale];
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
- * Get all the locale dictionaries.
1973
- * @returns The dictionaries.
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 getAllDictionaries() {
1976
- return I18n._localeDictionaries;
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
- * Add a locale changed handler.
1980
- * @param id The id of the handler.
1981
- * @param handler The handler to add.
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 addLocaleHandler(id, handler) {
1984
- I18n._localeChangedHandlers[id] = handler;
2255
+ static hexToUtf8(hex) {
2256
+ return Converter.bytesToUtf8(Converter.hexToBytes(HexHelper.stripPrefix(hex)));
1985
2257
  }
1986
2258
  /**
1987
- * Remove a locale changed handler.
1988
- * @param id The id of the handler.
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 removeLocaleHandler(id) {
1991
- delete I18n._localeChangedHandlers[id];
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
- * Add a dictionary changed handler.
1995
- * @param id The id of the handler.
1996
- * @param handler The handler to add.
2271
+ * Convert a binary string to bytes.
2272
+ * @param binary The binary string.
2273
+ * @returns The bytes.
1997
2274
  */
1998
- static addDictionaryHandler(id, handler) {
1999
- I18n._dictionaryChangedHandlers[id] = handler;
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
- * Remove a dictionary changed handler.
2003
- * @param id The id of the handler.
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 removeDictionaryHandler(id) {
2006
- delete I18n._dictionaryChangedHandlers[id];
2287
+ static bytesToBase64(bytes) {
2288
+ return Base64.encode(bytes);
2007
2289
  }
2008
2290
  /**
2009
- * Format a message.
2010
- * @param key The key of the message to format.
2011
- * @param values The values to substitute into the message.
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 formatMessage(key, values, overrideLocale) {
2016
- let cl = overrideLocale ?? I18n._currentLocale;
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
- * Check if the dictionaries have a message for the given key.
2037
- * @param key The key to check for existence.
2038
- * @returns True if the key exists.
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 hasMessage(key) {
2041
- return Is.string(I18n._localeDictionaries[I18n._currentLocale]?.[key]);
2303
+ static bytesToBase64Url(bytes) {
2304
+ return Base64Url.encode(bytes);
2042
2305
  }
2043
2306
  /**
2044
- * Flatten the translation property paths for faster lookup.
2045
- * @param translation The translation to merge.
2046
- * @param propertyPath The current root path.
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 flattenTranslationKeys(translation, propertyPath, mergedKeys) {
2051
- for (const key in translation) {
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
- * Format Errors and returns just their messages.
2072
- * @param error The error to format.
2073
- * @returns The error formatted including any inner errors.
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 formatErrors(error) {
2076
- return ErrorHelper.localizeErrors(error).map(e => e.message);
2319
+ static bytesToBase58(bytes) {
2320
+ return Base58.encode(bytes);
2077
2321
  }
2078
2322
  /**
2079
- * Localize the content of an error and any inner errors.
2080
- * @param error The error to format.
2081
- * @returns The localized version of the errors flattened.
2323
+ * Convert a base58 string to bytes.
2324
+ * @param base58 The base58 string.
2325
+ * @returns The bytes.
2082
2326
  */
2083
- static localizeErrors(error) {
2084
- const formattedErrors = [];
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
- * Localize the content of an error and any inner errors.
2121
- * @param error The error to format.
2122
- * @returns The localized version of the errors flattened.
2331
+ * Build the static lookup tables.
2332
+ * @internal
2123
2333
  */
2124
- static formatValidationErrors(error) {
2125
- if (Is.object(error.properties) &&
2126
- Object.keys(error.properties).length > 0 &&
2127
- Is.object(error.properties) &&
2128
- Is.arrayValue(error.properties.validationFailures)) {
2129
- const validationErrors = [];
2130
- for (const validationFailure of error.properties.validationFailures) {
2131
- const errorI18n = `error.${validationFailure.reason}`;
2132
- const errorMessage = I18n.hasMessage(errorI18n)
2133
- ? I18n.formatMessage(errorI18n, validationFailure.properties)
2134
- : errorI18n;
2135
- let v = `${validationFailure.property}: ${errorMessage}`;
2136
- if (Is.object(validationFailure.properties) &&
2137
- Is.notEmpty(validationFailure.properties.value)) {
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
  }
@@ -2147,707 +2354,1019 @@ class ErrorHelper {
2147
2354
  // Copyright 2024 IOTA Stiftung.
2148
2355
  // SPDX-License-Identifier: Apache-2.0.
2149
2356
  /**
2150
- * Coerce an object from one type to another.
2357
+ * Helpers methods for JSON objects.
2151
2358
  */
2152
- class Coerce {
2359
+ class JsonHelper {
2153
2360
  /**
2154
- * Coerce the value to a string.
2155
- * @param value The value to coerce.
2156
- * @throws TypeError If the value can not be coerced.
2157
- * @returns The value if it can be coerced.
2361
+ * Serializes in canonical format.
2362
+ * Based on https://www.rfc-editor.org/rfc/rfc8785.
2363
+ * @param object The object to be serialized.
2364
+ * @returns The serialized object.
2158
2365
  */
2159
- static string(value) {
2160
- if (Is.undefined(value)) {
2161
- return value;
2162
- }
2163
- if (Is.string(value)) {
2164
- return value;
2165
- }
2166
- if (Is.number(value)) {
2167
- return value.toString();
2366
+ static canonicalize(object) {
2367
+ const buffer = [];
2368
+ if (object === null ||
2369
+ typeof object !== "object" ||
2370
+ ("toJSON" in object && object.toJSON instanceof Function)) {
2371
+ // Primitive data type
2372
+ buffer.push(JSON.stringify(object));
2168
2373
  }
2169
- if (Is.boolean(value)) {
2170
- return value ? "true" : "false";
2374
+ else if (Array.isArray(object)) {
2375
+ // Array maintain element order
2376
+ const parts = [];
2377
+ for (const element of object) {
2378
+ if (element === undefined) {
2379
+ parts.push("null");
2380
+ }
2381
+ else {
2382
+ parts.push(JsonHelper.canonicalize(element));
2383
+ }
2384
+ }
2385
+ buffer.push(`[${parts.join(",")}]`);
2171
2386
  }
2172
- if (Is.date(value)) {
2173
- return value.toISOString();
2387
+ else {
2388
+ // Object sort properties
2389
+ const props = [];
2390
+ const keys = Object.keys(object).sort();
2391
+ const o = object;
2392
+ for (const key of keys) {
2393
+ if (o[key] !== undefined) {
2394
+ props.push(`${JSON.stringify(key)}:${JsonHelper.canonicalize(o[key])}`);
2395
+ }
2396
+ }
2397
+ buffer.push(`{${props.join(",")}}`);
2174
2398
  }
2399
+ return buffer.join("");
2175
2400
  }
2176
2401
  /**
2177
- * Coerce the value to a number.
2178
- * @param value The value to coerce.
2179
- * @throws TypeError If the value can not be coerced.
2180
- * @returns The value if it can be coerced.
2181
- */
2182
- static number(value) {
2183
- if (Is.undefined(value)) {
2184
- return value;
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
- }
2402
+ * Creates a RFC 6902 diff set.
2403
+ * Based on https://www.rfc-editor.org/rfc/rfc6902.
2404
+ * @param object1 The first object.
2405
+ * @param object2 The second object.
2406
+ * @returns The list of patches.
2407
+ */
2408
+ static diff(object1, object2) {
2409
+ const operations = createPatch(object1, object2);
2410
+ return operations;
2201
2411
  }
2202
2412
  /**
2203
- * Coerce the value to a bigint.
2204
- * @param value The value to coerce.
2205
- * @throws TypeError If the value can not be coerced.
2206
- * @returns The value if it can be coerced.
2413
+ * Applies a RFC 6902 diff set to an object.
2414
+ * Based on https://www.rfc-editor.org/rfc/rfc6902.
2415
+ * @param object The object to patch.
2416
+ * @param patches The second object.
2417
+ * @returns The updated object.
2207
2418
  */
2208
- static bigint(value) {
2209
- if (Is.undefined(value)) {
2210
- return value;
2211
- }
2212
- if (Is.bigint(value)) {
2213
- return value;
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);
2222
- }
2223
- }
2224
- if (Is.boolean(value)) {
2225
- return value ? 1n : 0n;
2226
- }
2419
+ static patch(object, patches) {
2420
+ return applyPatch(object, patches);
2227
2421
  }
2228
2422
  /**
2229
- * Coerce the value to a boolean.
2230
- * @param value The value to coerce.
2231
- * @throws TypeError If the value can not be coerced.
2232
- * @returns The value if it can be coerced.
2423
+ * Stringify the JSON with support for extended data types date/bigint/uint8array.
2424
+ * @param object The object to stringify.
2425
+ * @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
2426
+ * @returns The stringified object.
2233
2427
  */
2234
- static boolean(value) {
2235
- if (Is.undefined(value)) {
2236
- return value;
2237
- }
2238
- if (Is.boolean(value)) {
2239
- return value;
2240
- }
2241
- if (Is.number(value)) {
2242
- // eslint-disable-next-line no-unneeded-ternary
2243
- return value ? true : false;
2244
- }
2245
- if (Is.string(value)) {
2246
- if (/true/i.test(value)) {
2247
- return true;
2428
+ static stringifyEx(object, space) {
2429
+ // We want to keep the 'this' intact for the replacer
2430
+ // eslint-disable-next-line @typescript-eslint/unbound-method
2431
+ return JSON.stringify(object, JsonHelper.stringifyExReplacer, space);
2432
+ }
2433
+ /**
2434
+ * Parse the JSON string with support for extended data types date/bigint/uint8array.
2435
+ * @param json The object to pause.
2436
+ * @returns The object.
2437
+ */
2438
+ static parseEx(json) {
2439
+ // We want to keep the 'this' intact for the reviver
2440
+ // eslint-disable-next-line @typescript-eslint/unbound-method
2441
+ return JSON.parse(json, JsonHelper.parseExReviver);
2442
+ }
2443
+ /**
2444
+ * Replacer function to handle extended data types.
2445
+ * @param this The object.
2446
+ * @param key The key.
2447
+ * @param value The value.
2448
+ * @returns The value.
2449
+ */
2450
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2451
+ static stringifyExReplacer(key, value) {
2452
+ const rawValue = this[key];
2453
+ if (Is.bigint(rawValue)) {
2454
+ return {
2455
+ "@ext": "bigint",
2456
+ value: rawValue.toString()
2457
+ };
2458
+ }
2459
+ else if (Is.date(rawValue)) {
2460
+ return {
2461
+ "@ext": "date",
2462
+ value: rawValue.getTime()
2463
+ };
2464
+ }
2465
+ else if (Is.uint8Array(rawValue)) {
2466
+ return {
2467
+ "@ext": "uint8array",
2468
+ value: Converter.bytesToBase64(rawValue)
2469
+ };
2470
+ }
2471
+ return value;
2472
+ }
2473
+ /**
2474
+ * Reviver function to handle extended data types.
2475
+ * @param this The object.
2476
+ * @param key The key.
2477
+ * @param value The value.
2478
+ * @returns The value.
2479
+ */
2480
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2481
+ static parseExReviver(key, value) {
2482
+ if (Is.object(value)) {
2483
+ if (value["@ext"] === "bigint") {
2484
+ return BigInt(value.value);
2248
2485
  }
2249
- if (/false/i.test(value)) {
2250
- return false;
2486
+ else if (value["@ext"] === "date") {
2487
+ return new Date(value.value);
2488
+ }
2489
+ else if (value["@ext"] === "uint8array") {
2490
+ return Converter.base64ToBytes(value.value);
2251
2491
  }
2252
2492
  }
2493
+ return value;
2253
2494
  }
2495
+ }
2496
+
2497
+ /**
2498
+ * Class to help with objects.
2499
+ */
2500
+ class ObjectHelper {
2254
2501
  /**
2255
- * Coerce the value to a date.
2256
- * @param value The value to coerce.
2257
- * @throws TypeError If the value can not be coerced.
2258
- * @returns The value if it can be coerced.
2502
+ * Runtime name for the class.
2503
+ * @internal
2259
2504
  */
2260
- static date(value) {
2261
- if (Is.undefined(value)) {
2262
- return value;
2263
- }
2264
- if (Is.date(value)) {
2265
- return value;
2266
- }
2267
- if (Is.number(value)) {
2268
- return new Date(value);
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
- }
2505
+ static _CLASS_NAME = "ObjectHelper";
2506
+ /**
2507
+ * Convert an object to bytes.
2508
+ * @param obj The object to convert.
2509
+ * @param format Format the JSON content.
2510
+ * @returns The object as bytes.
2511
+ */
2512
+ static toBytes(obj, format = false) {
2513
+ if (obj === undefined) {
2514
+ return new Uint8Array();
2276
2515
  }
2516
+ const json = format ? JSON.stringify(obj, undefined, "\t") : JSON.stringify(obj);
2517
+ return Converter.utf8ToBytes(json);
2277
2518
  }
2278
2519
  /**
2279
- * Coerce the value to a date/time.
2280
- * @param value The value to coerce.
2281
- * @throws TypeError If the value can not be coerced.
2282
- * @returns The value if it can be coerced.
2520
+ * Convert a bytes to an object.
2521
+ * @param bytes The bytes to convert to an object.
2522
+ * @returns The object.
2523
+ * @throws GeneralError if there was an error parsing the JSON.
2283
2524
  */
2284
- static dateTime(value) {
2285
- if (Is.undefined(value)) {
2286
- return value;
2287
- }
2288
- if (Is.date(value)) {
2289
- return value;
2525
+ static fromBytes(bytes) {
2526
+ if (Is.empty(bytes) || bytes.length === 0) {
2527
+ return undefined;
2290
2528
  }
2291
- if (Is.number(value)) {
2292
- return new Date(value);
2529
+ try {
2530
+ const utf8 = Converter.bytesToUtf8(bytes);
2531
+ return JSON.parse(utf8);
2293
2532
  }
2294
- if (Is.string(value)) {
2295
- const dt = new Date(value);
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
- }
2533
+ catch (err) {
2534
+ throw new GeneralError(ObjectHelper._CLASS_NAME, "failedBytesToJSON", undefined, err);
2300
2535
  }
2301
2536
  }
2302
2537
  /**
2303
- * Coerce the value to a time.
2304
- * @param value The value to coerce.
2305
- * @throws TypeError If the value can not be coerced.
2306
- * @returns The value if it can be coerced.
2538
+ * Make a deep clone of an object.
2539
+ * @param obj The object to clone.
2540
+ * @returns The objects clone.
2307
2541
  */
2308
- static time(value) {
2309
- if (Is.undefined(value)) {
2310
- return value;
2542
+ static clone(obj) {
2543
+ if (Is.undefined(obj)) {
2544
+ return undefined;
2311
2545
  }
2312
- if (Is.date(value)) {
2313
- return value;
2546
+ return structuredClone(obj);
2547
+ }
2548
+ /**
2549
+ * Deep merge objects.
2550
+ * @param obj1 The first object to merge.
2551
+ * @param obj2 The second object to merge.
2552
+ * @returns The combined deep merge of the objects.
2553
+ */
2554
+ static merge(obj1, obj2) {
2555
+ if (Is.empty(obj1)) {
2556
+ return ObjectHelper.clone(obj2);
2314
2557
  }
2315
- if (Is.number(value)) {
2316
- const dt = new Date(value);
2317
- dt.setFullYear(1970, 0, 1);
2318
- return dt;
2558
+ if (Is.empty(obj2)) {
2559
+ return ObjectHelper.clone(obj1);
2319
2560
  }
2320
- if (Is.string(value)) {
2321
- const dt = new Date(value);
2322
- if (!Number.isNaN(dt.getTime())) {
2323
- const utc = Date.UTC(1970, 0, 1, dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
2324
- return new Date(utc);
2561
+ const obj1Clone = ObjectHelper.clone(obj1);
2562
+ if (Is.object(obj1Clone) && Is.object(obj2)) {
2563
+ const keys = Object.keys(obj2);
2564
+ for (const key of keys) {
2565
+ if (Is.object(obj1Clone[key]) && Is.object(obj2[key])) {
2566
+ ObjectHelper.propertySet(obj1Clone, key, ObjectHelper.merge(obj1Clone[key], obj2[key]));
2567
+ }
2568
+ else {
2569
+ ObjectHelper.propertySet(obj1Clone, key, obj2[key]);
2570
+ }
2325
2571
  }
2326
2572
  }
2573
+ return obj1Clone;
2327
2574
  }
2328
2575
  /**
2329
- * Coerce the value to an object.
2330
- * @param value The value to coerce.
2331
- * @throws TypeError If the value can not be coerced.
2332
- * @returns The value if it can be coerced.
2576
+ * Does one object equal another.
2577
+ * @param obj1 The first object to compare.
2578
+ * @param obj2 The second object to compare.
2579
+ * @param strictPropertyOrder Should the properties be in the same order, defaults to true.
2580
+ * @returns True is the objects are equal.
2333
2581
  */
2334
- static object(value) {
2335
- if (Is.undefined(value)) {
2336
- return value;
2337
- }
2338
- if (Is.object(value)) {
2339
- return value;
2582
+ static equal(obj1, obj2, strictPropertyOrder) {
2583
+ if (strictPropertyOrder ?? true) {
2584
+ return JSON.stringify(obj1) === JSON.stringify(obj2);
2340
2585
  }
2341
- if (Is.stringValue(value)) {
2342
- try {
2343
- return JSON.parse(value);
2586
+ return JsonHelper.canonicalize(obj1) === JsonHelper.canonicalize(obj2);
2587
+ }
2588
+ /**
2589
+ * Get the property of an unknown object.
2590
+ * @param obj The object to get the property from.
2591
+ * @param property The property to get, can be separated by dots for nested path.
2592
+ * @returns The property.
2593
+ */
2594
+ static propertyGet(obj, property) {
2595
+ const pathParts = property.split(".");
2596
+ let pathValue = obj;
2597
+ for (const pathPart of pathParts) {
2598
+ // Is the path part numeric i.e. an array index.
2599
+ const arrayMatch = /^(\d+)$/.exec(pathPart);
2600
+ if (arrayMatch) {
2601
+ const arrayIndex = Number.parseInt(arrayMatch[1], 10);
2602
+ if (Is.arrayValue(pathValue) && arrayIndex < pathValue.length) {
2603
+ // There is no prop name so this is a direct array index on the current object
2604
+ pathValue = pathValue[arrayIndex];
2605
+ }
2606
+ else {
2607
+ // Array index for non array object so return
2608
+ return undefined;
2609
+ }
2610
+ }
2611
+ else if (Is.object(pathValue)) {
2612
+ // No array part in path so assume object sub property
2613
+ pathValue = pathValue[pathPart];
2614
+ }
2615
+ else {
2616
+ return undefined;
2344
2617
  }
2345
- catch { }
2346
2618
  }
2619
+ return pathValue;
2347
2620
  }
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
2621
  /**
2357
- * Replaces any unsafe characters in the filename.
2358
- * @param filename The filename to make safe.
2359
- * @returns The safe filename.
2622
+ * Set the property of an unknown object.
2623
+ * @param obj The object to set the property from.
2624
+ * @param property The property to set.
2625
+ * @param value The value to set.
2626
+ * @throws GeneralError if the property target is not an object.
2360
2627
  */
2361
- static safeFilename(filename) {
2362
- let safe = Coerce.string(filename);
2363
- if (Is.empty(safe)) {
2364
- return "";
2628
+ static propertySet(obj, property, value) {
2629
+ const pathParts = property.split(".");
2630
+ let pathValue = obj;
2631
+ let parentObj;
2632
+ for (let i = 0; i < pathParts.length; i++) {
2633
+ const pathPart = pathParts[i];
2634
+ // Is the path part numeric i.e. an array index.
2635
+ const arrayMatch = /^(\d+)$/.exec(pathPart);
2636
+ const arrayIndex = arrayMatch ? Number.parseInt(arrayMatch[1], 10) : -1;
2637
+ if (i === pathParts.length - 1) {
2638
+ // Last part of path so set the value
2639
+ if (arrayIndex >= 0) {
2640
+ if (Is.array(pathValue)) {
2641
+ pathValue[arrayIndex] = value;
2642
+ }
2643
+ else {
2644
+ throw new GeneralError(ObjectHelper._CLASS_NAME, "cannotSetArrayIndex", {
2645
+ property,
2646
+ index: arrayIndex
2647
+ });
2648
+ }
2649
+ }
2650
+ else if (Is.object(pathValue)) {
2651
+ pathValue[pathPart] = value;
2652
+ }
2653
+ else {
2654
+ throw new GeneralError(ObjectHelper._CLASS_NAME, "cannotSetProperty", { property });
2655
+ }
2656
+ }
2657
+ else {
2658
+ parentObj = pathValue;
2659
+ if (Is.object(pathValue)) {
2660
+ pathValue = pathValue[pathPart];
2661
+ }
2662
+ else if (Is.array(pathValue)) {
2663
+ pathValue = pathValue[arrayIndex];
2664
+ }
2665
+ if (Is.empty(pathValue)) {
2666
+ const nextArrayMatch = /^(\d+)$/.exec(pathParts[i + 1]);
2667
+ const nextArrayIndex = nextArrayMatch ? Number.parseInt(nextArrayMatch[1], 10) : -1;
2668
+ if (nextArrayIndex >= 0) {
2669
+ pathValue = [];
2670
+ }
2671
+ else {
2672
+ pathValue = {};
2673
+ }
2674
+ if (Is.object(parentObj)) {
2675
+ parentObj[pathPart] = pathValue;
2676
+ }
2677
+ else if (Is.array(parentObj)) {
2678
+ parentObj[arrayIndex] = pathValue;
2679
+ }
2680
+ }
2681
+ }
2365
2682
  }
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
2683
  }
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
2684
  /**
2387
- * Serializes in canonical format.
2388
- * Based on https://www.rfc-editor.org/rfc/rfc8785.
2389
- * @param object The object to be serialized.
2390
- * @returns The serialized object.
2685
+ * Delete the property of an unknown object.
2686
+ * @param obj The object to set the property from.
2687
+ * @param property The property to set
2391
2688
  */
2392
- static canonicalize(object) {
2393
- const buffer = [];
2394
- if (object === null ||
2395
- typeof object !== "object" ||
2396
- ("toJSON" in object && object.toJSON instanceof Function)) {
2397
- // Primitive data type
2398
- buffer.push(JSON.stringify(object));
2689
+ static propertyDelete(obj, property) {
2690
+ if (Is.object(obj)) {
2691
+ delete obj[property];
2399
2692
  }
2400
- else if (Array.isArray(object)) {
2401
- // Array maintain element order
2402
- const parts = [];
2403
- for (const element of object) {
2404
- if (element === undefined) {
2405
- parts.push("null");
2406
- }
2407
- else {
2408
- parts.push(JsonHelper.canonicalize(element));
2693
+ }
2694
+ /**
2695
+ * Extract a property from the object, providing alternative names.
2696
+ * @param obj The object to extract from.
2697
+ * @param propertyNames The possible names for the property.
2698
+ * @param removeProperties Remove the properties from the object, defaults to true.
2699
+ * @returns The property if available.
2700
+ */
2701
+ static extractProperty(obj, propertyNames, removeProperties = true) {
2702
+ let retVal;
2703
+ if (Is.object(obj)) {
2704
+ const names = Is.string(propertyNames) ? [propertyNames] : propertyNames;
2705
+ for (const prop of names) {
2706
+ retVal ??= ObjectHelper.propertyGet(obj, prop);
2707
+ if (removeProperties) {
2708
+ ObjectHelper.propertyDelete(obj, prop);
2409
2709
  }
2410
2710
  }
2411
- buffer.push(`[${parts.join(",")}]`);
2412
2711
  }
2413
- else {
2414
- // Object sort properties
2415
- const props = [];
2416
- const keys = Object.keys(object).sort();
2417
- const o = object;
2712
+ return retVal;
2713
+ }
2714
+ /**
2715
+ * Pick a subset of properties from an object.
2716
+ * @param obj The object to pick the properties from.
2717
+ * @param keys The property keys to pick.
2718
+ * @returns The partial object.
2719
+ */
2720
+ static pick(obj, keys) {
2721
+ if (Is.object(obj) && Is.arrayValue(keys)) {
2722
+ const result = {};
2418
2723
  for (const key of keys) {
2419
- if (o[key] !== undefined) {
2420
- props.push(`${JSON.stringify(key)}:${JsonHelper.canonicalize(o[key])}`);
2421
- }
2724
+ result[key] = obj[key];
2422
2725
  }
2423
- buffer.push(`{${props.join(",")}}`);
2726
+ return result;
2424
2727
  }
2425
- return buffer.join("");
2728
+ return obj;
2426
2729
  }
2427
2730
  /**
2428
- * Creates a RFC 6902 diff set.
2429
- * Based on https://www.rfc-editor.org/rfc/rfc6902.
2430
- * @param object1 The first object.
2431
- * @param object2 The second object.
2432
- * @returns The list of patches.
2731
+ * Omit a subset of properties from an object.
2732
+ * @param obj The object to omit the properties from.
2733
+ * @param keys The property keys to omit.
2734
+ * @returns The partial object.
2433
2735
  */
2434
- static diff(object1, object2) {
2435
- const operations = createPatch(object1, object2);
2436
- return operations;
2736
+ static omit(obj, keys) {
2737
+ if (Is.object(obj) && Is.arrayValue(keys)) {
2738
+ const result = { ...obj };
2739
+ for (const key of keys) {
2740
+ delete result[key];
2741
+ }
2742
+ return result;
2743
+ }
2744
+ return obj;
2437
2745
  }
2438
- /**
2439
- * Applies a RFC 6902 diff set to an object.
2440
- * Based on https://www.rfc-editor.org/rfc/rfc6902.
2441
- * @param object The object to patch.
2442
- * @param patches The second object.
2443
- * @returns The updated object.
2444
- */
2445
- static patch(object, patches) {
2446
- return applyPatch(object, patches);
2746
+ }
2747
+
2748
+ // Copyright 2024 IOTA Stiftung.
2749
+ // SPDX-License-Identifier: Apache-2.0.
2750
+ /**
2751
+ * Environment variable helper.
2752
+ */
2753
+ class EnvHelper {
2754
+ /**
2755
+ * Get the environment variable as an object with camel cased names.
2756
+ * @param envVars The environment variables.
2757
+ * @param prefix The prefix of the environment variables, if not provided gets all.
2758
+ * @returns The object with camel cased names.
2759
+ */
2760
+ static envToJson(envVars, prefix) {
2761
+ const result = {};
2762
+ if (!Is.empty(envVars)) {
2763
+ if (Is.empty(prefix)) {
2764
+ for (const envVar in envVars) {
2765
+ if (Is.stringValue(envVars[envVar])) {
2766
+ const camelCaseName = StringHelper.camelCase(envVar.toLowerCase());
2767
+ ObjectHelper.propertySet(result, camelCaseName, envVars[envVar]);
2768
+ }
2769
+ }
2770
+ }
2771
+ else {
2772
+ for (const envVar in envVars) {
2773
+ if (envVar.startsWith(prefix) && Is.stringValue(envVars[envVar])) {
2774
+ const camelCaseName = StringHelper.camelCase(envVar.replace(prefix, "").toLowerCase());
2775
+ ObjectHelper.propertySet(result, camelCaseName, envVars[envVar]);
2776
+ }
2777
+ }
2778
+ }
2779
+ }
2780
+ return result;
2447
2781
  }
2448
2782
  }
2449
2783
 
2450
2784
  // Copyright 2024 IOTA Stiftung.
2451
2785
  // SPDX-License-Identifier: Apache-2.0.
2452
- /* eslint-disable no-bitwise */
2453
2786
  /**
2454
- * Convert arrays to and from different formats.
2787
+ * Class to perform internationalization.
2455
2788
  */
2456
- class Converter {
2789
+ class I18n {
2457
2790
  /**
2458
- * Lookup table for encoding.
2791
+ * The default translation.
2792
+ */
2793
+ static DEFAULT_LOCALE = "en";
2794
+ /**
2795
+ * Dictionaries for lookups.
2459
2796
  * @internal
2460
2797
  */
2461
- static _ENCODE_LOOKUP;
2798
+ static _localeDictionaries = {};
2462
2799
  /**
2463
- * Lookup table for decoding.
2800
+ * The current locale.
2464
2801
  * @internal
2465
2802
  */
2466
- static _DECODE_LOOKUP;
2803
+ static _currentLocale = I18n.DEFAULT_LOCALE;
2467
2804
  /**
2468
- * Encode a raw array to UTF8 string.
2469
- * @param array The bytes to encode.
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.
2805
+ * Change handler for the locale being updated.
2806
+ * @internal
2473
2807
  */
2474
- static bytesToUtf8(array, startIndex, length) {
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
- }
2808
+ static _localeChangedHandlers = {};
2809
+ /**
2810
+ * Change handler for the dictionaries being updated.
2811
+ * @internal
2812
+ */
2813
+ static _dictionaryChangedHandlers = {};
2814
+ /**
2815
+ * Set the locale.
2816
+ * @param locale The new locale.
2817
+ */
2818
+ static setLocale(locale) {
2819
+ I18n._currentLocale = locale;
2820
+ for (const callback in I18n._localeChangedHandlers) {
2821
+ I18n._localeChangedHandlers[callback](I18n._currentLocale);
2501
2822
  }
2502
- return str;
2503
2823
  }
2504
2824
  /**
2505
- * Convert a UTF8 string to raw array.
2506
- * @param utf8 The text to decode.
2507
- * @returns The array.
2825
+ * Get the locale.
2826
+ * @returns The current locale.
2508
2827
  */
2509
- static utf8ToBytes(utf8) {
2510
- const bytes = [];
2511
- for (let i = 0; i < utf8.length; i++) {
2512
- let charCode = utf8.charCodeAt(i);
2513
- if (charCode < 0x80) {
2514
- bytes.push(charCode);
2515
- }
2516
- else if (charCode < 0x800) {
2517
- bytes.push(0xc0 | (charCode >> 6), 0x80 | (charCode & 0x3f));
2828
+ static getLocale() {
2829
+ return I18n._currentLocale;
2830
+ }
2831
+ /**
2832
+ * Add a locale dictionary.
2833
+ * @param locale The locale.
2834
+ * @param dictionary The dictionary to add.
2835
+ */
2836
+ static addDictionary(locale, dictionary) {
2837
+ const mergedKeys = {};
2838
+ I18n.flattenTranslationKeys(dictionary, "", mergedKeys);
2839
+ I18n._localeDictionaries[locale] = mergedKeys;
2840
+ for (const callback in I18n._dictionaryChangedHandlers) {
2841
+ I18n._dictionaryChangedHandlers[callback](I18n._currentLocale);
2842
+ }
2843
+ }
2844
+ /**
2845
+ * Get a locale dictionary.
2846
+ * @param locale The locale.
2847
+ * @returns The dictionary of undefined if it does not exist.
2848
+ */
2849
+ static getDictionary(locale) {
2850
+ return I18n._localeDictionaries[locale];
2851
+ }
2852
+ /**
2853
+ * Get all the locale dictionaries.
2854
+ * @returns The dictionaries.
2855
+ */
2856
+ static getAllDictionaries() {
2857
+ return I18n._localeDictionaries;
2858
+ }
2859
+ /**
2860
+ * Add a locale changed handler.
2861
+ * @param id The id of the handler.
2862
+ * @param handler The handler to add.
2863
+ */
2864
+ static addLocaleHandler(id, handler) {
2865
+ I18n._localeChangedHandlers[id] = handler;
2866
+ }
2867
+ /**
2868
+ * Remove a locale changed handler.
2869
+ * @param id The id of the handler.
2870
+ */
2871
+ static removeLocaleHandler(id) {
2872
+ delete I18n._localeChangedHandlers[id];
2873
+ }
2874
+ /**
2875
+ * Add a dictionary changed handler.
2876
+ * @param id The id of the handler.
2877
+ * @param handler The handler to add.
2878
+ */
2879
+ static addDictionaryHandler(id, handler) {
2880
+ I18n._dictionaryChangedHandlers[id] = handler;
2881
+ }
2882
+ /**
2883
+ * Remove a dictionary changed handler.
2884
+ * @param id The id of the handler.
2885
+ */
2886
+ static removeDictionaryHandler(id) {
2887
+ delete I18n._dictionaryChangedHandlers[id];
2888
+ }
2889
+ /**
2890
+ * Format a message.
2891
+ * @param key The key of the message to format.
2892
+ * @param values The values to substitute into the message.
2893
+ * @param overrideLocale Override the locale.
2894
+ * @returns The formatted string.
2895
+ */
2896
+ static formatMessage(key, values, overrideLocale) {
2897
+ let cl = overrideLocale ?? I18n._currentLocale;
2898
+ if (cl.startsWith("debug-")) {
2899
+ cl = I18n.DEFAULT_LOCALE;
2900
+ }
2901
+ if (!I18n._localeDictionaries[cl]) {
2902
+ return `!!Missing ${cl}`;
2903
+ }
2904
+ if (!I18n._localeDictionaries[cl][key]) {
2905
+ return `!!Missing ${cl}.${key}`;
2906
+ }
2907
+ if (I18n._currentLocale === "debug-k") {
2908
+ return key;
2909
+ }
2910
+ let ret = new IntlMessageFormat(I18n._localeDictionaries[cl][key], cl).format(values);
2911
+ if (I18n._currentLocale === "debug-x") {
2912
+ ret = ret.replace(/[a-z]/g, "x").replace(/[A-Z]/g, "x").replace(/\d/g, "n");
2913
+ }
2914
+ return ret;
2915
+ }
2916
+ /**
2917
+ * Check if the dictionaries have a message for the given key.
2918
+ * @param key The key to check for existence.
2919
+ * @returns True if the key exists.
2920
+ */
2921
+ static hasMessage(key) {
2922
+ return Is.string(I18n._localeDictionaries[I18n._currentLocale]?.[key]);
2923
+ }
2924
+ /**
2925
+ * Flatten the translation property paths for faster lookup.
2926
+ * @param translation The translation to merge.
2927
+ * @param propertyPath The current root path.
2928
+ * @param mergedKeys The merged keys dictionary to populate.
2929
+ * @internal
2930
+ */
2931
+ static flattenTranslationKeys(translation, propertyPath, mergedKeys) {
2932
+ for (const key in translation) {
2933
+ const val = translation[key];
2934
+ const mergedPath = propertyPath.length > 0 ? `${propertyPath}.${key}` : key;
2935
+ if (Is.string(val)) {
2936
+ mergedKeys[mergedPath] = val;
2518
2937
  }
2519
- else if (charCode < 0xd800 || charCode >= 0xe000) {
2520
- bytes.push(0xe0 | (charCode >> 12), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f));
2938
+ else if (Is.object(val)) {
2939
+ I18n.flattenTranslationKeys(val, mergedPath, mergedKeys);
2521
2940
  }
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));
2941
+ }
2942
+ }
2943
+ }
2944
+
2945
+ // Copyright 2024 IOTA Stiftung.
2946
+ // SPDX-License-Identifier: Apache-2.0.
2947
+ /**
2948
+ * Error helper functions.
2949
+ */
2950
+ class ErrorHelper {
2951
+ /**
2952
+ * Format Errors and returns just their messages.
2953
+ * @param error The error to format.
2954
+ * @returns The error formatted including any inner errors.
2955
+ */
2956
+ static formatErrors(error) {
2957
+ return ErrorHelper.localizeErrors(error).map(e => e.message);
2958
+ }
2959
+ /**
2960
+ * Localize the content of an error and any inner errors.
2961
+ * @param error The error to format.
2962
+ * @returns The localized version of the errors flattened.
2963
+ */
2964
+ static localizeErrors(error) {
2965
+ const formattedErrors = [];
2966
+ if (Is.notEmpty(error)) {
2967
+ const errors = BaseError.flatten(error);
2968
+ for (const err of errors) {
2969
+ const errorNameKey = `errorNames.${StringHelper.camelCase(err.name)}`;
2970
+ const errorMessageKey = `error.${err.message}`;
2971
+ // If there is no error message then it is probably
2972
+ // from a 3rd party lib, so don't format it just display
2973
+ const hasErrorName = I18n.hasMessage(errorNameKey);
2974
+ const hasErrorMessage = I18n.hasMessage(errorMessageKey);
2975
+ const localizedError = {
2976
+ name: I18n.formatMessage(hasErrorName ? errorNameKey : "errorNames.error"),
2977
+ message: hasErrorMessage
2978
+ ? I18n.formatMessage(errorMessageKey, err.properties)
2979
+ : err.message
2980
+ };
2981
+ if (Is.stringValue(err.source)) {
2982
+ localizedError.source = err.source;
2983
+ }
2984
+ if (Is.stringValue(err.stack)) {
2985
+ // Remove the first line from the stack traces as they
2986
+ // just have the error type and message duplicated
2987
+ const lines = err.stack.split("\n");
2988
+ lines.shift();
2989
+ localizedError.stack = lines.join("\n");
2990
+ }
2991
+ const additional = ErrorHelper.formatValidationErrors(err);
2992
+ if (Is.stringValue(additional)) {
2993
+ localizedError.additional = additional;
2994
+ }
2995
+ formattedErrors.push(localizedError);
2530
2996
  }
2531
2997
  }
2532
- return Uint8Array.from(bytes);
2998
+ return formattedErrors;
2533
2999
  }
2534
3000
  /**
2535
- * Encode a raw array to hex string.
2536
- * @param array The bytes to encode.
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.
3001
+ * Localize the content of an error and any inner errors.
3002
+ * @param error The error to format.
3003
+ * @returns The localized version of the errors flattened.
2542
3004
  */
2543
- static bytesToHex(array, includePrefix = false, startIndex, length, reverse) {
2544
- let hex = "";
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]];
3005
+ static formatValidationErrors(error) {
3006
+ if (Is.object(error.properties) &&
3007
+ Object.keys(error.properties).length > 0 &&
3008
+ Is.object(error.properties) &&
3009
+ Is.arrayValue(error.properties.validationFailures)) {
3010
+ const validationErrors = [];
3011
+ for (const validationFailure of error.properties.validationFailures) {
3012
+ const errorI18n = `error.${validationFailure.reason}`;
3013
+ const errorMessage = I18n.hasMessage(errorI18n)
3014
+ ? I18n.formatMessage(errorI18n, validationFailure.properties)
3015
+ : errorI18n;
3016
+ let v = `${validationFailure.property}: ${errorMessage}`;
3017
+ if (Is.object(validationFailure.properties) &&
3018
+ Is.notEmpty(validationFailure.properties.value)) {
3019
+ v += ` = ${JSON.stringify(validationFailure.properties.value)}`;
2557
3020
  }
3021
+ validationErrors.push(v);
2558
3022
  }
3023
+ return validationErrors.join("\n");
2559
3024
  }
2560
- return includePrefix ? HexHelper.addPrefix(hex) : hex;
2561
3025
  }
3026
+ }
3027
+
3028
+ // Copyright 2024 IOTA Stiftung.
3029
+ // SPDX-License-Identifier: Apache-2.0.
3030
+ /**
3031
+ * The types the extracted data can be coerced to.
3032
+ */
3033
+ // eslint-disable-next-line @typescript-eslint/naming-convention
3034
+ const CoerceType = {
2562
3035
  /**
2563
- * Decode a hex string to raw array.
2564
- * @param hex The hex to decode.
2565
- * @param reverse Store the characters in reverse.
2566
- * @returns The array.
3036
+ * String.
2567
3037
  */
2568
- static hexToBytes(hex, reverse) {
2569
- const strippedHex = HexHelper.stripPrefix(hex);
2570
- const sizeof = strippedHex.length >> 1;
2571
- const length = sizeof << 1;
2572
- const array = new Uint8Array(sizeof);
2573
- this.buildHexLookups();
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
- }
2585
- }
2586
- return array;
2587
- }
3038
+ String: "string",
2588
3039
  /**
2589
- * Convert the UTF8 to hex.
2590
- * @param utf8 The text to convert.
2591
- * @param includePrefix Include the 0x prefix on the returned hex.
2592
- * @returns The hex version of the bytes.
3040
+ * Number.
2593
3041
  */
2594
- static utf8ToHex(utf8, includePrefix = false) {
2595
- const hex = Converter.bytesToHex(Converter.utf8ToBytes(utf8));
2596
- return includePrefix ? HexHelper.addPrefix(hex) : hex;
2597
- }
3042
+ Number: "number",
2598
3043
  /**
2599
- * Convert the hex text to text.
2600
- * @param hex The hex to convert.
2601
- * @returns The UTF8 version of the bytes.
3044
+ * Integer.
2602
3045
  */
2603
- static hexToUtf8(hex) {
2604
- return Converter.bytesToUtf8(Converter.hexToBytes(HexHelper.stripPrefix(hex)));
2605
- }
3046
+ Integer: "integer",
2606
3047
  /**
2607
- * Convert bytes to binary string.
2608
- * @param bytes The bytes to convert.
2609
- * @returns A binary string of the bytes.
3048
+ * Boolean.
2610
3049
  */
2611
- static bytesToBinary(bytes) {
2612
- const b = [];
2613
- for (let i = 0; i < bytes.length; i++) {
2614
- b.push(bytes[i].toString(2).padStart(8, "0"));
2615
- }
2616
- return b.join("");
2617
- }
3050
+ Boolean: "boolean",
2618
3051
  /**
2619
- * Convert a binary string to bytes.
2620
- * @param binary The binary string.
2621
- * @returns The bytes.
3052
+ * Big Integer.
2622
3053
  */
2623
- static binaryToBytes(binary) {
2624
- const bytes = new Uint8Array(Math.ceil(binary.length / 8));
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;
2629
- }
3054
+ BigInt: "bigint",
2630
3055
  /**
2631
- * Convert bytes to base64 string.
2632
- * @param bytes The bytes to convert.
2633
- * @returns A base64 string of the bytes.
3056
+ * Date.
2634
3057
  */
2635
- static bytesToBase64(bytes) {
2636
- return Base64.encode(bytes);
2637
- }
3058
+ Date: "date",
2638
3059
  /**
2639
- * Convert a base64 string to bytes.
2640
- * @param base64 The base64 string.
2641
- * @returns The bytes.
3060
+ * Date Time.
2642
3061
  */
2643
- static base64ToBytes(base64) {
2644
- return Base64.decode(base64);
2645
- }
3062
+ DateTime: "datetime",
2646
3063
  /**
2647
- * Convert bytes to base64 url string.
2648
- * @param bytes The bytes to convert.
2649
- * @returns A base64 url string of the bytes.
3064
+ * Time.
2650
3065
  */
2651
- static bytesToBase64Url(bytes) {
2652
- return Base64Url.encode(bytes);
2653
- }
3066
+ Time: "time",
2654
3067
  /**
2655
- * Convert a base64 url string to bytes.
2656
- * @param base64Url The base64 url string.
2657
- * @returns The bytes.
3068
+ * Object.
2658
3069
  */
2659
- static base64UrlToBytes(base64Url) {
2660
- return Base64Url.decode(base64Url);
2661
- }
3070
+ Object: "object",
2662
3071
  /**
2663
- * Build the static lookup tables.
2664
- * @internal
3072
+ * Uint8Array.
2665
3073
  */
2666
- static buildHexLookups() {
2667
- if (!Converter._ENCODE_LOOKUP || !Converter._DECODE_LOOKUP) {
2668
- const alphabet = "0123456789abcdef";
2669
- Converter._ENCODE_LOOKUP = [];
2670
- Converter._DECODE_LOOKUP = [];
2671
- for (let i = 0; i < 256; i++) {
2672
- Converter._ENCODE_LOOKUP[i] = alphabet[(i >> 4) & 0xf] + alphabet[i & 0xf];
2673
- if (i < 16) {
2674
- if (i < 10) {
2675
- Converter._DECODE_LOOKUP[0x30 + i] = i;
2676
- }
2677
- else {
2678
- Converter._DECODE_LOOKUP[0x61 - 10 + i] = i;
2679
- }
2680
- }
2681
- }
2682
- }
2683
- }
2684
- }
3074
+ Uint8Array: "uint8array"
3075
+ };
2685
3076
 
3077
+ // Copyright 2024 IOTA Stiftung.
3078
+ // SPDX-License-Identifier: Apache-2.0.
2686
3079
  /**
2687
- * Class to help with objects.
3080
+ * Coerce an object from one type to another.
2688
3081
  */
2689
- class ObjectHelper {
3082
+ class Coerce {
2690
3083
  /**
2691
- * Runtime name for the class.
2692
- * @internal
3084
+ * Coerce the value to a string.
3085
+ * @param value The value to coerce.
3086
+ * @throws TypeError If the value can not be coerced.
3087
+ * @returns The value if it can be coerced.
2693
3088
  */
2694
- static _CLASS_NAME = "ObjectHelper";
3089
+ static string(value) {
3090
+ if (Is.undefined(value)) {
3091
+ return value;
3092
+ }
3093
+ if (Is.string(value)) {
3094
+ return value;
3095
+ }
3096
+ if (Is.number(value)) {
3097
+ return value.toString();
3098
+ }
3099
+ if (Is.boolean(value)) {
3100
+ return value ? "true" : "false";
3101
+ }
3102
+ if (Is.date(value)) {
3103
+ return value.toISOString();
3104
+ }
3105
+ }
2695
3106
  /**
2696
- * Convert an object to bytes.
2697
- * @param obj The object to convert.
2698
- * @param format Format the JSON content.
2699
- * @returns The object as bytes.
3107
+ * Coerce the value to a number.
3108
+ * @param value The value to coerce.
3109
+ * @throws TypeError If the value can not be coerced.
3110
+ * @returns The value if it can be coerced.
2700
3111
  */
2701
- static toBytes(obj, format = false) {
2702
- if (obj === undefined) {
2703
- return new Uint8Array();
3112
+ static number(value) {
3113
+ if (Is.undefined(value)) {
3114
+ return value;
3115
+ }
3116
+ if (Is.number(value)) {
3117
+ return value;
3118
+ }
3119
+ if (Is.string(value)) {
3120
+ const parsed = Number.parseFloat(value);
3121
+ if (Is.number(parsed)) {
3122
+ return parsed;
3123
+ }
3124
+ }
3125
+ if (Is.boolean(value)) {
3126
+ return value ? 1 : 0;
3127
+ }
3128
+ if (Is.date(value)) {
3129
+ return value.getTime();
2704
3130
  }
2705
- const json = format ? JSON.stringify(obj, undefined, "\t") : JSON.stringify(obj);
2706
- return Converter.utf8ToBytes(json);
2707
3131
  }
2708
3132
  /**
2709
- * Convert a bytes to an object.
2710
- * @param bytes The bytes to convert to an object.
2711
- * @returns The object.
2712
- * @throws GeneralError if there was an error parsing the JSON.
3133
+ * Coerce the value to an integer.
3134
+ * @param value The value to coerce.
3135
+ * @throws TypeError If the value can not be coerced.
3136
+ * @returns The value if it can be coerced.
2713
3137
  */
2714
- static fromBytes(bytes) {
2715
- if (Is.empty(bytes) || bytes.length === 0) {
2716
- return undefined;
3138
+ static integer(value) {
3139
+ const num = Coerce.number(value);
3140
+ if (!Is.undefined(num)) {
3141
+ return Math.trunc(num);
2717
3142
  }
2718
- try {
2719
- const utf8 = Converter.bytesToUtf8(bytes);
2720
- return JSON.parse(utf8);
3143
+ }
3144
+ /**
3145
+ * Coerce the value to a bigint.
3146
+ * @param value The value to coerce.
3147
+ * @throws TypeError If the value can not be coerced.
3148
+ * @returns The value if it can be coerced.
3149
+ */
3150
+ static bigint(value) {
3151
+ if (Is.undefined(value)) {
3152
+ return value;
3153
+ }
3154
+ if (Is.bigint(value)) {
3155
+ return value;
3156
+ }
3157
+ if (Is.number(value)) {
3158
+ return BigInt(value);
2721
3159
  }
2722
- catch (err) {
2723
- throw new GeneralError(ObjectHelper._CLASS_NAME, "failedBytesToJSON", undefined, err);
3160
+ if (Is.string(value)) {
3161
+ const parsed = Number.parseFloat(value);
3162
+ if (Is.integer(parsed)) {
3163
+ return BigInt(parsed);
3164
+ }
3165
+ }
3166
+ if (Is.boolean(value)) {
3167
+ return value ? 1n : 0n;
2724
3168
  }
2725
3169
  }
2726
3170
  /**
2727
- * Make a deep clone of an object.
2728
- * @param obj The object to clone.
2729
- * @returns The objects clone.
3171
+ * Coerce the value to a boolean.
3172
+ * @param value The value to coerce.
3173
+ * @throws TypeError If the value can not be coerced.
3174
+ * @returns The value if it can be coerced.
2730
3175
  */
2731
- static clone(obj) {
2732
- if (Is.undefined(obj)) {
2733
- return undefined;
3176
+ static boolean(value) {
3177
+ if (Is.undefined(value)) {
3178
+ return value;
3179
+ }
3180
+ if (Is.boolean(value)) {
3181
+ return value;
3182
+ }
3183
+ if (Is.number(value)) {
3184
+ // eslint-disable-next-line no-unneeded-ternary
3185
+ return value ? true : false;
3186
+ }
3187
+ if (Is.string(value)) {
3188
+ if (/true/i.test(value)) {
3189
+ return true;
3190
+ }
3191
+ if (/false/i.test(value)) {
3192
+ return false;
3193
+ }
2734
3194
  }
2735
- return structuredClone(obj);
2736
3195
  }
2737
3196
  /**
2738
- * Deep merge objects.
2739
- * @param obj1 The first object to merge.
2740
- * @param obj2 The second object to merge.
2741
- * @returns The combined deep merge of the objects.
3197
+ * Coerce the value to a date.
3198
+ * @param value The value to coerce.
3199
+ * @throws TypeError If the value can not be coerced.
3200
+ * @returns The value if it can be coerced.
2742
3201
  */
2743
- static merge(obj1, obj2) {
2744
- if (Is.empty(obj1)) {
2745
- return ObjectHelper.clone(obj2);
3202
+ static date(value) {
3203
+ if (Is.undefined(value)) {
3204
+ return value;
2746
3205
  }
2747
- if (Is.empty(obj2)) {
2748
- return ObjectHelper.clone(obj1);
3206
+ if (Is.date(value)) {
3207
+ return value;
2749
3208
  }
2750
- const obj1Clone = ObjectHelper.clone(obj1);
2751
- if (Is.object(obj1Clone) && Is.object(obj2)) {
2752
- const keys = Object.keys(obj2);
2753
- for (const key of keys) {
2754
- if (Is.object(obj1Clone[key]) && Is.object(obj2[key])) {
2755
- ObjectHelper.propertySet(obj1Clone, key, ObjectHelper.merge(obj1Clone[key], obj2[key]));
2756
- }
2757
- else {
2758
- ObjectHelper.propertySet(obj1Clone, key, obj2[key]);
2759
- }
3209
+ if (Is.number(value)) {
3210
+ return new Date(value);
3211
+ }
3212
+ if (Is.string(value)) {
3213
+ const dt = new Date(value);
3214
+ if (!Number.isNaN(dt.getTime())) {
3215
+ const utc = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate());
3216
+ return new Date(utc);
2760
3217
  }
2761
3218
  }
2762
- return obj1Clone;
2763
3219
  }
2764
3220
  /**
2765
- * Does one object equal another.
2766
- * @param obj1 The first object to compare.
2767
- * @param obj2 The second object to compare.
2768
- * @param strictPropertyOrder Should the properties be in the same order, defaults to true.
2769
- * @returns True is the objects are equal.
3221
+ * Coerce the value to a date/time.
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.
2770
3225
  */
2771
- static equal(obj1, obj2, strictPropertyOrder) {
2772
- if (strictPropertyOrder ?? true) {
2773
- return JSON.stringify(obj1) === JSON.stringify(obj2);
3226
+ static dateTime(value) {
3227
+ if (Is.undefined(value)) {
3228
+ return value;
3229
+ }
3230
+ if (Is.date(value)) {
3231
+ return value;
3232
+ }
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(), dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
3240
+ return new Date(utc);
3241
+ }
2774
3242
  }
2775
- return JsonHelper.canonicalize(obj1) === JsonHelper.canonicalize(obj2);
2776
3243
  }
2777
3244
  /**
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.
3245
+ * Coerce the value to a 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.
2782
3249
  */
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
- }
3250
+ static time(value) {
3251
+ if (Is.undefined(value)) {
3252
+ return value;
3253
+ }
3254
+ if (Is.date(value)) {
2795
3255
  return value;
2796
3256
  }
2797
- return Is.object(obj) ? obj[property] : undefined;
3257
+ if (Is.number(value)) {
3258
+ const dt = new Date(value);
3259
+ dt.setFullYear(1970, 0, 1);
3260
+ return dt;
3261
+ }
3262
+ if (Is.string(value)) {
3263
+ const dt = new Date(value);
3264
+ if (!Number.isNaN(dt.getTime())) {
3265
+ const utc = Date.UTC(1970, 0, 1, dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
3266
+ return new Date(utc);
3267
+ }
3268
+ }
2798
3269
  }
2799
3270
  /**
2800
- * Set the property of an unknown object.
2801
- * @param obj The object to set the property from.
2802
- * @param property The property to set.
2803
- * @param value The value to set.
3271
+ * Coerce the value to an object.
3272
+ * @param value The value to coerce.
3273
+ * @throws TypeError If the value can not be coerced.
3274
+ * @returns The value if it can be coerced.
2804
3275
  */
2805
- static propertySet(obj, property, value) {
2806
- if (Is.object(obj)) {
2807
- obj[property] = value;
3276
+ static object(value) {
3277
+ if (Is.undefined(value)) {
3278
+ return value;
3279
+ }
3280
+ if (Is.object(value)) {
3281
+ return value;
3282
+ }
3283
+ if (Is.stringValue(value)) {
3284
+ try {
3285
+ return JSON.parse(value);
3286
+ }
3287
+ catch { }
2808
3288
  }
2809
3289
  }
2810
3290
  /**
2811
- * Delete the property of an unknown object.
2812
- * @param obj The object to set the property from.
2813
- * @param property The property to set
3291
+ * Coerce the value to a Uint8Array.
3292
+ * @param value The value to coerce.
3293
+ * @throws TypeError If the value can not be coerced.
3294
+ * @returns The value if it can be coerced.
2814
3295
  */
2815
- static propertyDelete(obj, property) {
2816
- if (Is.object(obj)) {
2817
- delete obj[property];
3296
+ static uint8Array(value) {
3297
+ if (Is.undefined(value)) {
3298
+ return value;
3299
+ }
3300
+ if (Is.string(value)) {
3301
+ if (Is.stringHex(value.toLowerCase(), true)) {
3302
+ return Converter.hexToBytes(value.toLowerCase());
3303
+ }
3304
+ if (Is.stringBase64(value)) {
3305
+ return Converter.base64ToBytes(value);
3306
+ }
2818
3307
  }
2819
3308
  }
2820
3309
  /**
2821
- * Pick a subset of properties from an object.
2822
- * @param obj The object to pick the properties from.
2823
- * @param keys The property keys to pick.
2824
- * @returns The partial object.
2825
- */
2826
- static pick(obj, keys) {
2827
- if (Is.object(obj) && Is.arrayValue(keys)) {
2828
- const result = {};
2829
- for (const key of keys) {
2830
- result[key] = obj[key];
2831
- }
2832
- return result;
3310
+ * Coerces a value based on the coercion type.
3311
+ * @param value The value to coerce.
3312
+ * @param type The coercion type to perform.
3313
+ * @returns The coerced value.
3314
+ */
3315
+ static byType(value, type) {
3316
+ switch (type) {
3317
+ case CoerceType.String:
3318
+ return Coerce.string(value);
3319
+ case CoerceType.Number:
3320
+ return Coerce.number(value);
3321
+ case CoerceType.Integer:
3322
+ return Coerce.integer(value);
3323
+ case CoerceType.BigInt:
3324
+ return Coerce.bigint(value);
3325
+ case CoerceType.Boolean:
3326
+ return Coerce.boolean(value);
3327
+ case CoerceType.Date:
3328
+ return Coerce.date(value);
3329
+ case CoerceType.DateTime:
3330
+ return Coerce.dateTime(value);
3331
+ case CoerceType.Time:
3332
+ return Coerce.time(value);
3333
+ case CoerceType.Object:
3334
+ return Coerce.object(value);
3335
+ case CoerceType.Uint8Array:
3336
+ return Coerce.uint8Array(value);
3337
+ default:
3338
+ return value;
2833
3339
  }
2834
- return obj;
2835
3340
  }
3341
+ }
3342
+
3343
+ // Copyright 2024 IOTA Stiftung.
3344
+ // SPDX-License-Identifier: Apache-2.0.
3345
+ /**
3346
+ * Class to help with filenames.
3347
+ */
3348
+ class FilenameHelper {
2836
3349
  /**
2837
- * Omit a subset of properties from an object.
2838
- * @param obj The object to omit the properties from.
2839
- * @param keys The property keys to omit.
2840
- * @returns The partial object.
3350
+ * Replaces any unsafe characters in the filename.
3351
+ * @param filename The filename to make safe.
3352
+ * @returns The safe filename.
2841
3353
  */
2842
- static omit(obj, keys) {
2843
- if (Is.object(obj) && Is.arrayValue(keys)) {
2844
- const result = { ...obj };
2845
- for (const key of keys) {
2846
- delete result[key];
2847
- }
2848
- return result;
3354
+ static safeFilename(filename) {
3355
+ let safe = Coerce.string(filename);
3356
+ if (Is.empty(safe)) {
3357
+ return "";
2849
3358
  }
2850
- return obj;
3359
+ // Common non filename characters
3360
+ safe = safe.replace(/["*/:<>?\\|]/g, "_");
3361
+ // Windows non filename characters
3362
+ safe = safe.replace(/^(con|prn|aux|nul|com\d|lpt\d)$/i, "_");
3363
+ // Control characters
3364
+ safe = safe.replace(/[\u0000-\u001F\u0080-\u009F]/g, "_");
3365
+ // Relative paths
3366
+ safe = safe.replace(/^\.+/, "_");
3367
+ // Trailing periods
3368
+ safe = safe.replace(/\.+$/, "");
3369
+ return safe;
2851
3370
  }
2852
3371
  }
2853
3372
 
@@ -2881,7 +3400,7 @@ const CompressionType = {
2881
3400
  */
2882
3401
  Gzip: "gzip",
2883
3402
  /**
2884
- * deflate.
3403
+ * Deflate.
2885
3404
  */
2886
3405
  Deflate: "deflate"
2887
3406
  };
@@ -3504,6 +4023,7 @@ class Validation {
3504
4023
  * @param options Additional options for the validation.
3505
4024
  * @param options.minLength The minimum length of the string.
3506
4025
  * @param options.maxLength The maximum length of the string.
4026
+ * @param options.format Specific format to check.
3507
4027
  * @returns True if the value is a valid string.
3508
4028
  */
3509
4029
  static string(property, value, failures, fieldNameResource, options) {
@@ -3522,6 +4042,47 @@ class Validation {
3522
4042
  const maxLimitDefined = Is.integer(maxLength);
3523
4043
  const belowMin = minLimitDefined && value.length < minLength;
3524
4044
  const aboveMax = maxLimitDefined && value.length > maxLength;
4045
+ if (options?.format === "base58" && !Is.stringBase58(value)) {
4046
+ failures.push({
4047
+ property,
4048
+ reason: "validation.beTextBase58",
4049
+ properties: {
4050
+ fieldName: fieldNameResource ?? "validation.defaultFieldName",
4051
+ value
4052
+ }
4053
+ });
4054
+ }
4055
+ else if (options?.format === "base64" && !Is.stringBase64(value)) {
4056
+ failures.push({
4057
+ property,
4058
+ reason: "validation.beTextBase64",
4059
+ properties: {
4060
+ fieldName: fieldNameResource ?? "validation.defaultFieldName",
4061
+ value
4062
+ }
4063
+ });
4064
+ }
4065
+ else if (options?.format === "hex" && !Is.stringHex(value)) {
4066
+ failures.push({
4067
+ property,
4068
+ reason: "validation.beTextHex",
4069
+ properties: {
4070
+ fieldName: fieldNameResource ?? "validation.defaultFieldName",
4071
+ value
4072
+ }
4073
+ });
4074
+ }
4075
+ else if (Is.regexp(options?.format) && !options.format.test(value)) {
4076
+ failures.push({
4077
+ property,
4078
+ reason: "validation.beTextRegExp",
4079
+ properties: {
4080
+ fieldName: fieldNameResource ?? "validation.defaultFieldName",
4081
+ value,
4082
+ format: options?.format
4083
+ }
4084
+ });
4085
+ }
3525
4086
  if (minLimitDefined && maxLimitDefined && (belowMin || aboveMax)) {
3526
4087
  failures.push({
3527
4088
  property,
@@ -4181,4 +4742,4 @@ class Validation {
4181
4742
  }
4182
4743
  }
4183
4744
 
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 };
4745
+ 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, UnauthorizedError, UnprocessableError, Url, Urn, Validation, ValidationError };