@twin.org/core 0.0.1-next.5 → 0.0.1-next.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.cjs +1401 -736
- package/dist/esm/index.mjs +1398 -737
- package/dist/types/encoding/base58.d.ts +18 -0
- package/dist/types/factories/factory.d.ts +20 -1
- package/dist/types/helpers/envHelper.d.ts +16 -0
- package/dist/types/helpers/jsonHelper.d.ts +30 -0
- package/dist/types/helpers/objectHelper.d.ts +33 -0
- package/dist/types/helpers/uint8ArrayHelper.d.ts +11 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/models/IComponent.d.ts +12 -3
- package/dist/types/models/coerceType.d.ts +49 -0
- package/dist/types/models/compressionType.d.ts +1 -1
- package/dist/types/utils/asyncCache.d.ts +8 -0
- package/dist/types/utils/coerce.d.ts +22 -0
- package/dist/types/utils/converter.d.ts +12 -0
- package/dist/types/utils/guards.d.ts +16 -0
- package/dist/types/utils/is.d.ts +12 -0
- package/dist/types/utils/validation.d.ts +2 -0
- package/docs/changelog.md +15 -1
- package/docs/reference/classes/AlreadyExistsError.md +67 -25
- package/docs/reference/classes/ArrayHelper.md +6 -2
- package/docs/reference/classes/AsyncCache.md +56 -6
- package/docs/reference/classes/Base32.md +6 -2
- package/docs/reference/classes/Base58.md +61 -0
- package/docs/reference/classes/Base64.md +9 -3
- package/docs/reference/classes/Base64Url.md +6 -2
- package/docs/reference/classes/BaseError.md +65 -23
- package/docs/reference/classes/BitString.md +18 -6
- package/docs/reference/classes/Coerce.md +104 -8
- package/docs/reference/classes/Compression.md +16 -8
- package/docs/reference/classes/ConflictError.md +70 -26
- package/docs/reference/classes/Converter.md +104 -20
- package/docs/reference/classes/EnvHelper.md +43 -0
- package/docs/reference/classes/ErrorHelper.md +9 -3
- package/docs/reference/classes/Factory.md +78 -10
- package/docs/reference/classes/FilenameHelper.md +3 -1
- package/docs/reference/classes/GeneralError.md +65 -25
- package/docs/reference/classes/GuardError.md +70 -26
- package/docs/reference/classes/Guards.md +286 -74
- package/docs/reference/classes/HexHelper.md +15 -5
- package/docs/reference/classes/I18n.md +42 -16
- package/docs/reference/classes/Is.md +160 -44
- package/docs/reference/classes/JsonHelper.md +137 -5
- package/docs/reference/classes/NotFoundError.md +67 -25
- package/docs/reference/classes/NotImplementedError.md +61 -23
- package/docs/reference/classes/NotSupportedError.md +64 -24
- package/docs/reference/classes/ObjectHelper.md +188 -20
- package/docs/reference/classes/RandomHelper.md +3 -1
- package/docs/reference/classes/StringHelper.md +51 -17
- package/docs/reference/classes/Uint8ArrayHelper.md +35 -0
- package/docs/reference/classes/UnauthorizedError.md +64 -24
- package/docs/reference/classes/UnprocessableError.md +65 -25
- package/docs/reference/classes/Url.md +30 -10
- package/docs/reference/classes/Urn.md +54 -18
- package/docs/reference/classes/Validation.md +315 -109
- package/docs/reference/classes/ValidationError.md +64 -24
- package/docs/reference/index.md +5 -0
- package/docs/reference/interfaces/IComponent.md +30 -8
- package/docs/reference/interfaces/IError.md +1 -1
- package/docs/reference/interfaces/ILocaleDictionary.md +1 -1
- package/docs/reference/interfaces/IValidationFailure.md +1 -1
- package/docs/reference/type-aliases/CoerceType.md +5 -0
- package/docs/reference/variables/CoerceType.md +67 -0
- package/docs/reference/variables/CompressionType.md +1 -1
- package/locales/en.json +21 -1
- package/package.json +6 -6
package/dist/cjs/index.cjs
CHANGED
|
@@ -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
|
|
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;
|
|
@@ -137,7 +142,17 @@ class Is {
|
|
|
137
142
|
static stringBase64Url(value) {
|
|
138
143
|
return (Is.stringValue(value) &&
|
|
139
144
|
// eslint-disable-next-line unicorn/better-regex
|
|
140
|
-
/^(
|
|
145
|
+
/^([A-Za-z0-9-_])*$/.test(value));
|
|
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));
|
|
141
156
|
}
|
|
142
157
|
/**
|
|
143
158
|
* Is the value a hex string.
|
|
@@ -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
|
|
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
|
-
*
|
|
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,952 +2103,1335 @@ 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
|
-
*
|
|
2108
|
+
* Convert arrays to and from different formats.
|
|
1909
2109
|
*/
|
|
1910
|
-
class
|
|
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
|
-
*
|
|
2112
|
+
* Lookup table for encoding.
|
|
1927
2113
|
* @internal
|
|
1928
2114
|
*/
|
|
1929
|
-
static
|
|
2115
|
+
static _ENCODE_LOOKUP;
|
|
1930
2116
|
/**
|
|
1931
|
-
*
|
|
2117
|
+
* Lookup table for decoding.
|
|
1932
2118
|
* @internal
|
|
1933
2119
|
*/
|
|
1934
|
-
static
|
|
2120
|
+
static _DECODE_LOOKUP;
|
|
1935
2121
|
/**
|
|
1936
|
-
*
|
|
1937
|
-
* @param
|
|
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
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
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
|
-
*
|
|
1947
|
-
* @
|
|
2159
|
+
* Convert a UTF8 string to raw array.
|
|
2160
|
+
* @param utf8 The text to decode.
|
|
2161
|
+
* @returns The array.
|
|
1948
2162
|
*/
|
|
1949
|
-
static
|
|
1950
|
-
|
|
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
|
-
*
|
|
1954
|
-
* @param
|
|
1955
|
-
* @param
|
|
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
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
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
|
-
*
|
|
1967
|
-
* @param
|
|
1968
|
-
* @
|
|
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
|
|
1971
|
-
|
|
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
|
-
*
|
|
1975
|
-
* @
|
|
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
|
|
1978
|
-
|
|
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
|
-
*
|
|
1982
|
-
* @param
|
|
1983
|
-
* @
|
|
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
|
|
1986
|
-
|
|
2257
|
+
static hexToUtf8(hex) {
|
|
2258
|
+
return Converter.bytesToUtf8(Converter.hexToBytes(HexHelper.stripPrefix(hex)));
|
|
1987
2259
|
}
|
|
1988
2260
|
/**
|
|
1989
|
-
*
|
|
1990
|
-
* @param
|
|
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
|
|
1993
|
-
|
|
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
|
-
*
|
|
1997
|
-
* @param
|
|
1998
|
-
* @
|
|
2273
|
+
* Convert a binary string to bytes.
|
|
2274
|
+
* @param binary The binary string.
|
|
2275
|
+
* @returns The bytes.
|
|
1999
2276
|
*/
|
|
2000
|
-
static
|
|
2001
|
-
|
|
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
|
-
*
|
|
2005
|
-
* @param
|
|
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
|
|
2008
|
-
|
|
2289
|
+
static bytesToBase64(bytes) {
|
|
2290
|
+
return Base64.encode(bytes);
|
|
2009
2291
|
}
|
|
2010
2292
|
/**
|
|
2011
|
-
*
|
|
2012
|
-
* @param
|
|
2013
|
-
* @
|
|
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
|
|
2018
|
-
|
|
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
|
-
*
|
|
2039
|
-
* @param
|
|
2040
|
-
* @returns
|
|
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
|
|
2043
|
-
return
|
|
2305
|
+
static bytesToBase64Url(bytes) {
|
|
2306
|
+
return Base64Url.encode(bytes);
|
|
2044
2307
|
}
|
|
2045
2308
|
/**
|
|
2046
|
-
*
|
|
2047
|
-
* @param
|
|
2048
|
-
* @
|
|
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
|
|
2053
|
-
|
|
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
|
-
*
|
|
2074
|
-
* @param
|
|
2075
|
-
* @returns
|
|
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
|
|
2078
|
-
return
|
|
2321
|
+
static bytesToBase58(bytes) {
|
|
2322
|
+
return Base58.encode(bytes);
|
|
2079
2323
|
}
|
|
2080
2324
|
/**
|
|
2081
|
-
*
|
|
2082
|
-
* @param
|
|
2083
|
-
* @returns The
|
|
2325
|
+
* Convert a base58 string to bytes.
|
|
2326
|
+
* @param base58 The base58 string.
|
|
2327
|
+
* @returns The bytes.
|
|
2084
2328
|
*/
|
|
2085
|
-
static
|
|
2086
|
-
|
|
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
|
-
*
|
|
2123
|
-
* @
|
|
2124
|
-
* @returns The localized version of the errors flattened.
|
|
2333
|
+
* Build the static lookup tables.
|
|
2334
|
+
* @internal
|
|
2125
2335
|
*/
|
|
2126
|
-
static
|
|
2127
|
-
if (
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
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
|
}
|
|
2148
2355
|
|
|
2149
|
-
// Copyright 2024 IOTA Stiftung.
|
|
2150
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
2151
2356
|
/**
|
|
2152
|
-
*
|
|
2357
|
+
* Helpers methods for JSON objects.
|
|
2153
2358
|
*/
|
|
2154
|
-
class
|
|
2359
|
+
class JsonHelper {
|
|
2155
2360
|
/**
|
|
2156
|
-
*
|
|
2157
|
-
* @
|
|
2158
|
-
* @throws TypeError If the value can not be coerced.
|
|
2159
|
-
* @returns The value if it can be coerced.
|
|
2361
|
+
* Runtime name for the class.
|
|
2362
|
+
* @internal
|
|
2160
2363
|
*/
|
|
2161
|
-
static
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2364
|
+
static _CLASS_NAME = "JsonHelper";
|
|
2365
|
+
/**
|
|
2366
|
+
* Serializes in canonical format.
|
|
2367
|
+
* Based on https://www.rfc-editor.org/rfc/rfc8785.
|
|
2368
|
+
* @param object The object to be serialized.
|
|
2369
|
+
* @returns The serialized object.
|
|
2370
|
+
*/
|
|
2371
|
+
static canonicalize(object) {
|
|
2372
|
+
const buffer = [];
|
|
2373
|
+
if (object === null ||
|
|
2374
|
+
typeof object !== "object" ||
|
|
2375
|
+
("toJSON" in object && object.toJSON instanceof Function)) {
|
|
2376
|
+
// Primitive data type
|
|
2377
|
+
buffer.push(JSON.stringify(object));
|
|
2170
2378
|
}
|
|
2171
|
-
if (
|
|
2172
|
-
|
|
2379
|
+
else if (Array.isArray(object)) {
|
|
2380
|
+
// Array maintain element order
|
|
2381
|
+
const parts = [];
|
|
2382
|
+
for (const element of object) {
|
|
2383
|
+
if (element === undefined) {
|
|
2384
|
+
parts.push("null");
|
|
2385
|
+
}
|
|
2386
|
+
else {
|
|
2387
|
+
parts.push(JsonHelper.canonicalize(element));
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
buffer.push(`[${parts.join(",")}]`);
|
|
2173
2391
|
}
|
|
2174
|
-
|
|
2175
|
-
|
|
2392
|
+
else {
|
|
2393
|
+
// Object sort properties
|
|
2394
|
+
const props = [];
|
|
2395
|
+
const keys = Object.keys(object).sort();
|
|
2396
|
+
const o = object;
|
|
2397
|
+
for (const key of keys) {
|
|
2398
|
+
if (o[key] !== undefined) {
|
|
2399
|
+
props.push(`${JSON.stringify(key)}:${JsonHelper.canonicalize(o[key])}`);
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
buffer.push(`{${props.join(",")}}`);
|
|
2176
2403
|
}
|
|
2404
|
+
return buffer.join("");
|
|
2177
2405
|
}
|
|
2178
2406
|
/**
|
|
2179
|
-
*
|
|
2180
|
-
*
|
|
2181
|
-
* @
|
|
2182
|
-
* @
|
|
2407
|
+
* Creates a RFC 6902 diff set.
|
|
2408
|
+
* Based on https://www.rfc-editor.org/rfc/rfc6902.
|
|
2409
|
+
* @param object1 The first object.
|
|
2410
|
+
* @param object2 The second object.
|
|
2411
|
+
* @returns The list of patches.
|
|
2183
2412
|
*/
|
|
2184
|
-
static
|
|
2185
|
-
|
|
2186
|
-
|
|
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
|
-
}
|
|
2413
|
+
static diff(object1, object2) {
|
|
2414
|
+
const operations = rfc6902.createPatch(object1, object2);
|
|
2415
|
+
return operations;
|
|
2203
2416
|
}
|
|
2204
2417
|
/**
|
|
2205
|
-
*
|
|
2206
|
-
*
|
|
2207
|
-
* @
|
|
2208
|
-
* @
|
|
2418
|
+
* Applies a RFC 6902 diff set to an object.
|
|
2419
|
+
* Based on https://www.rfc-editor.org/rfc/rfc6902.
|
|
2420
|
+
* @param object The object to patch.
|
|
2421
|
+
* @param patches The second object.
|
|
2422
|
+
* @returns The updated object.
|
|
2423
|
+
* @throws GeneralError if the patch fails.
|
|
2209
2424
|
*/
|
|
2210
|
-
static
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
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);
|
|
2425
|
+
static patch(object, patches) {
|
|
2426
|
+
const clone = ObjectHelper.clone(object);
|
|
2427
|
+
const result = rfc6902.applyPatch(clone, patches);
|
|
2428
|
+
for (let i = 0; i < result.length; i++) {
|
|
2429
|
+
if (!Is.empty(result[i])) {
|
|
2430
|
+
throw new GeneralError(JsonHelper._CLASS_NAME, "failedPatch", { index: i }, result[i]);
|
|
2224
2431
|
}
|
|
2225
2432
|
}
|
|
2226
|
-
|
|
2227
|
-
return value ? 1n : 0n;
|
|
2228
|
-
}
|
|
2433
|
+
return clone;
|
|
2229
2434
|
}
|
|
2230
2435
|
/**
|
|
2231
|
-
*
|
|
2232
|
-
* @param
|
|
2233
|
-
* @
|
|
2234
|
-
* @returns The
|
|
2436
|
+
* Stringify the JSON with support for extended data types date/bigint/uint8array.
|
|
2437
|
+
* @param object The object to stringify.
|
|
2438
|
+
* @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
|
|
2439
|
+
* @returns The stringified object.
|
|
2235
2440
|
*/
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2441
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2442
|
+
static stringifyEx(object, space) {
|
|
2443
|
+
// We want to keep the 'this' intact for the replacer
|
|
2444
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
2445
|
+
return JSON.stringify(object, JsonHelper.stringifyExReplacer, space);
|
|
2446
|
+
}
|
|
2447
|
+
/**
|
|
2448
|
+
* Parse the JSON string with support for extended data types date/bigint/uint8array.
|
|
2449
|
+
* @param json The object to pause.
|
|
2450
|
+
* @returns The object.
|
|
2451
|
+
*/
|
|
2452
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2453
|
+
static parseEx(json) {
|
|
2454
|
+
// We want to keep the 'this' intact for the reviver
|
|
2455
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
2456
|
+
return JSON.parse(json, JsonHelper.parseExReviver);
|
|
2457
|
+
}
|
|
2458
|
+
/**
|
|
2459
|
+
* Replacer function to handle extended data types.
|
|
2460
|
+
* @param this The object.
|
|
2461
|
+
* @param key The key.
|
|
2462
|
+
* @param value The value.
|
|
2463
|
+
* @returns The value.
|
|
2464
|
+
*/
|
|
2465
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2466
|
+
static stringifyExReplacer(key, value) {
|
|
2467
|
+
const rawValue = this[key];
|
|
2468
|
+
if (Is.bigint(rawValue)) {
|
|
2469
|
+
return {
|
|
2470
|
+
"@ext": "bigint",
|
|
2471
|
+
value: rawValue.toString()
|
|
2472
|
+
};
|
|
2473
|
+
}
|
|
2474
|
+
else if (Is.date(rawValue)) {
|
|
2475
|
+
return {
|
|
2476
|
+
"@ext": "date",
|
|
2477
|
+
value: rawValue.getTime()
|
|
2478
|
+
};
|
|
2479
|
+
}
|
|
2480
|
+
else if (Is.uint8Array(rawValue)) {
|
|
2481
|
+
return {
|
|
2482
|
+
"@ext": "uint8array",
|
|
2483
|
+
value: Converter.bytesToBase64(rawValue)
|
|
2484
|
+
};
|
|
2485
|
+
}
|
|
2486
|
+
return value;
|
|
2487
|
+
}
|
|
2488
|
+
/**
|
|
2489
|
+
* Reviver function to handle extended data types.
|
|
2490
|
+
* @param this The object.
|
|
2491
|
+
* @param key The key.
|
|
2492
|
+
* @param value The value.
|
|
2493
|
+
* @returns The value.
|
|
2494
|
+
*/
|
|
2495
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2496
|
+
static parseExReviver(key, value) {
|
|
2497
|
+
if (Is.object(value)) {
|
|
2498
|
+
if (value["@ext"] === "bigint") {
|
|
2499
|
+
return BigInt(value.value);
|
|
2250
2500
|
}
|
|
2251
|
-
if (
|
|
2252
|
-
return
|
|
2501
|
+
else if (value["@ext"] === "date") {
|
|
2502
|
+
return new Date(value.value);
|
|
2503
|
+
}
|
|
2504
|
+
else if (value["@ext"] === "uint8array") {
|
|
2505
|
+
return Converter.base64ToBytes(value.value);
|
|
2253
2506
|
}
|
|
2254
2507
|
}
|
|
2508
|
+
return value;
|
|
2255
2509
|
}
|
|
2510
|
+
}
|
|
2511
|
+
|
|
2512
|
+
/**
|
|
2513
|
+
* Class to help with objects.
|
|
2514
|
+
*/
|
|
2515
|
+
class ObjectHelper {
|
|
2256
2516
|
/**
|
|
2257
|
-
*
|
|
2258
|
-
* @
|
|
2259
|
-
* @throws TypeError If the value can not be coerced.
|
|
2260
|
-
* @returns The value if it can be coerced.
|
|
2517
|
+
* Runtime name for the class.
|
|
2518
|
+
* @internal
|
|
2261
2519
|
*/
|
|
2262
|
-
static
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
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
|
-
}
|
|
2520
|
+
static _CLASS_NAME = "ObjectHelper";
|
|
2521
|
+
/**
|
|
2522
|
+
* Convert an object to bytes.
|
|
2523
|
+
* @param obj The object to convert.
|
|
2524
|
+
* @param format Format the JSON content.
|
|
2525
|
+
* @returns The object as bytes.
|
|
2526
|
+
*/
|
|
2527
|
+
static toBytes(obj, format = false) {
|
|
2528
|
+
if (obj === undefined) {
|
|
2529
|
+
return new Uint8Array();
|
|
2278
2530
|
}
|
|
2531
|
+
const json = format ? JSON.stringify(obj, undefined, "\t") : JSON.stringify(obj);
|
|
2532
|
+
return Converter.utf8ToBytes(json);
|
|
2279
2533
|
}
|
|
2280
2534
|
/**
|
|
2281
|
-
*
|
|
2282
|
-
* @param
|
|
2283
|
-
* @
|
|
2284
|
-
* @
|
|
2535
|
+
* Convert a bytes to an object.
|
|
2536
|
+
* @param bytes The bytes to convert to an object.
|
|
2537
|
+
* @returns The object.
|
|
2538
|
+
* @throws GeneralError if there was an error parsing the JSON.
|
|
2285
2539
|
*/
|
|
2286
|
-
static
|
|
2287
|
-
if (Is.
|
|
2288
|
-
return
|
|
2289
|
-
}
|
|
2290
|
-
if (Is.date(value)) {
|
|
2291
|
-
return value;
|
|
2540
|
+
static fromBytes(bytes) {
|
|
2541
|
+
if (Is.empty(bytes) || bytes.length === 0) {
|
|
2542
|
+
return undefined;
|
|
2292
2543
|
}
|
|
2293
|
-
|
|
2294
|
-
|
|
2544
|
+
try {
|
|
2545
|
+
const utf8 = Converter.bytesToUtf8(bytes);
|
|
2546
|
+
return JSON.parse(utf8);
|
|
2295
2547
|
}
|
|
2296
|
-
|
|
2297
|
-
|
|
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
|
-
}
|
|
2548
|
+
catch (err) {
|
|
2549
|
+
throw new GeneralError(ObjectHelper._CLASS_NAME, "failedBytesToJSON", undefined, err);
|
|
2302
2550
|
}
|
|
2303
2551
|
}
|
|
2304
2552
|
/**
|
|
2305
|
-
*
|
|
2306
|
-
* @param
|
|
2307
|
-
* @
|
|
2308
|
-
* @returns The value if it can be coerced.
|
|
2553
|
+
* Make a deep clone of an object.
|
|
2554
|
+
* @param obj The object to clone.
|
|
2555
|
+
* @returns The objects clone.
|
|
2309
2556
|
*/
|
|
2310
|
-
static
|
|
2311
|
-
if (Is.undefined(
|
|
2312
|
-
return
|
|
2557
|
+
static clone(obj) {
|
|
2558
|
+
if (Is.undefined(obj)) {
|
|
2559
|
+
return undefined;
|
|
2313
2560
|
}
|
|
2314
|
-
|
|
2315
|
-
|
|
2561
|
+
return structuredClone(obj);
|
|
2562
|
+
}
|
|
2563
|
+
/**
|
|
2564
|
+
* Deep merge objects.
|
|
2565
|
+
* @param obj1 The first object to merge.
|
|
2566
|
+
* @param obj2 The second object to merge.
|
|
2567
|
+
* @returns The combined deep merge of the objects.
|
|
2568
|
+
*/
|
|
2569
|
+
static merge(obj1, obj2) {
|
|
2570
|
+
if (Is.empty(obj1)) {
|
|
2571
|
+
return ObjectHelper.clone(obj2);
|
|
2316
2572
|
}
|
|
2317
|
-
if (Is.
|
|
2318
|
-
|
|
2319
|
-
dt.setFullYear(1970, 0, 1);
|
|
2320
|
-
return dt;
|
|
2573
|
+
if (Is.empty(obj2)) {
|
|
2574
|
+
return ObjectHelper.clone(obj1);
|
|
2321
2575
|
}
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2576
|
+
const obj1Clone = ObjectHelper.clone(obj1);
|
|
2577
|
+
if (Is.object(obj1Clone) && Is.object(obj2)) {
|
|
2578
|
+
const keys = Object.keys(obj2);
|
|
2579
|
+
for (const key of keys) {
|
|
2580
|
+
if (Is.object(obj1Clone[key]) && Is.object(obj2[key])) {
|
|
2581
|
+
ObjectHelper.propertySet(obj1Clone, key, ObjectHelper.merge(obj1Clone[key], obj2[key]));
|
|
2582
|
+
}
|
|
2583
|
+
else {
|
|
2584
|
+
ObjectHelper.propertySet(obj1Clone, key, obj2[key]);
|
|
2585
|
+
}
|
|
2327
2586
|
}
|
|
2328
2587
|
}
|
|
2588
|
+
return obj1Clone;
|
|
2329
2589
|
}
|
|
2330
2590
|
/**
|
|
2331
|
-
*
|
|
2332
|
-
* @param
|
|
2333
|
-
* @
|
|
2334
|
-
* @
|
|
2591
|
+
* Does one object equal another.
|
|
2592
|
+
* @param obj1 The first object to compare.
|
|
2593
|
+
* @param obj2 The second object to compare.
|
|
2594
|
+
* @param strictPropertyOrder Should the properties be in the same order, defaults to true.
|
|
2595
|
+
* @returns True is the objects are equal.
|
|
2335
2596
|
*/
|
|
2336
|
-
static
|
|
2337
|
-
if (
|
|
2338
|
-
return
|
|
2339
|
-
}
|
|
2340
|
-
if (Is.object(value)) {
|
|
2341
|
-
return value;
|
|
2597
|
+
static equal(obj1, obj2, strictPropertyOrder) {
|
|
2598
|
+
if (strictPropertyOrder ?? true) {
|
|
2599
|
+
return JSON.stringify(obj1) === JSON.stringify(obj2);
|
|
2342
2600
|
}
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2601
|
+
return JsonHelper.canonicalize(obj1) === JsonHelper.canonicalize(obj2);
|
|
2602
|
+
}
|
|
2603
|
+
/**
|
|
2604
|
+
* Get the property of an unknown object.
|
|
2605
|
+
* @param obj The object to get the property from.
|
|
2606
|
+
* @param property The property to get, can be separated by dots for nested path.
|
|
2607
|
+
* @returns The property.
|
|
2608
|
+
*/
|
|
2609
|
+
static propertyGet(obj, property) {
|
|
2610
|
+
const pathParts = property.split(".");
|
|
2611
|
+
let pathValue = obj;
|
|
2612
|
+
for (const pathPart of pathParts) {
|
|
2613
|
+
// Is the path part numeric i.e. an array index.
|
|
2614
|
+
const arrayMatch = /^(\d+)$/.exec(pathPart);
|
|
2615
|
+
if (arrayMatch) {
|
|
2616
|
+
const arrayIndex = Number.parseInt(arrayMatch[1], 10);
|
|
2617
|
+
if (Is.arrayValue(pathValue) && arrayIndex < pathValue.length) {
|
|
2618
|
+
// There is no prop name so this is a direct array index on the current object
|
|
2619
|
+
pathValue = pathValue[arrayIndex];
|
|
2620
|
+
}
|
|
2621
|
+
else {
|
|
2622
|
+
// Array index for non array object so return
|
|
2623
|
+
return undefined;
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
else if (Is.object(pathValue)) {
|
|
2627
|
+
// No array part in path so assume object sub property
|
|
2628
|
+
pathValue = pathValue[pathPart];
|
|
2629
|
+
}
|
|
2630
|
+
else {
|
|
2631
|
+
return undefined;
|
|
2346
2632
|
}
|
|
2347
|
-
catch { }
|
|
2348
2633
|
}
|
|
2634
|
+
return pathValue;
|
|
2349
2635
|
}
|
|
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
2636
|
/**
|
|
2359
|
-
*
|
|
2360
|
-
* @param
|
|
2361
|
-
* @
|
|
2637
|
+
* Set the property of an unknown object.
|
|
2638
|
+
* @param obj The object to set the property from.
|
|
2639
|
+
* @param property The property to set.
|
|
2640
|
+
* @param value The value to set.
|
|
2641
|
+
* @throws GeneralError if the property target is not an object.
|
|
2362
2642
|
*/
|
|
2363
|
-
static
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2643
|
+
static propertySet(obj, property, value) {
|
|
2644
|
+
const pathParts = property.split(".");
|
|
2645
|
+
let pathValue = obj;
|
|
2646
|
+
let parentObj;
|
|
2647
|
+
for (let i = 0; i < pathParts.length; i++) {
|
|
2648
|
+
const pathPart = pathParts[i];
|
|
2649
|
+
// Is the path part numeric i.e. an array index.
|
|
2650
|
+
const arrayMatch = /^(\d+)$/.exec(pathPart);
|
|
2651
|
+
const arrayIndex = arrayMatch ? Number.parseInt(arrayMatch[1], 10) : -1;
|
|
2652
|
+
if (i === pathParts.length - 1) {
|
|
2653
|
+
// Last part of path so set the value
|
|
2654
|
+
if (arrayIndex >= 0) {
|
|
2655
|
+
if (Is.array(pathValue)) {
|
|
2656
|
+
pathValue[arrayIndex] = value;
|
|
2657
|
+
}
|
|
2658
|
+
else {
|
|
2659
|
+
throw new GeneralError(ObjectHelper._CLASS_NAME, "cannotSetArrayIndex", {
|
|
2660
|
+
property,
|
|
2661
|
+
index: arrayIndex
|
|
2662
|
+
});
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
2665
|
+
else if (Is.object(pathValue)) {
|
|
2666
|
+
pathValue[pathPart] = value;
|
|
2667
|
+
}
|
|
2668
|
+
else {
|
|
2669
|
+
throw new GeneralError(ObjectHelper._CLASS_NAME, "cannotSetProperty", { property });
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
else {
|
|
2673
|
+
parentObj = pathValue;
|
|
2674
|
+
if (Is.object(pathValue)) {
|
|
2675
|
+
pathValue = pathValue[pathPart];
|
|
2676
|
+
}
|
|
2677
|
+
else if (Is.array(pathValue)) {
|
|
2678
|
+
pathValue = pathValue[arrayIndex];
|
|
2679
|
+
}
|
|
2680
|
+
if (Is.empty(pathValue)) {
|
|
2681
|
+
const nextArrayMatch = /^(\d+)$/.exec(pathParts[i + 1]);
|
|
2682
|
+
const nextArrayIndex = nextArrayMatch ? Number.parseInt(nextArrayMatch[1], 10) : -1;
|
|
2683
|
+
if (nextArrayIndex >= 0) {
|
|
2684
|
+
pathValue = [];
|
|
2685
|
+
}
|
|
2686
|
+
else {
|
|
2687
|
+
pathValue = {};
|
|
2688
|
+
}
|
|
2689
|
+
if (Is.object(parentObj)) {
|
|
2690
|
+
parentObj[pathPart] = pathValue;
|
|
2691
|
+
}
|
|
2692
|
+
else if (Is.array(parentObj)) {
|
|
2693
|
+
parentObj[arrayIndex] = pathValue;
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2367
2697
|
}
|
|
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
2698
|
}
|
|
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
2699
|
/**
|
|
2389
|
-
*
|
|
2390
|
-
*
|
|
2391
|
-
* @param
|
|
2392
|
-
* @returns The serialized object.
|
|
2700
|
+
* Delete the property of an unknown object.
|
|
2701
|
+
* @param obj The object to set the property from.
|
|
2702
|
+
* @param property The property to set
|
|
2393
2703
|
*/
|
|
2394
|
-
static
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
typeof object !== "object" ||
|
|
2398
|
-
("toJSON" in object && object.toJSON instanceof Function)) {
|
|
2399
|
-
// Primitive data type
|
|
2400
|
-
buffer.push(JSON.stringify(object));
|
|
2704
|
+
static propertyDelete(obj, property) {
|
|
2705
|
+
if (Is.object(obj)) {
|
|
2706
|
+
delete obj[property];
|
|
2401
2707
|
}
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2708
|
+
}
|
|
2709
|
+
/**
|
|
2710
|
+
* Extract a property from the object, providing alternative names.
|
|
2711
|
+
* @param obj The object to extract from.
|
|
2712
|
+
* @param propertyNames The possible names for the property.
|
|
2713
|
+
* @param removeProperties Remove the properties from the object, defaults to true.
|
|
2714
|
+
* @returns The property if available.
|
|
2715
|
+
*/
|
|
2716
|
+
static extractProperty(obj, propertyNames, removeProperties = true) {
|
|
2717
|
+
let retVal;
|
|
2718
|
+
if (Is.object(obj)) {
|
|
2719
|
+
const names = Is.string(propertyNames) ? [propertyNames] : propertyNames;
|
|
2720
|
+
for (const prop of names) {
|
|
2721
|
+
retVal ??= ObjectHelper.propertyGet(obj, prop);
|
|
2722
|
+
if (removeProperties) {
|
|
2723
|
+
ObjectHelper.propertyDelete(obj, prop);
|
|
2411
2724
|
}
|
|
2412
2725
|
}
|
|
2413
|
-
buffer.push(`[${parts.join(",")}]`);
|
|
2414
2726
|
}
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2727
|
+
return retVal;
|
|
2728
|
+
}
|
|
2729
|
+
/**
|
|
2730
|
+
* Pick a subset of properties from an object.
|
|
2731
|
+
* @param obj The object to pick the properties from.
|
|
2732
|
+
* @param keys The property keys to pick.
|
|
2733
|
+
* @returns The partial object.
|
|
2734
|
+
*/
|
|
2735
|
+
static pick(obj, keys) {
|
|
2736
|
+
if (Is.object(obj) && Is.arrayValue(keys)) {
|
|
2737
|
+
const result = {};
|
|
2420
2738
|
for (const key of keys) {
|
|
2421
|
-
|
|
2422
|
-
props.push(`${JSON.stringify(key)}:${JsonHelper.canonicalize(o[key])}`);
|
|
2423
|
-
}
|
|
2739
|
+
result[key] = obj[key];
|
|
2424
2740
|
}
|
|
2425
|
-
|
|
2741
|
+
return result;
|
|
2426
2742
|
}
|
|
2427
|
-
return
|
|
2743
|
+
return obj;
|
|
2428
2744
|
}
|
|
2429
2745
|
/**
|
|
2430
|
-
*
|
|
2431
|
-
*
|
|
2432
|
-
* @param
|
|
2433
|
-
* @
|
|
2434
|
-
* @returns The list of patches.
|
|
2746
|
+
* Omit a subset of properties from an object.
|
|
2747
|
+
* @param obj The object to omit the properties from.
|
|
2748
|
+
* @param keys The property keys to omit.
|
|
2749
|
+
* @returns The partial object.
|
|
2435
2750
|
*/
|
|
2436
|
-
static
|
|
2437
|
-
|
|
2438
|
-
|
|
2751
|
+
static omit(obj, keys) {
|
|
2752
|
+
if (Is.object(obj) && Is.arrayValue(keys)) {
|
|
2753
|
+
const result = { ...obj };
|
|
2754
|
+
for (const key of keys) {
|
|
2755
|
+
delete result[key];
|
|
2756
|
+
}
|
|
2757
|
+
return result;
|
|
2758
|
+
}
|
|
2759
|
+
return obj;
|
|
2439
2760
|
}
|
|
2440
2761
|
/**
|
|
2441
|
-
*
|
|
2442
|
-
*
|
|
2443
|
-
* @
|
|
2444
|
-
* @param patches The second object.
|
|
2445
|
-
* @returns The updated object.
|
|
2762
|
+
* Converter the non JSON primitives to extended types.
|
|
2763
|
+
* @param obj The object to convert.
|
|
2764
|
+
* @returns The object with extended properties.
|
|
2446
2765
|
*/
|
|
2447
|
-
|
|
2448
|
-
|
|
2766
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2767
|
+
static toExtended(obj) {
|
|
2768
|
+
const jsonExtended = JsonHelper.stringifyEx(obj);
|
|
2769
|
+
return JSON.parse(jsonExtended);
|
|
2770
|
+
}
|
|
2771
|
+
/**
|
|
2772
|
+
* Converter the extended types to non JSON primitives.
|
|
2773
|
+
* @param obj The object to convert.
|
|
2774
|
+
* @returns The object with regular properties.
|
|
2775
|
+
*/
|
|
2776
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2777
|
+
static fromExtended(obj) {
|
|
2778
|
+
const jsonExtended = JsonHelper.stringifyEx(obj);
|
|
2779
|
+
return JsonHelper.parseEx(jsonExtended);
|
|
2780
|
+
}
|
|
2781
|
+
/**
|
|
2782
|
+
* Remove empty properties from an object.
|
|
2783
|
+
* @param obj The object to remove the empty properties from.
|
|
2784
|
+
* @param options The options for the removal.
|
|
2785
|
+
* @param options.removeUndefined Remove undefined properties, defaults to true.
|
|
2786
|
+
* @param options.removeNull Remove null properties, defaults to false.
|
|
2787
|
+
* @returns The object with empty properties removed.
|
|
2788
|
+
*/
|
|
2789
|
+
static removeEmptyProperties(obj, options) {
|
|
2790
|
+
if (Is.object(obj)) {
|
|
2791
|
+
const removeUndefined = options?.removeUndefined ?? true;
|
|
2792
|
+
const removeNull = options?.removeNull ?? false;
|
|
2793
|
+
const newObj = {};
|
|
2794
|
+
const keys = Object.keys(obj);
|
|
2795
|
+
for (const key of keys) {
|
|
2796
|
+
if (!((removeUndefined && Is.undefined(obj[key])) || (removeNull && Is.null(obj[key])))) {
|
|
2797
|
+
newObj[key] = ObjectHelper.removeEmptyProperties(obj[key], options);
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
return newObj;
|
|
2801
|
+
}
|
|
2802
|
+
else if (Is.array(obj)) {
|
|
2803
|
+
const arr = [];
|
|
2804
|
+
for (const element of obj) {
|
|
2805
|
+
arr.push(ObjectHelper.removeEmptyProperties(element, options));
|
|
2806
|
+
}
|
|
2807
|
+
return arr;
|
|
2808
|
+
}
|
|
2809
|
+
return obj;
|
|
2449
2810
|
}
|
|
2450
2811
|
}
|
|
2451
2812
|
|
|
2452
2813
|
// Copyright 2024 IOTA Stiftung.
|
|
2453
2814
|
// SPDX-License-Identifier: Apache-2.0.
|
|
2454
|
-
/* eslint-disable no-bitwise */
|
|
2455
2815
|
/**
|
|
2456
|
-
*
|
|
2816
|
+
* Environment variable helper.
|
|
2457
2817
|
*/
|
|
2458
|
-
class
|
|
2818
|
+
class EnvHelper {
|
|
2819
|
+
/**
|
|
2820
|
+
* Get the environment variable as an object with camel cased names.
|
|
2821
|
+
* @param envVars The environment variables.
|
|
2822
|
+
* @param prefix The prefix of the environment variables, if not provided gets all.
|
|
2823
|
+
* @returns The object with camel cased names.
|
|
2824
|
+
*/
|
|
2825
|
+
static envToJson(envVars, prefix) {
|
|
2826
|
+
const result = {};
|
|
2827
|
+
if (!Is.empty(envVars)) {
|
|
2828
|
+
if (Is.empty(prefix)) {
|
|
2829
|
+
for (const envVar in envVars) {
|
|
2830
|
+
if (Is.stringValue(envVars[envVar])) {
|
|
2831
|
+
const camelCaseName = StringHelper.camelCase(envVar.toLowerCase());
|
|
2832
|
+
ObjectHelper.propertySet(result, camelCaseName, envVars[envVar]);
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
else {
|
|
2837
|
+
for (const envVar in envVars) {
|
|
2838
|
+
if (envVar.startsWith(prefix) && Is.stringValue(envVars[envVar])) {
|
|
2839
|
+
const camelCaseName = StringHelper.camelCase(envVar.replace(prefix, "").toLowerCase());
|
|
2840
|
+
ObjectHelper.propertySet(result, camelCaseName, envVars[envVar]);
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2845
|
+
return result;
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
|
|
2849
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2850
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
2851
|
+
/**
|
|
2852
|
+
* Class to perform internationalization.
|
|
2853
|
+
*/
|
|
2854
|
+
class I18n {
|
|
2459
2855
|
/**
|
|
2460
|
-
*
|
|
2856
|
+
* The default translation.
|
|
2857
|
+
*/
|
|
2858
|
+
static DEFAULT_LOCALE = "en";
|
|
2859
|
+
/**
|
|
2860
|
+
* Dictionaries for lookups.
|
|
2461
2861
|
* @internal
|
|
2462
2862
|
*/
|
|
2463
|
-
static
|
|
2863
|
+
static _localeDictionaries = {};
|
|
2464
2864
|
/**
|
|
2465
|
-
*
|
|
2865
|
+
* The current locale.
|
|
2466
2866
|
* @internal
|
|
2467
2867
|
*/
|
|
2468
|
-
static
|
|
2868
|
+
static _currentLocale = I18n.DEFAULT_LOCALE;
|
|
2469
2869
|
/**
|
|
2470
|
-
*
|
|
2471
|
-
* @
|
|
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.
|
|
2870
|
+
* Change handler for the locale being updated.
|
|
2871
|
+
* @internal
|
|
2475
2872
|
*/
|
|
2476
|
-
static
|
|
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
|
-
}
|
|
2503
|
-
}
|
|
2504
|
-
return str;
|
|
2505
|
-
}
|
|
2873
|
+
static _localeChangedHandlers = {};
|
|
2506
2874
|
/**
|
|
2507
|
-
*
|
|
2508
|
-
* @
|
|
2509
|
-
* @returns The array.
|
|
2875
|
+
* Change handler for the dictionaries being updated.
|
|
2876
|
+
* @internal
|
|
2510
2877
|
*/
|
|
2511
|
-
static
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
}
|
|
2521
|
-
else if (charCode < 0xd800 || charCode >= 0xe000) {
|
|
2522
|
-
bytes.push(0xe0 | (charCode >> 12), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f));
|
|
2523
|
-
}
|
|
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));
|
|
2532
|
-
}
|
|
2878
|
+
static _dictionaryChangedHandlers = {};
|
|
2879
|
+
/**
|
|
2880
|
+
* Set the locale.
|
|
2881
|
+
* @param locale The new locale.
|
|
2882
|
+
*/
|
|
2883
|
+
static setLocale(locale) {
|
|
2884
|
+
I18n._currentLocale = locale;
|
|
2885
|
+
for (const callback in I18n._localeChangedHandlers) {
|
|
2886
|
+
I18n._localeChangedHandlers[callback](I18n._currentLocale);
|
|
2533
2887
|
}
|
|
2534
|
-
return Uint8Array.from(bytes);
|
|
2535
2888
|
}
|
|
2536
2889
|
/**
|
|
2537
|
-
*
|
|
2538
|
-
* @
|
|
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.
|
|
2890
|
+
* Get the locale.
|
|
2891
|
+
* @returns The current locale.
|
|
2544
2892
|
*/
|
|
2545
|
-
static
|
|
2546
|
-
|
|
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]];
|
|
2559
|
-
}
|
|
2560
|
-
}
|
|
2561
|
-
}
|
|
2562
|
-
return includePrefix ? HexHelper.addPrefix(hex) : hex;
|
|
2893
|
+
static getLocale() {
|
|
2894
|
+
return I18n._currentLocale;
|
|
2563
2895
|
}
|
|
2564
2896
|
/**
|
|
2565
|
-
*
|
|
2566
|
-
* @param
|
|
2567
|
-
* @param
|
|
2568
|
-
* @returns The array.
|
|
2897
|
+
* Add a locale dictionary.
|
|
2898
|
+
* @param locale The locale.
|
|
2899
|
+
* @param dictionary The dictionary to add.
|
|
2569
2900
|
*/
|
|
2570
|
-
static
|
|
2571
|
-
const
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
const
|
|
2575
|
-
|
|
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
|
-
}
|
|
2901
|
+
static addDictionary(locale, dictionary) {
|
|
2902
|
+
const mergedKeys = {};
|
|
2903
|
+
I18n.flattenTranslationKeys(dictionary, "", mergedKeys);
|
|
2904
|
+
I18n._localeDictionaries[locale] = mergedKeys;
|
|
2905
|
+
for (const callback in I18n._dictionaryChangedHandlers) {
|
|
2906
|
+
I18n._dictionaryChangedHandlers[callback](I18n._currentLocale);
|
|
2587
2907
|
}
|
|
2588
|
-
return array;
|
|
2589
2908
|
}
|
|
2590
2909
|
/**
|
|
2591
|
-
*
|
|
2592
|
-
* @param
|
|
2593
|
-
* @
|
|
2594
|
-
* @returns The hex version of the bytes.
|
|
2910
|
+
* Get a locale dictionary.
|
|
2911
|
+
* @param locale The locale.
|
|
2912
|
+
* @returns The dictionary of undefined if it does not exist.
|
|
2595
2913
|
*/
|
|
2596
|
-
static
|
|
2597
|
-
|
|
2598
|
-
return includePrefix ? HexHelper.addPrefix(hex) : hex;
|
|
2914
|
+
static getDictionary(locale) {
|
|
2915
|
+
return I18n._localeDictionaries[locale];
|
|
2599
2916
|
}
|
|
2600
2917
|
/**
|
|
2601
|
-
*
|
|
2602
|
-
* @
|
|
2603
|
-
* @returns The UTF8 version of the bytes.
|
|
2918
|
+
* Get all the locale dictionaries.
|
|
2919
|
+
* @returns The dictionaries.
|
|
2604
2920
|
*/
|
|
2605
|
-
static
|
|
2606
|
-
return
|
|
2921
|
+
static getAllDictionaries() {
|
|
2922
|
+
return I18n._localeDictionaries;
|
|
2607
2923
|
}
|
|
2608
2924
|
/**
|
|
2609
|
-
*
|
|
2610
|
-
* @param
|
|
2611
|
-
* @
|
|
2925
|
+
* Add a locale changed handler.
|
|
2926
|
+
* @param id The id of the handler.
|
|
2927
|
+
* @param handler The handler to add.
|
|
2612
2928
|
*/
|
|
2613
|
-
static
|
|
2614
|
-
|
|
2615
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
2616
|
-
b.push(bytes[i].toString(2).padStart(8, "0"));
|
|
2617
|
-
}
|
|
2618
|
-
return b.join("");
|
|
2929
|
+
static addLocaleHandler(id, handler) {
|
|
2930
|
+
I18n._localeChangedHandlers[id] = handler;
|
|
2619
2931
|
}
|
|
2620
2932
|
/**
|
|
2621
|
-
*
|
|
2622
|
-
* @param
|
|
2623
|
-
* @returns The bytes.
|
|
2933
|
+
* Remove a locale changed handler.
|
|
2934
|
+
* @param id The id of the handler.
|
|
2624
2935
|
*/
|
|
2625
|
-
static
|
|
2626
|
-
|
|
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;
|
|
2936
|
+
static removeLocaleHandler(id) {
|
|
2937
|
+
delete I18n._localeChangedHandlers[id];
|
|
2631
2938
|
}
|
|
2632
2939
|
/**
|
|
2633
|
-
*
|
|
2634
|
-
* @param
|
|
2635
|
-
* @
|
|
2940
|
+
* Add a dictionary changed handler.
|
|
2941
|
+
* @param id The id of the handler.
|
|
2942
|
+
* @param handler The handler to add.
|
|
2636
2943
|
*/
|
|
2637
|
-
static
|
|
2638
|
-
|
|
2944
|
+
static addDictionaryHandler(id, handler) {
|
|
2945
|
+
I18n._dictionaryChangedHandlers[id] = handler;
|
|
2639
2946
|
}
|
|
2640
2947
|
/**
|
|
2641
|
-
*
|
|
2642
|
-
* @param
|
|
2643
|
-
* @returns The bytes.
|
|
2948
|
+
* Remove a dictionary changed handler.
|
|
2949
|
+
* @param id The id of the handler.
|
|
2644
2950
|
*/
|
|
2645
|
-
static
|
|
2646
|
-
|
|
2951
|
+
static removeDictionaryHandler(id) {
|
|
2952
|
+
delete I18n._dictionaryChangedHandlers[id];
|
|
2647
2953
|
}
|
|
2648
2954
|
/**
|
|
2649
|
-
*
|
|
2650
|
-
* @param
|
|
2651
|
-
* @
|
|
2955
|
+
* Format a message.
|
|
2956
|
+
* @param key The key of the message to format.
|
|
2957
|
+
* @param values The values to substitute into the message.
|
|
2958
|
+
* @param overrideLocale Override the locale.
|
|
2959
|
+
* @returns The formatted string.
|
|
2652
2960
|
*/
|
|
2653
|
-
static
|
|
2654
|
-
|
|
2961
|
+
static formatMessage(key, values, overrideLocale) {
|
|
2962
|
+
let cl = overrideLocale ?? I18n._currentLocale;
|
|
2963
|
+
if (cl.startsWith("debug-")) {
|
|
2964
|
+
cl = I18n.DEFAULT_LOCALE;
|
|
2965
|
+
}
|
|
2966
|
+
if (!I18n._localeDictionaries[cl]) {
|
|
2967
|
+
return `!!Missing ${cl}`;
|
|
2968
|
+
}
|
|
2969
|
+
if (!I18n._localeDictionaries[cl][key]) {
|
|
2970
|
+
return `!!Missing ${cl}.${key}`;
|
|
2971
|
+
}
|
|
2972
|
+
if (I18n._currentLocale === "debug-k") {
|
|
2973
|
+
return key;
|
|
2974
|
+
}
|
|
2975
|
+
let ret = new intlMessageformat.IntlMessageFormat(I18n._localeDictionaries[cl][key], cl).format(values);
|
|
2976
|
+
if (I18n._currentLocale === "debug-x") {
|
|
2977
|
+
ret = ret.replace(/[a-z]/g, "x").replace(/[A-Z]/g, "x").replace(/\d/g, "n");
|
|
2978
|
+
}
|
|
2979
|
+
return ret;
|
|
2655
2980
|
}
|
|
2656
2981
|
/**
|
|
2657
|
-
*
|
|
2658
|
-
* @param
|
|
2659
|
-
* @returns
|
|
2982
|
+
* Check if the dictionaries have a message for the given key.
|
|
2983
|
+
* @param key The key to check for existence.
|
|
2984
|
+
* @returns True if the key exists.
|
|
2660
2985
|
*/
|
|
2661
|
-
static
|
|
2662
|
-
return
|
|
2986
|
+
static hasMessage(key) {
|
|
2987
|
+
return Is.string(I18n._localeDictionaries[I18n._currentLocale]?.[key]);
|
|
2663
2988
|
}
|
|
2664
2989
|
/**
|
|
2665
|
-
*
|
|
2990
|
+
* Flatten the translation property paths for faster lookup.
|
|
2991
|
+
* @param translation The translation to merge.
|
|
2992
|
+
* @param propertyPath The current root path.
|
|
2993
|
+
* @param mergedKeys The merged keys dictionary to populate.
|
|
2666
2994
|
* @internal
|
|
2667
2995
|
*/
|
|
2668
|
-
static
|
|
2669
|
-
|
|
2670
|
-
const
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2996
|
+
static flattenTranslationKeys(translation, propertyPath, mergedKeys) {
|
|
2997
|
+
for (const key in translation) {
|
|
2998
|
+
const val = translation[key];
|
|
2999
|
+
const mergedPath = propertyPath.length > 0 ? `${propertyPath}.${key}` : key;
|
|
3000
|
+
if (Is.string(val)) {
|
|
3001
|
+
mergedKeys[mergedPath] = val;
|
|
3002
|
+
}
|
|
3003
|
+
else if (Is.object(val)) {
|
|
3004
|
+
I18n.flattenTranslationKeys(val, mergedPath, mergedKeys);
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
3009
|
+
|
|
3010
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3011
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3012
|
+
/**
|
|
3013
|
+
* Error helper functions.
|
|
3014
|
+
*/
|
|
3015
|
+
class ErrorHelper {
|
|
3016
|
+
/**
|
|
3017
|
+
* Format Errors and returns just their messages.
|
|
3018
|
+
* @param error The error to format.
|
|
3019
|
+
* @returns The error formatted including any inner errors.
|
|
3020
|
+
*/
|
|
3021
|
+
static formatErrors(error) {
|
|
3022
|
+
return ErrorHelper.localizeErrors(error).map(e => e.message);
|
|
3023
|
+
}
|
|
3024
|
+
/**
|
|
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.
|
|
3028
|
+
*/
|
|
3029
|
+
static localizeErrors(error) {
|
|
3030
|
+
const formattedErrors = [];
|
|
3031
|
+
if (Is.notEmpty(error)) {
|
|
3032
|
+
const errors = BaseError.flatten(error);
|
|
3033
|
+
for (const err of errors) {
|
|
3034
|
+
const errorNameKey = `errorNames.${StringHelper.camelCase(err.name)}`;
|
|
3035
|
+
const errorMessageKey = `error.${err.message}`;
|
|
3036
|
+
// If there is no error message then it is probably
|
|
3037
|
+
// from a 3rd party lib, so don't format it just display
|
|
3038
|
+
const hasErrorName = I18n.hasMessage(errorNameKey);
|
|
3039
|
+
const hasErrorMessage = I18n.hasMessage(errorMessageKey);
|
|
3040
|
+
const localizedError = {
|
|
3041
|
+
name: I18n.formatMessage(hasErrorName ? errorNameKey : "errorNames.error"),
|
|
3042
|
+
message: hasErrorMessage
|
|
3043
|
+
? I18n.formatMessage(errorMessageKey, err.properties)
|
|
3044
|
+
: err.message
|
|
3045
|
+
};
|
|
3046
|
+
if (Is.stringValue(err.source)) {
|
|
3047
|
+
localizedError.source = err.source;
|
|
3048
|
+
}
|
|
3049
|
+
if (Is.stringValue(err.stack)) {
|
|
3050
|
+
// Remove the first line from the stack traces as they
|
|
3051
|
+
// just have the error type and message duplicated
|
|
3052
|
+
const lines = err.stack.split("\n");
|
|
3053
|
+
lines.shift();
|
|
3054
|
+
localizedError.stack = lines.join("\n");
|
|
3055
|
+
}
|
|
3056
|
+
const additional = ErrorHelper.formatValidationErrors(err);
|
|
3057
|
+
if (Is.stringValue(additional)) {
|
|
3058
|
+
localizedError.additional = additional;
|
|
3059
|
+
}
|
|
3060
|
+
formattedErrors.push(localizedError);
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
return formattedErrors;
|
|
3064
|
+
}
|
|
3065
|
+
/**
|
|
3066
|
+
* Localize the content of an error and any inner errors.
|
|
3067
|
+
* @param error The error to format.
|
|
3068
|
+
* @returns The localized version of the errors flattened.
|
|
3069
|
+
*/
|
|
3070
|
+
static formatValidationErrors(error) {
|
|
3071
|
+
if (Is.object(error.properties) &&
|
|
3072
|
+
Object.keys(error.properties).length > 0 &&
|
|
3073
|
+
Is.object(error.properties) &&
|
|
3074
|
+
Is.arrayValue(error.properties.validationFailures)) {
|
|
3075
|
+
const validationErrors = [];
|
|
3076
|
+
for (const validationFailure of error.properties.validationFailures) {
|
|
3077
|
+
const errorI18n = `error.${validationFailure.reason}`;
|
|
3078
|
+
const errorMessage = I18n.hasMessage(errorI18n)
|
|
3079
|
+
? I18n.formatMessage(errorI18n, validationFailure.properties)
|
|
3080
|
+
: errorI18n;
|
|
3081
|
+
let v = `${validationFailure.property}: ${errorMessage}`;
|
|
3082
|
+
if (Is.object(validationFailure.properties) &&
|
|
3083
|
+
Is.notEmpty(validationFailure.properties.value)) {
|
|
3084
|
+
v += ` = ${JSON.stringify(validationFailure.properties.value)}`;
|
|
2682
3085
|
}
|
|
3086
|
+
validationErrors.push(v);
|
|
2683
3087
|
}
|
|
3088
|
+
return validationErrors.join("\n");
|
|
2684
3089
|
}
|
|
2685
3090
|
}
|
|
2686
3091
|
}
|
|
2687
3092
|
|
|
3093
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3094
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
2688
3095
|
/**
|
|
2689
|
-
*
|
|
3096
|
+
* The types the extracted data can be coerced to.
|
|
2690
3097
|
*/
|
|
2691
|
-
|
|
3098
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
3099
|
+
const CoerceType = {
|
|
2692
3100
|
/**
|
|
2693
|
-
*
|
|
2694
|
-
* @internal
|
|
3101
|
+
* String.
|
|
2695
3102
|
*/
|
|
2696
|
-
|
|
3103
|
+
String: "string",
|
|
2697
3104
|
/**
|
|
2698
|
-
*
|
|
2699
|
-
* @param obj The object to convert.
|
|
2700
|
-
* @param format Format the JSON content.
|
|
2701
|
-
* @returns The object as bytes.
|
|
3105
|
+
* Number.
|
|
2702
3106
|
*/
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
3107
|
+
Number: "number",
|
|
3108
|
+
/**
|
|
3109
|
+
* Integer.
|
|
3110
|
+
*/
|
|
3111
|
+
Integer: "integer",
|
|
3112
|
+
/**
|
|
3113
|
+
* Boolean.
|
|
3114
|
+
*/
|
|
3115
|
+
Boolean: "boolean",
|
|
3116
|
+
/**
|
|
3117
|
+
* Big Integer.
|
|
3118
|
+
*/
|
|
3119
|
+
BigInt: "bigint",
|
|
3120
|
+
/**
|
|
3121
|
+
* Date.
|
|
3122
|
+
*/
|
|
3123
|
+
Date: "date",
|
|
3124
|
+
/**
|
|
3125
|
+
* Date Time.
|
|
3126
|
+
*/
|
|
3127
|
+
DateTime: "datetime",
|
|
3128
|
+
/**
|
|
3129
|
+
* Time.
|
|
3130
|
+
*/
|
|
3131
|
+
Time: "time",
|
|
3132
|
+
/**
|
|
3133
|
+
* Object.
|
|
3134
|
+
*/
|
|
3135
|
+
Object: "object",
|
|
3136
|
+
/**
|
|
3137
|
+
* Uint8Array.
|
|
3138
|
+
*/
|
|
3139
|
+
Uint8Array: "uint8array"
|
|
3140
|
+
};
|
|
3141
|
+
|
|
3142
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3143
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3144
|
+
/**
|
|
3145
|
+
* Coerce an object from one type to another.
|
|
3146
|
+
*/
|
|
3147
|
+
class Coerce {
|
|
3148
|
+
/**
|
|
3149
|
+
* Coerce the value to a string.
|
|
3150
|
+
* @param value The value to coerce.
|
|
3151
|
+
* @throws TypeError If the value can not be coerced.
|
|
3152
|
+
* @returns The value if it can be coerced.
|
|
3153
|
+
*/
|
|
3154
|
+
static string(value) {
|
|
3155
|
+
if (Is.undefined(value)) {
|
|
3156
|
+
return value;
|
|
3157
|
+
}
|
|
3158
|
+
if (Is.string(value)) {
|
|
3159
|
+
return value;
|
|
3160
|
+
}
|
|
3161
|
+
if (Is.number(value)) {
|
|
3162
|
+
return value.toString();
|
|
3163
|
+
}
|
|
3164
|
+
if (Is.boolean(value)) {
|
|
3165
|
+
return value ? "true" : "false";
|
|
3166
|
+
}
|
|
3167
|
+
if (Is.date(value)) {
|
|
3168
|
+
return value.toISOString();
|
|
3169
|
+
}
|
|
3170
|
+
}
|
|
3171
|
+
/**
|
|
3172
|
+
* Coerce the value to a number.
|
|
3173
|
+
* @param value The value to coerce.
|
|
3174
|
+
* @throws TypeError If the value can not be coerced.
|
|
3175
|
+
* @returns The value if it can be coerced.
|
|
3176
|
+
*/
|
|
3177
|
+
static number(value) {
|
|
3178
|
+
if (Is.undefined(value)) {
|
|
3179
|
+
return value;
|
|
3180
|
+
}
|
|
3181
|
+
if (Is.number(value)) {
|
|
3182
|
+
return value;
|
|
3183
|
+
}
|
|
3184
|
+
if (Is.string(value)) {
|
|
3185
|
+
const parsed = Number.parseFloat(value);
|
|
3186
|
+
if (Is.number(parsed)) {
|
|
3187
|
+
return parsed;
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
if (Is.boolean(value)) {
|
|
3191
|
+
return value ? 1 : 0;
|
|
3192
|
+
}
|
|
3193
|
+
if (Is.date(value)) {
|
|
3194
|
+
return value.getTime();
|
|
3195
|
+
}
|
|
3196
|
+
}
|
|
3197
|
+
/**
|
|
3198
|
+
* Coerce the value to an integer.
|
|
3199
|
+
* @param value The value to coerce.
|
|
3200
|
+
* @throws TypeError If the value can not be coerced.
|
|
3201
|
+
* @returns The value if it can be coerced.
|
|
3202
|
+
*/
|
|
3203
|
+
static integer(value) {
|
|
3204
|
+
const num = Coerce.number(value);
|
|
3205
|
+
if (!Is.undefined(num)) {
|
|
3206
|
+
return Math.trunc(num);
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
/**
|
|
3210
|
+
* Coerce the value to a bigint.
|
|
3211
|
+
* @param value The value to coerce.
|
|
3212
|
+
* @throws TypeError If the value can not be coerced.
|
|
3213
|
+
* @returns The value if it can be coerced.
|
|
3214
|
+
*/
|
|
3215
|
+
static bigint(value) {
|
|
3216
|
+
if (Is.undefined(value)) {
|
|
3217
|
+
return value;
|
|
3218
|
+
}
|
|
3219
|
+
if (Is.bigint(value)) {
|
|
3220
|
+
return value;
|
|
3221
|
+
}
|
|
3222
|
+
if (Is.number(value)) {
|
|
3223
|
+
return BigInt(value);
|
|
3224
|
+
}
|
|
3225
|
+
if (Is.string(value)) {
|
|
3226
|
+
const parsed = Number.parseFloat(value);
|
|
3227
|
+
if (Is.integer(parsed)) {
|
|
3228
|
+
return BigInt(parsed);
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
3231
|
+
if (Is.boolean(value)) {
|
|
3232
|
+
return value ? 1n : 0n;
|
|
3233
|
+
}
|
|
3234
|
+
}
|
|
3235
|
+
/**
|
|
3236
|
+
* Coerce the value to a boolean.
|
|
3237
|
+
* @param value The value to coerce.
|
|
3238
|
+
* @throws TypeError If the value can not be coerced.
|
|
3239
|
+
* @returns The value if it can be coerced.
|
|
3240
|
+
*/
|
|
3241
|
+
static boolean(value) {
|
|
3242
|
+
if (Is.undefined(value)) {
|
|
3243
|
+
return value;
|
|
3244
|
+
}
|
|
3245
|
+
if (Is.boolean(value)) {
|
|
3246
|
+
return value;
|
|
3247
|
+
}
|
|
3248
|
+
if (Is.number(value)) {
|
|
3249
|
+
// eslint-disable-next-line no-unneeded-ternary
|
|
3250
|
+
return value ? true : false;
|
|
3251
|
+
}
|
|
3252
|
+
if (Is.string(value)) {
|
|
3253
|
+
if (/true/i.test(value)) {
|
|
3254
|
+
return true;
|
|
3255
|
+
}
|
|
3256
|
+
if (/false/i.test(value)) {
|
|
3257
|
+
return false;
|
|
3258
|
+
}
|
|
2706
3259
|
}
|
|
2707
|
-
const json = format ? JSON.stringify(obj, undefined, "\t") : JSON.stringify(obj);
|
|
2708
|
-
return Converter.utf8ToBytes(json);
|
|
2709
3260
|
}
|
|
2710
3261
|
/**
|
|
2711
|
-
*
|
|
2712
|
-
* @param
|
|
2713
|
-
* @
|
|
2714
|
-
* @
|
|
3262
|
+
* Coerce the value to a date.
|
|
3263
|
+
* @param value The value to coerce.
|
|
3264
|
+
* @throws TypeError If the value can not be coerced.
|
|
3265
|
+
* @returns The value if it can be coerced.
|
|
2715
3266
|
*/
|
|
2716
|
-
static
|
|
2717
|
-
if (Is.
|
|
2718
|
-
return
|
|
3267
|
+
static date(value) {
|
|
3268
|
+
if (Is.undefined(value)) {
|
|
3269
|
+
return value;
|
|
2719
3270
|
}
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
return JSON.parse(utf8);
|
|
3271
|
+
if (Is.date(value)) {
|
|
3272
|
+
return value;
|
|
2723
3273
|
}
|
|
2724
|
-
|
|
2725
|
-
|
|
3274
|
+
if (Is.number(value)) {
|
|
3275
|
+
return new Date(value);
|
|
2726
3276
|
}
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
static clone(obj) {
|
|
2734
|
-
if (Is.undefined(obj)) {
|
|
2735
|
-
return undefined;
|
|
3277
|
+
if (Is.string(value)) {
|
|
3278
|
+
const dt = new Date(value);
|
|
3279
|
+
if (!Number.isNaN(dt.getTime())) {
|
|
3280
|
+
const utc = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate());
|
|
3281
|
+
return new Date(utc);
|
|
3282
|
+
}
|
|
2736
3283
|
}
|
|
2737
|
-
return structuredClone(obj);
|
|
2738
3284
|
}
|
|
2739
3285
|
/**
|
|
2740
|
-
*
|
|
2741
|
-
* @param
|
|
2742
|
-
* @
|
|
2743
|
-
* @returns The
|
|
3286
|
+
* Coerce the value to a date/time.
|
|
3287
|
+
* @param value The value to coerce.
|
|
3288
|
+
* @throws TypeError If the value can not be coerced.
|
|
3289
|
+
* @returns The value if it can be coerced.
|
|
2744
3290
|
*/
|
|
2745
|
-
static
|
|
2746
|
-
if (Is.
|
|
2747
|
-
return
|
|
3291
|
+
static dateTime(value) {
|
|
3292
|
+
if (Is.undefined(value)) {
|
|
3293
|
+
return value;
|
|
2748
3294
|
}
|
|
2749
|
-
if (Is.
|
|
2750
|
-
return
|
|
3295
|
+
if (Is.date(value)) {
|
|
3296
|
+
return value;
|
|
2751
3297
|
}
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
ObjectHelper.propertySet(obj1Clone, key, obj2[key]);
|
|
2761
|
-
}
|
|
3298
|
+
if (Is.number(value)) {
|
|
3299
|
+
return new Date(value);
|
|
3300
|
+
}
|
|
3301
|
+
if (Is.string(value)) {
|
|
3302
|
+
const dt = new Date(value);
|
|
3303
|
+
if (!Number.isNaN(dt.getTime())) {
|
|
3304
|
+
const utc = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate(), dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
|
|
3305
|
+
return new Date(utc);
|
|
2762
3306
|
}
|
|
2763
3307
|
}
|
|
2764
|
-
return obj1Clone;
|
|
2765
3308
|
}
|
|
2766
3309
|
/**
|
|
2767
|
-
*
|
|
2768
|
-
* @param
|
|
2769
|
-
* @
|
|
2770
|
-
* @
|
|
2771
|
-
* @returns True is the objects are equal.
|
|
3310
|
+
* Coerce the value to a time.
|
|
3311
|
+
* @param value The value to coerce.
|
|
3312
|
+
* @throws TypeError If the value can not be coerced.
|
|
3313
|
+
* @returns The value if it can be coerced.
|
|
2772
3314
|
*/
|
|
2773
|
-
static
|
|
2774
|
-
if (
|
|
2775
|
-
return
|
|
3315
|
+
static time(value) {
|
|
3316
|
+
if (Is.undefined(value)) {
|
|
3317
|
+
return value;
|
|
2776
3318
|
}
|
|
2777
|
-
|
|
2778
|
-
}
|
|
2779
|
-
/**
|
|
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.
|
|
2784
|
-
*/
|
|
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
|
-
}
|
|
3319
|
+
if (Is.date(value)) {
|
|
2797
3320
|
return value;
|
|
2798
3321
|
}
|
|
2799
|
-
|
|
3322
|
+
if (Is.number(value)) {
|
|
3323
|
+
const dt = new Date(value);
|
|
3324
|
+
dt.setFullYear(1970, 0, 1);
|
|
3325
|
+
return dt;
|
|
3326
|
+
}
|
|
3327
|
+
if (Is.string(value)) {
|
|
3328
|
+
const dt = new Date(value);
|
|
3329
|
+
if (!Number.isNaN(dt.getTime())) {
|
|
3330
|
+
const utc = Date.UTC(1970, 0, 1, dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
|
|
3331
|
+
return new Date(utc);
|
|
3332
|
+
}
|
|
3333
|
+
}
|
|
2800
3334
|
}
|
|
2801
3335
|
/**
|
|
2802
|
-
*
|
|
2803
|
-
* @param
|
|
2804
|
-
* @
|
|
2805
|
-
* @
|
|
3336
|
+
* Coerce the value to an object.
|
|
3337
|
+
* @param value The value to coerce.
|
|
3338
|
+
* @throws TypeError If the value can not be coerced.
|
|
3339
|
+
* @returns The value if it can be coerced.
|
|
2806
3340
|
*/
|
|
2807
|
-
static
|
|
2808
|
-
if (Is.
|
|
2809
|
-
|
|
3341
|
+
static object(value) {
|
|
3342
|
+
if (Is.undefined(value)) {
|
|
3343
|
+
return value;
|
|
3344
|
+
}
|
|
3345
|
+
if (Is.object(value)) {
|
|
3346
|
+
return value;
|
|
3347
|
+
}
|
|
3348
|
+
if (Is.stringValue(value)) {
|
|
3349
|
+
try {
|
|
3350
|
+
return JSON.parse(value);
|
|
3351
|
+
}
|
|
3352
|
+
catch { }
|
|
2810
3353
|
}
|
|
2811
3354
|
}
|
|
2812
3355
|
/**
|
|
2813
|
-
*
|
|
2814
|
-
* @param
|
|
2815
|
-
* @
|
|
3356
|
+
* Coerce the value to a Uint8Array.
|
|
3357
|
+
* @param value The value to coerce.
|
|
3358
|
+
* @throws TypeError If the value can not be coerced.
|
|
3359
|
+
* @returns The value if it can be coerced.
|
|
2816
3360
|
*/
|
|
2817
|
-
static
|
|
2818
|
-
if (Is.
|
|
2819
|
-
|
|
3361
|
+
static uint8Array(value) {
|
|
3362
|
+
if (Is.undefined(value)) {
|
|
3363
|
+
return value;
|
|
3364
|
+
}
|
|
3365
|
+
if (Is.string(value)) {
|
|
3366
|
+
if (Is.stringHex(value.toLowerCase(), true)) {
|
|
3367
|
+
return Converter.hexToBytes(value.toLowerCase());
|
|
3368
|
+
}
|
|
3369
|
+
if (Is.stringBase64(value)) {
|
|
3370
|
+
return Converter.base64ToBytes(value);
|
|
3371
|
+
}
|
|
2820
3372
|
}
|
|
2821
3373
|
}
|
|
2822
3374
|
/**
|
|
2823
|
-
*
|
|
2824
|
-
* @param
|
|
2825
|
-
* @param
|
|
2826
|
-
* @returns The
|
|
2827
|
-
*/
|
|
2828
|
-
static
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
3375
|
+
* Coerces a value based on the coercion type.
|
|
3376
|
+
* @param value The value to coerce.
|
|
3377
|
+
* @param type The coercion type to perform.
|
|
3378
|
+
* @returns The coerced value.
|
|
3379
|
+
*/
|
|
3380
|
+
static byType(value, type) {
|
|
3381
|
+
switch (type) {
|
|
3382
|
+
case CoerceType.String:
|
|
3383
|
+
return Coerce.string(value);
|
|
3384
|
+
case CoerceType.Number:
|
|
3385
|
+
return Coerce.number(value);
|
|
3386
|
+
case CoerceType.Integer:
|
|
3387
|
+
return Coerce.integer(value);
|
|
3388
|
+
case CoerceType.BigInt:
|
|
3389
|
+
return Coerce.bigint(value);
|
|
3390
|
+
case CoerceType.Boolean:
|
|
3391
|
+
return Coerce.boolean(value);
|
|
3392
|
+
case CoerceType.Date:
|
|
3393
|
+
return Coerce.date(value);
|
|
3394
|
+
case CoerceType.DateTime:
|
|
3395
|
+
return Coerce.dateTime(value);
|
|
3396
|
+
case CoerceType.Time:
|
|
3397
|
+
return Coerce.time(value);
|
|
3398
|
+
case CoerceType.Object:
|
|
3399
|
+
return Coerce.object(value);
|
|
3400
|
+
case CoerceType.Uint8Array:
|
|
3401
|
+
return Coerce.uint8Array(value);
|
|
3402
|
+
default:
|
|
3403
|
+
return value;
|
|
2835
3404
|
}
|
|
2836
|
-
return obj;
|
|
2837
3405
|
}
|
|
3406
|
+
}
|
|
3407
|
+
|
|
3408
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3409
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3410
|
+
/**
|
|
3411
|
+
* Class to help with filenames.
|
|
3412
|
+
*/
|
|
3413
|
+
class FilenameHelper {
|
|
2838
3414
|
/**
|
|
2839
|
-
*
|
|
2840
|
-
* @param
|
|
2841
|
-
* @
|
|
2842
|
-
* @returns The partial object.
|
|
3415
|
+
* Replaces any unsafe characters in the filename.
|
|
3416
|
+
* @param filename The filename to make safe.
|
|
3417
|
+
* @returns The safe filename.
|
|
2843
3418
|
*/
|
|
2844
|
-
static
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
delete result[key];
|
|
2849
|
-
}
|
|
2850
|
-
return result;
|
|
3419
|
+
static safeFilename(filename) {
|
|
3420
|
+
let safe = Coerce.string(filename);
|
|
3421
|
+
if (Is.empty(safe)) {
|
|
3422
|
+
return "";
|
|
2851
3423
|
}
|
|
2852
|
-
|
|
3424
|
+
// Common non filename characters
|
|
3425
|
+
safe = safe.replace(/["*/:<>?\\|]/g, "_");
|
|
3426
|
+
// Windows non filename characters
|
|
3427
|
+
safe = safe.replace(/^(con|prn|aux|nul|com\d|lpt\d)$/i, "_");
|
|
3428
|
+
// Control characters
|
|
3429
|
+
safe = safe.replace(/[\u0000-\u001F\u0080-\u009F]/g, "_");
|
|
3430
|
+
// Relative paths
|
|
3431
|
+
safe = safe.replace(/^\.+/, "_");
|
|
3432
|
+
// Trailing periods
|
|
3433
|
+
safe = safe.replace(/\.+$/, "");
|
|
3434
|
+
return safe;
|
|
2853
3435
|
}
|
|
2854
3436
|
}
|
|
2855
3437
|
|
|
@@ -2871,6 +3453,32 @@ class RandomHelper {
|
|
|
2871
3453
|
}
|
|
2872
3454
|
}
|
|
2873
3455
|
|
|
3456
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3457
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3458
|
+
/**
|
|
3459
|
+
* Class to help with uint8 arrays.
|
|
3460
|
+
*/
|
|
3461
|
+
class Uint8ArrayHelper {
|
|
3462
|
+
/**
|
|
3463
|
+
* Concatenate multiple arrays.
|
|
3464
|
+
* @param arrays The array to concatenate.
|
|
3465
|
+
* @returns The combined array.
|
|
3466
|
+
*/
|
|
3467
|
+
static concat(arrays) {
|
|
3468
|
+
let totalLength = 0;
|
|
3469
|
+
for (const array of arrays) {
|
|
3470
|
+
totalLength += array.length;
|
|
3471
|
+
}
|
|
3472
|
+
const concatBytes = new Uint8Array(totalLength);
|
|
3473
|
+
let offset = 0;
|
|
3474
|
+
for (const array of arrays) {
|
|
3475
|
+
concatBytes.set(array, offset);
|
|
3476
|
+
offset += array.length;
|
|
3477
|
+
}
|
|
3478
|
+
return concatBytes;
|
|
3479
|
+
}
|
|
3480
|
+
}
|
|
3481
|
+
|
|
2874
3482
|
// Copyright 2024 IOTA Stiftung.
|
|
2875
3483
|
// SPDX-License-Identifier: Apache-2.0.
|
|
2876
3484
|
/**
|
|
@@ -2883,7 +3491,7 @@ const CompressionType = {
|
|
|
2883
3491
|
*/
|
|
2884
3492
|
Gzip: "gzip",
|
|
2885
3493
|
/**
|
|
2886
|
-
*
|
|
3494
|
+
* Deflate.
|
|
2887
3495
|
*/
|
|
2888
3496
|
Deflate: "deflate"
|
|
2889
3497
|
};
|
|
@@ -3367,6 +3975,19 @@ class AsyncCache {
|
|
|
3367
3975
|
static async get(key) {
|
|
3368
3976
|
return AsyncCache._cache[key]?.response;
|
|
3369
3977
|
}
|
|
3978
|
+
/**
|
|
3979
|
+
* Set an entry into the cache.
|
|
3980
|
+
* @param key The key to set in the cache.
|
|
3981
|
+
* @param value The value to set in the cache.
|
|
3982
|
+
* @param ttlMs The TTL of the entry in the cache in ms, defaults to 1s.
|
|
3983
|
+
* @returns Nothing.
|
|
3984
|
+
*/
|
|
3985
|
+
static async set(key, value, ttlMs) {
|
|
3986
|
+
AsyncCache._cache[key] = {
|
|
3987
|
+
response: Promise.resolve(value),
|
|
3988
|
+
expires: Date.now() + (ttlMs ?? 1000)
|
|
3989
|
+
};
|
|
3990
|
+
}
|
|
3370
3991
|
/**
|
|
3371
3992
|
* Remove an entry from the cache.
|
|
3372
3993
|
* @param key The key to remove from the cache.
|
|
@@ -3421,11 +4042,10 @@ class Compression {
|
|
|
3421
4042
|
Guards.uint8Array(Compression._CLASS_NAME, "bytes", bytes);
|
|
3422
4043
|
Guards.arrayOneOf(Compression._CLASS_NAME, "type", type, Object.values(CompressionType));
|
|
3423
4044
|
const blob = new Blob([bytes]);
|
|
3424
|
-
const
|
|
3425
|
-
const
|
|
3426
|
-
const compressedBlob = await new Response(
|
|
3427
|
-
const
|
|
3428
|
-
const compressedBytes = new Uint8Array(ab);
|
|
4045
|
+
const compressionStream = new CompressionStream(type);
|
|
4046
|
+
const compressionPipe = blob.stream().pipeThrough(compressionStream);
|
|
4047
|
+
const compressedBlob = await new Response(compressionPipe).blob();
|
|
4048
|
+
const compressedBytes = new Uint8Array(await compressedBlob.arrayBuffer());
|
|
3429
4049
|
// GZIP header contains a byte which specifies the OS the
|
|
3430
4050
|
// compression was performed on. We set this to 3 (Unix) to ensure
|
|
3431
4051
|
// that we produce consistent results.
|
|
@@ -3444,11 +4064,10 @@ class Compression {
|
|
|
3444
4064
|
Guards.uint8Array(Compression._CLASS_NAME, "compressedBytes", compressedBytes);
|
|
3445
4065
|
Guards.arrayOneOf(Compression._CLASS_NAME, "type", type, Object.values(CompressionType));
|
|
3446
4066
|
const blob = new Blob([compressedBytes]);
|
|
3447
|
-
const
|
|
3448
|
-
const
|
|
3449
|
-
const decompressedBlob = await new Response(
|
|
3450
|
-
|
|
3451
|
-
return new Uint8Array(ab);
|
|
4067
|
+
const decompressionStream = new DecompressionStream(type);
|
|
4068
|
+
const decompressionPipe = blob.stream().pipeThrough(decompressionStream);
|
|
4069
|
+
const decompressedBlob = await new Response(decompressionPipe).blob();
|
|
4070
|
+
return new Uint8Array(await decompressedBlob.bytes());
|
|
3452
4071
|
}
|
|
3453
4072
|
}
|
|
3454
4073
|
|
|
@@ -3506,6 +4125,7 @@ class Validation {
|
|
|
3506
4125
|
* @param options Additional options for the validation.
|
|
3507
4126
|
* @param options.minLength The minimum length of the string.
|
|
3508
4127
|
* @param options.maxLength The maximum length of the string.
|
|
4128
|
+
* @param options.format Specific format to check.
|
|
3509
4129
|
* @returns True if the value is a valid string.
|
|
3510
4130
|
*/
|
|
3511
4131
|
static string(property, value, failures, fieldNameResource, options) {
|
|
@@ -3524,6 +4144,47 @@ class Validation {
|
|
|
3524
4144
|
const maxLimitDefined = Is.integer(maxLength);
|
|
3525
4145
|
const belowMin = minLimitDefined && value.length < minLength;
|
|
3526
4146
|
const aboveMax = maxLimitDefined && value.length > maxLength;
|
|
4147
|
+
if (options?.format === "base58" && !Is.stringBase58(value)) {
|
|
4148
|
+
failures.push({
|
|
4149
|
+
property,
|
|
4150
|
+
reason: "validation.beTextBase58",
|
|
4151
|
+
properties: {
|
|
4152
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
4153
|
+
value
|
|
4154
|
+
}
|
|
4155
|
+
});
|
|
4156
|
+
}
|
|
4157
|
+
else if (options?.format === "base64" && !Is.stringBase64(value)) {
|
|
4158
|
+
failures.push({
|
|
4159
|
+
property,
|
|
4160
|
+
reason: "validation.beTextBase64",
|
|
4161
|
+
properties: {
|
|
4162
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
4163
|
+
value
|
|
4164
|
+
}
|
|
4165
|
+
});
|
|
4166
|
+
}
|
|
4167
|
+
else if (options?.format === "hex" && !Is.stringHex(value)) {
|
|
4168
|
+
failures.push({
|
|
4169
|
+
property,
|
|
4170
|
+
reason: "validation.beTextHex",
|
|
4171
|
+
properties: {
|
|
4172
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
4173
|
+
value
|
|
4174
|
+
}
|
|
4175
|
+
});
|
|
4176
|
+
}
|
|
4177
|
+
else if (Is.regexp(options?.format) && !options.format.test(value)) {
|
|
4178
|
+
failures.push({
|
|
4179
|
+
property,
|
|
4180
|
+
reason: "validation.beTextRegExp",
|
|
4181
|
+
properties: {
|
|
4182
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
4183
|
+
value,
|
|
4184
|
+
format: options?.format
|
|
4185
|
+
}
|
|
4186
|
+
});
|
|
4187
|
+
}
|
|
3527
4188
|
if (minLimitDefined && maxLimitDefined && (belowMin || aboveMax)) {
|
|
3528
4189
|
failures.push({
|
|
3529
4190
|
property,
|
|
@@ -4187,16 +4848,19 @@ exports.AlreadyExistsError = AlreadyExistsError;
|
|
|
4187
4848
|
exports.ArrayHelper = ArrayHelper;
|
|
4188
4849
|
exports.AsyncCache = AsyncCache;
|
|
4189
4850
|
exports.Base32 = Base32;
|
|
4851
|
+
exports.Base58 = Base58;
|
|
4190
4852
|
exports.Base64 = Base64;
|
|
4191
4853
|
exports.Base64Url = Base64Url;
|
|
4192
4854
|
exports.BaseError = BaseError;
|
|
4193
4855
|
exports.BitString = BitString;
|
|
4194
4856
|
exports.Coerce = Coerce;
|
|
4857
|
+
exports.CoerceType = CoerceType;
|
|
4195
4858
|
exports.ComponentFactory = ComponentFactory;
|
|
4196
4859
|
exports.Compression = Compression;
|
|
4197
4860
|
exports.CompressionType = CompressionType;
|
|
4198
4861
|
exports.ConflictError = ConflictError;
|
|
4199
4862
|
exports.Converter = Converter;
|
|
4863
|
+
exports.EnvHelper = EnvHelper;
|
|
4200
4864
|
exports.ErrorHelper = ErrorHelper;
|
|
4201
4865
|
exports.Factory = Factory;
|
|
4202
4866
|
exports.FilenameHelper = FilenameHelper;
|
|
@@ -4213,6 +4877,7 @@ exports.NotSupportedError = NotSupportedError;
|
|
|
4213
4877
|
exports.ObjectHelper = ObjectHelper;
|
|
4214
4878
|
exports.RandomHelper = RandomHelper;
|
|
4215
4879
|
exports.StringHelper = StringHelper;
|
|
4880
|
+
exports.Uint8ArrayHelper = Uint8ArrayHelper;
|
|
4216
4881
|
exports.UnauthorizedError = UnauthorizedError;
|
|
4217
4882
|
exports.UnprocessableError = UnprocessableError;
|
|
4218
4883
|
exports.Url = Url;
|