@twin.org/core 0.0.1-next.2 → 0.0.1-next.20
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 +1022 -724
- package/dist/esm/index.mjs +1021 -725
- 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/objectHelper.d.ts +8 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/models/IComponent.d.ts +12 -3
- package/dist/types/models/compressionType.d.ts +1 -1
- package/dist/types/utils/converter.d.ts +12 -0
- package/dist/types/utils/guards.d.ts +8 -0
- package/dist/types/utils/is.d.ts +12 -0
- package/dist/types/utils/validation.d.ts +2 -0
- package/docs/changelog.md +1 -1
- package/docs/reference/classes/AlreadyExistsError.md +67 -25
- package/docs/reference/classes/ArrayHelper.md +6 -2
- package/docs/reference/classes/AsyncCache.md +18 -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 +24 -8
- package/docs/reference/classes/Compression.md +12 -4
- 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 +248 -72
- package/docs/reference/classes/HexHelper.md +15 -5
- package/docs/reference/classes/I18n.md +42 -16
- package/docs/reference/classes/Is.md +156 -40
- package/docs/reference/classes/JsonHelper.md +15 -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 +98 -20
- package/docs/reference/classes/RandomHelper.md +3 -1
- package/docs/reference/classes/StringHelper.md +51 -17
- 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 +313 -107
- package/docs/reference/classes/ValidationError.md +64 -24
- package/docs/reference/index.md +2 -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/variables/CompressionType.md +1 -1
- package/locales/en.json +14 -0
- package/package.json +4 -30
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.
|
|
@@ -139,6 +139,16 @@ class Is {
|
|
|
139
139
|
// eslint-disable-next-line unicorn/better-regex
|
|
140
140
|
/^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2}==|[A-Za-z0-9-_]{3}=)?$/.test(value));
|
|
141
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Is the value a base58 string.
|
|
144
|
+
* @param value The value to test.
|
|
145
|
+
* @returns True if the value is a base58 string.
|
|
146
|
+
*/
|
|
147
|
+
static stringBase58(value) {
|
|
148
|
+
return (Is.stringValue(value) &&
|
|
149
|
+
// eslint-disable-next-line unicorn/better-regex
|
|
150
|
+
/^[A-HJ-NP-Za-km-z1-9]*$/.test(value));
|
|
151
|
+
}
|
|
142
152
|
/**
|
|
143
153
|
* Is the value a hex string.
|
|
144
154
|
* @param value The value to test.
|
|
@@ -351,6 +361,14 @@ class Is {
|
|
|
351
361
|
static promise(value) {
|
|
352
362
|
return value instanceof Promise;
|
|
353
363
|
}
|
|
364
|
+
/**
|
|
365
|
+
* Is the value a regexp.
|
|
366
|
+
* @param value The value to test.
|
|
367
|
+
* @returns True if the value is a regexp.
|
|
368
|
+
*/
|
|
369
|
+
static regexp(value) {
|
|
370
|
+
return value instanceof RegExp;
|
|
371
|
+
}
|
|
354
372
|
}
|
|
355
373
|
|
|
356
374
|
// Copyright 2024 IOTA Stiftung.
|
|
@@ -900,6 +918,127 @@ class Base32 {
|
|
|
900
918
|
}
|
|
901
919
|
}
|
|
902
920
|
|
|
921
|
+
/**
|
|
922
|
+
* Class to help with base58 Encoding/Decoding.
|
|
923
|
+
*/
|
|
924
|
+
class Base58 {
|
|
925
|
+
/**
|
|
926
|
+
* Runtime name for the class.
|
|
927
|
+
* @internal
|
|
928
|
+
*/
|
|
929
|
+
static _CLASS_NAME = "Base58";
|
|
930
|
+
/**
|
|
931
|
+
* Alphabet table for encoding.
|
|
932
|
+
* @internal
|
|
933
|
+
*/
|
|
934
|
+
static _ALPHABET =
|
|
935
|
+
// cspell:disable-next-line
|
|
936
|
+
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
937
|
+
/**
|
|
938
|
+
* Reverse map for decoding.
|
|
939
|
+
* @internal
|
|
940
|
+
*/
|
|
941
|
+
static _ALPHABET_REVERSE = [
|
|
942
|
+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
943
|
+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
944
|
+
-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,
|
|
945
|
+
17, 18, 19, 20, 21, -1, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1, -1, 33,
|
|
946
|
+
34, 35, 36, 37, 38, 39, 40, 41, 42, 43, -1, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
|
|
947
|
+
57, -1, -1, -1, -1, -1
|
|
948
|
+
];
|
|
949
|
+
/**
|
|
950
|
+
* Convert the base 58 string to a byte array.
|
|
951
|
+
* @param base58 The base58 string to convert.
|
|
952
|
+
* @returns The byte array.
|
|
953
|
+
* @throws If the input string contains a character not in the Base58 alphabet.
|
|
954
|
+
*/
|
|
955
|
+
static decode(base58) {
|
|
956
|
+
let zeroes = 0;
|
|
957
|
+
for (let i = 0; i < base58.length; i++) {
|
|
958
|
+
if (base58[i] !== "1") {
|
|
959
|
+
break;
|
|
960
|
+
}
|
|
961
|
+
zeroes += 1;
|
|
962
|
+
}
|
|
963
|
+
const size = Math.trunc((base58.length * 733) / 1000) + 1;
|
|
964
|
+
const b256 = new Uint8Array(size).fill(0);
|
|
965
|
+
let length = 0;
|
|
966
|
+
for (let i = zeroes; i < base58.length; i++) {
|
|
967
|
+
const ch = base58.charCodeAt(i);
|
|
968
|
+
if (ch & 0xff80) {
|
|
969
|
+
throw new GeneralError(Base58._CLASS_NAME, "invalidCharacter", { invalidCharacter: ch });
|
|
970
|
+
}
|
|
971
|
+
const val = Base58._ALPHABET_REVERSE[ch];
|
|
972
|
+
if (val === -1) {
|
|
973
|
+
throw new GeneralError(Base58._CLASS_NAME, "invalidCharacter", { invalidCharacter: ch });
|
|
974
|
+
}
|
|
975
|
+
let carry = val;
|
|
976
|
+
let j = 0;
|
|
977
|
+
for (let k = size - 1; k >= 0; k--, j++) {
|
|
978
|
+
if (carry === 0 && j >= length) {
|
|
979
|
+
break;
|
|
980
|
+
}
|
|
981
|
+
carry += b256[k] * 58;
|
|
982
|
+
b256[k] = carry;
|
|
983
|
+
carry >>>= 8;
|
|
984
|
+
}
|
|
985
|
+
length = j;
|
|
986
|
+
}
|
|
987
|
+
const out = new Uint8Array(zeroes + length);
|
|
988
|
+
let j;
|
|
989
|
+
for (j = 0; j < zeroes; j++) {
|
|
990
|
+
out[j] = 0;
|
|
991
|
+
}
|
|
992
|
+
let i = size - length;
|
|
993
|
+
while (i < size) {
|
|
994
|
+
out[j++] = b256[i++];
|
|
995
|
+
}
|
|
996
|
+
return out;
|
|
997
|
+
}
|
|
998
|
+
/**
|
|
999
|
+
* Convert a byte array to base 58.
|
|
1000
|
+
* @param bytes The byte array to encode.
|
|
1001
|
+
* @returns The data as base58 string.
|
|
1002
|
+
*/
|
|
1003
|
+
static encode(bytes) {
|
|
1004
|
+
let zeroes = 0;
|
|
1005
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
1006
|
+
if (bytes[i] !== 0) {
|
|
1007
|
+
break;
|
|
1008
|
+
}
|
|
1009
|
+
zeroes += 1;
|
|
1010
|
+
}
|
|
1011
|
+
const size = Math.trunc(((bytes.length - zeroes) * 138) / 100) + 1;
|
|
1012
|
+
const b58 = new Uint8Array(size).fill(0);
|
|
1013
|
+
let length = 0;
|
|
1014
|
+
for (let i = zeroes; i < bytes.length; i++) {
|
|
1015
|
+
let carry = bytes[i];
|
|
1016
|
+
let j = 0;
|
|
1017
|
+
for (let k = size - 1; k >= 0; k--, j++) {
|
|
1018
|
+
if (carry === 0 && j >= length) {
|
|
1019
|
+
break;
|
|
1020
|
+
}
|
|
1021
|
+
carry += b58[k] * 256;
|
|
1022
|
+
b58[k] = carry % 58;
|
|
1023
|
+
carry = Math.trunc(carry / 58);
|
|
1024
|
+
}
|
|
1025
|
+
length = j;
|
|
1026
|
+
}
|
|
1027
|
+
let i = size - length;
|
|
1028
|
+
while (i < size && b58[i] === 0) {
|
|
1029
|
+
i += 1;
|
|
1030
|
+
}
|
|
1031
|
+
let str = "";
|
|
1032
|
+
for (let j = 0; j < zeroes; j++) {
|
|
1033
|
+
str += "1";
|
|
1034
|
+
}
|
|
1035
|
+
while (i < size) {
|
|
1036
|
+
str += Base58._ALPHABET[b58[i++]];
|
|
1037
|
+
}
|
|
1038
|
+
return str;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
|
|
903
1042
|
// Copyright 2024 IOTA Stiftung.
|
|
904
1043
|
// SPDX-License-Identifier: Apache-2.0.
|
|
905
1044
|
/* eslint-disable no-bitwise */
|
|
@@ -1054,7 +1193,7 @@ class Base64 {
|
|
|
1054
1193
|
const maxChunkLength = 16383; // must be multiple of 3
|
|
1055
1194
|
// go through the array every three bytes, we'll deal with trailing stuff later
|
|
1056
1195
|
for (let i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
|
|
1057
|
-
parts.push(Base64.encodeChunk(bytes, i, i + maxChunkLength
|
|
1196
|
+
parts.push(Base64.encodeChunk(bytes, i, Math.min(i + maxChunkLength, len2)));
|
|
1058
1197
|
}
|
|
1059
1198
|
// pad the end with zeros, but make sure to not forget the extra bytes
|
|
1060
1199
|
if (extraBytes === 1) {
|
|
@@ -1419,6 +1558,18 @@ class Guards {
|
|
|
1419
1558
|
throw new GuardError(source, "guard.base64Url", property, value);
|
|
1420
1559
|
}
|
|
1421
1560
|
}
|
|
1561
|
+
/**
|
|
1562
|
+
* Is the property a base58 string.
|
|
1563
|
+
* @param source The source of the error.
|
|
1564
|
+
* @param property The name of the property.
|
|
1565
|
+
* @param value The value to test.
|
|
1566
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1567
|
+
*/
|
|
1568
|
+
static stringBase58(source, property, value) {
|
|
1569
|
+
if (!Is.stringBase58(value)) {
|
|
1570
|
+
throw new GuardError(source, "guard.base58", property, value);
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1422
1573
|
/**
|
|
1423
1574
|
* Is the property a string with a hex value.
|
|
1424
1575
|
* @param source The source of the error.
|
|
@@ -1720,6 +1871,29 @@ class Factory {
|
|
|
1720
1871
|
}
|
|
1721
1872
|
return Factory._factories[typeName];
|
|
1722
1873
|
}
|
|
1874
|
+
/**
|
|
1875
|
+
* Get all the factories.
|
|
1876
|
+
* @returns All the factories.
|
|
1877
|
+
*/
|
|
1878
|
+
static getFactories() {
|
|
1879
|
+
return Factory._factories;
|
|
1880
|
+
}
|
|
1881
|
+
/**
|
|
1882
|
+
* Reset all the factories, which removes any created instances, but not the registrations.
|
|
1883
|
+
*/
|
|
1884
|
+
static resetFactories() {
|
|
1885
|
+
for (const typeName in Factory._factories) {
|
|
1886
|
+
Factory._factories[typeName].reset();
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
/**
|
|
1890
|
+
* Clear all the factories, which removes anything registered with the factories.
|
|
1891
|
+
*/
|
|
1892
|
+
static clearFactories() {
|
|
1893
|
+
for (const typeName in Factory._factories) {
|
|
1894
|
+
Factory._factories[typeName].clear();
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1723
1897
|
/**
|
|
1724
1898
|
* Register a new generator.
|
|
1725
1899
|
* @param name The name of the generator.
|
|
@@ -1791,7 +1965,7 @@ class Factory {
|
|
|
1791
1965
|
}
|
|
1792
1966
|
}
|
|
1793
1967
|
/**
|
|
1794
|
-
*
|
|
1968
|
+
* Remove all the instances and leave the generators intact.
|
|
1795
1969
|
*/
|
|
1796
1970
|
reset() {
|
|
1797
1971
|
for (const name in this._generators) {
|
|
@@ -1799,6 +1973,14 @@ class Factory {
|
|
|
1799
1973
|
}
|
|
1800
1974
|
this._instances = {};
|
|
1801
1975
|
}
|
|
1976
|
+
/**
|
|
1977
|
+
* Remove all the instances and the generators.
|
|
1978
|
+
*/
|
|
1979
|
+
clear() {
|
|
1980
|
+
this._instances = {};
|
|
1981
|
+
this._generators = {};
|
|
1982
|
+
this._orderCounter = 0;
|
|
1983
|
+
}
|
|
1802
1984
|
/**
|
|
1803
1985
|
* Get all the instances as a map.
|
|
1804
1986
|
* @returns The instances as a map.
|
|
@@ -1905,951 +2087,1023 @@ class ArrayHelper {
|
|
|
1905
2087
|
// Copyright 2024 IOTA Stiftung.
|
|
1906
2088
|
// SPDX-License-Identifier: Apache-2.0.
|
|
1907
2089
|
/**
|
|
1908
|
-
*
|
|
2090
|
+
* Helpers methods for JSON objects.
|
|
1909
2091
|
*/
|
|
1910
|
-
class
|
|
2092
|
+
class JsonHelper {
|
|
1911
2093
|
/**
|
|
1912
|
-
*
|
|
2094
|
+
* Serializes in canonical format.
|
|
2095
|
+
* Based on https://www.rfc-editor.org/rfc/rfc8785.
|
|
2096
|
+
* @param object The object to be serialized.
|
|
2097
|
+
* @returns The serialized object.
|
|
1913
2098
|
*/
|
|
1914
|
-
static
|
|
2099
|
+
static canonicalize(object) {
|
|
2100
|
+
const buffer = [];
|
|
2101
|
+
if (object === null ||
|
|
2102
|
+
typeof object !== "object" ||
|
|
2103
|
+
("toJSON" in object && object.toJSON instanceof Function)) {
|
|
2104
|
+
// Primitive data type
|
|
2105
|
+
buffer.push(JSON.stringify(object));
|
|
2106
|
+
}
|
|
2107
|
+
else if (Array.isArray(object)) {
|
|
2108
|
+
// Array maintain element order
|
|
2109
|
+
const parts = [];
|
|
2110
|
+
for (const element of object) {
|
|
2111
|
+
if (element === undefined) {
|
|
2112
|
+
parts.push("null");
|
|
2113
|
+
}
|
|
2114
|
+
else {
|
|
2115
|
+
parts.push(JsonHelper.canonicalize(element));
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
buffer.push(`[${parts.join(",")}]`);
|
|
2119
|
+
}
|
|
2120
|
+
else {
|
|
2121
|
+
// Object sort properties
|
|
2122
|
+
const props = [];
|
|
2123
|
+
const keys = Object.keys(object).sort();
|
|
2124
|
+
const o = object;
|
|
2125
|
+
for (const key of keys) {
|
|
2126
|
+
if (o[key] !== undefined) {
|
|
2127
|
+
props.push(`${JSON.stringify(key)}:${JsonHelper.canonicalize(o[key])}`);
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
buffer.push(`{${props.join(",")}}`);
|
|
2131
|
+
}
|
|
2132
|
+
return buffer.join("");
|
|
2133
|
+
}
|
|
1915
2134
|
/**
|
|
1916
|
-
*
|
|
1917
|
-
*
|
|
2135
|
+
* Creates a RFC 6902 diff set.
|
|
2136
|
+
* Based on https://www.rfc-editor.org/rfc/rfc6902.
|
|
2137
|
+
* @param object1 The first object.
|
|
2138
|
+
* @param object2 The second object.
|
|
2139
|
+
* @returns The list of patches.
|
|
1918
2140
|
*/
|
|
1919
|
-
static
|
|
2141
|
+
static diff(object1, object2) {
|
|
2142
|
+
const operations = rfc6902.createPatch(object1, object2);
|
|
2143
|
+
return operations;
|
|
2144
|
+
}
|
|
1920
2145
|
/**
|
|
1921
|
-
*
|
|
1922
|
-
*
|
|
2146
|
+
* Applies a RFC 6902 diff set to an object.
|
|
2147
|
+
* Based on https://www.rfc-editor.org/rfc/rfc6902.
|
|
2148
|
+
* @param object The object to patch.
|
|
2149
|
+
* @param patches The second object.
|
|
2150
|
+
* @returns The updated object.
|
|
1923
2151
|
*/
|
|
1924
|
-
static
|
|
2152
|
+
static patch(object, patches) {
|
|
2153
|
+
return rfc6902.applyPatch(object, patches);
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2158
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
2159
|
+
/* eslint-disable no-bitwise */
|
|
2160
|
+
/**
|
|
2161
|
+
* Convert arrays to and from different formats.
|
|
2162
|
+
*/
|
|
2163
|
+
class Converter {
|
|
1925
2164
|
/**
|
|
1926
|
-
*
|
|
2165
|
+
* Lookup table for encoding.
|
|
1927
2166
|
* @internal
|
|
1928
2167
|
*/
|
|
1929
|
-
static
|
|
2168
|
+
static _ENCODE_LOOKUP;
|
|
1930
2169
|
/**
|
|
1931
|
-
*
|
|
2170
|
+
* Lookup table for decoding.
|
|
1932
2171
|
* @internal
|
|
1933
2172
|
*/
|
|
1934
|
-
static
|
|
2173
|
+
static _DECODE_LOOKUP;
|
|
1935
2174
|
/**
|
|
1936
|
-
*
|
|
1937
|
-
* @param
|
|
2175
|
+
* Encode a raw array to UTF8 string.
|
|
2176
|
+
* @param array The bytes to encode.
|
|
2177
|
+
* @param startIndex The index to start in the bytes.
|
|
2178
|
+
* @param length The length of bytes to read.
|
|
2179
|
+
* @returns The array formatted as UTF8.
|
|
1938
2180
|
*/
|
|
1939
|
-
static
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
2181
|
+
static bytesToUtf8(array, startIndex, length) {
|
|
2182
|
+
const start = startIndex ?? 0;
|
|
2183
|
+
const len = length ?? array.length;
|
|
2184
|
+
let str = "";
|
|
2185
|
+
for (let i = start; i < start + len; i++) {
|
|
2186
|
+
const value = array[i];
|
|
2187
|
+
if (value < 0x80) {
|
|
2188
|
+
str += String.fromCharCode(value);
|
|
2189
|
+
}
|
|
2190
|
+
else if (value > 0xbf && value < 0xe0) {
|
|
2191
|
+
str += String.fromCharCode(((value & 0x1f) << 6) | (array[i + 1] & 0x3f));
|
|
2192
|
+
i += 1;
|
|
2193
|
+
}
|
|
2194
|
+
else if (value > 0xdf && value < 0xf0) {
|
|
2195
|
+
str += String.fromCharCode(((value & 0x0f) << 12) | ((array[i + 1] & 0x3f) << 6) | (array[i + 2] & 0x3f));
|
|
2196
|
+
i += 2;
|
|
2197
|
+
}
|
|
2198
|
+
else {
|
|
2199
|
+
// surrogate pair
|
|
2200
|
+
const charCode = (((value & 0x07) << 18) |
|
|
2201
|
+
((array[i + 1] & 0x3f) << 12) |
|
|
2202
|
+
((array[i + 2] & 0x3f) << 6) |
|
|
2203
|
+
(array[i + 3] & 0x3f)) -
|
|
2204
|
+
0x010000;
|
|
2205
|
+
str += String.fromCharCode((charCode >> 10) | 0xd800, (charCode & 0x03ff) | 0xdc00);
|
|
2206
|
+
i += 3;
|
|
2207
|
+
}
|
|
1943
2208
|
}
|
|
2209
|
+
return str;
|
|
1944
2210
|
}
|
|
1945
2211
|
/**
|
|
1946
|
-
*
|
|
1947
|
-
* @
|
|
1948
|
-
|
|
1949
|
-
static getLocale() {
|
|
1950
|
-
return I18n._currentLocale;
|
|
1951
|
-
}
|
|
1952
|
-
/**
|
|
1953
|
-
* Add a locale dictionary.
|
|
1954
|
-
* @param locale The locale.
|
|
1955
|
-
* @param dictionary The dictionary to add.
|
|
2212
|
+
* Convert a UTF8 string to raw array.
|
|
2213
|
+
* @param utf8 The text to decode.
|
|
2214
|
+
* @returns The array.
|
|
1956
2215
|
*/
|
|
1957
|
-
static
|
|
1958
|
-
const
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
2216
|
+
static utf8ToBytes(utf8) {
|
|
2217
|
+
const bytes = [];
|
|
2218
|
+
for (let i = 0; i < utf8.length; i++) {
|
|
2219
|
+
let charCode = utf8.charCodeAt(i);
|
|
2220
|
+
if (charCode < 0x80) {
|
|
2221
|
+
bytes.push(charCode);
|
|
2222
|
+
}
|
|
2223
|
+
else if (charCode < 0x800) {
|
|
2224
|
+
bytes.push(0xc0 | (charCode >> 6), 0x80 | (charCode & 0x3f));
|
|
2225
|
+
}
|
|
2226
|
+
else if (charCode < 0xd800 || charCode >= 0xe000) {
|
|
2227
|
+
bytes.push(0xe0 | (charCode >> 12), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f));
|
|
2228
|
+
}
|
|
2229
|
+
else {
|
|
2230
|
+
// surrogate pair
|
|
2231
|
+
i++;
|
|
2232
|
+
// UTF-16 encodes 0x10000-0x10FFFF by
|
|
2233
|
+
// subtracting 0x10000 and splitting the
|
|
2234
|
+
// 20 bits of 0x0-0xFFFFF into two halves
|
|
2235
|
+
charCode = 0x10000 + (((charCode & 0x3ff) << 10) | (utf8.charCodeAt(i) & 0x3ff));
|
|
2236
|
+
bytes.push(0xf0 | (charCode >> 18), 0x80 | ((charCode >> 12) & 0x3f), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f));
|
|
2237
|
+
}
|
|
1963
2238
|
}
|
|
2239
|
+
return Uint8Array.from(bytes);
|
|
1964
2240
|
}
|
|
1965
2241
|
/**
|
|
1966
|
-
*
|
|
1967
|
-
* @param
|
|
1968
|
-
* @
|
|
2242
|
+
* Encode a raw array to hex string.
|
|
2243
|
+
* @param array The bytes to encode.
|
|
2244
|
+
* @param includePrefix Include the 0x prefix on the returned hex.
|
|
2245
|
+
* @param startIndex The index to start in the bytes.
|
|
2246
|
+
* @param length The length of bytes to read.
|
|
2247
|
+
* @param reverse Reverse the combine direction.
|
|
2248
|
+
* @returns The array formatted as hex.
|
|
1969
2249
|
*/
|
|
1970
|
-
static
|
|
1971
|
-
|
|
1972
|
-
|
|
2250
|
+
static bytesToHex(array, includePrefix = false, startIndex, length, reverse) {
|
|
2251
|
+
let hex = "";
|
|
2252
|
+
this.buildHexLookups();
|
|
2253
|
+
if (Converter._ENCODE_LOOKUP) {
|
|
2254
|
+
const len = length ?? array.length;
|
|
2255
|
+
const start = startIndex ?? 0;
|
|
2256
|
+
if (reverse) {
|
|
2257
|
+
for (let i = 0; i < len; i++) {
|
|
2258
|
+
hex = Converter._ENCODE_LOOKUP[array[start + i]] + hex;
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
else {
|
|
2262
|
+
for (let i = 0; i < len; i++) {
|
|
2263
|
+
hex += Converter._ENCODE_LOOKUP[array[start + i]];
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
return includePrefix ? HexHelper.addPrefix(hex) : hex;
|
|
2268
|
+
}
|
|
1973
2269
|
/**
|
|
1974
|
-
*
|
|
1975
|
-
* @
|
|
2270
|
+
* Decode a hex string to raw array.
|
|
2271
|
+
* @param hex The hex to decode.
|
|
2272
|
+
* @param reverse Store the characters in reverse.
|
|
2273
|
+
* @returns The array.
|
|
1976
2274
|
*/
|
|
1977
|
-
static
|
|
1978
|
-
|
|
2275
|
+
static hexToBytes(hex, reverse) {
|
|
2276
|
+
const strippedHex = HexHelper.stripPrefix(hex);
|
|
2277
|
+
const sizeof = strippedHex.length >> 1;
|
|
2278
|
+
const length = sizeof << 1;
|
|
2279
|
+
const array = new Uint8Array(sizeof);
|
|
2280
|
+
this.buildHexLookups();
|
|
2281
|
+
if (Converter._DECODE_LOOKUP) {
|
|
2282
|
+
let i = 0;
|
|
2283
|
+
let n = 0;
|
|
2284
|
+
while (i < length) {
|
|
2285
|
+
array[n++] =
|
|
2286
|
+
(Converter._DECODE_LOOKUP[strippedHex.charCodeAt(i++)] << 4) |
|
|
2287
|
+
Converter._DECODE_LOOKUP[strippedHex.charCodeAt(i++)];
|
|
2288
|
+
}
|
|
2289
|
+
if (reverse) {
|
|
2290
|
+
array.reverse();
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
return array;
|
|
1979
2294
|
}
|
|
1980
2295
|
/**
|
|
1981
|
-
*
|
|
1982
|
-
* @param
|
|
1983
|
-
* @param
|
|
2296
|
+
* Convert the UTF8 to hex.
|
|
2297
|
+
* @param utf8 The text to convert.
|
|
2298
|
+
* @param includePrefix Include the 0x prefix on the returned hex.
|
|
2299
|
+
* @returns The hex version of the bytes.
|
|
1984
2300
|
*/
|
|
1985
|
-
static
|
|
1986
|
-
|
|
2301
|
+
static utf8ToHex(utf8, includePrefix = false) {
|
|
2302
|
+
const hex = Converter.bytesToHex(Converter.utf8ToBytes(utf8));
|
|
2303
|
+
return includePrefix ? HexHelper.addPrefix(hex) : hex;
|
|
1987
2304
|
}
|
|
1988
2305
|
/**
|
|
1989
|
-
*
|
|
1990
|
-
* @param
|
|
2306
|
+
* Convert the hex text to text.
|
|
2307
|
+
* @param hex The hex to convert.
|
|
2308
|
+
* @returns The UTF8 version of the bytes.
|
|
1991
2309
|
*/
|
|
1992
|
-
static
|
|
1993
|
-
|
|
2310
|
+
static hexToUtf8(hex) {
|
|
2311
|
+
return Converter.bytesToUtf8(Converter.hexToBytes(HexHelper.stripPrefix(hex)));
|
|
1994
2312
|
}
|
|
1995
2313
|
/**
|
|
1996
|
-
*
|
|
1997
|
-
* @param
|
|
1998
|
-
* @
|
|
2314
|
+
* Convert bytes to binary string.
|
|
2315
|
+
* @param bytes The bytes to convert.
|
|
2316
|
+
* @returns A binary string of the bytes.
|
|
1999
2317
|
*/
|
|
2000
|
-
static
|
|
2001
|
-
|
|
2318
|
+
static bytesToBinary(bytes) {
|
|
2319
|
+
const b = [];
|
|
2320
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2321
|
+
b.push(bytes[i].toString(2).padStart(8, "0"));
|
|
2322
|
+
}
|
|
2323
|
+
return b.join("");
|
|
2002
2324
|
}
|
|
2003
2325
|
/**
|
|
2004
|
-
*
|
|
2005
|
-
* @param
|
|
2326
|
+
* Convert a binary string to bytes.
|
|
2327
|
+
* @param binary The binary string.
|
|
2328
|
+
* @returns The bytes.
|
|
2006
2329
|
*/
|
|
2007
|
-
static
|
|
2008
|
-
|
|
2330
|
+
static binaryToBytes(binary) {
|
|
2331
|
+
const bytes = new Uint8Array(Math.ceil(binary.length / 8));
|
|
2332
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2333
|
+
bytes[i] = Number.parseInt(binary.slice(i * 8, (i + 1) * 8), 2);
|
|
2334
|
+
}
|
|
2335
|
+
return bytes;
|
|
2009
2336
|
}
|
|
2010
2337
|
/**
|
|
2011
|
-
*
|
|
2012
|
-
* @param
|
|
2013
|
-
* @
|
|
2014
|
-
* @param overrideLocale Override the locale.
|
|
2015
|
-
* @returns The formatted string.
|
|
2338
|
+
* Convert bytes to base64 string.
|
|
2339
|
+
* @param bytes The bytes to convert.
|
|
2340
|
+
* @returns A base64 string of the bytes.
|
|
2016
2341
|
*/
|
|
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;
|
|
2342
|
+
static bytesToBase64(bytes) {
|
|
2343
|
+
return Base64.encode(bytes);
|
|
2036
2344
|
}
|
|
2037
2345
|
/**
|
|
2038
|
-
*
|
|
2039
|
-
* @param
|
|
2040
|
-
* @returns
|
|
2346
|
+
* Convert a base64 string to bytes.
|
|
2347
|
+
* @param base64 The base64 string.
|
|
2348
|
+
* @returns The bytes.
|
|
2041
2349
|
*/
|
|
2042
|
-
static
|
|
2043
|
-
return
|
|
2350
|
+
static base64ToBytes(base64) {
|
|
2351
|
+
return Base64.decode(base64);
|
|
2044
2352
|
}
|
|
2045
2353
|
/**
|
|
2046
|
-
*
|
|
2047
|
-
* @param
|
|
2048
|
-
* @
|
|
2049
|
-
* @param mergedKeys The merged keys dictionary to populate.
|
|
2050
|
-
* @internal
|
|
2354
|
+
* Convert bytes to base64 url string.
|
|
2355
|
+
* @param bytes The bytes to convert.
|
|
2356
|
+
* @returns A base64 url string of the bytes.
|
|
2051
2357
|
*/
|
|
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
|
-
}
|
|
2358
|
+
static bytesToBase64Url(bytes) {
|
|
2359
|
+
return Base64Url.encode(bytes);
|
|
2063
2360
|
}
|
|
2064
|
-
}
|
|
2065
|
-
|
|
2066
|
-
// Copyright 2024 IOTA Stiftung.
|
|
2067
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
2068
|
-
/**
|
|
2069
|
-
* Error helper functions.
|
|
2070
|
-
*/
|
|
2071
|
-
class ErrorHelper {
|
|
2072
2361
|
/**
|
|
2073
|
-
*
|
|
2074
|
-
* @param
|
|
2075
|
-
* @returns The
|
|
2362
|
+
* Convert a base64 url string to bytes.
|
|
2363
|
+
* @param base64Url The base64 url string.
|
|
2364
|
+
* @returns The bytes.
|
|
2076
2365
|
*/
|
|
2077
|
-
static
|
|
2078
|
-
return
|
|
2366
|
+
static base64UrlToBytes(base64Url) {
|
|
2367
|
+
return Base64Url.decode(base64Url);
|
|
2079
2368
|
}
|
|
2080
2369
|
/**
|
|
2081
|
-
*
|
|
2082
|
-
* @param
|
|
2083
|
-
* @returns
|
|
2370
|
+
* Convert bytes to base58 string.
|
|
2371
|
+
* @param bytes The bytes to convert.
|
|
2372
|
+
* @returns A base58 string of the bytes.
|
|
2084
2373
|
*/
|
|
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;
|
|
2374
|
+
static bytesToBase58(bytes) {
|
|
2375
|
+
return Base58.encode(bytes);
|
|
2120
2376
|
}
|
|
2121
2377
|
/**
|
|
2122
|
-
*
|
|
2123
|
-
* @param
|
|
2124
|
-
* @returns The
|
|
2378
|
+
* Convert a base58 string to bytes.
|
|
2379
|
+
* @param base58 The base58 string.
|
|
2380
|
+
* @returns The bytes.
|
|
2125
2381
|
*/
|
|
2126
|
-
static
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2382
|
+
static base58ToBytes(base58) {
|
|
2383
|
+
return Base58.decode(base58);
|
|
2384
|
+
}
|
|
2385
|
+
/**
|
|
2386
|
+
* Build the static lookup tables.
|
|
2387
|
+
* @internal
|
|
2388
|
+
*/
|
|
2389
|
+
static buildHexLookups() {
|
|
2390
|
+
if (!Converter._ENCODE_LOOKUP || !Converter._DECODE_LOOKUP) {
|
|
2391
|
+
const alphabet = "0123456789abcdef";
|
|
2392
|
+
Converter._ENCODE_LOOKUP = [];
|
|
2393
|
+
Converter._DECODE_LOOKUP = [];
|
|
2394
|
+
for (let i = 0; i < 256; i++) {
|
|
2395
|
+
Converter._ENCODE_LOOKUP[i] = alphabet[(i >> 4) & 0xf] + alphabet[i & 0xf];
|
|
2396
|
+
if (i < 16) {
|
|
2397
|
+
if (i < 10) {
|
|
2398
|
+
Converter._DECODE_LOOKUP[0x30 + i] = i;
|
|
2399
|
+
}
|
|
2400
|
+
else {
|
|
2401
|
+
Converter._DECODE_LOOKUP[0x61 - 10 + i] = i;
|
|
2402
|
+
}
|
|
2141
2403
|
}
|
|
2142
|
-
validationErrors.push(v);
|
|
2143
2404
|
}
|
|
2144
|
-
return validationErrors.join("\n");
|
|
2145
2405
|
}
|
|
2146
2406
|
}
|
|
2147
2407
|
}
|
|
2148
2408
|
|
|
2149
|
-
// Copyright 2024 IOTA Stiftung.
|
|
2150
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
2151
2409
|
/**
|
|
2152
|
-
*
|
|
2410
|
+
* Class to help with objects.
|
|
2153
2411
|
*/
|
|
2154
|
-
class
|
|
2412
|
+
class ObjectHelper {
|
|
2155
2413
|
/**
|
|
2156
|
-
*
|
|
2157
|
-
* @
|
|
2158
|
-
* @throws TypeError If the value can not be coerced.
|
|
2159
|
-
* @returns The value if it can be coerced.
|
|
2414
|
+
* Runtime name for the class.
|
|
2415
|
+
* @internal
|
|
2160
2416
|
*/
|
|
2161
|
-
static
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
if (Is.boolean(value)) {
|
|
2172
|
-
return value ? "true" : "false";
|
|
2173
|
-
}
|
|
2174
|
-
if (Is.date(value)) {
|
|
2175
|
-
return value.toISOString();
|
|
2417
|
+
static _CLASS_NAME = "ObjectHelper";
|
|
2418
|
+
/**
|
|
2419
|
+
* Convert an object to bytes.
|
|
2420
|
+
* @param obj The object to convert.
|
|
2421
|
+
* @param format Format the JSON content.
|
|
2422
|
+
* @returns The object as bytes.
|
|
2423
|
+
*/
|
|
2424
|
+
static toBytes(obj, format = false) {
|
|
2425
|
+
if (obj === undefined) {
|
|
2426
|
+
return new Uint8Array();
|
|
2176
2427
|
}
|
|
2428
|
+
const json = format ? JSON.stringify(obj, undefined, "\t") : JSON.stringify(obj);
|
|
2429
|
+
return Converter.utf8ToBytes(json);
|
|
2177
2430
|
}
|
|
2178
2431
|
/**
|
|
2179
|
-
*
|
|
2180
|
-
* @param
|
|
2181
|
-
* @
|
|
2182
|
-
* @
|
|
2432
|
+
* Convert a bytes to an object.
|
|
2433
|
+
* @param bytes The bytes to convert to an object.
|
|
2434
|
+
* @returns The object.
|
|
2435
|
+
* @throws GeneralError if there was an error parsing the JSON.
|
|
2183
2436
|
*/
|
|
2184
|
-
static
|
|
2185
|
-
if (Is.
|
|
2186
|
-
return
|
|
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
|
-
}
|
|
2437
|
+
static fromBytes(bytes) {
|
|
2438
|
+
if (Is.empty(bytes) || bytes.length === 0) {
|
|
2439
|
+
return undefined;
|
|
2196
2440
|
}
|
|
2197
|
-
|
|
2198
|
-
|
|
2441
|
+
try {
|
|
2442
|
+
const utf8 = Converter.bytesToUtf8(bytes);
|
|
2443
|
+
return JSON.parse(utf8);
|
|
2199
2444
|
}
|
|
2200
|
-
|
|
2201
|
-
|
|
2445
|
+
catch (err) {
|
|
2446
|
+
throw new GeneralError(ObjectHelper._CLASS_NAME, "failedBytesToJSON", undefined, err);
|
|
2202
2447
|
}
|
|
2203
2448
|
}
|
|
2204
2449
|
/**
|
|
2205
|
-
*
|
|
2206
|
-
* @param
|
|
2207
|
-
* @
|
|
2208
|
-
* @returns The value if it can be coerced.
|
|
2450
|
+
* Make a deep clone of an object.
|
|
2451
|
+
* @param obj The object to clone.
|
|
2452
|
+
* @returns The objects clone.
|
|
2209
2453
|
*/
|
|
2210
|
-
static
|
|
2211
|
-
if (Is.undefined(
|
|
2212
|
-
return
|
|
2213
|
-
}
|
|
2214
|
-
if (Is.bigint(value)) {
|
|
2215
|
-
return value;
|
|
2216
|
-
}
|
|
2217
|
-
if (Is.number(value)) {
|
|
2218
|
-
return BigInt(value);
|
|
2219
|
-
}
|
|
2220
|
-
if (Is.string(value)) {
|
|
2221
|
-
const parsed = Number.parseFloat(value);
|
|
2222
|
-
if (Is.integer(parsed)) {
|
|
2223
|
-
return BigInt(parsed);
|
|
2224
|
-
}
|
|
2225
|
-
}
|
|
2226
|
-
if (Is.boolean(value)) {
|
|
2227
|
-
return value ? 1n : 0n;
|
|
2454
|
+
static clone(obj) {
|
|
2455
|
+
if (Is.undefined(obj)) {
|
|
2456
|
+
return undefined;
|
|
2228
2457
|
}
|
|
2458
|
+
return structuredClone(obj);
|
|
2229
2459
|
}
|
|
2230
2460
|
/**
|
|
2231
|
-
*
|
|
2232
|
-
* @param
|
|
2233
|
-
* @
|
|
2234
|
-
* @returns The
|
|
2461
|
+
* Deep merge objects.
|
|
2462
|
+
* @param obj1 The first object to merge.
|
|
2463
|
+
* @param obj2 The second object to merge.
|
|
2464
|
+
* @returns The combined deep merge of the objects.
|
|
2235
2465
|
*/
|
|
2236
|
-
static
|
|
2237
|
-
if (Is.
|
|
2238
|
-
return
|
|
2239
|
-
}
|
|
2240
|
-
if (Is.boolean(value)) {
|
|
2241
|
-
return value;
|
|
2466
|
+
static merge(obj1, obj2) {
|
|
2467
|
+
if (Is.empty(obj1)) {
|
|
2468
|
+
return ObjectHelper.clone(obj2);
|
|
2242
2469
|
}
|
|
2243
|
-
if (Is.
|
|
2244
|
-
|
|
2245
|
-
return value ? true : false;
|
|
2470
|
+
if (Is.empty(obj2)) {
|
|
2471
|
+
return ObjectHelper.clone(obj1);
|
|
2246
2472
|
}
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2473
|
+
const obj1Clone = ObjectHelper.clone(obj1);
|
|
2474
|
+
if (Is.object(obj1Clone) && Is.object(obj2)) {
|
|
2475
|
+
const keys = Object.keys(obj2);
|
|
2476
|
+
for (const key of keys) {
|
|
2477
|
+
if (Is.object(obj1Clone[key]) && Is.object(obj2[key])) {
|
|
2478
|
+
ObjectHelper.propertySet(obj1Clone, key, ObjectHelper.merge(obj1Clone[key], obj2[key]));
|
|
2479
|
+
}
|
|
2480
|
+
else {
|
|
2481
|
+
ObjectHelper.propertySet(obj1Clone, key, obj2[key]);
|
|
2482
|
+
}
|
|
2253
2483
|
}
|
|
2254
2484
|
}
|
|
2485
|
+
return obj1Clone;
|
|
2255
2486
|
}
|
|
2256
2487
|
/**
|
|
2257
|
-
*
|
|
2258
|
-
* @param
|
|
2259
|
-
* @
|
|
2260
|
-
* @
|
|
2488
|
+
* Does one object equal another.
|
|
2489
|
+
* @param obj1 The first object to compare.
|
|
2490
|
+
* @param obj2 The second object to compare.
|
|
2491
|
+
* @param strictPropertyOrder Should the properties be in the same order, defaults to true.
|
|
2492
|
+
* @returns True is the objects are equal.
|
|
2261
2493
|
*/
|
|
2262
|
-
static
|
|
2263
|
-
if (
|
|
2264
|
-
return
|
|
2265
|
-
}
|
|
2266
|
-
if (Is.date(value)) {
|
|
2267
|
-
return value;
|
|
2268
|
-
}
|
|
2269
|
-
if (Is.number(value)) {
|
|
2270
|
-
return new Date(value);
|
|
2271
|
-
}
|
|
2272
|
-
if (Is.string(value)) {
|
|
2273
|
-
const dt = new Date(value);
|
|
2274
|
-
if (!Number.isNaN(dt.getTime())) {
|
|
2275
|
-
const utc = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate());
|
|
2276
|
-
return new Date(utc);
|
|
2277
|
-
}
|
|
2494
|
+
static equal(obj1, obj2, strictPropertyOrder) {
|
|
2495
|
+
if (strictPropertyOrder ?? true) {
|
|
2496
|
+
return JSON.stringify(obj1) === JSON.stringify(obj2);
|
|
2278
2497
|
}
|
|
2498
|
+
return JsonHelper.canonicalize(obj1) === JsonHelper.canonicalize(obj2);
|
|
2279
2499
|
}
|
|
2280
2500
|
/**
|
|
2281
|
-
*
|
|
2282
|
-
* @param
|
|
2283
|
-
* @
|
|
2284
|
-
* @returns The
|
|
2501
|
+
* Get the property of an unknown object.
|
|
2502
|
+
* @param obj The object to get the property from.
|
|
2503
|
+
* @param property The property to get, can be separated by dots for nested path.
|
|
2504
|
+
* @returns The property.
|
|
2285
2505
|
*/
|
|
2286
|
-
static
|
|
2287
|
-
if (
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
const dt = new Date(value);
|
|
2298
|
-
if (!Number.isNaN(dt.getTime())) {
|
|
2299
|
-
const utc = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate(), dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
|
|
2300
|
-
return new Date(utc);
|
|
2506
|
+
static propertyGet(obj, property) {
|
|
2507
|
+
if (property.includes(".")) {
|
|
2508
|
+
const parts = property.split(".");
|
|
2509
|
+
let value = obj;
|
|
2510
|
+
for (const part of parts) {
|
|
2511
|
+
if (Is.object(value)) {
|
|
2512
|
+
value = value[part];
|
|
2513
|
+
}
|
|
2514
|
+
else {
|
|
2515
|
+
return undefined;
|
|
2516
|
+
}
|
|
2301
2517
|
}
|
|
2518
|
+
return value;
|
|
2302
2519
|
}
|
|
2520
|
+
return Is.object(obj) ? obj[property] : undefined;
|
|
2303
2521
|
}
|
|
2304
2522
|
/**
|
|
2305
|
-
*
|
|
2306
|
-
* @param
|
|
2307
|
-
* @
|
|
2308
|
-
* @
|
|
2523
|
+
* Set the property of an unknown object.
|
|
2524
|
+
* @param obj The object to set the property from.
|
|
2525
|
+
* @param property The property to set.
|
|
2526
|
+
* @param value The value to set.
|
|
2309
2527
|
*/
|
|
2310
|
-
static
|
|
2311
|
-
if (Is.
|
|
2312
|
-
|
|
2313
|
-
}
|
|
2314
|
-
if (Is.date(value)) {
|
|
2315
|
-
return value;
|
|
2316
|
-
}
|
|
2317
|
-
if (Is.number(value)) {
|
|
2318
|
-
const dt = new Date(value);
|
|
2319
|
-
dt.setFullYear(1970, 0, 1);
|
|
2320
|
-
return dt;
|
|
2321
|
-
}
|
|
2322
|
-
if (Is.string(value)) {
|
|
2323
|
-
const dt = new Date(value);
|
|
2324
|
-
if (!Number.isNaN(dt.getTime())) {
|
|
2325
|
-
const utc = Date.UTC(1970, 0, 1, dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
|
|
2326
|
-
return new Date(utc);
|
|
2327
|
-
}
|
|
2528
|
+
static propertySet(obj, property, value) {
|
|
2529
|
+
if (Is.object(obj)) {
|
|
2530
|
+
obj[property] = value;
|
|
2328
2531
|
}
|
|
2329
2532
|
}
|
|
2330
2533
|
/**
|
|
2331
|
-
*
|
|
2332
|
-
* @param
|
|
2333
|
-
* @
|
|
2334
|
-
* @returns The value if it can be coerced.
|
|
2534
|
+
* Delete the property of an unknown object.
|
|
2535
|
+
* @param obj The object to set the property from.
|
|
2536
|
+
* @param property The property to set
|
|
2335
2537
|
*/
|
|
2336
|
-
static
|
|
2337
|
-
if (Is.
|
|
2338
|
-
|
|
2339
|
-
}
|
|
2340
|
-
if (Is.object(value)) {
|
|
2341
|
-
return value;
|
|
2538
|
+
static propertyDelete(obj, property) {
|
|
2539
|
+
if (Is.object(obj)) {
|
|
2540
|
+
delete obj[property];
|
|
2342
2541
|
}
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2542
|
+
}
|
|
2543
|
+
/**
|
|
2544
|
+
* Extract a property from the object, providing alternative names.
|
|
2545
|
+
* @param obj The object to extract from.
|
|
2546
|
+
* @param propertyNames The possible names for the property.
|
|
2547
|
+
* @param removeProperties Remove the properties from the object, defaults to true.
|
|
2548
|
+
* @returns The property if available.
|
|
2549
|
+
*/
|
|
2550
|
+
static extractProperty(obj, propertyNames, removeProperties = true) {
|
|
2551
|
+
let retVal;
|
|
2552
|
+
if (Is.object(obj)) {
|
|
2553
|
+
const names = Is.string(propertyNames) ? [propertyNames] : propertyNames;
|
|
2554
|
+
for (const prop of names) {
|
|
2555
|
+
retVal ??= ObjectHelper.propertyGet(obj, prop);
|
|
2556
|
+
if (removeProperties) {
|
|
2557
|
+
ObjectHelper.propertyDelete(obj, prop);
|
|
2558
|
+
}
|
|
2346
2559
|
}
|
|
2347
|
-
catch { }
|
|
2348
2560
|
}
|
|
2561
|
+
return retVal;
|
|
2349
2562
|
}
|
|
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
2563
|
/**
|
|
2359
|
-
*
|
|
2360
|
-
* @param
|
|
2361
|
-
* @
|
|
2564
|
+
* Pick a subset of properties from an object.
|
|
2565
|
+
* @param obj The object to pick the properties from.
|
|
2566
|
+
* @param keys The property keys to pick.
|
|
2567
|
+
* @returns The partial object.
|
|
2362
2568
|
*/
|
|
2363
|
-
static
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2569
|
+
static pick(obj, keys) {
|
|
2570
|
+
if (Is.object(obj) && Is.arrayValue(keys)) {
|
|
2571
|
+
const result = {};
|
|
2572
|
+
for (const key of keys) {
|
|
2573
|
+
result[key] = obj[key];
|
|
2574
|
+
}
|
|
2575
|
+
return result;
|
|
2367
2576
|
}
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2577
|
+
return obj;
|
|
2578
|
+
}
|
|
2579
|
+
/**
|
|
2580
|
+
* Omit a subset of properties from an object.
|
|
2581
|
+
* @param obj The object to omit the properties from.
|
|
2582
|
+
* @param keys The property keys to omit.
|
|
2583
|
+
* @returns The partial object.
|
|
2584
|
+
*/
|
|
2585
|
+
static omit(obj, keys) {
|
|
2586
|
+
if (Is.object(obj) && Is.arrayValue(keys)) {
|
|
2587
|
+
const result = { ...obj };
|
|
2588
|
+
for (const key of keys) {
|
|
2589
|
+
delete result[key];
|
|
2590
|
+
}
|
|
2591
|
+
return result;
|
|
2592
|
+
}
|
|
2593
|
+
return obj;
|
|
2379
2594
|
}
|
|
2380
2595
|
}
|
|
2381
2596
|
|
|
2382
2597
|
// Copyright 2024 IOTA Stiftung.
|
|
2383
2598
|
// SPDX-License-Identifier: Apache-2.0.
|
|
2384
2599
|
/**
|
|
2385
|
-
*
|
|
2600
|
+
* Environment variable helper.
|
|
2386
2601
|
*/
|
|
2387
|
-
class
|
|
2388
|
-
/**
|
|
2389
|
-
*
|
|
2390
|
-
*
|
|
2391
|
-
* @param
|
|
2392
|
-
* @returns The
|
|
2393
|
-
*/
|
|
2394
|
-
static
|
|
2395
|
-
const
|
|
2396
|
-
if (
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
// Array maintain element order
|
|
2404
|
-
const parts = [];
|
|
2405
|
-
for (const element of object) {
|
|
2406
|
-
if (element === undefined) {
|
|
2407
|
-
parts.push("null");
|
|
2408
|
-
}
|
|
2409
|
-
else {
|
|
2410
|
-
parts.push(JsonHelper.canonicalize(element));
|
|
2602
|
+
class EnvHelper {
|
|
2603
|
+
/**
|
|
2604
|
+
* Get the environment variable as an object with camel cased names.
|
|
2605
|
+
* @param envVars The environment variables.
|
|
2606
|
+
* @param prefix The prefix of the environment variables, if not provided gets all.
|
|
2607
|
+
* @returns The object with camel cased names.
|
|
2608
|
+
*/
|
|
2609
|
+
static envToJson(envVars, prefix) {
|
|
2610
|
+
const result = {};
|
|
2611
|
+
if (!Is.empty(envVars)) {
|
|
2612
|
+
if (Is.empty(prefix)) {
|
|
2613
|
+
for (const envVar in envVars) {
|
|
2614
|
+
if (Is.stringValue(envVars[envVar])) {
|
|
2615
|
+
const camelCaseName = StringHelper.camelCase(envVar.toLowerCase());
|
|
2616
|
+
ObjectHelper.propertySet(result, camelCaseName, envVars[envVar]);
|
|
2617
|
+
}
|
|
2411
2618
|
}
|
|
2412
2619
|
}
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
const o = object;
|
|
2420
|
-
for (const key of keys) {
|
|
2421
|
-
if (o[key] !== undefined) {
|
|
2422
|
-
props.push(`${JSON.stringify(key)}:${JsonHelper.canonicalize(o[key])}`);
|
|
2620
|
+
else {
|
|
2621
|
+
for (const envVar in envVars) {
|
|
2622
|
+
if (envVar.startsWith(prefix) && Is.stringValue(envVars[envVar])) {
|
|
2623
|
+
const camelCaseName = StringHelper.camelCase(envVar.replace(prefix, "").toLowerCase());
|
|
2624
|
+
ObjectHelper.propertySet(result, camelCaseName, envVars[envVar]);
|
|
2625
|
+
}
|
|
2423
2626
|
}
|
|
2424
2627
|
}
|
|
2425
|
-
buffer.push(`{${props.join(",")}}`);
|
|
2426
2628
|
}
|
|
2427
|
-
return
|
|
2428
|
-
}
|
|
2429
|
-
/**
|
|
2430
|
-
* Creates a RFC 6902 diff set.
|
|
2431
|
-
* Based on https://www.rfc-editor.org/rfc/rfc6902.
|
|
2432
|
-
* @param object1 The first object.
|
|
2433
|
-
* @param object2 The second object.
|
|
2434
|
-
* @returns The list of patches.
|
|
2435
|
-
*/
|
|
2436
|
-
static diff(object1, object2) {
|
|
2437
|
-
const operations = rfc6902.createPatch(object1, object2);
|
|
2438
|
-
return operations;
|
|
2439
|
-
}
|
|
2440
|
-
/**
|
|
2441
|
-
* Applies a RFC 6902 diff set to an object.
|
|
2442
|
-
* Based on https://www.rfc-editor.org/rfc/rfc6902.
|
|
2443
|
-
* @param object The object to patch.
|
|
2444
|
-
* @param patches The second object.
|
|
2445
|
-
* @returns The updated object.
|
|
2446
|
-
*/
|
|
2447
|
-
static patch(object, patches) {
|
|
2448
|
-
return rfc6902.applyPatch(object, patches);
|
|
2629
|
+
return result;
|
|
2449
2630
|
}
|
|
2450
2631
|
}
|
|
2451
2632
|
|
|
2452
2633
|
// Copyright 2024 IOTA Stiftung.
|
|
2453
2634
|
// SPDX-License-Identifier: Apache-2.0.
|
|
2454
|
-
/* eslint-disable no-bitwise */
|
|
2455
2635
|
/**
|
|
2456
|
-
*
|
|
2636
|
+
* Class to perform internationalization.
|
|
2457
2637
|
*/
|
|
2458
|
-
class
|
|
2638
|
+
class I18n {
|
|
2459
2639
|
/**
|
|
2460
|
-
*
|
|
2640
|
+
* The default translation.
|
|
2641
|
+
*/
|
|
2642
|
+
static DEFAULT_LOCALE = "en";
|
|
2643
|
+
/**
|
|
2644
|
+
* Dictionaries for lookups.
|
|
2461
2645
|
* @internal
|
|
2462
2646
|
*/
|
|
2463
|
-
static
|
|
2647
|
+
static _localeDictionaries = {};
|
|
2464
2648
|
/**
|
|
2465
|
-
*
|
|
2649
|
+
* The current locale.
|
|
2466
2650
|
* @internal
|
|
2467
2651
|
*/
|
|
2468
|
-
static
|
|
2652
|
+
static _currentLocale = I18n.DEFAULT_LOCALE;
|
|
2469
2653
|
/**
|
|
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.
|
|
2654
|
+
* Change handler for the locale being updated.
|
|
2655
|
+
* @internal
|
|
2475
2656
|
*/
|
|
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
|
-
}
|
|
2657
|
+
static _localeChangedHandlers = {};
|
|
2506
2658
|
/**
|
|
2507
|
-
*
|
|
2508
|
-
* @
|
|
2509
|
-
* @returns The array.
|
|
2659
|
+
* Change handler for the dictionaries being updated.
|
|
2660
|
+
* @internal
|
|
2510
2661
|
*/
|
|
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
|
-
}
|
|
2662
|
+
static _dictionaryChangedHandlers = {};
|
|
2663
|
+
/**
|
|
2664
|
+
* Set the locale.
|
|
2665
|
+
* @param locale The new locale.
|
|
2666
|
+
*/
|
|
2667
|
+
static setLocale(locale) {
|
|
2668
|
+
I18n._currentLocale = locale;
|
|
2669
|
+
for (const callback in I18n._localeChangedHandlers) {
|
|
2670
|
+
I18n._localeChangedHandlers[callback](I18n._currentLocale);
|
|
2533
2671
|
}
|
|
2534
|
-
return Uint8Array.from(bytes);
|
|
2535
2672
|
}
|
|
2536
2673
|
/**
|
|
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.
|
|
2674
|
+
* Get the locale.
|
|
2675
|
+
* @returns The current locale.
|
|
2544
2676
|
*/
|
|
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;
|
|
2677
|
+
static getLocale() {
|
|
2678
|
+
return I18n._currentLocale;
|
|
2563
2679
|
}
|
|
2564
2680
|
/**
|
|
2565
|
-
*
|
|
2566
|
-
* @param
|
|
2567
|
-
* @param
|
|
2568
|
-
* @returns The array.
|
|
2681
|
+
* Add a locale dictionary.
|
|
2682
|
+
* @param locale The locale.
|
|
2683
|
+
* @param dictionary The dictionary to add.
|
|
2569
2684
|
*/
|
|
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
|
-
}
|
|
2685
|
+
static addDictionary(locale, dictionary) {
|
|
2686
|
+
const mergedKeys = {};
|
|
2687
|
+
I18n.flattenTranslationKeys(dictionary, "", mergedKeys);
|
|
2688
|
+
I18n._localeDictionaries[locale] = mergedKeys;
|
|
2689
|
+
for (const callback in I18n._dictionaryChangedHandlers) {
|
|
2690
|
+
I18n._dictionaryChangedHandlers[callback](I18n._currentLocale);
|
|
2587
2691
|
}
|
|
2588
|
-
return array;
|
|
2589
2692
|
}
|
|
2590
2693
|
/**
|
|
2591
|
-
*
|
|
2592
|
-
* @param
|
|
2593
|
-
* @
|
|
2594
|
-
* @returns The hex version of the bytes.
|
|
2694
|
+
* Get a locale dictionary.
|
|
2695
|
+
* @param locale The locale.
|
|
2696
|
+
* @returns The dictionary of undefined if it does not exist.
|
|
2595
2697
|
*/
|
|
2596
|
-
static
|
|
2597
|
-
|
|
2598
|
-
return includePrefix ? HexHelper.addPrefix(hex) : hex;
|
|
2698
|
+
static getDictionary(locale) {
|
|
2699
|
+
return I18n._localeDictionaries[locale];
|
|
2599
2700
|
}
|
|
2600
2701
|
/**
|
|
2601
|
-
*
|
|
2602
|
-
* @
|
|
2603
|
-
* @returns The UTF8 version of the bytes.
|
|
2702
|
+
* Get all the locale dictionaries.
|
|
2703
|
+
* @returns The dictionaries.
|
|
2604
2704
|
*/
|
|
2605
|
-
static
|
|
2606
|
-
return
|
|
2705
|
+
static getAllDictionaries() {
|
|
2706
|
+
return I18n._localeDictionaries;
|
|
2607
2707
|
}
|
|
2608
2708
|
/**
|
|
2609
|
-
*
|
|
2610
|
-
* @param
|
|
2611
|
-
* @
|
|
2612
|
-
*/
|
|
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("");
|
|
2709
|
+
* Add a locale changed handler.
|
|
2710
|
+
* @param id The id of the handler.
|
|
2711
|
+
* @param handler The handler to add.
|
|
2712
|
+
*/
|
|
2713
|
+
static addLocaleHandler(id, handler) {
|
|
2714
|
+
I18n._localeChangedHandlers[id] = handler;
|
|
2619
2715
|
}
|
|
2620
2716
|
/**
|
|
2621
|
-
*
|
|
2622
|
-
* @param
|
|
2623
|
-
* @returns The bytes.
|
|
2717
|
+
* Remove a locale changed handler.
|
|
2718
|
+
* @param id The id of the handler.
|
|
2624
2719
|
*/
|
|
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;
|
|
2720
|
+
static removeLocaleHandler(id) {
|
|
2721
|
+
delete I18n._localeChangedHandlers[id];
|
|
2631
2722
|
}
|
|
2632
2723
|
/**
|
|
2633
|
-
*
|
|
2634
|
-
* @param
|
|
2635
|
-
* @
|
|
2724
|
+
* Add a dictionary changed handler.
|
|
2725
|
+
* @param id The id of the handler.
|
|
2726
|
+
* @param handler The handler to add.
|
|
2636
2727
|
*/
|
|
2637
|
-
static
|
|
2638
|
-
|
|
2728
|
+
static addDictionaryHandler(id, handler) {
|
|
2729
|
+
I18n._dictionaryChangedHandlers[id] = handler;
|
|
2639
2730
|
}
|
|
2640
2731
|
/**
|
|
2641
|
-
*
|
|
2642
|
-
* @param
|
|
2643
|
-
* @returns The bytes.
|
|
2732
|
+
* Remove a dictionary changed handler.
|
|
2733
|
+
* @param id The id of the handler.
|
|
2644
2734
|
*/
|
|
2645
|
-
static
|
|
2646
|
-
|
|
2735
|
+
static removeDictionaryHandler(id) {
|
|
2736
|
+
delete I18n._dictionaryChangedHandlers[id];
|
|
2647
2737
|
}
|
|
2648
2738
|
/**
|
|
2649
|
-
*
|
|
2650
|
-
* @param
|
|
2651
|
-
* @
|
|
2739
|
+
* Format a message.
|
|
2740
|
+
* @param key The key of the message to format.
|
|
2741
|
+
* @param values The values to substitute into the message.
|
|
2742
|
+
* @param overrideLocale Override the locale.
|
|
2743
|
+
* @returns The formatted string.
|
|
2652
2744
|
*/
|
|
2653
|
-
static
|
|
2654
|
-
|
|
2745
|
+
static formatMessage(key, values, overrideLocale) {
|
|
2746
|
+
let cl = overrideLocale ?? I18n._currentLocale;
|
|
2747
|
+
if (cl.startsWith("debug-")) {
|
|
2748
|
+
cl = I18n.DEFAULT_LOCALE;
|
|
2749
|
+
}
|
|
2750
|
+
if (!I18n._localeDictionaries[cl]) {
|
|
2751
|
+
return `!!Missing ${cl}`;
|
|
2752
|
+
}
|
|
2753
|
+
if (!I18n._localeDictionaries[cl][key]) {
|
|
2754
|
+
return `!!Missing ${cl}.${key}`;
|
|
2755
|
+
}
|
|
2756
|
+
if (I18n._currentLocale === "debug-k") {
|
|
2757
|
+
return key;
|
|
2758
|
+
}
|
|
2759
|
+
let ret = new intlMessageformat.IntlMessageFormat(I18n._localeDictionaries[cl][key], cl).format(values);
|
|
2760
|
+
if (I18n._currentLocale === "debug-x") {
|
|
2761
|
+
ret = ret.replace(/[a-z]/g, "x").replace(/[A-Z]/g, "x").replace(/\d/g, "n");
|
|
2762
|
+
}
|
|
2763
|
+
return ret;
|
|
2655
2764
|
}
|
|
2656
2765
|
/**
|
|
2657
|
-
*
|
|
2658
|
-
* @param
|
|
2659
|
-
* @returns
|
|
2766
|
+
* Check if the dictionaries have a message for the given key.
|
|
2767
|
+
* @param key The key to check for existence.
|
|
2768
|
+
* @returns True if the key exists.
|
|
2660
2769
|
*/
|
|
2661
|
-
static
|
|
2662
|
-
return
|
|
2770
|
+
static hasMessage(key) {
|
|
2771
|
+
return Is.string(I18n._localeDictionaries[I18n._currentLocale]?.[key]);
|
|
2663
2772
|
}
|
|
2664
2773
|
/**
|
|
2665
|
-
*
|
|
2774
|
+
* Flatten the translation property paths for faster lookup.
|
|
2775
|
+
* @param translation The translation to merge.
|
|
2776
|
+
* @param propertyPath The current root path.
|
|
2777
|
+
* @param mergedKeys The merged keys dictionary to populate.
|
|
2666
2778
|
* @internal
|
|
2667
2779
|
*/
|
|
2668
|
-
static
|
|
2669
|
-
|
|
2670
|
-
const
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
Converter._DECODE_LOOKUP[0x30 + i] = i;
|
|
2678
|
-
}
|
|
2679
|
-
else {
|
|
2680
|
-
Converter._DECODE_LOOKUP[0x61 - 10 + i] = i;
|
|
2681
|
-
}
|
|
2682
|
-
}
|
|
2780
|
+
static flattenTranslationKeys(translation, propertyPath, mergedKeys) {
|
|
2781
|
+
for (const key in translation) {
|
|
2782
|
+
const val = translation[key];
|
|
2783
|
+
const mergedPath = propertyPath.length > 0 ? `${propertyPath}.${key}` : key;
|
|
2784
|
+
if (Is.string(val)) {
|
|
2785
|
+
mergedKeys[mergedPath] = val;
|
|
2786
|
+
}
|
|
2787
|
+
else if (Is.object(val)) {
|
|
2788
|
+
I18n.flattenTranslationKeys(val, mergedPath, mergedKeys);
|
|
2683
2789
|
}
|
|
2684
2790
|
}
|
|
2685
2791
|
}
|
|
2686
2792
|
}
|
|
2687
2793
|
|
|
2794
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2795
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
2688
2796
|
/**
|
|
2689
|
-
*
|
|
2797
|
+
* Error helper functions.
|
|
2690
2798
|
*/
|
|
2691
|
-
class
|
|
2799
|
+
class ErrorHelper {
|
|
2692
2800
|
/**
|
|
2693
|
-
*
|
|
2694
|
-
* @
|
|
2801
|
+
* Format Errors and returns just their messages.
|
|
2802
|
+
* @param error The error to format.
|
|
2803
|
+
* @returns The error formatted including any inner errors.
|
|
2695
2804
|
*/
|
|
2696
|
-
static
|
|
2805
|
+
static formatErrors(error) {
|
|
2806
|
+
return ErrorHelper.localizeErrors(error).map(e => e.message);
|
|
2807
|
+
}
|
|
2697
2808
|
/**
|
|
2698
|
-
*
|
|
2699
|
-
* @param
|
|
2700
|
-
* @
|
|
2701
|
-
* @returns The object as bytes.
|
|
2809
|
+
* Localize the content of an error and any inner errors.
|
|
2810
|
+
* @param error The error to format.
|
|
2811
|
+
* @returns The localized version of the errors flattened.
|
|
2702
2812
|
*/
|
|
2703
|
-
static
|
|
2704
|
-
|
|
2705
|
-
|
|
2813
|
+
static localizeErrors(error) {
|
|
2814
|
+
const formattedErrors = [];
|
|
2815
|
+
if (Is.notEmpty(error)) {
|
|
2816
|
+
const errors = BaseError.flatten(error);
|
|
2817
|
+
for (const err of errors) {
|
|
2818
|
+
const errorNameKey = `errorNames.${StringHelper.camelCase(err.name)}`;
|
|
2819
|
+
const errorMessageKey = `error.${err.message}`;
|
|
2820
|
+
// If there is no error message then it is probably
|
|
2821
|
+
// from a 3rd party lib, so don't format it just display
|
|
2822
|
+
const hasErrorName = I18n.hasMessage(errorNameKey);
|
|
2823
|
+
const hasErrorMessage = I18n.hasMessage(errorMessageKey);
|
|
2824
|
+
const localizedError = {
|
|
2825
|
+
name: I18n.formatMessage(hasErrorName ? errorNameKey : "errorNames.error"),
|
|
2826
|
+
message: hasErrorMessage
|
|
2827
|
+
? I18n.formatMessage(errorMessageKey, err.properties)
|
|
2828
|
+
: err.message
|
|
2829
|
+
};
|
|
2830
|
+
if (Is.stringValue(err.source)) {
|
|
2831
|
+
localizedError.source = err.source;
|
|
2832
|
+
}
|
|
2833
|
+
if (Is.stringValue(err.stack)) {
|
|
2834
|
+
// Remove the first line from the stack traces as they
|
|
2835
|
+
// just have the error type and message duplicated
|
|
2836
|
+
const lines = err.stack.split("\n");
|
|
2837
|
+
lines.shift();
|
|
2838
|
+
localizedError.stack = lines.join("\n");
|
|
2839
|
+
}
|
|
2840
|
+
const additional = ErrorHelper.formatValidationErrors(err);
|
|
2841
|
+
if (Is.stringValue(additional)) {
|
|
2842
|
+
localizedError.additional = additional;
|
|
2843
|
+
}
|
|
2844
|
+
formattedErrors.push(localizedError);
|
|
2845
|
+
}
|
|
2706
2846
|
}
|
|
2707
|
-
|
|
2708
|
-
return Converter.utf8ToBytes(json);
|
|
2847
|
+
return formattedErrors;
|
|
2709
2848
|
}
|
|
2710
2849
|
/**
|
|
2711
|
-
*
|
|
2712
|
-
* @param
|
|
2713
|
-
* @returns The
|
|
2714
|
-
* @throws GeneralError if there was an error parsing the JSON.
|
|
2850
|
+
* Localize the content of an error and any inner errors.
|
|
2851
|
+
* @param error The error to format.
|
|
2852
|
+
* @returns The localized version of the errors flattened.
|
|
2715
2853
|
*/
|
|
2716
|
-
static
|
|
2717
|
-
if (Is.
|
|
2718
|
-
|
|
2854
|
+
static formatValidationErrors(error) {
|
|
2855
|
+
if (Is.object(error.properties) &&
|
|
2856
|
+
Object.keys(error.properties).length > 0 &&
|
|
2857
|
+
Is.object(error.properties) &&
|
|
2858
|
+
Is.arrayValue(error.properties.validationFailures)) {
|
|
2859
|
+
const validationErrors = [];
|
|
2860
|
+
for (const validationFailure of error.properties.validationFailures) {
|
|
2861
|
+
const errorI18n = `error.${validationFailure.reason}`;
|
|
2862
|
+
const errorMessage = I18n.hasMessage(errorI18n)
|
|
2863
|
+
? I18n.formatMessage(errorI18n, validationFailure.properties)
|
|
2864
|
+
: errorI18n;
|
|
2865
|
+
let v = `${validationFailure.property}: ${errorMessage}`;
|
|
2866
|
+
if (Is.object(validationFailure.properties) &&
|
|
2867
|
+
Is.notEmpty(validationFailure.properties.value)) {
|
|
2868
|
+
v += ` = ${JSON.stringify(validationFailure.properties.value)}`;
|
|
2869
|
+
}
|
|
2870
|
+
validationErrors.push(v);
|
|
2871
|
+
}
|
|
2872
|
+
return validationErrors.join("\n");
|
|
2719
2873
|
}
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
|
|
2877
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2878
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
2879
|
+
/**
|
|
2880
|
+
* Coerce an object from one type to another.
|
|
2881
|
+
*/
|
|
2882
|
+
class Coerce {
|
|
2883
|
+
/**
|
|
2884
|
+
* Coerce the value to a string.
|
|
2885
|
+
* @param value The value to coerce.
|
|
2886
|
+
* @throws TypeError If the value can not be coerced.
|
|
2887
|
+
* @returns The value if it can be coerced.
|
|
2888
|
+
*/
|
|
2889
|
+
static string(value) {
|
|
2890
|
+
if (Is.undefined(value)) {
|
|
2891
|
+
return value;
|
|
2723
2892
|
}
|
|
2724
|
-
|
|
2725
|
-
|
|
2893
|
+
if (Is.string(value)) {
|
|
2894
|
+
return value;
|
|
2895
|
+
}
|
|
2896
|
+
if (Is.number(value)) {
|
|
2897
|
+
return value.toString();
|
|
2898
|
+
}
|
|
2899
|
+
if (Is.boolean(value)) {
|
|
2900
|
+
return value ? "true" : "false";
|
|
2901
|
+
}
|
|
2902
|
+
if (Is.date(value)) {
|
|
2903
|
+
return value.toISOString();
|
|
2726
2904
|
}
|
|
2727
2905
|
}
|
|
2728
2906
|
/**
|
|
2729
|
-
*
|
|
2730
|
-
* @param
|
|
2731
|
-
* @
|
|
2907
|
+
* Coerce the value to a number.
|
|
2908
|
+
* @param value The value to coerce.
|
|
2909
|
+
* @throws TypeError If the value can not be coerced.
|
|
2910
|
+
* @returns The value if it can be coerced.
|
|
2732
2911
|
*/
|
|
2733
|
-
static
|
|
2734
|
-
if (Is.undefined(
|
|
2735
|
-
return
|
|
2912
|
+
static number(value) {
|
|
2913
|
+
if (Is.undefined(value)) {
|
|
2914
|
+
return value;
|
|
2915
|
+
}
|
|
2916
|
+
if (Is.number(value)) {
|
|
2917
|
+
return value;
|
|
2918
|
+
}
|
|
2919
|
+
if (Is.string(value)) {
|
|
2920
|
+
const parsed = Number.parseFloat(value);
|
|
2921
|
+
if (Is.number(parsed)) {
|
|
2922
|
+
return parsed;
|
|
2923
|
+
}
|
|
2924
|
+
}
|
|
2925
|
+
if (Is.boolean(value)) {
|
|
2926
|
+
return value ? 1 : 0;
|
|
2927
|
+
}
|
|
2928
|
+
if (Is.date(value)) {
|
|
2929
|
+
return value.getTime();
|
|
2736
2930
|
}
|
|
2737
|
-
return structuredClone(obj);
|
|
2738
2931
|
}
|
|
2739
2932
|
/**
|
|
2740
|
-
*
|
|
2741
|
-
* @param
|
|
2742
|
-
* @
|
|
2743
|
-
* @returns The
|
|
2933
|
+
* Coerce the value to a bigint.
|
|
2934
|
+
* @param value The value to coerce.
|
|
2935
|
+
* @throws TypeError If the value can not be coerced.
|
|
2936
|
+
* @returns The value if it can be coerced.
|
|
2744
2937
|
*/
|
|
2745
|
-
static
|
|
2746
|
-
if (Is.
|
|
2747
|
-
return
|
|
2938
|
+
static bigint(value) {
|
|
2939
|
+
if (Is.undefined(value)) {
|
|
2940
|
+
return value;
|
|
2748
2941
|
}
|
|
2749
|
-
if (Is.
|
|
2750
|
-
return
|
|
2942
|
+
if (Is.bigint(value)) {
|
|
2943
|
+
return value;
|
|
2751
2944
|
}
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
else {
|
|
2760
|
-
ObjectHelper.propertySet(obj1Clone, key, obj2[key]);
|
|
2761
|
-
}
|
|
2945
|
+
if (Is.number(value)) {
|
|
2946
|
+
return BigInt(value);
|
|
2947
|
+
}
|
|
2948
|
+
if (Is.string(value)) {
|
|
2949
|
+
const parsed = Number.parseFloat(value);
|
|
2950
|
+
if (Is.integer(parsed)) {
|
|
2951
|
+
return BigInt(parsed);
|
|
2762
2952
|
}
|
|
2763
2953
|
}
|
|
2764
|
-
|
|
2954
|
+
if (Is.boolean(value)) {
|
|
2955
|
+
return value ? 1n : 0n;
|
|
2956
|
+
}
|
|
2765
2957
|
}
|
|
2766
2958
|
/**
|
|
2767
|
-
*
|
|
2768
|
-
* @param
|
|
2769
|
-
* @
|
|
2770
|
-
* @
|
|
2771
|
-
* @returns True is the objects are equal.
|
|
2959
|
+
* Coerce the value to a boolean.
|
|
2960
|
+
* @param value The value to coerce.
|
|
2961
|
+
* @throws TypeError If the value can not be coerced.
|
|
2962
|
+
* @returns The value if it can be coerced.
|
|
2772
2963
|
*/
|
|
2773
|
-
static
|
|
2774
|
-
if (
|
|
2775
|
-
return
|
|
2964
|
+
static boolean(value) {
|
|
2965
|
+
if (Is.undefined(value)) {
|
|
2966
|
+
return value;
|
|
2967
|
+
}
|
|
2968
|
+
if (Is.boolean(value)) {
|
|
2969
|
+
return value;
|
|
2970
|
+
}
|
|
2971
|
+
if (Is.number(value)) {
|
|
2972
|
+
// eslint-disable-next-line no-unneeded-ternary
|
|
2973
|
+
return value ? true : false;
|
|
2974
|
+
}
|
|
2975
|
+
if (Is.string(value)) {
|
|
2976
|
+
if (/true/i.test(value)) {
|
|
2977
|
+
return true;
|
|
2978
|
+
}
|
|
2979
|
+
if (/false/i.test(value)) {
|
|
2980
|
+
return false;
|
|
2981
|
+
}
|
|
2776
2982
|
}
|
|
2777
|
-
return JsonHelper.canonicalize(obj1) === JsonHelper.canonicalize(obj2);
|
|
2778
2983
|
}
|
|
2779
2984
|
/**
|
|
2780
|
-
*
|
|
2781
|
-
* @param
|
|
2782
|
-
* @
|
|
2783
|
-
* @returns The
|
|
2985
|
+
* Coerce the value to a date.
|
|
2986
|
+
* @param value The value to coerce.
|
|
2987
|
+
* @throws TypeError If the value can not be coerced.
|
|
2988
|
+
* @returns The value if it can be coerced.
|
|
2784
2989
|
*/
|
|
2785
|
-
static
|
|
2786
|
-
if (
|
|
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
|
-
}
|
|
2990
|
+
static date(value) {
|
|
2991
|
+
if (Is.undefined(value)) {
|
|
2797
2992
|
return value;
|
|
2798
2993
|
}
|
|
2799
|
-
|
|
2994
|
+
if (Is.date(value)) {
|
|
2995
|
+
return value;
|
|
2996
|
+
}
|
|
2997
|
+
if (Is.number(value)) {
|
|
2998
|
+
return new Date(value);
|
|
2999
|
+
}
|
|
3000
|
+
if (Is.string(value)) {
|
|
3001
|
+
const dt = new Date(value);
|
|
3002
|
+
if (!Number.isNaN(dt.getTime())) {
|
|
3003
|
+
const utc = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate());
|
|
3004
|
+
return new Date(utc);
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
2800
3007
|
}
|
|
2801
3008
|
/**
|
|
2802
|
-
*
|
|
2803
|
-
* @param
|
|
2804
|
-
* @
|
|
2805
|
-
* @
|
|
3009
|
+
* Coerce the value to a date/time.
|
|
3010
|
+
* @param value The value to coerce.
|
|
3011
|
+
* @throws TypeError If the value can not be coerced.
|
|
3012
|
+
* @returns The value if it can be coerced.
|
|
2806
3013
|
*/
|
|
2807
|
-
static
|
|
2808
|
-
if (Is.
|
|
2809
|
-
|
|
3014
|
+
static dateTime(value) {
|
|
3015
|
+
if (Is.undefined(value)) {
|
|
3016
|
+
return value;
|
|
3017
|
+
}
|
|
3018
|
+
if (Is.date(value)) {
|
|
3019
|
+
return value;
|
|
3020
|
+
}
|
|
3021
|
+
if (Is.number(value)) {
|
|
3022
|
+
return new Date(value);
|
|
3023
|
+
}
|
|
3024
|
+
if (Is.string(value)) {
|
|
3025
|
+
const dt = new Date(value);
|
|
3026
|
+
if (!Number.isNaN(dt.getTime())) {
|
|
3027
|
+
const utc = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate(), dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
|
|
3028
|
+
return new Date(utc);
|
|
3029
|
+
}
|
|
2810
3030
|
}
|
|
2811
3031
|
}
|
|
2812
3032
|
/**
|
|
2813
|
-
*
|
|
2814
|
-
* @param
|
|
2815
|
-
* @
|
|
3033
|
+
* Coerce the value to a time.
|
|
3034
|
+
* @param value The value to coerce.
|
|
3035
|
+
* @throws TypeError If the value can not be coerced.
|
|
3036
|
+
* @returns The value if it can be coerced.
|
|
2816
3037
|
*/
|
|
2817
|
-
static
|
|
2818
|
-
if (Is.
|
|
2819
|
-
|
|
3038
|
+
static time(value) {
|
|
3039
|
+
if (Is.undefined(value)) {
|
|
3040
|
+
return value;
|
|
3041
|
+
}
|
|
3042
|
+
if (Is.date(value)) {
|
|
3043
|
+
return value;
|
|
3044
|
+
}
|
|
3045
|
+
if (Is.number(value)) {
|
|
3046
|
+
const dt = new Date(value);
|
|
3047
|
+
dt.setFullYear(1970, 0, 1);
|
|
3048
|
+
return dt;
|
|
3049
|
+
}
|
|
3050
|
+
if (Is.string(value)) {
|
|
3051
|
+
const dt = new Date(value);
|
|
3052
|
+
if (!Number.isNaN(dt.getTime())) {
|
|
3053
|
+
const utc = Date.UTC(1970, 0, 1, dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
|
|
3054
|
+
return new Date(utc);
|
|
3055
|
+
}
|
|
2820
3056
|
}
|
|
2821
3057
|
}
|
|
2822
3058
|
/**
|
|
2823
|
-
*
|
|
2824
|
-
* @param
|
|
2825
|
-
* @
|
|
2826
|
-
* @returns The
|
|
3059
|
+
* Coerce the value to an object.
|
|
3060
|
+
* @param value The value to coerce.
|
|
3061
|
+
* @throws TypeError If the value can not be coerced.
|
|
3062
|
+
* @returns The value if it can be coerced.
|
|
2827
3063
|
*/
|
|
2828
|
-
static
|
|
2829
|
-
if (Is.
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
3064
|
+
static object(value) {
|
|
3065
|
+
if (Is.undefined(value)) {
|
|
3066
|
+
return value;
|
|
3067
|
+
}
|
|
3068
|
+
if (Is.object(value)) {
|
|
3069
|
+
return value;
|
|
3070
|
+
}
|
|
3071
|
+
if (Is.stringValue(value)) {
|
|
3072
|
+
try {
|
|
3073
|
+
return JSON.parse(value);
|
|
2833
3074
|
}
|
|
2834
|
-
|
|
3075
|
+
catch { }
|
|
2835
3076
|
}
|
|
2836
|
-
return obj;
|
|
2837
3077
|
}
|
|
3078
|
+
}
|
|
3079
|
+
|
|
3080
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3081
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3082
|
+
/**
|
|
3083
|
+
* Class to help with filenames.
|
|
3084
|
+
*/
|
|
3085
|
+
class FilenameHelper {
|
|
2838
3086
|
/**
|
|
2839
|
-
*
|
|
2840
|
-
* @param
|
|
2841
|
-
* @
|
|
2842
|
-
* @returns The partial object.
|
|
3087
|
+
* Replaces any unsafe characters in the filename.
|
|
3088
|
+
* @param filename The filename to make safe.
|
|
3089
|
+
* @returns The safe filename.
|
|
2843
3090
|
*/
|
|
2844
|
-
static
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
delete result[key];
|
|
2849
|
-
}
|
|
2850
|
-
return result;
|
|
3091
|
+
static safeFilename(filename) {
|
|
3092
|
+
let safe = Coerce.string(filename);
|
|
3093
|
+
if (Is.empty(safe)) {
|
|
3094
|
+
return "";
|
|
2851
3095
|
}
|
|
2852
|
-
|
|
3096
|
+
// Common non filename characters
|
|
3097
|
+
safe = safe.replace(/["*/:<>?\\|]/g, "_");
|
|
3098
|
+
// Windows non filename characters
|
|
3099
|
+
safe = safe.replace(/^(con|prn|aux|nul|com\d|lpt\d)$/i, "_");
|
|
3100
|
+
// Control characters
|
|
3101
|
+
safe = safe.replace(/[\u0000-\u001F\u0080-\u009F]/g, "_");
|
|
3102
|
+
// Relative paths
|
|
3103
|
+
safe = safe.replace(/^\.+/, "_");
|
|
3104
|
+
// Trailing periods
|
|
3105
|
+
safe = safe.replace(/\.+$/, "");
|
|
3106
|
+
return safe;
|
|
2853
3107
|
}
|
|
2854
3108
|
}
|
|
2855
3109
|
|
|
@@ -2883,7 +3137,7 @@ const CompressionType = {
|
|
|
2883
3137
|
*/
|
|
2884
3138
|
Gzip: "gzip",
|
|
2885
3139
|
/**
|
|
2886
|
-
*
|
|
3140
|
+
* Deflate.
|
|
2887
3141
|
*/
|
|
2888
3142
|
Deflate: "deflate"
|
|
2889
3143
|
};
|
|
@@ -3506,6 +3760,7 @@ class Validation {
|
|
|
3506
3760
|
* @param options Additional options for the validation.
|
|
3507
3761
|
* @param options.minLength The minimum length of the string.
|
|
3508
3762
|
* @param options.maxLength The maximum length of the string.
|
|
3763
|
+
* @param options.format Specific format to check.
|
|
3509
3764
|
* @returns True if the value is a valid string.
|
|
3510
3765
|
*/
|
|
3511
3766
|
static string(property, value, failures, fieldNameResource, options) {
|
|
@@ -3524,6 +3779,47 @@ class Validation {
|
|
|
3524
3779
|
const maxLimitDefined = Is.integer(maxLength);
|
|
3525
3780
|
const belowMin = minLimitDefined && value.length < minLength;
|
|
3526
3781
|
const aboveMax = maxLimitDefined && value.length > maxLength;
|
|
3782
|
+
if (options?.format === "base58" && !Is.stringBase58(value)) {
|
|
3783
|
+
failures.push({
|
|
3784
|
+
property,
|
|
3785
|
+
reason: "validation.beTextBase58",
|
|
3786
|
+
properties: {
|
|
3787
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
3788
|
+
value
|
|
3789
|
+
}
|
|
3790
|
+
});
|
|
3791
|
+
}
|
|
3792
|
+
else if (options?.format === "base64" && !Is.stringBase64(value)) {
|
|
3793
|
+
failures.push({
|
|
3794
|
+
property,
|
|
3795
|
+
reason: "validation.beTextBase64",
|
|
3796
|
+
properties: {
|
|
3797
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
3798
|
+
value
|
|
3799
|
+
}
|
|
3800
|
+
});
|
|
3801
|
+
}
|
|
3802
|
+
else if (options?.format === "hex" && !Is.stringHex(value)) {
|
|
3803
|
+
failures.push({
|
|
3804
|
+
property,
|
|
3805
|
+
reason: "validation.beTextHex",
|
|
3806
|
+
properties: {
|
|
3807
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
3808
|
+
value
|
|
3809
|
+
}
|
|
3810
|
+
});
|
|
3811
|
+
}
|
|
3812
|
+
else if (Is.regexp(options?.format) && !options.format.test(value)) {
|
|
3813
|
+
failures.push({
|
|
3814
|
+
property,
|
|
3815
|
+
reason: "validation.beTextRegExp",
|
|
3816
|
+
properties: {
|
|
3817
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
3818
|
+
value,
|
|
3819
|
+
format: options?.format
|
|
3820
|
+
}
|
|
3821
|
+
});
|
|
3822
|
+
}
|
|
3527
3823
|
if (minLimitDefined && maxLimitDefined && (belowMin || aboveMax)) {
|
|
3528
3824
|
failures.push({
|
|
3529
3825
|
property,
|
|
@@ -4187,6 +4483,7 @@ exports.AlreadyExistsError = AlreadyExistsError;
|
|
|
4187
4483
|
exports.ArrayHelper = ArrayHelper;
|
|
4188
4484
|
exports.AsyncCache = AsyncCache;
|
|
4189
4485
|
exports.Base32 = Base32;
|
|
4486
|
+
exports.Base58 = Base58;
|
|
4190
4487
|
exports.Base64 = Base64;
|
|
4191
4488
|
exports.Base64Url = Base64Url;
|
|
4192
4489
|
exports.BaseError = BaseError;
|
|
@@ -4197,6 +4494,7 @@ exports.Compression = Compression;
|
|
|
4197
4494
|
exports.CompressionType = CompressionType;
|
|
4198
4495
|
exports.ConflictError = ConflictError;
|
|
4199
4496
|
exports.Converter = Converter;
|
|
4497
|
+
exports.EnvHelper = EnvHelper;
|
|
4200
4498
|
exports.ErrorHelper = ErrorHelper;
|
|
4201
4499
|
exports.Factory = Factory;
|
|
4202
4500
|
exports.FilenameHelper = FilenameHelper;
|