@twin.org/core 0.0.1-next.7 → 0.0.1-next.70
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 +1680 -792
- package/dist/esm/index.mjs +1676 -793
- package/dist/types/encoding/base58.d.ts +18 -0
- package/dist/types/errors/baseError.d.ts +2 -1
- package/dist/types/factories/factory.d.ts +20 -1
- package/dist/types/helpers/arrayHelper.d.ts +13 -0
- package/dist/types/helpers/envHelper.d.ts +16 -0
- package/dist/types/helpers/errorHelper.d.ts +2 -1
- 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 +7 -0
- package/dist/types/models/IComponent.d.ts +12 -3
- package/dist/types/models/II18nShared.d.ts +29 -0
- package/dist/types/models/coerceType.d.ts +49 -0
- package/dist/types/models/compressionType.d.ts +1 -1
- package/dist/types/models/objectOrArray.d.ts +4 -0
- package/dist/types/utils/asyncCache.d.ts +10 -1
- 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 +35 -0
- package/dist/types/utils/is.d.ts +12 -0
- package/dist/types/utils/sharedStore.d.ts +23 -0
- package/dist/types/utils/validation.d.ts +2 -0
- package/docs/changelog.md +212 -1
- package/docs/reference/classes/AlreadyExistsError.md +77 -27
- package/docs/reference/classes/ArrayHelper.md +79 -5
- package/docs/reference/classes/AsyncCache.md +75 -13
- package/docs/reference/classes/Base32.md +9 -5
- package/docs/reference/classes/Base58.md +61 -0
- package/docs/reference/classes/Base64.md +12 -6
- package/docs/reference/classes/Base64Url.md +9 -5
- package/docs/reference/classes/BaseError.md +79 -29
- package/docs/reference/classes/BitString.md +23 -11
- package/docs/reference/classes/Coerce.md +110 -12
- package/docs/reference/classes/Compression.md +19 -11
- package/docs/reference/classes/ConflictError.md +80 -28
- package/docs/reference/classes/Converter.md +110 -26
- package/docs/reference/classes/EnvHelper.md +45 -0
- package/docs/reference/classes/ErrorHelper.md +19 -7
- package/docs/reference/classes/Factory.md +95 -17
- package/docs/reference/classes/FilenameHelper.md +6 -4
- package/docs/reference/classes/GeneralError.md +75 -27
- package/docs/reference/classes/GuardError.md +80 -28
- package/docs/reference/classes/Guards.md +398 -80
- package/docs/reference/classes/HexHelper.md +18 -8
- package/docs/reference/classes/I18n.md +46 -20
- package/docs/reference/classes/Is.md +179 -51
- package/docs/reference/classes/JsonHelper.md +146 -10
- package/docs/reference/classes/NotFoundError.md +77 -27
- package/docs/reference/classes/NotImplementedError.md +71 -25
- package/docs/reference/classes/NotSupportedError.md +74 -26
- package/docs/reference/classes/ObjectHelper.md +225 -35
- package/docs/reference/classes/RandomHelper.md +6 -4
- package/docs/reference/classes/SharedStore.md +94 -0
- package/docs/reference/classes/StringHelper.md +54 -20
- package/docs/reference/classes/Uint8ArrayHelper.md +35 -0
- package/docs/reference/classes/UnauthorizedError.md +74 -26
- package/docs/reference/classes/UnprocessableError.md +75 -27
- package/docs/reference/classes/Url.md +37 -17
- package/docs/reference/classes/Urn.md +63 -27
- package/docs/reference/classes/Validation.md +349 -135
- package/docs/reference/classes/ValidationError.md +74 -26
- package/docs/reference/index.md +8 -0
- package/docs/reference/interfaces/IComponent.md +30 -8
- package/docs/reference/interfaces/IError.md +2 -2
- package/docs/reference/interfaces/II18nShared.md +47 -0
- package/docs/reference/interfaces/IKeyValue.md +3 -1
- package/docs/reference/interfaces/ILabelledValue.md +3 -1
- package/docs/reference/interfaces/ILocaleDictionary.md +1 -1
- package/docs/reference/interfaces/IPatchOperation.md +1 -1
- package/docs/reference/interfaces/IValidationFailure.md +1 -1
- package/docs/reference/type-aliases/CoerceType.md +5 -0
- package/docs/reference/type-aliases/CompressionType.md +1 -1
- package/docs/reference/type-aliases/ObjectOrArray.md +11 -0
- package/docs/reference/variables/CoerceType.md +67 -0
- package/docs/reference/variables/CompressionType.md +1 -1
- package/locales/en.json +23 -1
- package/package.json +7 -7
package/dist/esm/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { IntlMessageFormat } from 'intl-messageformat';
|
|
2
1
|
import { createPatch, applyPatch } from 'rfc6902';
|
|
2
|
+
import { IntlMessageFormat } from 'intl-messageformat';
|
|
3
3
|
|
|
4
4
|
// Copyright 2024 IOTA Stiftung.
|
|
5
5
|
// SPDX-License-Identifier: Apache-2.0.
|
|
@@ -111,7 +111,12 @@ class Is {
|
|
|
111
111
|
}
|
|
112
112
|
try {
|
|
113
113
|
const json = JSON.parse(value);
|
|
114
|
-
return
|
|
114
|
+
return (Is.object(json) ||
|
|
115
|
+
Is.array(json) ||
|
|
116
|
+
Is.string(json) ||
|
|
117
|
+
Is.number(json) ||
|
|
118
|
+
Is.boolean(json) ||
|
|
119
|
+
Is.null(json));
|
|
115
120
|
}
|
|
116
121
|
catch {
|
|
117
122
|
return false;
|
|
@@ -135,7 +140,17 @@ class Is {
|
|
|
135
140
|
static stringBase64Url(value) {
|
|
136
141
|
return (Is.stringValue(value) &&
|
|
137
142
|
// eslint-disable-next-line unicorn/better-regex
|
|
138
|
-
/^(
|
|
143
|
+
/^([A-Za-z0-9-_])*$/.test(value));
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Is the value a base58 string.
|
|
147
|
+
* @param value The value to test.
|
|
148
|
+
* @returns True if the value is a base58 string.
|
|
149
|
+
*/
|
|
150
|
+
static stringBase58(value) {
|
|
151
|
+
return (Is.stringValue(value) &&
|
|
152
|
+
// eslint-disable-next-line unicorn/better-regex
|
|
153
|
+
/^[A-HJ-NP-Za-km-z1-9]*$/.test(value));
|
|
139
154
|
}
|
|
140
155
|
/**
|
|
141
156
|
* Is the value a hex string.
|
|
@@ -349,6 +364,14 @@ class Is {
|
|
|
349
364
|
static promise(value) {
|
|
350
365
|
return value instanceof Promise;
|
|
351
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* Is the value a regexp.
|
|
369
|
+
* @param value The value to test.
|
|
370
|
+
* @returns True if the value is a regexp.
|
|
371
|
+
*/
|
|
372
|
+
static regexp(value) {
|
|
373
|
+
return value instanceof RegExp;
|
|
374
|
+
}
|
|
352
375
|
}
|
|
353
376
|
|
|
354
377
|
// Copyright 2024 IOTA Stiftung.
|
|
@@ -644,7 +667,10 @@ class BaseError extends Error {
|
|
|
644
667
|
let properties;
|
|
645
668
|
let inner;
|
|
646
669
|
let stack;
|
|
647
|
-
if (Is.object(err)) {
|
|
670
|
+
if (Is.object(err) && Is.stringValue(err.error)) {
|
|
671
|
+
message = err.error;
|
|
672
|
+
}
|
|
673
|
+
else if (Is.object(err)) {
|
|
648
674
|
if (Is.stringValue(err.name)) {
|
|
649
675
|
name = err.name;
|
|
650
676
|
}
|
|
@@ -664,9 +690,6 @@ class BaseError extends Error {
|
|
|
664
690
|
stack = err.stack;
|
|
665
691
|
}
|
|
666
692
|
}
|
|
667
|
-
else if (Is.object(err) && Is.stringValue(err.error)) {
|
|
668
|
-
message = err.error;
|
|
669
|
-
}
|
|
670
693
|
else if (Is.stringValue(err)) {
|
|
671
694
|
message = err;
|
|
672
695
|
}
|
|
@@ -684,7 +707,7 @@ class BaseError extends Error {
|
|
|
684
707
|
*/
|
|
685
708
|
static flatten(err) {
|
|
686
709
|
const flattened = [];
|
|
687
|
-
let e = BaseError.fromError(err).toJsonObject();
|
|
710
|
+
let e = BaseError.fromError(err).toJsonObject(true);
|
|
688
711
|
while (e) {
|
|
689
712
|
const inner = e.inner;
|
|
690
713
|
e.inner = undefined;
|
|
@@ -780,9 +803,10 @@ class BaseError extends Error {
|
|
|
780
803
|
}
|
|
781
804
|
/**
|
|
782
805
|
* Serialize the error to the error model.
|
|
806
|
+
* @param includeStackTrace Whether to include the error stack in the model, defaults to false.
|
|
783
807
|
* @returns The error model.
|
|
784
808
|
*/
|
|
785
|
-
toJsonObject() {
|
|
809
|
+
toJsonObject(includeStackTrace) {
|
|
786
810
|
const err = {};
|
|
787
811
|
if (Is.stringValue(this.name)) {
|
|
788
812
|
err.name = this.name;
|
|
@@ -796,11 +820,11 @@ class BaseError extends Error {
|
|
|
796
820
|
if (Is.object(this.properties)) {
|
|
797
821
|
err.properties = this.properties;
|
|
798
822
|
}
|
|
799
|
-
if (Is.stringValue(this.stack)) {
|
|
823
|
+
if ((includeStackTrace ?? false) && Is.stringValue(this.stack)) {
|
|
800
824
|
err.stack = this.stack;
|
|
801
825
|
}
|
|
802
826
|
if (Is.notEmpty(this.inner)) {
|
|
803
|
-
err.inner = BaseError.fromError(this.inner).toJsonObject();
|
|
827
|
+
err.inner = BaseError.fromError(this.inner).toJsonObject(includeStackTrace);
|
|
804
828
|
}
|
|
805
829
|
return err;
|
|
806
830
|
}
|
|
@@ -898,6 +922,127 @@ class Base32 {
|
|
|
898
922
|
}
|
|
899
923
|
}
|
|
900
924
|
|
|
925
|
+
/**
|
|
926
|
+
* Class to help with base58 Encoding/Decoding.
|
|
927
|
+
*/
|
|
928
|
+
class Base58 {
|
|
929
|
+
/**
|
|
930
|
+
* Runtime name for the class.
|
|
931
|
+
* @internal
|
|
932
|
+
*/
|
|
933
|
+
static _CLASS_NAME = "Base58";
|
|
934
|
+
/**
|
|
935
|
+
* Alphabet table for encoding.
|
|
936
|
+
* @internal
|
|
937
|
+
*/
|
|
938
|
+
static _ALPHABET =
|
|
939
|
+
// cspell:disable-next-line
|
|
940
|
+
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
941
|
+
/**
|
|
942
|
+
* Reverse map for decoding.
|
|
943
|
+
* @internal
|
|
944
|
+
*/
|
|
945
|
+
static _ALPHABET_REVERSE = [
|
|
946
|
+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
947
|
+
-1, -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, 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,
|
|
949
|
+
17, 18, 19, 20, 21, -1, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1, -1, 33,
|
|
950
|
+
34, 35, 36, 37, 38, 39, 40, 41, 42, 43, -1, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
|
|
951
|
+
57, -1, -1, -1, -1, -1
|
|
952
|
+
];
|
|
953
|
+
/**
|
|
954
|
+
* Convert the base 58 string to a byte array.
|
|
955
|
+
* @param base58 The base58 string to convert.
|
|
956
|
+
* @returns The byte array.
|
|
957
|
+
* @throws If the input string contains a character not in the Base58 alphabet.
|
|
958
|
+
*/
|
|
959
|
+
static decode(base58) {
|
|
960
|
+
let zeroes = 0;
|
|
961
|
+
for (let i = 0; i < base58.length; i++) {
|
|
962
|
+
if (base58[i] !== "1") {
|
|
963
|
+
break;
|
|
964
|
+
}
|
|
965
|
+
zeroes += 1;
|
|
966
|
+
}
|
|
967
|
+
const size = Math.trunc((base58.length * 733) / 1000) + 1;
|
|
968
|
+
const b256 = new Uint8Array(size).fill(0);
|
|
969
|
+
let length = 0;
|
|
970
|
+
for (let i = zeroes; i < base58.length; i++) {
|
|
971
|
+
const ch = base58.charCodeAt(i);
|
|
972
|
+
if (ch & 0xff80) {
|
|
973
|
+
throw new GeneralError(Base58._CLASS_NAME, "invalidCharacter", { invalidCharacter: ch });
|
|
974
|
+
}
|
|
975
|
+
const val = Base58._ALPHABET_REVERSE[ch];
|
|
976
|
+
if (val === -1) {
|
|
977
|
+
throw new GeneralError(Base58._CLASS_NAME, "invalidCharacter", { invalidCharacter: ch });
|
|
978
|
+
}
|
|
979
|
+
let carry = val;
|
|
980
|
+
let j = 0;
|
|
981
|
+
for (let k = size - 1; k >= 0; k--, j++) {
|
|
982
|
+
if (carry === 0 && j >= length) {
|
|
983
|
+
break;
|
|
984
|
+
}
|
|
985
|
+
carry += b256[k] * 58;
|
|
986
|
+
b256[k] = carry;
|
|
987
|
+
carry >>>= 8;
|
|
988
|
+
}
|
|
989
|
+
length = j;
|
|
990
|
+
}
|
|
991
|
+
const out = new Uint8Array(zeroes + length);
|
|
992
|
+
let j;
|
|
993
|
+
for (j = 0; j < zeroes; j++) {
|
|
994
|
+
out[j] = 0;
|
|
995
|
+
}
|
|
996
|
+
let i = size - length;
|
|
997
|
+
while (i < size) {
|
|
998
|
+
out[j++] = b256[i++];
|
|
999
|
+
}
|
|
1000
|
+
return out;
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* Convert a byte array to base 58.
|
|
1004
|
+
* @param bytes The byte array to encode.
|
|
1005
|
+
* @returns The data as base58 string.
|
|
1006
|
+
*/
|
|
1007
|
+
static encode(bytes) {
|
|
1008
|
+
let zeroes = 0;
|
|
1009
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
1010
|
+
if (bytes[i] !== 0) {
|
|
1011
|
+
break;
|
|
1012
|
+
}
|
|
1013
|
+
zeroes += 1;
|
|
1014
|
+
}
|
|
1015
|
+
const size = Math.trunc(((bytes.length - zeroes) * 138) / 100) + 1;
|
|
1016
|
+
const b58 = new Uint8Array(size).fill(0);
|
|
1017
|
+
let length = 0;
|
|
1018
|
+
for (let i = zeroes; i < bytes.length; i++) {
|
|
1019
|
+
let carry = bytes[i];
|
|
1020
|
+
let j = 0;
|
|
1021
|
+
for (let k = size - 1; k >= 0; k--, j++) {
|
|
1022
|
+
if (carry === 0 && j >= length) {
|
|
1023
|
+
break;
|
|
1024
|
+
}
|
|
1025
|
+
carry += b58[k] * 256;
|
|
1026
|
+
b58[k] = carry % 58;
|
|
1027
|
+
carry = Math.trunc(carry / 58);
|
|
1028
|
+
}
|
|
1029
|
+
length = j;
|
|
1030
|
+
}
|
|
1031
|
+
let i = size - length;
|
|
1032
|
+
while (i < size && b58[i] === 0) {
|
|
1033
|
+
i += 1;
|
|
1034
|
+
}
|
|
1035
|
+
let str = "";
|
|
1036
|
+
for (let j = 0; j < zeroes; j++) {
|
|
1037
|
+
str += "1";
|
|
1038
|
+
}
|
|
1039
|
+
while (i < size) {
|
|
1040
|
+
str += Base58._ALPHABET[b58[i++]];
|
|
1041
|
+
}
|
|
1042
|
+
return str;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
|
|
901
1046
|
// Copyright 2024 IOTA Stiftung.
|
|
902
1047
|
// SPDX-License-Identifier: Apache-2.0.
|
|
903
1048
|
/* eslint-disable no-bitwise */
|
|
@@ -1052,7 +1197,7 @@ class Base64 {
|
|
|
1052
1197
|
const maxChunkLength = 16383; // must be multiple of 3
|
|
1053
1198
|
// go through the array every three bytes, we'll deal with trailing stuff later
|
|
1054
1199
|
for (let i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
|
|
1055
|
-
parts.push(Base64.encodeChunk(bytes, i, i + maxChunkLength
|
|
1200
|
+
parts.push(Base64.encodeChunk(bytes, i, Math.min(i + maxChunkLength, len2)));
|
|
1056
1201
|
}
|
|
1057
1202
|
// pad the end with zeros, but make sure to not forget the extra bytes
|
|
1058
1203
|
if (extraBytes === 1) {
|
|
@@ -1348,6 +1493,49 @@ class ValidationError extends BaseError {
|
|
|
1348
1493
|
}
|
|
1349
1494
|
}
|
|
1350
1495
|
|
|
1496
|
+
/**
|
|
1497
|
+
* Class to help with arrays.
|
|
1498
|
+
*/
|
|
1499
|
+
class ArrayHelper {
|
|
1500
|
+
/**
|
|
1501
|
+
* Do the two arrays match.
|
|
1502
|
+
* @param arr1 The first array.
|
|
1503
|
+
* @param arr2 The second array.
|
|
1504
|
+
* @returns True if both arrays are empty of have the same values.
|
|
1505
|
+
*/
|
|
1506
|
+
static matches(arr1, arr2) {
|
|
1507
|
+
if (Is.empty(arr1) && Is.empty(arr2)) {
|
|
1508
|
+
return true;
|
|
1509
|
+
}
|
|
1510
|
+
if (!((Is.array(arr1) && Is.array(arr2)) || (Is.typedArray(arr1) && Is.typedArray(arr2)))) {
|
|
1511
|
+
return false;
|
|
1512
|
+
}
|
|
1513
|
+
if (arr1.length !== arr2.length) {
|
|
1514
|
+
return false;
|
|
1515
|
+
}
|
|
1516
|
+
for (let i = 0; i < arr1.length; i++) {
|
|
1517
|
+
if (arr1[i] !== arr2[i]) {
|
|
1518
|
+
return false;
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
return true;
|
|
1522
|
+
}
|
|
1523
|
+
/**
|
|
1524
|
+
* Convert an object or array to an array.
|
|
1525
|
+
* @param value The object or array to convert.
|
|
1526
|
+
* @returns The array.
|
|
1527
|
+
*/
|
|
1528
|
+
static fromObjectOrArray(value) {
|
|
1529
|
+
if (Is.empty(value)) {
|
|
1530
|
+
return undefined;
|
|
1531
|
+
}
|
|
1532
|
+
if (Is.array(value)) {
|
|
1533
|
+
return value;
|
|
1534
|
+
}
|
|
1535
|
+
return [value];
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1351
1539
|
// Copyright 2024 IOTA Stiftung.
|
|
1352
1540
|
// SPDX-License-Identifier: Apache-2.0.
|
|
1353
1541
|
/**
|
|
@@ -1393,6 +1581,18 @@ class Guards {
|
|
|
1393
1581
|
throw new GuardError(source, "guard.stringEmpty", property, value);
|
|
1394
1582
|
}
|
|
1395
1583
|
}
|
|
1584
|
+
/**
|
|
1585
|
+
* Is the property a JSON value.
|
|
1586
|
+
* @param source The source of the error.
|
|
1587
|
+
* @param property The name of the property.
|
|
1588
|
+
* @param value The value to test.
|
|
1589
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1590
|
+
*/
|
|
1591
|
+
static json(source, property, value) {
|
|
1592
|
+
if (!Is.json(value)) {
|
|
1593
|
+
throw new GuardError(source, "guard.stringJson", property, value);
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1396
1596
|
/**
|
|
1397
1597
|
* Is the property a base64 string.
|
|
1398
1598
|
* @param source The source of the error.
|
|
@@ -1417,6 +1617,18 @@ class Guards {
|
|
|
1417
1617
|
throw new GuardError(source, "guard.base64Url", property, value);
|
|
1418
1618
|
}
|
|
1419
1619
|
}
|
|
1620
|
+
/**
|
|
1621
|
+
* Is the property a base58 string.
|
|
1622
|
+
* @param source The source of the error.
|
|
1623
|
+
* @param property The name of the property.
|
|
1624
|
+
* @param value The value to test.
|
|
1625
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1626
|
+
*/
|
|
1627
|
+
static stringBase58(source, property, value) {
|
|
1628
|
+
if (!Is.stringBase58(value)) {
|
|
1629
|
+
throw new GuardError(source, "guard.base58", property, value);
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1420
1632
|
/**
|
|
1421
1633
|
* Is the property a string with a hex value.
|
|
1422
1634
|
* @param source The source of the error.
|
|
@@ -1606,6 +1818,50 @@ class Guards {
|
|
|
1606
1818
|
throw new GuardError(source, "guard.arrayOneOf", property, value, options.join(", "));
|
|
1607
1819
|
}
|
|
1608
1820
|
}
|
|
1821
|
+
/**
|
|
1822
|
+
* Does the array start with the specified data.
|
|
1823
|
+
* @param source The source of the error.
|
|
1824
|
+
* @param property The name of the property.
|
|
1825
|
+
* @param value The value to test.
|
|
1826
|
+
* @param startValues The values that must start the array.
|
|
1827
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1828
|
+
*/
|
|
1829
|
+
static arrayStartsWith(source, property, value, startValues) {
|
|
1830
|
+
if (!Is.arrayValue(value)) {
|
|
1831
|
+
throw new GuardError(source, "guard.array", property, value);
|
|
1832
|
+
}
|
|
1833
|
+
const startValuesArray = ArrayHelper.fromObjectOrArray(startValues);
|
|
1834
|
+
if (!Is.arrayValue(startValuesArray)) {
|
|
1835
|
+
throw new GuardError(source, "guard.array", property, startValuesArray);
|
|
1836
|
+
}
|
|
1837
|
+
for (let i = 0; i < startValuesArray.length; i++) {
|
|
1838
|
+
if (value[i] !== startValuesArray[i]) {
|
|
1839
|
+
throw new GuardError(source, "guard.arrayStartsWith", property, value, startValuesArray.join(", "));
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
/**
|
|
1844
|
+
* Does the array end with the specified data.
|
|
1845
|
+
* @param source The source of the error.
|
|
1846
|
+
* @param property The name of the property.
|
|
1847
|
+
* @param value The value to test.
|
|
1848
|
+
* @param endValues The values that must end the array.
|
|
1849
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1850
|
+
*/
|
|
1851
|
+
static arrayEndsWith(source, property, value, endValues) {
|
|
1852
|
+
if (!Is.arrayValue(value)) {
|
|
1853
|
+
throw new GuardError(source, "guard.array", property, value);
|
|
1854
|
+
}
|
|
1855
|
+
const endValuesArray = ArrayHelper.fromObjectOrArray(endValues);
|
|
1856
|
+
if (!Is.arrayValue(endValuesArray)) {
|
|
1857
|
+
throw new GuardError(source, "guard.array", property, endValuesArray);
|
|
1858
|
+
}
|
|
1859
|
+
for (let i = 0; i < endValuesArray.length; i++) {
|
|
1860
|
+
if (value[value.length - i - 1] !== endValuesArray[endValuesArray.length - i - 1]) {
|
|
1861
|
+
throw new GuardError(source, "guard.arrayEndsWith", property, value, endValuesArray.join(", "));
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1609
1865
|
/**
|
|
1610
1866
|
* Is the property a Uint8Array.
|
|
1611
1867
|
* @param source The source of the error.
|
|
@@ -1646,6 +1902,53 @@ class Guards {
|
|
|
1646
1902
|
}
|
|
1647
1903
|
}
|
|
1648
1904
|
|
|
1905
|
+
// Copyright 2024 IOTA Stiftung.
|
|
1906
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
1907
|
+
/**
|
|
1908
|
+
* Provide a store for shared objects which can be accesses through multiple
|
|
1909
|
+
* instance loads of a packages.
|
|
1910
|
+
*/
|
|
1911
|
+
class SharedStore {
|
|
1912
|
+
/**
|
|
1913
|
+
* Get a property from the shared store.
|
|
1914
|
+
* @param prop The name of the property to get.
|
|
1915
|
+
* @returns The property if it exists.
|
|
1916
|
+
*/
|
|
1917
|
+
static get(prop) {
|
|
1918
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1919
|
+
const shared = globalThis.__TWIN_SHARED__;
|
|
1920
|
+
if (Is.undefined(shared)) {
|
|
1921
|
+
return;
|
|
1922
|
+
}
|
|
1923
|
+
return shared[prop];
|
|
1924
|
+
}
|
|
1925
|
+
/**
|
|
1926
|
+
* Set the property in the shared store.
|
|
1927
|
+
* @param prop The name of the property to set.
|
|
1928
|
+
* @param value The value to set.
|
|
1929
|
+
*/
|
|
1930
|
+
static set(prop, value) {
|
|
1931
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1932
|
+
if (Is.undefined(globalThis.__TWIN_SHARED__)) {
|
|
1933
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1934
|
+
globalThis.__TWIN_SHARED__ = {};
|
|
1935
|
+
}
|
|
1936
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1937
|
+
globalThis.__TWIN_SHARED__[prop] = value;
|
|
1938
|
+
}
|
|
1939
|
+
/**
|
|
1940
|
+
* Remove a property from the shared store.
|
|
1941
|
+
* @param prop The name of the property to remove.
|
|
1942
|
+
*/
|
|
1943
|
+
static remove(prop) {
|
|
1944
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1945
|
+
const shared = globalThis.__TWIN_SHARED__;
|
|
1946
|
+
if (!Is.undefined(shared)) {
|
|
1947
|
+
delete shared[prop];
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1649
1952
|
/**
|
|
1650
1953
|
* Factory for creating implementation of generic types.
|
|
1651
1954
|
*/
|
|
@@ -1655,11 +1958,6 @@ class Factory {
|
|
|
1655
1958
|
* @internal
|
|
1656
1959
|
*/
|
|
1657
1960
|
static _CLASS_NAME = "Factory";
|
|
1658
|
-
/**
|
|
1659
|
-
* Store all the created factories.
|
|
1660
|
-
* @internal
|
|
1661
|
-
*/
|
|
1662
|
-
static _factories = {};
|
|
1663
1961
|
/**
|
|
1664
1962
|
* Type name for the instances.
|
|
1665
1963
|
* @internal
|
|
@@ -1713,10 +2011,41 @@ class Factory {
|
|
|
1713
2011
|
* @returns The factory instance.
|
|
1714
2012
|
*/
|
|
1715
2013
|
static createFactory(typeName, autoInstance = false, matcher) {
|
|
1716
|
-
|
|
1717
|
-
|
|
2014
|
+
const factories = Factory.getFactories();
|
|
2015
|
+
if (Is.undefined(factories[typeName])) {
|
|
2016
|
+
factories[typeName] = new Factory(typeName, autoInstance, matcher);
|
|
2017
|
+
}
|
|
2018
|
+
return factories[typeName];
|
|
2019
|
+
}
|
|
2020
|
+
/**
|
|
2021
|
+
* Get all the factories.
|
|
2022
|
+
* @returns All the factories.
|
|
2023
|
+
*/
|
|
2024
|
+
static getFactories() {
|
|
2025
|
+
let factories = SharedStore.get("factories");
|
|
2026
|
+
if (Is.undefined(factories)) {
|
|
2027
|
+
factories = {};
|
|
2028
|
+
SharedStore.set("factories", factories);
|
|
2029
|
+
}
|
|
2030
|
+
return factories;
|
|
2031
|
+
}
|
|
2032
|
+
/**
|
|
2033
|
+
* Reset all the factories, which removes any created instances, but not the registrations.
|
|
2034
|
+
*/
|
|
2035
|
+
static resetFactories() {
|
|
2036
|
+
const factories = Factory.getFactories();
|
|
2037
|
+
for (const typeName in factories) {
|
|
2038
|
+
factories[typeName].reset();
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
/**
|
|
2042
|
+
* Clear all the factories, which removes anything registered with the factories.
|
|
2043
|
+
*/
|
|
2044
|
+
static clearFactories() {
|
|
2045
|
+
const factories = Factory.getFactories();
|
|
2046
|
+
for (const typeName in factories) {
|
|
2047
|
+
factories[typeName].clear();
|
|
1718
2048
|
}
|
|
1719
|
-
return Factory._factories[typeName];
|
|
1720
2049
|
}
|
|
1721
2050
|
/**
|
|
1722
2051
|
* Register a new generator.
|
|
@@ -1789,7 +2118,7 @@ class Factory {
|
|
|
1789
2118
|
}
|
|
1790
2119
|
}
|
|
1791
2120
|
/**
|
|
1792
|
-
*
|
|
2121
|
+
* Remove all the instances and leave the generators intact.
|
|
1793
2122
|
*/
|
|
1794
2123
|
reset() {
|
|
1795
2124
|
for (const name in this._generators) {
|
|
@@ -1797,6 +2126,14 @@ class Factory {
|
|
|
1797
2126
|
}
|
|
1798
2127
|
this._instances = {};
|
|
1799
2128
|
}
|
|
2129
|
+
/**
|
|
2130
|
+
* Remove all the instances and the generators.
|
|
2131
|
+
*/
|
|
2132
|
+
clear() {
|
|
2133
|
+
this._instances = {};
|
|
2134
|
+
this._generators = {};
|
|
2135
|
+
this._orderCounter = 0;
|
|
2136
|
+
}
|
|
1800
2137
|
/**
|
|
1801
2138
|
* Get all the instances as a map.
|
|
1802
2139
|
* @returns The instances as a map.
|
|
@@ -1871,983 +2208,1360 @@ const ComponentFactory = Factory.createFactory("component");
|
|
|
1871
2208
|
|
|
1872
2209
|
// Copyright 2024 IOTA Stiftung.
|
|
1873
2210
|
// SPDX-License-Identifier: Apache-2.0.
|
|
2211
|
+
/* eslint-disable no-bitwise */
|
|
1874
2212
|
/**
|
|
1875
|
-
*
|
|
1876
|
-
*/
|
|
1877
|
-
class ArrayHelper {
|
|
1878
|
-
/**
|
|
1879
|
-
* Do the two arrays match.
|
|
1880
|
-
* @param arr1 The first array.
|
|
1881
|
-
* @param arr2 The second array.
|
|
1882
|
-
* @returns True if both arrays are empty of have the same values.
|
|
1883
|
-
*/
|
|
1884
|
-
static matches(arr1, arr2) {
|
|
1885
|
-
if (Is.empty(arr1) && Is.empty(arr2)) {
|
|
1886
|
-
return true;
|
|
1887
|
-
}
|
|
1888
|
-
if (!((Is.array(arr1) && Is.array(arr2)) || (Is.typedArray(arr1) && Is.typedArray(arr2)))) {
|
|
1889
|
-
return false;
|
|
1890
|
-
}
|
|
1891
|
-
if (arr1.length !== arr2.length) {
|
|
1892
|
-
return false;
|
|
1893
|
-
}
|
|
1894
|
-
for (let i = 0; i < arr1.length; i++) {
|
|
1895
|
-
if (arr1[i] !== arr2[i]) {
|
|
1896
|
-
return false;
|
|
1897
|
-
}
|
|
1898
|
-
}
|
|
1899
|
-
return true;
|
|
1900
|
-
}
|
|
1901
|
-
}
|
|
1902
|
-
|
|
1903
|
-
// Copyright 2024 IOTA Stiftung.
|
|
1904
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
1905
|
-
/**
|
|
1906
|
-
* Class to perform internationalization.
|
|
2213
|
+
* Convert arrays to and from different formats.
|
|
1907
2214
|
*/
|
|
1908
|
-
class
|
|
1909
|
-
/**
|
|
1910
|
-
* The default translation.
|
|
1911
|
-
*/
|
|
1912
|
-
static DEFAULT_LOCALE = "en";
|
|
2215
|
+
class Converter {
|
|
1913
2216
|
/**
|
|
1914
|
-
*
|
|
2217
|
+
* Lookup table for encoding.
|
|
1915
2218
|
* @internal
|
|
1916
2219
|
*/
|
|
1917
|
-
static
|
|
2220
|
+
static _ENCODE_LOOKUP;
|
|
1918
2221
|
/**
|
|
1919
|
-
*
|
|
2222
|
+
* Lookup table for decoding.
|
|
1920
2223
|
* @internal
|
|
1921
2224
|
*/
|
|
1922
|
-
static
|
|
2225
|
+
static _DECODE_LOOKUP;
|
|
1923
2226
|
/**
|
|
1924
|
-
*
|
|
1925
|
-
* @
|
|
2227
|
+
* Encode a raw array to UTF8 string.
|
|
2228
|
+
* @param array The bytes to encode.
|
|
2229
|
+
* @param startIndex The index to start in the bytes.
|
|
2230
|
+
* @param length The length of bytes to read.
|
|
2231
|
+
* @returns The array formatted as UTF8.
|
|
1926
2232
|
*/
|
|
1927
|
-
static
|
|
2233
|
+
static bytesToUtf8(array, startIndex, length) {
|
|
2234
|
+
const start = startIndex ?? 0;
|
|
2235
|
+
const len = length ?? array.length;
|
|
2236
|
+
let str = "";
|
|
2237
|
+
for (let i = start; i < start + len; i++) {
|
|
2238
|
+
const value = array[i];
|
|
2239
|
+
if (value < 0x80) {
|
|
2240
|
+
str += String.fromCharCode(value);
|
|
2241
|
+
}
|
|
2242
|
+
else if (value > 0xbf && value < 0xe0) {
|
|
2243
|
+
str += String.fromCharCode(((value & 0x1f) << 6) | (array[i + 1] & 0x3f));
|
|
2244
|
+
i += 1;
|
|
2245
|
+
}
|
|
2246
|
+
else if (value > 0xdf && value < 0xf0) {
|
|
2247
|
+
str += String.fromCharCode(((value & 0x0f) << 12) | ((array[i + 1] & 0x3f) << 6) | (array[i + 2] & 0x3f));
|
|
2248
|
+
i += 2;
|
|
2249
|
+
}
|
|
2250
|
+
else {
|
|
2251
|
+
// surrogate pair
|
|
2252
|
+
const charCode = (((value & 0x07) << 18) |
|
|
2253
|
+
((array[i + 1] & 0x3f) << 12) |
|
|
2254
|
+
((array[i + 2] & 0x3f) << 6) |
|
|
2255
|
+
(array[i + 3] & 0x3f)) -
|
|
2256
|
+
0x010000;
|
|
2257
|
+
str += String.fromCharCode((charCode >> 10) | 0xd800, (charCode & 0x03ff) | 0xdc00);
|
|
2258
|
+
i += 3;
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
return str;
|
|
2262
|
+
}
|
|
1928
2263
|
/**
|
|
1929
|
-
*
|
|
1930
|
-
* @
|
|
2264
|
+
* Convert a UTF8 string to raw array.
|
|
2265
|
+
* @param utf8 The text to decode.
|
|
2266
|
+
* @returns The array.
|
|
1931
2267
|
*/
|
|
1932
|
-
static
|
|
2268
|
+
static utf8ToBytes(utf8) {
|
|
2269
|
+
const bytes = [];
|
|
2270
|
+
for (let i = 0; i < utf8.length; i++) {
|
|
2271
|
+
let charCode = utf8.charCodeAt(i);
|
|
2272
|
+
if (charCode < 0x80) {
|
|
2273
|
+
bytes.push(charCode);
|
|
2274
|
+
}
|
|
2275
|
+
else if (charCode < 0x800) {
|
|
2276
|
+
bytes.push(0xc0 | (charCode >> 6), 0x80 | (charCode & 0x3f));
|
|
2277
|
+
}
|
|
2278
|
+
else if (charCode < 0xd800 || charCode >= 0xe000) {
|
|
2279
|
+
bytes.push(0xe0 | (charCode >> 12), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f));
|
|
2280
|
+
}
|
|
2281
|
+
else {
|
|
2282
|
+
// surrogate pair
|
|
2283
|
+
i++;
|
|
2284
|
+
// UTF-16 encodes 0x10000-0x10FFFF by
|
|
2285
|
+
// subtracting 0x10000 and splitting the
|
|
2286
|
+
// 20 bits of 0x0-0xFFFFF into two halves
|
|
2287
|
+
charCode = 0x10000 + (((charCode & 0x3ff) << 10) | (utf8.charCodeAt(i) & 0x3ff));
|
|
2288
|
+
bytes.push(0xf0 | (charCode >> 18), 0x80 | ((charCode >> 12) & 0x3f), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f));
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
return Uint8Array.from(bytes);
|
|
2292
|
+
}
|
|
1933
2293
|
/**
|
|
1934
|
-
*
|
|
1935
|
-
* @param
|
|
2294
|
+
* Encode a raw array to hex string.
|
|
2295
|
+
* @param array The bytes to encode.
|
|
2296
|
+
* @param includePrefix Include the 0x prefix on the returned hex.
|
|
2297
|
+
* @param startIndex The index to start in the bytes.
|
|
2298
|
+
* @param length The length of bytes to read.
|
|
2299
|
+
* @param reverse Reverse the combine direction.
|
|
2300
|
+
* @returns The array formatted as hex.
|
|
1936
2301
|
*/
|
|
1937
|
-
static
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
2302
|
+
static bytesToHex(array, includePrefix = false, startIndex, length, reverse) {
|
|
2303
|
+
let hex = "";
|
|
2304
|
+
this.buildHexLookups();
|
|
2305
|
+
if (Converter._ENCODE_LOOKUP) {
|
|
2306
|
+
const len = length ?? array.length;
|
|
2307
|
+
const start = startIndex ?? 0;
|
|
2308
|
+
if (reverse) {
|
|
2309
|
+
for (let i = 0; i < len; i++) {
|
|
2310
|
+
hex = Converter._ENCODE_LOOKUP[array[start + i]] + hex;
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
else {
|
|
2314
|
+
for (let i = 0; i < len; i++) {
|
|
2315
|
+
hex += Converter._ENCODE_LOOKUP[array[start + i]];
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
1941
2318
|
}
|
|
2319
|
+
return includePrefix ? HexHelper.addPrefix(hex) : hex;
|
|
1942
2320
|
}
|
|
1943
2321
|
/**
|
|
1944
|
-
*
|
|
1945
|
-
* @
|
|
2322
|
+
* Decode a hex string to raw array.
|
|
2323
|
+
* @param hex The hex to decode.
|
|
2324
|
+
* @param reverse Store the characters in reverse.
|
|
2325
|
+
* @returns The array.
|
|
1946
2326
|
*/
|
|
1947
|
-
static
|
|
1948
|
-
|
|
2327
|
+
static hexToBytes(hex, reverse) {
|
|
2328
|
+
const strippedHex = HexHelper.stripPrefix(hex);
|
|
2329
|
+
const sizeof = strippedHex.length >> 1;
|
|
2330
|
+
const length = sizeof << 1;
|
|
2331
|
+
const array = new Uint8Array(sizeof);
|
|
2332
|
+
this.buildHexLookups();
|
|
2333
|
+
if (Converter._DECODE_LOOKUP) {
|
|
2334
|
+
let i = 0;
|
|
2335
|
+
let n = 0;
|
|
2336
|
+
while (i < length) {
|
|
2337
|
+
array[n++] =
|
|
2338
|
+
(Converter._DECODE_LOOKUP[strippedHex.charCodeAt(i++)] << 4) |
|
|
2339
|
+
Converter._DECODE_LOOKUP[strippedHex.charCodeAt(i++)];
|
|
2340
|
+
}
|
|
2341
|
+
if (reverse) {
|
|
2342
|
+
array.reverse();
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
return array;
|
|
1949
2346
|
}
|
|
1950
2347
|
/**
|
|
1951
|
-
*
|
|
1952
|
-
* @param
|
|
1953
|
-
* @param
|
|
2348
|
+
* Convert the UTF8 to hex.
|
|
2349
|
+
* @param utf8 The text to convert.
|
|
2350
|
+
* @param includePrefix Include the 0x prefix on the returned hex.
|
|
2351
|
+
* @returns The hex version of the bytes.
|
|
1954
2352
|
*/
|
|
1955
|
-
static
|
|
1956
|
-
const
|
|
1957
|
-
|
|
1958
|
-
I18n._localeDictionaries[locale] = mergedKeys;
|
|
1959
|
-
for (const callback in I18n._dictionaryChangedHandlers) {
|
|
1960
|
-
I18n._dictionaryChangedHandlers[callback](I18n._currentLocale);
|
|
1961
|
-
}
|
|
2353
|
+
static utf8ToHex(utf8, includePrefix = false) {
|
|
2354
|
+
const hex = Converter.bytesToHex(Converter.utf8ToBytes(utf8));
|
|
2355
|
+
return includePrefix ? HexHelper.addPrefix(hex) : hex;
|
|
1962
2356
|
}
|
|
1963
2357
|
/**
|
|
1964
|
-
*
|
|
1965
|
-
* @param
|
|
1966
|
-
* @returns The
|
|
2358
|
+
* Convert the hex text to text.
|
|
2359
|
+
* @param hex The hex to convert.
|
|
2360
|
+
* @returns The UTF8 version of the bytes.
|
|
1967
2361
|
*/
|
|
1968
|
-
static
|
|
1969
|
-
return
|
|
2362
|
+
static hexToUtf8(hex) {
|
|
2363
|
+
return Converter.bytesToUtf8(Converter.hexToBytes(HexHelper.stripPrefix(hex)));
|
|
1970
2364
|
}
|
|
1971
2365
|
/**
|
|
1972
|
-
*
|
|
1973
|
-
* @
|
|
2366
|
+
* Convert bytes to binary string.
|
|
2367
|
+
* @param bytes The bytes to convert.
|
|
2368
|
+
* @returns A binary string of the bytes.
|
|
1974
2369
|
*/
|
|
1975
|
-
static
|
|
1976
|
-
|
|
2370
|
+
static bytesToBinary(bytes) {
|
|
2371
|
+
const b = [];
|
|
2372
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2373
|
+
b.push(bytes[i].toString(2).padStart(8, "0"));
|
|
2374
|
+
}
|
|
2375
|
+
return b.join("");
|
|
1977
2376
|
}
|
|
1978
2377
|
/**
|
|
1979
|
-
*
|
|
1980
|
-
* @param
|
|
1981
|
-
* @
|
|
2378
|
+
* Convert a binary string to bytes.
|
|
2379
|
+
* @param binary The binary string.
|
|
2380
|
+
* @returns The bytes.
|
|
1982
2381
|
*/
|
|
1983
|
-
static
|
|
1984
|
-
|
|
2382
|
+
static binaryToBytes(binary) {
|
|
2383
|
+
const bytes = new Uint8Array(Math.ceil(binary.length / 8));
|
|
2384
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2385
|
+
bytes[i] = Number.parseInt(binary.slice(i * 8, (i + 1) * 8), 2);
|
|
2386
|
+
}
|
|
2387
|
+
return bytes;
|
|
1985
2388
|
}
|
|
1986
2389
|
/**
|
|
1987
|
-
*
|
|
1988
|
-
* @param
|
|
2390
|
+
* Convert bytes to base64 string.
|
|
2391
|
+
* @param bytes The bytes to convert.
|
|
2392
|
+
* @returns A base64 string of the bytes.
|
|
1989
2393
|
*/
|
|
1990
|
-
static
|
|
1991
|
-
|
|
2394
|
+
static bytesToBase64(bytes) {
|
|
2395
|
+
return Base64.encode(bytes);
|
|
1992
2396
|
}
|
|
1993
2397
|
/**
|
|
1994
|
-
*
|
|
1995
|
-
* @param
|
|
1996
|
-
* @
|
|
2398
|
+
* Convert a base64 string to bytes.
|
|
2399
|
+
* @param base64 The base64 string.
|
|
2400
|
+
* @returns The bytes.
|
|
1997
2401
|
*/
|
|
1998
|
-
static
|
|
1999
|
-
|
|
2402
|
+
static base64ToBytes(base64) {
|
|
2403
|
+
return Base64.decode(base64);
|
|
2000
2404
|
}
|
|
2001
2405
|
/**
|
|
2002
|
-
*
|
|
2003
|
-
* @param
|
|
2406
|
+
* Convert bytes to base64 url string.
|
|
2407
|
+
* @param bytes The bytes to convert.
|
|
2408
|
+
* @returns A base64 url string of the bytes.
|
|
2004
2409
|
*/
|
|
2005
|
-
static
|
|
2006
|
-
|
|
2410
|
+
static bytesToBase64Url(bytes) {
|
|
2411
|
+
return Base64Url.encode(bytes);
|
|
2007
2412
|
}
|
|
2008
2413
|
/**
|
|
2009
|
-
*
|
|
2010
|
-
* @param
|
|
2011
|
-
* @
|
|
2012
|
-
* @param overrideLocale Override the locale.
|
|
2013
|
-
* @returns The formatted string.
|
|
2414
|
+
* Convert a base64 url string to bytes.
|
|
2415
|
+
* @param base64Url The base64 url string.
|
|
2416
|
+
* @returns The bytes.
|
|
2014
2417
|
*/
|
|
2015
|
-
static
|
|
2016
|
-
|
|
2017
|
-
if (cl.startsWith("debug-")) {
|
|
2018
|
-
cl = I18n.DEFAULT_LOCALE;
|
|
2019
|
-
}
|
|
2020
|
-
if (!I18n._localeDictionaries[cl]) {
|
|
2021
|
-
return `!!Missing ${cl}`;
|
|
2022
|
-
}
|
|
2023
|
-
if (!I18n._localeDictionaries[cl][key]) {
|
|
2024
|
-
return `!!Missing ${cl}.${key}`;
|
|
2025
|
-
}
|
|
2026
|
-
if (I18n._currentLocale === "debug-k") {
|
|
2027
|
-
return key;
|
|
2028
|
-
}
|
|
2029
|
-
let ret = new IntlMessageFormat(I18n._localeDictionaries[cl][key], cl).format(values);
|
|
2030
|
-
if (I18n._currentLocale === "debug-x") {
|
|
2031
|
-
ret = ret.replace(/[a-z]/g, "x").replace(/[A-Z]/g, "x").replace(/\d/g, "n");
|
|
2032
|
-
}
|
|
2033
|
-
return ret;
|
|
2418
|
+
static base64UrlToBytes(base64Url) {
|
|
2419
|
+
return Base64Url.decode(base64Url);
|
|
2034
2420
|
}
|
|
2035
2421
|
/**
|
|
2036
|
-
*
|
|
2037
|
-
* @param
|
|
2038
|
-
* @returns
|
|
2422
|
+
* Convert bytes to base58 string.
|
|
2423
|
+
* @param bytes The bytes to convert.
|
|
2424
|
+
* @returns A base58 string of the bytes.
|
|
2039
2425
|
*/
|
|
2040
|
-
static
|
|
2041
|
-
return
|
|
2426
|
+
static bytesToBase58(bytes) {
|
|
2427
|
+
return Base58.encode(bytes);
|
|
2042
2428
|
}
|
|
2043
2429
|
/**
|
|
2044
|
-
*
|
|
2045
|
-
* @param
|
|
2046
|
-
* @
|
|
2047
|
-
|
|
2430
|
+
* Convert a base58 string to bytes.
|
|
2431
|
+
* @param base58 The base58 string.
|
|
2432
|
+
* @returns The bytes.
|
|
2433
|
+
*/
|
|
2434
|
+
static base58ToBytes(base58) {
|
|
2435
|
+
return Base58.decode(base58);
|
|
2436
|
+
}
|
|
2437
|
+
/**
|
|
2438
|
+
* Build the static lookup tables.
|
|
2048
2439
|
* @internal
|
|
2049
2440
|
*/
|
|
2050
|
-
static
|
|
2051
|
-
|
|
2052
|
-
const
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2441
|
+
static buildHexLookups() {
|
|
2442
|
+
if (!Converter._ENCODE_LOOKUP || !Converter._DECODE_LOOKUP) {
|
|
2443
|
+
const alphabet = "0123456789abcdef";
|
|
2444
|
+
Converter._ENCODE_LOOKUP = [];
|
|
2445
|
+
Converter._DECODE_LOOKUP = [];
|
|
2446
|
+
for (let i = 0; i < 256; i++) {
|
|
2447
|
+
Converter._ENCODE_LOOKUP[i] = alphabet[(i >> 4) & 0xf] + alphabet[i & 0xf];
|
|
2448
|
+
if (i < 16) {
|
|
2449
|
+
if (i < 10) {
|
|
2450
|
+
Converter._DECODE_LOOKUP[0x30 + i] = i;
|
|
2451
|
+
}
|
|
2452
|
+
else {
|
|
2453
|
+
Converter._DECODE_LOOKUP[0x61 - 10 + i] = i;
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2059
2456
|
}
|
|
2060
2457
|
}
|
|
2061
2458
|
}
|
|
2062
2459
|
}
|
|
2063
2460
|
|
|
2064
|
-
// Copyright 2024 IOTA Stiftung.
|
|
2065
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
2066
2461
|
/**
|
|
2067
|
-
*
|
|
2462
|
+
* Helpers methods for JSON objects.
|
|
2068
2463
|
*/
|
|
2069
|
-
class
|
|
2464
|
+
class JsonHelper {
|
|
2070
2465
|
/**
|
|
2071
|
-
*
|
|
2072
|
-
* @
|
|
2073
|
-
* @returns The error formatted including any inner errors.
|
|
2466
|
+
* Runtime name for the class.
|
|
2467
|
+
* @internal
|
|
2074
2468
|
*/
|
|
2075
|
-
static
|
|
2076
|
-
return ErrorHelper.localizeErrors(error).map(e => e.message);
|
|
2077
|
-
}
|
|
2469
|
+
static _CLASS_NAME = "JsonHelper";
|
|
2078
2470
|
/**
|
|
2079
|
-
*
|
|
2080
|
-
*
|
|
2081
|
-
* @
|
|
2471
|
+
* Serializes in canonical format.
|
|
2472
|
+
* Based on https://www.rfc-editor.org/rfc/rfc8785.
|
|
2473
|
+
* @param object The object to be serialized.
|
|
2474
|
+
* @returns The serialized object.
|
|
2082
2475
|
*/
|
|
2083
|
-
static
|
|
2084
|
-
const
|
|
2085
|
-
if (
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
? I18n.formatMessage(errorMessageKey, err.properties)
|
|
2098
|
-
: err.message
|
|
2099
|
-
};
|
|
2100
|
-
if (Is.stringValue(err.source)) {
|
|
2101
|
-
localizedError.source = err.source;
|
|
2102
|
-
}
|
|
2103
|
-
if (Is.stringValue(err.stack)) {
|
|
2104
|
-
// Remove the first line from the stack traces as they
|
|
2105
|
-
// just have the error type and message duplicated
|
|
2106
|
-
const lines = err.stack.split("\n");
|
|
2107
|
-
lines.shift();
|
|
2108
|
-
localizedError.stack = lines.join("\n");
|
|
2476
|
+
static canonicalize(object) {
|
|
2477
|
+
const buffer = [];
|
|
2478
|
+
if (object === null ||
|
|
2479
|
+
typeof object !== "object" ||
|
|
2480
|
+
("toJSON" in object && object.toJSON instanceof Function)) {
|
|
2481
|
+
// Primitive data type
|
|
2482
|
+
buffer.push(JSON.stringify(object));
|
|
2483
|
+
}
|
|
2484
|
+
else if (Array.isArray(object)) {
|
|
2485
|
+
// Array maintain element order
|
|
2486
|
+
const parts = [];
|
|
2487
|
+
for (const element of object) {
|
|
2488
|
+
if (element === undefined) {
|
|
2489
|
+
parts.push("null");
|
|
2109
2490
|
}
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
localizedError.additional = additional;
|
|
2491
|
+
else {
|
|
2492
|
+
parts.push(JsonHelper.canonicalize(element));
|
|
2113
2493
|
}
|
|
2114
|
-
formattedErrors.push(localizedError);
|
|
2115
2494
|
}
|
|
2495
|
+
buffer.push(`[${parts.join(",")}]`);
|
|
2116
2496
|
}
|
|
2117
|
-
|
|
2497
|
+
else {
|
|
2498
|
+
// Object sort properties
|
|
2499
|
+
const props = [];
|
|
2500
|
+
const keys = Object.keys(object).sort();
|
|
2501
|
+
const o = object;
|
|
2502
|
+
for (const key of keys) {
|
|
2503
|
+
if (o[key] !== undefined) {
|
|
2504
|
+
props.push(`${JSON.stringify(key)}:${JsonHelper.canonicalize(o[key])}`);
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
buffer.push(`{${props.join(",")}}`);
|
|
2508
|
+
}
|
|
2509
|
+
return buffer.join("");
|
|
2118
2510
|
}
|
|
2119
2511
|
/**
|
|
2120
|
-
*
|
|
2121
|
-
*
|
|
2122
|
-
* @
|
|
2512
|
+
* Creates a RFC 6902 diff set.
|
|
2513
|
+
* Based on https://www.rfc-editor.org/rfc/rfc6902.
|
|
2514
|
+
* @param object1 The first object.
|
|
2515
|
+
* @param object2 The second object.
|
|
2516
|
+
* @returns The list of patches.
|
|
2123
2517
|
*/
|
|
2124
|
-
static
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2518
|
+
static diff(object1, object2) {
|
|
2519
|
+
const operations = createPatch(object1, object2);
|
|
2520
|
+
return operations;
|
|
2521
|
+
}
|
|
2522
|
+
/**
|
|
2523
|
+
* Applies a RFC 6902 diff set to an object.
|
|
2524
|
+
* Based on https://www.rfc-editor.org/rfc/rfc6902.
|
|
2525
|
+
* @param object The object to patch.
|
|
2526
|
+
* @param patches The second object.
|
|
2527
|
+
* @returns The updated object.
|
|
2528
|
+
* @throws GeneralError if the patch fails.
|
|
2529
|
+
*/
|
|
2530
|
+
static patch(object, patches) {
|
|
2531
|
+
const clone = ObjectHelper.clone(object);
|
|
2532
|
+
const result = applyPatch(clone, patches);
|
|
2533
|
+
for (let i = 0; i < result.length; i++) {
|
|
2534
|
+
if (!Is.empty(result[i])) {
|
|
2535
|
+
throw new GeneralError(JsonHelper._CLASS_NAME, "failedPatch", { index: i }, result[i]);
|
|
2536
|
+
}
|
|
2537
|
+
}
|
|
2538
|
+
return clone;
|
|
2539
|
+
}
|
|
2540
|
+
/**
|
|
2541
|
+
* Stringify the JSON with support for extended data types date/bigint/uint8array.
|
|
2542
|
+
* @param object The object to stringify.
|
|
2543
|
+
* @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
|
|
2544
|
+
* @returns The stringified object.
|
|
2545
|
+
*/
|
|
2546
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2547
|
+
static stringifyEx(object, space) {
|
|
2548
|
+
// We want to keep the 'this' intact for the replacer
|
|
2549
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
2550
|
+
return JSON.stringify(object, JsonHelper.stringifyExReplacer, space);
|
|
2551
|
+
}
|
|
2552
|
+
/**
|
|
2553
|
+
* Parse the JSON string with support for extended data types date/bigint/uint8array.
|
|
2554
|
+
* @param json The object to pause.
|
|
2555
|
+
* @returns The object.
|
|
2556
|
+
*/
|
|
2557
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2558
|
+
static parseEx(json) {
|
|
2559
|
+
// We want to keep the 'this' intact for the reviver
|
|
2560
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
2561
|
+
return JSON.parse(json, JsonHelper.parseExReviver);
|
|
2562
|
+
}
|
|
2563
|
+
/**
|
|
2564
|
+
* Replacer function to handle extended data types.
|
|
2565
|
+
* @param this The object.
|
|
2566
|
+
* @param key The key.
|
|
2567
|
+
* @param value The value.
|
|
2568
|
+
* @returns The value.
|
|
2569
|
+
*/
|
|
2570
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2571
|
+
static stringifyExReplacer(key, value) {
|
|
2572
|
+
const rawValue = this[key];
|
|
2573
|
+
if (Is.bigint(rawValue)) {
|
|
2574
|
+
return {
|
|
2575
|
+
"@ext": "bigint",
|
|
2576
|
+
value: rawValue.toString()
|
|
2577
|
+
};
|
|
2578
|
+
}
|
|
2579
|
+
else if (Is.date(rawValue)) {
|
|
2580
|
+
return {
|
|
2581
|
+
"@ext": "date",
|
|
2582
|
+
value: rawValue.getTime()
|
|
2583
|
+
};
|
|
2584
|
+
}
|
|
2585
|
+
else if (Is.uint8Array(rawValue)) {
|
|
2586
|
+
return {
|
|
2587
|
+
"@ext": "uint8array",
|
|
2588
|
+
value: Converter.bytesToBase64(rawValue)
|
|
2589
|
+
};
|
|
2590
|
+
}
|
|
2591
|
+
return value;
|
|
2592
|
+
}
|
|
2593
|
+
/**
|
|
2594
|
+
* Reviver function to handle extended data types.
|
|
2595
|
+
* @param this The object.
|
|
2596
|
+
* @param key The key.
|
|
2597
|
+
* @param value The value.
|
|
2598
|
+
* @returns The value.
|
|
2599
|
+
*/
|
|
2600
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2601
|
+
static parseExReviver(key, value) {
|
|
2602
|
+
if (Is.object(value)) {
|
|
2603
|
+
if (value["@ext"] === "bigint") {
|
|
2604
|
+
return BigInt(value.value);
|
|
2605
|
+
}
|
|
2606
|
+
else if (value["@ext"] === "date") {
|
|
2607
|
+
return new Date(value.value);
|
|
2608
|
+
}
|
|
2609
|
+
else if (value["@ext"] === "uint8array") {
|
|
2610
|
+
return Converter.base64ToBytes(value.value);
|
|
2141
2611
|
}
|
|
2142
|
-
return validationErrors.join("\n");
|
|
2143
2612
|
}
|
|
2613
|
+
return value;
|
|
2144
2614
|
}
|
|
2145
2615
|
}
|
|
2146
2616
|
|
|
2147
|
-
// Copyright 2024 IOTA Stiftung.
|
|
2148
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
2149
2617
|
/**
|
|
2150
|
-
*
|
|
2618
|
+
* Class to help with objects.
|
|
2151
2619
|
*/
|
|
2152
|
-
class
|
|
2620
|
+
class ObjectHelper {
|
|
2153
2621
|
/**
|
|
2154
|
-
*
|
|
2155
|
-
* @
|
|
2156
|
-
* @throws TypeError If the value can not be coerced.
|
|
2157
|
-
* @returns The value if it can be coerced.
|
|
2622
|
+
* Runtime name for the class.
|
|
2623
|
+
* @internal
|
|
2158
2624
|
*/
|
|
2159
|
-
static
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
if (Is.boolean(value)) {
|
|
2170
|
-
return value ? "true" : "false";
|
|
2171
|
-
}
|
|
2172
|
-
if (Is.date(value)) {
|
|
2173
|
-
return value.toISOString();
|
|
2625
|
+
static _CLASS_NAME = "ObjectHelper";
|
|
2626
|
+
/**
|
|
2627
|
+
* Convert an object to bytes.
|
|
2628
|
+
* @param obj The object to convert.
|
|
2629
|
+
* @param format Format the JSON content.
|
|
2630
|
+
* @returns The object as bytes.
|
|
2631
|
+
*/
|
|
2632
|
+
static toBytes(obj, format = false) {
|
|
2633
|
+
if (obj === undefined) {
|
|
2634
|
+
return new Uint8Array();
|
|
2174
2635
|
}
|
|
2636
|
+
const json = format ? JSON.stringify(obj, undefined, "\t") : JSON.stringify(obj);
|
|
2637
|
+
return Converter.utf8ToBytes(json);
|
|
2175
2638
|
}
|
|
2176
2639
|
/**
|
|
2177
|
-
*
|
|
2178
|
-
* @param
|
|
2179
|
-
* @
|
|
2180
|
-
* @
|
|
2640
|
+
* Convert a bytes to an object.
|
|
2641
|
+
* @param bytes The bytes to convert to an object.
|
|
2642
|
+
* @returns The object.
|
|
2643
|
+
* @throws GeneralError if there was an error parsing the JSON.
|
|
2181
2644
|
*/
|
|
2182
|
-
static
|
|
2183
|
-
if (Is.
|
|
2184
|
-
return
|
|
2185
|
-
}
|
|
2186
|
-
if (Is.number(value)) {
|
|
2187
|
-
return value;
|
|
2188
|
-
}
|
|
2189
|
-
if (Is.string(value)) {
|
|
2190
|
-
const parsed = Number.parseFloat(value);
|
|
2191
|
-
if (Is.number(parsed)) {
|
|
2192
|
-
return parsed;
|
|
2193
|
-
}
|
|
2645
|
+
static fromBytes(bytes) {
|
|
2646
|
+
if (Is.empty(bytes) || bytes.length === 0) {
|
|
2647
|
+
return undefined;
|
|
2194
2648
|
}
|
|
2195
|
-
|
|
2196
|
-
|
|
2649
|
+
try {
|
|
2650
|
+
const utf8 = Converter.bytesToUtf8(bytes);
|
|
2651
|
+
return JSON.parse(utf8);
|
|
2197
2652
|
}
|
|
2198
|
-
|
|
2199
|
-
|
|
2653
|
+
catch (err) {
|
|
2654
|
+
throw new GeneralError(ObjectHelper._CLASS_NAME, "failedBytesToJSON", undefined, err);
|
|
2200
2655
|
}
|
|
2201
2656
|
}
|
|
2202
2657
|
/**
|
|
2203
|
-
*
|
|
2204
|
-
* @param
|
|
2205
|
-
* @
|
|
2206
|
-
* @returns The value if it can be coerced.
|
|
2658
|
+
* Make a deep clone of an object.
|
|
2659
|
+
* @param obj The object to clone.
|
|
2660
|
+
* @returns The objects clone.
|
|
2207
2661
|
*/
|
|
2208
|
-
static
|
|
2209
|
-
if (Is.undefined(
|
|
2210
|
-
return
|
|
2662
|
+
static clone(obj) {
|
|
2663
|
+
if (Is.undefined(obj)) {
|
|
2664
|
+
return undefined;
|
|
2211
2665
|
}
|
|
2212
|
-
|
|
2213
|
-
|
|
2666
|
+
return structuredClone(obj);
|
|
2667
|
+
}
|
|
2668
|
+
/**
|
|
2669
|
+
* Deep merge objects.
|
|
2670
|
+
* @param obj1 The first object to merge.
|
|
2671
|
+
* @param obj2 The second object to merge.
|
|
2672
|
+
* @returns The combined deep merge of the objects.
|
|
2673
|
+
*/
|
|
2674
|
+
static merge(obj1, obj2) {
|
|
2675
|
+
if (Is.empty(obj1)) {
|
|
2676
|
+
return ObjectHelper.clone(obj2);
|
|
2214
2677
|
}
|
|
2215
|
-
if (Is.
|
|
2216
|
-
return
|
|
2678
|
+
if (Is.empty(obj2)) {
|
|
2679
|
+
return ObjectHelper.clone(obj1);
|
|
2217
2680
|
}
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2681
|
+
const obj1Clone = ObjectHelper.clone(obj1);
|
|
2682
|
+
if (Is.object(obj1Clone) && Is.object(obj2)) {
|
|
2683
|
+
const keys = Object.keys(obj2);
|
|
2684
|
+
for (const key of keys) {
|
|
2685
|
+
if (Is.object(obj1Clone[key]) && Is.object(obj2[key])) {
|
|
2686
|
+
ObjectHelper.propertySet(obj1Clone, key, ObjectHelper.merge(obj1Clone[key], obj2[key]));
|
|
2687
|
+
}
|
|
2688
|
+
else {
|
|
2689
|
+
ObjectHelper.propertySet(obj1Clone, key, obj2[key]);
|
|
2690
|
+
}
|
|
2222
2691
|
}
|
|
2223
2692
|
}
|
|
2224
|
-
|
|
2225
|
-
return value ? 1n : 0n;
|
|
2226
|
-
}
|
|
2693
|
+
return obj1Clone;
|
|
2227
2694
|
}
|
|
2228
2695
|
/**
|
|
2229
|
-
*
|
|
2230
|
-
* @param
|
|
2231
|
-
* @
|
|
2232
|
-
* @
|
|
2696
|
+
* Does one object equal another.
|
|
2697
|
+
* @param obj1 The first object to compare.
|
|
2698
|
+
* @param obj2 The second object to compare.
|
|
2699
|
+
* @param strictPropertyOrder Should the properties be in the same order, defaults to true.
|
|
2700
|
+
* @returns True is the objects are equal.
|
|
2233
2701
|
*/
|
|
2234
|
-
static
|
|
2235
|
-
if (
|
|
2236
|
-
return
|
|
2237
|
-
}
|
|
2238
|
-
if (Is.boolean(value)) {
|
|
2239
|
-
return value;
|
|
2240
|
-
}
|
|
2241
|
-
if (Is.number(value)) {
|
|
2242
|
-
// eslint-disable-next-line no-unneeded-ternary
|
|
2243
|
-
return value ? true : false;
|
|
2702
|
+
static equal(obj1, obj2, strictPropertyOrder) {
|
|
2703
|
+
if (strictPropertyOrder ?? true) {
|
|
2704
|
+
return JSON.stringify(obj1) === JSON.stringify(obj2);
|
|
2244
2705
|
}
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2706
|
+
return JsonHelper.canonicalize(obj1) === JsonHelper.canonicalize(obj2);
|
|
2707
|
+
}
|
|
2708
|
+
/**
|
|
2709
|
+
* Get the property of an unknown object.
|
|
2710
|
+
* @param obj The object to get the property from.
|
|
2711
|
+
* @param property The property to get, can be separated by dots for nested path.
|
|
2712
|
+
* @returns The property.
|
|
2713
|
+
*/
|
|
2714
|
+
static propertyGet(obj, property) {
|
|
2715
|
+
const pathParts = property.split(".");
|
|
2716
|
+
let pathValue = obj;
|
|
2717
|
+
for (const pathPart of pathParts) {
|
|
2718
|
+
// Is the path part numeric i.e. an array index.
|
|
2719
|
+
const arrayMatch = /^(\d+)$/.exec(pathPart);
|
|
2720
|
+
if (arrayMatch) {
|
|
2721
|
+
const arrayIndex = Number.parseInt(arrayMatch[1], 10);
|
|
2722
|
+
if (Is.arrayValue(pathValue) && arrayIndex < pathValue.length) {
|
|
2723
|
+
// There is no prop name so this is a direct array index on the current object
|
|
2724
|
+
pathValue = pathValue[arrayIndex];
|
|
2725
|
+
}
|
|
2726
|
+
else {
|
|
2727
|
+
// Array index for non array object so return
|
|
2728
|
+
return undefined;
|
|
2729
|
+
}
|
|
2248
2730
|
}
|
|
2249
|
-
if (
|
|
2250
|
-
|
|
2731
|
+
else if (Is.object(pathValue)) {
|
|
2732
|
+
// No array part in path so assume object sub property
|
|
2733
|
+
pathValue = pathValue[pathPart];
|
|
2734
|
+
}
|
|
2735
|
+
else {
|
|
2736
|
+
return undefined;
|
|
2251
2737
|
}
|
|
2252
2738
|
}
|
|
2739
|
+
return pathValue;
|
|
2253
2740
|
}
|
|
2254
2741
|
/**
|
|
2255
|
-
*
|
|
2256
|
-
* @param
|
|
2257
|
-
* @
|
|
2258
|
-
* @
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2742
|
+
* Set the property of an unknown object.
|
|
2743
|
+
* @param obj The object to set the property from.
|
|
2744
|
+
* @param property The property to set.
|
|
2745
|
+
* @param value The value to set.
|
|
2746
|
+
* @throws GeneralError if the property target is not an object.
|
|
2747
|
+
*/
|
|
2748
|
+
static propertySet(obj, property, value) {
|
|
2749
|
+
const pathParts = property.split(".");
|
|
2750
|
+
let pathValue = obj;
|
|
2751
|
+
let parentObj;
|
|
2752
|
+
for (let i = 0; i < pathParts.length; i++) {
|
|
2753
|
+
const pathPart = pathParts[i];
|
|
2754
|
+
// Is the path part numeric i.e. an array index.
|
|
2755
|
+
const arrayMatch = /^(\d+)$/.exec(pathPart);
|
|
2756
|
+
const arrayIndex = arrayMatch ? Number.parseInt(arrayMatch[1], 10) : -1;
|
|
2757
|
+
if (i === pathParts.length - 1) {
|
|
2758
|
+
// Last part of path so set the value
|
|
2759
|
+
if (arrayIndex >= 0) {
|
|
2760
|
+
if (Is.array(pathValue)) {
|
|
2761
|
+
pathValue[arrayIndex] = value;
|
|
2762
|
+
}
|
|
2763
|
+
else if (Is.object(pathValue)) {
|
|
2764
|
+
pathValue[arrayIndex] = value;
|
|
2765
|
+
}
|
|
2766
|
+
else {
|
|
2767
|
+
throw new GeneralError(ObjectHelper._CLASS_NAME, "cannotSetArrayIndex", {
|
|
2768
|
+
property,
|
|
2769
|
+
index: arrayIndex
|
|
2770
|
+
});
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2773
|
+
else if (Is.object(pathValue)) {
|
|
2774
|
+
pathValue[pathPart] = value;
|
|
2775
|
+
}
|
|
2776
|
+
else {
|
|
2777
|
+
throw new GeneralError(ObjectHelper._CLASS_NAME, "cannotSetProperty", { property });
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
else {
|
|
2781
|
+
parentObj = pathValue;
|
|
2782
|
+
if (Is.object(pathValue)) {
|
|
2783
|
+
pathValue = pathValue[pathPart];
|
|
2784
|
+
}
|
|
2785
|
+
else if (Is.array(pathValue)) {
|
|
2786
|
+
pathValue = pathValue[arrayIndex];
|
|
2787
|
+
}
|
|
2788
|
+
if (Is.empty(pathValue)) {
|
|
2789
|
+
const nextArrayMatch = /^(\d+)$/.exec(pathParts[i + 1]);
|
|
2790
|
+
const nextArrayIndex = nextArrayMatch ? Number.parseInt(nextArrayMatch[1], 10) : -1;
|
|
2791
|
+
if (nextArrayIndex >= 0) {
|
|
2792
|
+
pathValue = [];
|
|
2793
|
+
}
|
|
2794
|
+
else {
|
|
2795
|
+
pathValue = {};
|
|
2796
|
+
}
|
|
2797
|
+
if (Is.object(parentObj)) {
|
|
2798
|
+
parentObj[pathPart] = pathValue;
|
|
2799
|
+
}
|
|
2800
|
+
else if (Is.array(parentObj)) {
|
|
2801
|
+
parentObj[arrayIndex] = pathValue;
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2275
2804
|
}
|
|
2276
2805
|
}
|
|
2277
2806
|
}
|
|
2278
2807
|
/**
|
|
2279
|
-
*
|
|
2280
|
-
* @param
|
|
2281
|
-
* @
|
|
2282
|
-
* @returns The value if it can be coerced.
|
|
2808
|
+
* Delete the property of an unknown object.
|
|
2809
|
+
* @param obj The object to set the property from.
|
|
2810
|
+
* @param property The property to set
|
|
2283
2811
|
*/
|
|
2284
|
-
static
|
|
2285
|
-
if (Is.
|
|
2286
|
-
|
|
2287
|
-
}
|
|
2288
|
-
if (Is.date(value)) {
|
|
2289
|
-
return value;
|
|
2290
|
-
}
|
|
2291
|
-
if (Is.number(value)) {
|
|
2292
|
-
return new Date(value);
|
|
2812
|
+
static propertyDelete(obj, property) {
|
|
2813
|
+
if (Is.object(obj)) {
|
|
2814
|
+
delete obj[property];
|
|
2293
2815
|
}
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2816
|
+
}
|
|
2817
|
+
/**
|
|
2818
|
+
* Extract a property from the object, providing alternative names.
|
|
2819
|
+
* @param obj The object to extract from.
|
|
2820
|
+
* @param propertyNames The possible names for the property.
|
|
2821
|
+
* @param removeProperties Remove the properties from the object, defaults to true.
|
|
2822
|
+
* @returns The property if available.
|
|
2823
|
+
*/
|
|
2824
|
+
static extractProperty(obj, propertyNames, removeProperties = true) {
|
|
2825
|
+
let retVal;
|
|
2826
|
+
if (Is.object(obj)) {
|
|
2827
|
+
const names = Is.string(propertyNames) ? [propertyNames] : propertyNames;
|
|
2828
|
+
for (const prop of names) {
|
|
2829
|
+
retVal ??= ObjectHelper.propertyGet(obj, prop);
|
|
2830
|
+
if (removeProperties) {
|
|
2831
|
+
ObjectHelper.propertyDelete(obj, prop);
|
|
2832
|
+
}
|
|
2299
2833
|
}
|
|
2300
2834
|
}
|
|
2835
|
+
return retVal;
|
|
2301
2836
|
}
|
|
2302
2837
|
/**
|
|
2303
|
-
*
|
|
2304
|
-
* @param
|
|
2305
|
-
* @
|
|
2306
|
-
* @returns The
|
|
2838
|
+
* Pick a subset of properties from an object.
|
|
2839
|
+
* @param obj The object to pick the properties from.
|
|
2840
|
+
* @param keys The property keys to pick.
|
|
2841
|
+
* @returns The partial object.
|
|
2307
2842
|
*/
|
|
2308
|
-
static
|
|
2309
|
-
if (Is.
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
return value;
|
|
2314
|
-
}
|
|
2315
|
-
if (Is.number(value)) {
|
|
2316
|
-
const dt = new Date(value);
|
|
2317
|
-
dt.setFullYear(1970, 0, 1);
|
|
2318
|
-
return dt;
|
|
2319
|
-
}
|
|
2320
|
-
if (Is.string(value)) {
|
|
2321
|
-
const dt = new Date(value);
|
|
2322
|
-
if (!Number.isNaN(dt.getTime())) {
|
|
2323
|
-
const utc = Date.UTC(1970, 0, 1, dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
|
|
2324
|
-
return new Date(utc);
|
|
2843
|
+
static pick(obj, keys) {
|
|
2844
|
+
if (Is.object(obj) && Is.arrayValue(keys)) {
|
|
2845
|
+
const result = {};
|
|
2846
|
+
for (const key of keys) {
|
|
2847
|
+
result[key] = obj[key];
|
|
2325
2848
|
}
|
|
2849
|
+
return result;
|
|
2326
2850
|
}
|
|
2851
|
+
return obj;
|
|
2327
2852
|
}
|
|
2328
2853
|
/**
|
|
2329
|
-
*
|
|
2330
|
-
* @param
|
|
2331
|
-
* @
|
|
2332
|
-
* @returns The
|
|
2854
|
+
* Omit a subset of properties from an object.
|
|
2855
|
+
* @param obj The object to omit the properties from.
|
|
2856
|
+
* @param keys The property keys to omit.
|
|
2857
|
+
* @returns The partial object.
|
|
2333
2858
|
*/
|
|
2334
|
-
static
|
|
2335
|
-
if (Is.
|
|
2336
|
-
|
|
2859
|
+
static omit(obj, keys) {
|
|
2860
|
+
if (Is.object(obj) && Is.arrayValue(keys)) {
|
|
2861
|
+
const result = { ...obj };
|
|
2862
|
+
for (const key of keys) {
|
|
2863
|
+
delete result[key];
|
|
2864
|
+
}
|
|
2865
|
+
return result;
|
|
2337
2866
|
}
|
|
2338
|
-
|
|
2339
|
-
|
|
2867
|
+
return obj;
|
|
2868
|
+
}
|
|
2869
|
+
/**
|
|
2870
|
+
* Converter the non JSON primitives to extended types.
|
|
2871
|
+
* @param obj The object to convert.
|
|
2872
|
+
* @returns The object with extended properties.
|
|
2873
|
+
*/
|
|
2874
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2875
|
+
static toExtended(obj) {
|
|
2876
|
+
const jsonExtended = JsonHelper.stringifyEx(obj);
|
|
2877
|
+
return JSON.parse(jsonExtended);
|
|
2878
|
+
}
|
|
2879
|
+
/**
|
|
2880
|
+
* Converter the extended types to non JSON primitives.
|
|
2881
|
+
* @param obj The object to convert.
|
|
2882
|
+
* @returns The object with regular properties.
|
|
2883
|
+
*/
|
|
2884
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2885
|
+
static fromExtended(obj) {
|
|
2886
|
+
const jsonExtended = JsonHelper.stringifyEx(obj);
|
|
2887
|
+
return JsonHelper.parseEx(jsonExtended);
|
|
2888
|
+
}
|
|
2889
|
+
/**
|
|
2890
|
+
* Remove empty properties from an object.
|
|
2891
|
+
* @param obj The object to remove the empty properties from.
|
|
2892
|
+
* @param options The options for the removal.
|
|
2893
|
+
* @param options.removeUndefined Remove undefined properties, defaults to true.
|
|
2894
|
+
* @param options.removeNull Remove null properties, defaults to false.
|
|
2895
|
+
* @returns The object with empty properties removed.
|
|
2896
|
+
*/
|
|
2897
|
+
static removeEmptyProperties(obj, options) {
|
|
2898
|
+
if (Is.object(obj)) {
|
|
2899
|
+
const removeUndefined = options?.removeUndefined ?? true;
|
|
2900
|
+
const removeNull = options?.removeNull ?? false;
|
|
2901
|
+
const newObj = {};
|
|
2902
|
+
const keys = Object.keys(obj);
|
|
2903
|
+
for (const key of keys) {
|
|
2904
|
+
if (!((removeUndefined && Is.undefined(obj[key])) || (removeNull && Is.null(obj[key])))) {
|
|
2905
|
+
newObj[key] = ObjectHelper.removeEmptyProperties(obj[key], options);
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
return newObj;
|
|
2340
2909
|
}
|
|
2341
|
-
if (Is.
|
|
2342
|
-
|
|
2343
|
-
|
|
2910
|
+
else if (Is.array(obj)) {
|
|
2911
|
+
const arr = [];
|
|
2912
|
+
for (const element of obj) {
|
|
2913
|
+
arr.push(ObjectHelper.removeEmptyProperties(element, options));
|
|
2344
2914
|
}
|
|
2345
|
-
|
|
2915
|
+
return arr;
|
|
2346
2916
|
}
|
|
2917
|
+
return obj;
|
|
2347
2918
|
}
|
|
2348
2919
|
}
|
|
2349
2920
|
|
|
2350
2921
|
// Copyright 2024 IOTA Stiftung.
|
|
2351
2922
|
// SPDX-License-Identifier: Apache-2.0.
|
|
2352
2923
|
/**
|
|
2353
|
-
*
|
|
2924
|
+
* Environment variable helper.
|
|
2354
2925
|
*/
|
|
2355
|
-
class
|
|
2356
|
-
/**
|
|
2357
|
-
*
|
|
2358
|
-
* @param
|
|
2359
|
-
* @
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2926
|
+
class EnvHelper {
|
|
2927
|
+
/**
|
|
2928
|
+
* Get the environment variable as an object with camel cased names.
|
|
2929
|
+
* @param envVars The environment variables.
|
|
2930
|
+
* @param prefix The prefix of the environment variables, if not provided gets all.
|
|
2931
|
+
* @returns The object with camel cased names.
|
|
2932
|
+
*/
|
|
2933
|
+
static envToJson(envVars, prefix) {
|
|
2934
|
+
const result = {};
|
|
2935
|
+
if (!Is.empty(envVars)) {
|
|
2936
|
+
if (Is.empty(prefix)) {
|
|
2937
|
+
for (const envVar in envVars) {
|
|
2938
|
+
if (Is.stringValue(envVars[envVar])) {
|
|
2939
|
+
const camelCaseName = StringHelper.camelCase(envVar.toLowerCase());
|
|
2940
|
+
ObjectHelper.propertySet(result, camelCaseName, envVars[envVar]);
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2944
|
+
else {
|
|
2945
|
+
for (const envVar in envVars) {
|
|
2946
|
+
if (envVar.startsWith(prefix) && Is.stringValue(envVars[envVar])) {
|
|
2947
|
+
const camelCaseName = StringHelper.camelCase(envVar.replace(prefix, "").toLowerCase());
|
|
2948
|
+
ObjectHelper.propertySet(result, camelCaseName, envVars[envVar]);
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
}
|
|
2365
2952
|
}
|
|
2366
|
-
|
|
2367
|
-
safe = safe.replace(/["*/:<>?\\|]/g, "_");
|
|
2368
|
-
// Windows non filename characters
|
|
2369
|
-
safe = safe.replace(/^(con|prn|aux|nul|com\d|lpt\d)$/i, "_");
|
|
2370
|
-
// Control characters
|
|
2371
|
-
safe = safe.replace(/[\u0000-\u001F\u0080-\u009F]/g, "_");
|
|
2372
|
-
// Relative paths
|
|
2373
|
-
safe = safe.replace(/^\.+/, "_");
|
|
2374
|
-
// Trailing periods
|
|
2375
|
-
safe = safe.replace(/\.+$/, "");
|
|
2376
|
-
return safe;
|
|
2953
|
+
return result;
|
|
2377
2954
|
}
|
|
2378
2955
|
}
|
|
2379
2956
|
|
|
2380
2957
|
// Copyright 2024 IOTA Stiftung.
|
|
2381
2958
|
// SPDX-License-Identifier: Apache-2.0.
|
|
2382
2959
|
/**
|
|
2383
|
-
*
|
|
2960
|
+
* Class to perform internationalization.
|
|
2384
2961
|
*/
|
|
2385
|
-
class
|
|
2962
|
+
class I18n {
|
|
2386
2963
|
/**
|
|
2387
|
-
*
|
|
2388
|
-
* Based on https://www.rfc-editor.org/rfc/rfc8785.
|
|
2389
|
-
* @param object The object to be serialized.
|
|
2390
|
-
* @returns The serialized object.
|
|
2964
|
+
* The default translation.
|
|
2391
2965
|
*/
|
|
2392
|
-
static
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
const parts = [];
|
|
2403
|
-
for (const element of object) {
|
|
2404
|
-
if (element === undefined) {
|
|
2405
|
-
parts.push("null");
|
|
2406
|
-
}
|
|
2407
|
-
else {
|
|
2408
|
-
parts.push(JsonHelper.canonicalize(element));
|
|
2409
|
-
}
|
|
2410
|
-
}
|
|
2411
|
-
buffer.push(`[${parts.join(",")}]`);
|
|
2966
|
+
static DEFAULT_LOCALE = "en";
|
|
2967
|
+
/**
|
|
2968
|
+
* Set the locale.
|
|
2969
|
+
* @param locale The new locale.
|
|
2970
|
+
*/
|
|
2971
|
+
static setLocale(locale) {
|
|
2972
|
+
const i18nShared = I18n.getI18nShared();
|
|
2973
|
+
i18nShared.currentLocale = locale;
|
|
2974
|
+
for (const callback in i18nShared.localeChangedHandlers) {
|
|
2975
|
+
i18nShared.localeChangedHandlers[callback](i18nShared.currentLocale);
|
|
2412
2976
|
}
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2977
|
+
}
|
|
2978
|
+
/**
|
|
2979
|
+
* Get the locale.
|
|
2980
|
+
* @returns The current locale.
|
|
2981
|
+
*/
|
|
2982
|
+
static getLocale() {
|
|
2983
|
+
const i18nShared = I18n.getI18nShared();
|
|
2984
|
+
return i18nShared.currentLocale;
|
|
2985
|
+
}
|
|
2986
|
+
/**
|
|
2987
|
+
* Add a locale dictionary.
|
|
2988
|
+
* @param locale The locale.
|
|
2989
|
+
* @param dictionary The dictionary to add.
|
|
2990
|
+
*/
|
|
2991
|
+
static addDictionary(locale, dictionary) {
|
|
2992
|
+
const i18nShared = I18n.getI18nShared();
|
|
2993
|
+
const mergedKeys = {};
|
|
2994
|
+
I18n.flattenTranslationKeys(dictionary, "", mergedKeys);
|
|
2995
|
+
i18nShared.localeDictionaries[locale] = mergedKeys;
|
|
2996
|
+
for (const callback in i18nShared.dictionaryChangedHandlers) {
|
|
2997
|
+
i18nShared.dictionaryChangedHandlers[callback](i18nShared.currentLocale);
|
|
2424
2998
|
}
|
|
2425
|
-
return buffer.join("");
|
|
2426
2999
|
}
|
|
2427
3000
|
/**
|
|
2428
|
-
*
|
|
2429
|
-
*
|
|
2430
|
-
* @
|
|
2431
|
-
* @param object2 The second object.
|
|
2432
|
-
* @returns The list of patches.
|
|
3001
|
+
* Get a locale dictionary.
|
|
3002
|
+
* @param locale The locale.
|
|
3003
|
+
* @returns The dictionary of undefined if it does not exist.
|
|
2433
3004
|
*/
|
|
2434
|
-
static
|
|
2435
|
-
const
|
|
2436
|
-
return
|
|
3005
|
+
static getDictionary(locale) {
|
|
3006
|
+
const i18nShared = I18n.getI18nShared();
|
|
3007
|
+
return i18nShared.localeDictionaries[locale];
|
|
2437
3008
|
}
|
|
2438
3009
|
/**
|
|
2439
|
-
*
|
|
2440
|
-
*
|
|
2441
|
-
* @param object The object to patch.
|
|
2442
|
-
* @param patches The second object.
|
|
2443
|
-
* @returns The updated object.
|
|
3010
|
+
* Get all the locale dictionaries.
|
|
3011
|
+
* @returns The dictionaries.
|
|
2444
3012
|
*/
|
|
2445
|
-
static
|
|
2446
|
-
|
|
3013
|
+
static getAllDictionaries() {
|
|
3014
|
+
const i18nShared = I18n.getI18nShared();
|
|
3015
|
+
return i18nShared.localeDictionaries;
|
|
2447
3016
|
}
|
|
2448
|
-
}
|
|
2449
|
-
|
|
2450
|
-
// Copyright 2024 IOTA Stiftung.
|
|
2451
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
2452
|
-
/* eslint-disable no-bitwise */
|
|
2453
|
-
/**
|
|
2454
|
-
* Convert arrays to and from different formats.
|
|
2455
|
-
*/
|
|
2456
|
-
class Converter {
|
|
2457
3017
|
/**
|
|
2458
|
-
*
|
|
2459
|
-
* @
|
|
3018
|
+
* Add a locale changed handler.
|
|
3019
|
+
* @param id The id of the handler.
|
|
3020
|
+
* @param handler The handler to add.
|
|
2460
3021
|
*/
|
|
2461
|
-
static
|
|
3022
|
+
static addLocaleHandler(id, handler) {
|
|
3023
|
+
const i18nShared = I18n.getI18nShared();
|
|
3024
|
+
i18nShared.localeChangedHandlers[id] = handler;
|
|
3025
|
+
}
|
|
2462
3026
|
/**
|
|
2463
|
-
*
|
|
2464
|
-
* @
|
|
3027
|
+
* Remove a locale changed handler.
|
|
3028
|
+
* @param id The id of the handler.
|
|
2465
3029
|
*/
|
|
2466
|
-
static
|
|
3030
|
+
static removeLocaleHandler(id) {
|
|
3031
|
+
const i18nShared = I18n.getI18nShared();
|
|
3032
|
+
delete i18nShared.localeChangedHandlers[id];
|
|
3033
|
+
}
|
|
2467
3034
|
/**
|
|
2468
|
-
*
|
|
2469
|
-
* @param
|
|
2470
|
-
* @param
|
|
2471
|
-
* @param length The length of bytes to read.
|
|
2472
|
-
* @returns The array formatted as UTF8.
|
|
3035
|
+
* Add a dictionary changed handler.
|
|
3036
|
+
* @param id The id of the handler.
|
|
3037
|
+
* @param handler The handler to add.
|
|
2473
3038
|
*/
|
|
2474
|
-
static
|
|
2475
|
-
const
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
str += String.fromCharCode((charCode >> 10) | 0xd800, (charCode & 0x03ff) | 0xdc00);
|
|
2499
|
-
i += 3;
|
|
2500
|
-
}
|
|
3039
|
+
static addDictionaryHandler(id, handler) {
|
|
3040
|
+
const i18nShared = I18n.getI18nShared();
|
|
3041
|
+
i18nShared.dictionaryChangedHandlers[id] = handler;
|
|
3042
|
+
}
|
|
3043
|
+
/**
|
|
3044
|
+
* Remove a dictionary changed handler.
|
|
3045
|
+
* @param id The id of the handler.
|
|
3046
|
+
*/
|
|
3047
|
+
static removeDictionaryHandler(id) {
|
|
3048
|
+
const i18nShared = I18n.getI18nShared();
|
|
3049
|
+
delete i18nShared.dictionaryChangedHandlers[id];
|
|
3050
|
+
}
|
|
3051
|
+
/**
|
|
3052
|
+
* Format a message.
|
|
3053
|
+
* @param key The key of the message to format.
|
|
3054
|
+
* @param values The values to substitute into the message.
|
|
3055
|
+
* @param overrideLocale Override the locale.
|
|
3056
|
+
* @returns The formatted string.
|
|
3057
|
+
*/
|
|
3058
|
+
static formatMessage(key, values, overrideLocale) {
|
|
3059
|
+
const i18nShared = I18n.getI18nShared();
|
|
3060
|
+
let cl = overrideLocale ?? i18nShared.currentLocale;
|
|
3061
|
+
if (cl.startsWith("debug-")) {
|
|
3062
|
+
cl = I18n.DEFAULT_LOCALE;
|
|
2501
3063
|
}
|
|
2502
|
-
|
|
3064
|
+
if (!i18nShared.localeDictionaries[cl]) {
|
|
3065
|
+
return `!!Missing ${cl}`;
|
|
3066
|
+
}
|
|
3067
|
+
if (!i18nShared.localeDictionaries[cl][key]) {
|
|
3068
|
+
return `!!Missing ${cl}.${key}`;
|
|
3069
|
+
}
|
|
3070
|
+
if (i18nShared.currentLocale === "debug-k") {
|
|
3071
|
+
return key;
|
|
3072
|
+
}
|
|
3073
|
+
let ret = new IntlMessageFormat(i18nShared.localeDictionaries[cl][key], cl).format(values);
|
|
3074
|
+
if (i18nShared.currentLocale === "debug-x") {
|
|
3075
|
+
ret = ret.replace(/[a-z]/g, "x").replace(/[A-Z]/g, "x").replace(/\d/g, "n");
|
|
3076
|
+
}
|
|
3077
|
+
return ret;
|
|
2503
3078
|
}
|
|
2504
3079
|
/**
|
|
2505
|
-
*
|
|
2506
|
-
* @param
|
|
2507
|
-
* @returns
|
|
3080
|
+
* Check if the dictionaries have a message for the given key.
|
|
3081
|
+
* @param key The key to check for existence.
|
|
3082
|
+
* @returns True if the key exists.
|
|
2508
3083
|
*/
|
|
2509
|
-
static
|
|
2510
|
-
const
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
3084
|
+
static hasMessage(key) {
|
|
3085
|
+
const i18nShared = I18n.getI18nShared();
|
|
3086
|
+
return Is.string(i18nShared.localeDictionaries[i18nShared.currentLocale]?.[key]);
|
|
3087
|
+
}
|
|
3088
|
+
/**
|
|
3089
|
+
* Flatten the translation property paths for faster lookup.
|
|
3090
|
+
* @param translation The translation to merge.
|
|
3091
|
+
* @param propertyPath The current root path.
|
|
3092
|
+
* @param mergedKeys The merged keys dictionary to populate.
|
|
3093
|
+
* @internal
|
|
3094
|
+
*/
|
|
3095
|
+
static flattenTranslationKeys(translation, propertyPath, mergedKeys) {
|
|
3096
|
+
for (const key in translation) {
|
|
3097
|
+
const val = translation[key];
|
|
3098
|
+
const mergedPath = propertyPath.length > 0 ? `${propertyPath}.${key}` : key;
|
|
3099
|
+
if (Is.string(val)) {
|
|
3100
|
+
mergedKeys[mergedPath] = val;
|
|
2518
3101
|
}
|
|
2519
|
-
else if (
|
|
2520
|
-
|
|
3102
|
+
else if (Is.object(val)) {
|
|
3103
|
+
I18n.flattenTranslationKeys(val, mergedPath, mergedKeys);
|
|
2521
3104
|
}
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
/**
|
|
3108
|
+
* Get the I18n shared data.
|
|
3109
|
+
* @returns The I18n shared data.
|
|
3110
|
+
* @internal
|
|
3111
|
+
*/
|
|
3112
|
+
static getI18nShared() {
|
|
3113
|
+
let i18nShared = SharedStore.get("i18n");
|
|
3114
|
+
if (Is.undefined(i18nShared)) {
|
|
3115
|
+
i18nShared = {
|
|
3116
|
+
localeDictionaries: {},
|
|
3117
|
+
currentLocale: I18n.DEFAULT_LOCALE,
|
|
3118
|
+
localeChangedHandlers: {},
|
|
3119
|
+
dictionaryChangedHandlers: {}
|
|
3120
|
+
};
|
|
3121
|
+
SharedStore.set("i18n", i18nShared);
|
|
3122
|
+
}
|
|
3123
|
+
return i18nShared;
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
|
|
3127
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3128
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3129
|
+
/**
|
|
3130
|
+
* Error helper functions.
|
|
3131
|
+
*/
|
|
3132
|
+
class ErrorHelper {
|
|
3133
|
+
/**
|
|
3134
|
+
* Format Errors and returns just their messages.
|
|
3135
|
+
* @param error The error to format.
|
|
3136
|
+
* @param includeDetails Whether to include error details, defaults to false.
|
|
3137
|
+
* @returns The error formatted including any inner errors.
|
|
3138
|
+
*/
|
|
3139
|
+
static formatErrors(error, includeDetails) {
|
|
3140
|
+
const localizedErrors = ErrorHelper.localizeErrors(error);
|
|
3141
|
+
if (includeDetails ?? false) {
|
|
3142
|
+
const output = [];
|
|
3143
|
+
for (const err of localizedErrors) {
|
|
3144
|
+
let detailedError = err.message;
|
|
3145
|
+
if (Is.stringValue(err.stack)) {
|
|
3146
|
+
detailedError += `\n${err.stack}`;
|
|
3147
|
+
}
|
|
3148
|
+
output.push(detailedError);
|
|
2530
3149
|
}
|
|
3150
|
+
return output;
|
|
2531
3151
|
}
|
|
2532
|
-
return
|
|
3152
|
+
return localizedErrors.map(e => e.message);
|
|
2533
3153
|
}
|
|
2534
3154
|
/**
|
|
2535
|
-
*
|
|
2536
|
-
* @param
|
|
2537
|
-
* @
|
|
2538
|
-
* @param startIndex The index to start in the bytes.
|
|
2539
|
-
* @param length The length of bytes to read.
|
|
2540
|
-
* @param reverse Reverse the combine direction.
|
|
2541
|
-
* @returns The array formatted as hex.
|
|
3155
|
+
* Localize the content of an error and any inner errors.
|
|
3156
|
+
* @param error The error to format.
|
|
3157
|
+
* @returns The localized version of the errors flattened.
|
|
2542
3158
|
*/
|
|
2543
|
-
static
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
const
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
3159
|
+
static localizeErrors(error) {
|
|
3160
|
+
const formattedErrors = [];
|
|
3161
|
+
if (Is.notEmpty(error)) {
|
|
3162
|
+
const errors = BaseError.flatten(error);
|
|
3163
|
+
for (const err of errors) {
|
|
3164
|
+
const errorNameKey = `errorNames.${StringHelper.camelCase(err.name)}`;
|
|
3165
|
+
const errorMessageKey = `error.${err.message}`;
|
|
3166
|
+
// If there is no error message then it is probably
|
|
3167
|
+
// from a 3rd party lib, so don't format it just display
|
|
3168
|
+
const hasErrorName = I18n.hasMessage(errorNameKey);
|
|
3169
|
+
const hasErrorMessage = I18n.hasMessage(errorMessageKey);
|
|
3170
|
+
const localizedError = {
|
|
3171
|
+
name: I18n.formatMessage(hasErrorName ? errorNameKey : "errorNames.error"),
|
|
3172
|
+
message: hasErrorMessage
|
|
3173
|
+
? I18n.formatMessage(errorMessageKey, err.properties)
|
|
3174
|
+
: err.message
|
|
3175
|
+
};
|
|
3176
|
+
if (Is.stringValue(err.source)) {
|
|
3177
|
+
localizedError.source = err.source;
|
|
2552
3178
|
}
|
|
3179
|
+
if (Is.stringValue(err.stack)) {
|
|
3180
|
+
// Remove the first line from the stack traces as they
|
|
3181
|
+
// just have the error type and message duplicated
|
|
3182
|
+
const lines = err.stack.split("\n");
|
|
3183
|
+
lines.shift();
|
|
3184
|
+
localizedError.stack = lines.join("\n");
|
|
3185
|
+
}
|
|
3186
|
+
const additional = ErrorHelper.formatValidationErrors(err);
|
|
3187
|
+
if (Is.stringValue(additional)) {
|
|
3188
|
+
localizedError.additional = additional;
|
|
3189
|
+
}
|
|
3190
|
+
formattedErrors.push(localizedError);
|
|
2553
3191
|
}
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
3192
|
+
}
|
|
3193
|
+
return formattedErrors;
|
|
3194
|
+
}
|
|
3195
|
+
/**
|
|
3196
|
+
* Localize the content of an error and any inner errors.
|
|
3197
|
+
* @param error The error to format.
|
|
3198
|
+
* @returns The localized version of the errors flattened.
|
|
3199
|
+
*/
|
|
3200
|
+
static formatValidationErrors(error) {
|
|
3201
|
+
if (Is.object(error.properties) &&
|
|
3202
|
+
Object.keys(error.properties).length > 0 &&
|
|
3203
|
+
Is.object(error.properties) &&
|
|
3204
|
+
Is.arrayValue(error.properties.validationFailures)) {
|
|
3205
|
+
const validationErrors = [];
|
|
3206
|
+
for (const validationFailure of error.properties.validationFailures) {
|
|
3207
|
+
const errorI18n = `error.${validationFailure.reason}`;
|
|
3208
|
+
const errorMessage = I18n.hasMessage(errorI18n)
|
|
3209
|
+
? I18n.formatMessage(errorI18n, validationFailure.properties)
|
|
3210
|
+
: errorI18n;
|
|
3211
|
+
let v = `${validationFailure.property}: ${errorMessage}`;
|
|
3212
|
+
if (Is.object(validationFailure.properties) &&
|
|
3213
|
+
Is.notEmpty(validationFailure.properties.value)) {
|
|
3214
|
+
v += ` = ${JSON.stringify(validationFailure.properties.value)}`;
|
|
2557
3215
|
}
|
|
3216
|
+
validationErrors.push(v);
|
|
2558
3217
|
}
|
|
3218
|
+
return validationErrors.join("\n");
|
|
2559
3219
|
}
|
|
2560
|
-
return includePrefix ? HexHelper.addPrefix(hex) : hex;
|
|
2561
3220
|
}
|
|
3221
|
+
}
|
|
3222
|
+
|
|
3223
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3224
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3225
|
+
/**
|
|
3226
|
+
* The types the extracted data can be coerced to.
|
|
3227
|
+
*/
|
|
3228
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
3229
|
+
const CoerceType = {
|
|
2562
3230
|
/**
|
|
2563
|
-
*
|
|
2564
|
-
* @param hex The hex to decode.
|
|
2565
|
-
* @param reverse Store the characters in reverse.
|
|
2566
|
-
* @returns The array.
|
|
3231
|
+
* String.
|
|
2567
3232
|
*/
|
|
2568
|
-
|
|
2569
|
-
const strippedHex = HexHelper.stripPrefix(hex);
|
|
2570
|
-
const sizeof = strippedHex.length >> 1;
|
|
2571
|
-
const length = sizeof << 1;
|
|
2572
|
-
const array = new Uint8Array(sizeof);
|
|
2573
|
-
this.buildHexLookups();
|
|
2574
|
-
if (Converter._DECODE_LOOKUP) {
|
|
2575
|
-
let i = 0;
|
|
2576
|
-
let n = 0;
|
|
2577
|
-
while (i < length) {
|
|
2578
|
-
array[n++] =
|
|
2579
|
-
(Converter._DECODE_LOOKUP[strippedHex.charCodeAt(i++)] << 4) |
|
|
2580
|
-
Converter._DECODE_LOOKUP[strippedHex.charCodeAt(i++)];
|
|
2581
|
-
}
|
|
2582
|
-
if (reverse) {
|
|
2583
|
-
array.reverse();
|
|
2584
|
-
}
|
|
2585
|
-
}
|
|
2586
|
-
return array;
|
|
2587
|
-
}
|
|
3233
|
+
String: "string",
|
|
2588
3234
|
/**
|
|
2589
|
-
*
|
|
2590
|
-
* @param utf8 The text to convert.
|
|
2591
|
-
* @param includePrefix Include the 0x prefix on the returned hex.
|
|
2592
|
-
* @returns The hex version of the bytes.
|
|
3235
|
+
* Number.
|
|
2593
3236
|
*/
|
|
2594
|
-
|
|
2595
|
-
const hex = Converter.bytesToHex(Converter.utf8ToBytes(utf8));
|
|
2596
|
-
return includePrefix ? HexHelper.addPrefix(hex) : hex;
|
|
2597
|
-
}
|
|
3237
|
+
Number: "number",
|
|
2598
3238
|
/**
|
|
2599
|
-
*
|
|
2600
|
-
* @param hex The hex to convert.
|
|
2601
|
-
* @returns The UTF8 version of the bytes.
|
|
3239
|
+
* Integer.
|
|
2602
3240
|
*/
|
|
2603
|
-
|
|
2604
|
-
return Converter.bytesToUtf8(Converter.hexToBytes(HexHelper.stripPrefix(hex)));
|
|
2605
|
-
}
|
|
3241
|
+
Integer: "integer",
|
|
2606
3242
|
/**
|
|
2607
|
-
*
|
|
2608
|
-
* @param bytes The bytes to convert.
|
|
2609
|
-
* @returns A binary string of the bytes.
|
|
3243
|
+
* Boolean.
|
|
2610
3244
|
*/
|
|
2611
|
-
|
|
2612
|
-
const b = [];
|
|
2613
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
2614
|
-
b.push(bytes[i].toString(2).padStart(8, "0"));
|
|
2615
|
-
}
|
|
2616
|
-
return b.join("");
|
|
2617
|
-
}
|
|
3245
|
+
Boolean: "boolean",
|
|
2618
3246
|
/**
|
|
2619
|
-
*
|
|
2620
|
-
* @param binary The binary string.
|
|
2621
|
-
* @returns The bytes.
|
|
3247
|
+
* Big Integer.
|
|
2622
3248
|
*/
|
|
2623
|
-
|
|
2624
|
-
const bytes = new Uint8Array(Math.ceil(binary.length / 8));
|
|
2625
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
2626
|
-
bytes[i] = Number.parseInt(binary.slice(i * 8, (i + 1) * 8), 2);
|
|
2627
|
-
}
|
|
2628
|
-
return bytes;
|
|
2629
|
-
}
|
|
3249
|
+
BigInt: "bigint",
|
|
2630
3250
|
/**
|
|
2631
|
-
*
|
|
2632
|
-
* @param bytes The bytes to convert.
|
|
2633
|
-
* @returns A base64 string of the bytes.
|
|
3251
|
+
* Date.
|
|
2634
3252
|
*/
|
|
2635
|
-
|
|
2636
|
-
return Base64.encode(bytes);
|
|
2637
|
-
}
|
|
3253
|
+
Date: "date",
|
|
2638
3254
|
/**
|
|
2639
|
-
*
|
|
2640
|
-
* @param base64 The base64 string.
|
|
2641
|
-
* @returns The bytes.
|
|
3255
|
+
* Date Time.
|
|
2642
3256
|
*/
|
|
2643
|
-
|
|
2644
|
-
return Base64.decode(base64);
|
|
2645
|
-
}
|
|
3257
|
+
DateTime: "datetime",
|
|
2646
3258
|
/**
|
|
2647
|
-
*
|
|
2648
|
-
* @param bytes The bytes to convert.
|
|
2649
|
-
* @returns A base64 url string of the bytes.
|
|
3259
|
+
* Time.
|
|
2650
3260
|
*/
|
|
2651
|
-
|
|
2652
|
-
return Base64Url.encode(bytes);
|
|
2653
|
-
}
|
|
3261
|
+
Time: "time",
|
|
2654
3262
|
/**
|
|
2655
|
-
*
|
|
2656
|
-
* @param base64Url The base64 url string.
|
|
2657
|
-
* @returns The bytes.
|
|
3263
|
+
* Object.
|
|
2658
3264
|
*/
|
|
2659
|
-
|
|
2660
|
-
return Base64Url.decode(base64Url);
|
|
2661
|
-
}
|
|
3265
|
+
Object: "object",
|
|
2662
3266
|
/**
|
|
2663
|
-
*
|
|
2664
|
-
* @internal
|
|
3267
|
+
* Uint8Array.
|
|
2665
3268
|
*/
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
const alphabet = "0123456789abcdef";
|
|
2669
|
-
Converter._ENCODE_LOOKUP = [];
|
|
2670
|
-
Converter._DECODE_LOOKUP = [];
|
|
2671
|
-
for (let i = 0; i < 256; i++) {
|
|
2672
|
-
Converter._ENCODE_LOOKUP[i] = alphabet[(i >> 4) & 0xf] + alphabet[i & 0xf];
|
|
2673
|
-
if (i < 16) {
|
|
2674
|
-
if (i < 10) {
|
|
2675
|
-
Converter._DECODE_LOOKUP[0x30 + i] = i;
|
|
2676
|
-
}
|
|
2677
|
-
else {
|
|
2678
|
-
Converter._DECODE_LOOKUP[0x61 - 10 + i] = i;
|
|
2679
|
-
}
|
|
2680
|
-
}
|
|
2681
|
-
}
|
|
2682
|
-
}
|
|
2683
|
-
}
|
|
2684
|
-
}
|
|
3269
|
+
Uint8Array: "uint8array"
|
|
3270
|
+
};
|
|
2685
3271
|
|
|
3272
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3273
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
2686
3274
|
/**
|
|
2687
|
-
*
|
|
3275
|
+
* Coerce an object from one type to another.
|
|
2688
3276
|
*/
|
|
2689
|
-
class
|
|
3277
|
+
class Coerce {
|
|
2690
3278
|
/**
|
|
2691
|
-
*
|
|
2692
|
-
* @
|
|
3279
|
+
* Coerce the value to a string.
|
|
3280
|
+
* @param value The value to coerce.
|
|
3281
|
+
* @throws TypeError If the value can not be coerced.
|
|
3282
|
+
* @returns The value if it can be coerced.
|
|
2693
3283
|
*/
|
|
2694
|
-
static
|
|
3284
|
+
static string(value) {
|
|
3285
|
+
if (Is.undefined(value)) {
|
|
3286
|
+
return value;
|
|
3287
|
+
}
|
|
3288
|
+
if (Is.string(value)) {
|
|
3289
|
+
return value;
|
|
3290
|
+
}
|
|
3291
|
+
if (Is.number(value)) {
|
|
3292
|
+
return value.toString();
|
|
3293
|
+
}
|
|
3294
|
+
if (Is.boolean(value)) {
|
|
3295
|
+
return value ? "true" : "false";
|
|
3296
|
+
}
|
|
3297
|
+
if (Is.date(value)) {
|
|
3298
|
+
return value.toISOString();
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
2695
3301
|
/**
|
|
2696
|
-
*
|
|
2697
|
-
* @param
|
|
2698
|
-
* @
|
|
2699
|
-
* @returns The
|
|
3302
|
+
* Coerce the value to a number.
|
|
3303
|
+
* @param value The value to coerce.
|
|
3304
|
+
* @throws TypeError If the value can not be coerced.
|
|
3305
|
+
* @returns The value if it can be coerced.
|
|
2700
3306
|
*/
|
|
2701
|
-
static
|
|
2702
|
-
if (
|
|
2703
|
-
return
|
|
3307
|
+
static number(value) {
|
|
3308
|
+
if (Is.undefined(value)) {
|
|
3309
|
+
return value;
|
|
3310
|
+
}
|
|
3311
|
+
if (Is.number(value)) {
|
|
3312
|
+
return value;
|
|
3313
|
+
}
|
|
3314
|
+
if (Is.string(value)) {
|
|
3315
|
+
const parsed = Number.parseFloat(value);
|
|
3316
|
+
if (Is.number(parsed)) {
|
|
3317
|
+
return parsed;
|
|
3318
|
+
}
|
|
3319
|
+
}
|
|
3320
|
+
if (Is.boolean(value)) {
|
|
3321
|
+
return value ? 1 : 0;
|
|
3322
|
+
}
|
|
3323
|
+
if (Is.date(value)) {
|
|
3324
|
+
return value.getTime();
|
|
2704
3325
|
}
|
|
2705
|
-
const json = format ? JSON.stringify(obj, undefined, "\t") : JSON.stringify(obj);
|
|
2706
|
-
return Converter.utf8ToBytes(json);
|
|
2707
3326
|
}
|
|
2708
3327
|
/**
|
|
2709
|
-
*
|
|
2710
|
-
* @param
|
|
2711
|
-
* @
|
|
2712
|
-
* @
|
|
3328
|
+
* Coerce the value to an integer.
|
|
3329
|
+
* @param value The value to coerce.
|
|
3330
|
+
* @throws TypeError If the value can not be coerced.
|
|
3331
|
+
* @returns The value if it can be coerced.
|
|
2713
3332
|
*/
|
|
2714
|
-
static
|
|
2715
|
-
|
|
2716
|
-
|
|
3333
|
+
static integer(value) {
|
|
3334
|
+
const num = Coerce.number(value);
|
|
3335
|
+
if (!Is.undefined(num)) {
|
|
3336
|
+
return Math.trunc(num);
|
|
2717
3337
|
}
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
3338
|
+
}
|
|
3339
|
+
/**
|
|
3340
|
+
* Coerce the value to a bigint.
|
|
3341
|
+
* @param value The value to coerce.
|
|
3342
|
+
* @throws TypeError If the value can not be coerced.
|
|
3343
|
+
* @returns The value if it can be coerced.
|
|
3344
|
+
*/
|
|
3345
|
+
static bigint(value) {
|
|
3346
|
+
if (Is.undefined(value)) {
|
|
3347
|
+
return value;
|
|
2721
3348
|
}
|
|
2722
|
-
|
|
2723
|
-
|
|
3349
|
+
if (Is.bigint(value)) {
|
|
3350
|
+
return value;
|
|
3351
|
+
}
|
|
3352
|
+
if (Is.number(value)) {
|
|
3353
|
+
return BigInt(value);
|
|
3354
|
+
}
|
|
3355
|
+
if (Is.string(value)) {
|
|
3356
|
+
const parsed = Number.parseFloat(value);
|
|
3357
|
+
if (Is.integer(parsed)) {
|
|
3358
|
+
return BigInt(parsed);
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3361
|
+
if (Is.boolean(value)) {
|
|
3362
|
+
return value ? 1n : 0n;
|
|
2724
3363
|
}
|
|
2725
3364
|
}
|
|
2726
3365
|
/**
|
|
2727
|
-
*
|
|
2728
|
-
* @param
|
|
2729
|
-
* @
|
|
3366
|
+
* Coerce the value to a boolean.
|
|
3367
|
+
* @param value The value to coerce.
|
|
3368
|
+
* @throws TypeError If the value can not be coerced.
|
|
3369
|
+
* @returns The value if it can be coerced.
|
|
2730
3370
|
*/
|
|
2731
|
-
static
|
|
2732
|
-
if (Is.undefined(
|
|
2733
|
-
return
|
|
3371
|
+
static boolean(value) {
|
|
3372
|
+
if (Is.undefined(value)) {
|
|
3373
|
+
return value;
|
|
3374
|
+
}
|
|
3375
|
+
if (Is.boolean(value)) {
|
|
3376
|
+
return value;
|
|
3377
|
+
}
|
|
3378
|
+
if (Is.number(value)) {
|
|
3379
|
+
// eslint-disable-next-line no-unneeded-ternary
|
|
3380
|
+
return value ? true : false;
|
|
3381
|
+
}
|
|
3382
|
+
if (Is.string(value)) {
|
|
3383
|
+
if (/true/i.test(value)) {
|
|
3384
|
+
return true;
|
|
3385
|
+
}
|
|
3386
|
+
if (/false/i.test(value)) {
|
|
3387
|
+
return false;
|
|
3388
|
+
}
|
|
2734
3389
|
}
|
|
2735
|
-
return structuredClone(obj);
|
|
2736
3390
|
}
|
|
2737
3391
|
/**
|
|
2738
|
-
*
|
|
2739
|
-
* @param
|
|
2740
|
-
* @
|
|
2741
|
-
* @returns The
|
|
3392
|
+
* Coerce the value to a date.
|
|
3393
|
+
* @param value The value to coerce.
|
|
3394
|
+
* @throws TypeError If the value can not be coerced.
|
|
3395
|
+
* @returns The value if it can be coerced.
|
|
2742
3396
|
*/
|
|
2743
|
-
static
|
|
2744
|
-
if (Is.
|
|
2745
|
-
return
|
|
3397
|
+
static date(value) {
|
|
3398
|
+
if (Is.undefined(value)) {
|
|
3399
|
+
return value;
|
|
2746
3400
|
}
|
|
2747
|
-
if (Is.
|
|
2748
|
-
return
|
|
3401
|
+
if (Is.date(value)) {
|
|
3402
|
+
return value;
|
|
2749
3403
|
}
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
ObjectHelper.propertySet(obj1Clone, key, obj2[key]);
|
|
2759
|
-
}
|
|
3404
|
+
if (Is.number(value)) {
|
|
3405
|
+
return new Date(value);
|
|
3406
|
+
}
|
|
3407
|
+
if (Is.string(value)) {
|
|
3408
|
+
const dt = new Date(value);
|
|
3409
|
+
if (!Number.isNaN(dt.getTime())) {
|
|
3410
|
+
const utc = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate());
|
|
3411
|
+
return new Date(utc);
|
|
2760
3412
|
}
|
|
2761
3413
|
}
|
|
2762
|
-
return obj1Clone;
|
|
2763
3414
|
}
|
|
2764
3415
|
/**
|
|
2765
|
-
*
|
|
2766
|
-
* @param
|
|
2767
|
-
* @
|
|
2768
|
-
* @
|
|
2769
|
-
* @returns True is the objects are equal.
|
|
3416
|
+
* Coerce the value to a date/time.
|
|
3417
|
+
* @param value The value to coerce.
|
|
3418
|
+
* @throws TypeError If the value can not be coerced.
|
|
3419
|
+
* @returns The value if it can be coerced.
|
|
2770
3420
|
*/
|
|
2771
|
-
static
|
|
2772
|
-
if (
|
|
2773
|
-
return
|
|
3421
|
+
static dateTime(value) {
|
|
3422
|
+
if (Is.undefined(value)) {
|
|
3423
|
+
return value;
|
|
3424
|
+
}
|
|
3425
|
+
if (Is.date(value)) {
|
|
3426
|
+
return value;
|
|
3427
|
+
}
|
|
3428
|
+
if (Is.number(value)) {
|
|
3429
|
+
return new Date(value);
|
|
3430
|
+
}
|
|
3431
|
+
if (Is.string(value)) {
|
|
3432
|
+
const dt = new Date(value);
|
|
3433
|
+
if (!Number.isNaN(dt.getTime())) {
|
|
3434
|
+
const utc = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate(), dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
|
|
3435
|
+
return new Date(utc);
|
|
3436
|
+
}
|
|
2774
3437
|
}
|
|
2775
|
-
return JsonHelper.canonicalize(obj1) === JsonHelper.canonicalize(obj2);
|
|
2776
3438
|
}
|
|
2777
3439
|
/**
|
|
2778
|
-
*
|
|
2779
|
-
* @param
|
|
2780
|
-
* @
|
|
2781
|
-
* @returns The
|
|
3440
|
+
* Coerce the value to a time.
|
|
3441
|
+
* @param value The value to coerce.
|
|
3442
|
+
* @throws TypeError If the value can not be coerced.
|
|
3443
|
+
* @returns The value if it can be coerced.
|
|
2782
3444
|
*/
|
|
2783
|
-
static
|
|
2784
|
-
if (
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
if (Is.object(value)) {
|
|
2789
|
-
value = value[part];
|
|
2790
|
-
}
|
|
2791
|
-
else {
|
|
2792
|
-
return undefined;
|
|
2793
|
-
}
|
|
2794
|
-
}
|
|
3445
|
+
static time(value) {
|
|
3446
|
+
if (Is.undefined(value)) {
|
|
3447
|
+
return value;
|
|
3448
|
+
}
|
|
3449
|
+
if (Is.date(value)) {
|
|
2795
3450
|
return value;
|
|
2796
3451
|
}
|
|
2797
|
-
|
|
3452
|
+
if (Is.number(value)) {
|
|
3453
|
+
const dt = new Date(value);
|
|
3454
|
+
dt.setFullYear(1970, 0, 1);
|
|
3455
|
+
return dt;
|
|
3456
|
+
}
|
|
3457
|
+
if (Is.string(value)) {
|
|
3458
|
+
const dt = new Date(value);
|
|
3459
|
+
if (!Number.isNaN(dt.getTime())) {
|
|
3460
|
+
const utc = Date.UTC(1970, 0, 1, dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
|
|
3461
|
+
return new Date(utc);
|
|
3462
|
+
}
|
|
3463
|
+
}
|
|
2798
3464
|
}
|
|
2799
3465
|
/**
|
|
2800
|
-
*
|
|
2801
|
-
* @param
|
|
2802
|
-
* @
|
|
2803
|
-
* @
|
|
3466
|
+
* Coerce the value to an object.
|
|
3467
|
+
* @param value The value to coerce.
|
|
3468
|
+
* @throws TypeError If the value can not be coerced.
|
|
3469
|
+
* @returns The value if it can be coerced.
|
|
2804
3470
|
*/
|
|
2805
|
-
static
|
|
2806
|
-
if (Is.
|
|
2807
|
-
|
|
3471
|
+
static object(value) {
|
|
3472
|
+
if (Is.undefined(value)) {
|
|
3473
|
+
return value;
|
|
3474
|
+
}
|
|
3475
|
+
if (Is.object(value)) {
|
|
3476
|
+
return value;
|
|
3477
|
+
}
|
|
3478
|
+
if (Is.stringValue(value)) {
|
|
3479
|
+
try {
|
|
3480
|
+
return JSON.parse(value);
|
|
3481
|
+
}
|
|
3482
|
+
catch { }
|
|
2808
3483
|
}
|
|
2809
3484
|
}
|
|
2810
3485
|
/**
|
|
2811
|
-
*
|
|
2812
|
-
* @param
|
|
2813
|
-
* @
|
|
3486
|
+
* Coerce the value to a Uint8Array.
|
|
3487
|
+
* @param value The value to coerce.
|
|
3488
|
+
* @throws TypeError If the value can not be coerced.
|
|
3489
|
+
* @returns The value if it can be coerced.
|
|
2814
3490
|
*/
|
|
2815
|
-
static
|
|
2816
|
-
if (Is.
|
|
2817
|
-
|
|
3491
|
+
static uint8Array(value) {
|
|
3492
|
+
if (Is.undefined(value)) {
|
|
3493
|
+
return value;
|
|
3494
|
+
}
|
|
3495
|
+
if (Is.string(value)) {
|
|
3496
|
+
if (Is.stringHex(value.toLowerCase(), true)) {
|
|
3497
|
+
return Converter.hexToBytes(value.toLowerCase());
|
|
3498
|
+
}
|
|
3499
|
+
if (Is.stringBase64(value)) {
|
|
3500
|
+
return Converter.base64ToBytes(value);
|
|
3501
|
+
}
|
|
2818
3502
|
}
|
|
2819
3503
|
}
|
|
2820
3504
|
/**
|
|
2821
|
-
*
|
|
2822
|
-
* @param
|
|
2823
|
-
* @param
|
|
2824
|
-
* @returns The
|
|
2825
|
-
*/
|
|
2826
|
-
static
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
3505
|
+
* Coerces a value based on the coercion type.
|
|
3506
|
+
* @param value The value to coerce.
|
|
3507
|
+
* @param type The coercion type to perform.
|
|
3508
|
+
* @returns The coerced value.
|
|
3509
|
+
*/
|
|
3510
|
+
static byType(value, type) {
|
|
3511
|
+
switch (type) {
|
|
3512
|
+
case CoerceType.String:
|
|
3513
|
+
return Coerce.string(value);
|
|
3514
|
+
case CoerceType.Number:
|
|
3515
|
+
return Coerce.number(value);
|
|
3516
|
+
case CoerceType.Integer:
|
|
3517
|
+
return Coerce.integer(value);
|
|
3518
|
+
case CoerceType.BigInt:
|
|
3519
|
+
return Coerce.bigint(value);
|
|
3520
|
+
case CoerceType.Boolean:
|
|
3521
|
+
return Coerce.boolean(value);
|
|
3522
|
+
case CoerceType.Date:
|
|
3523
|
+
return Coerce.date(value);
|
|
3524
|
+
case CoerceType.DateTime:
|
|
3525
|
+
return Coerce.dateTime(value);
|
|
3526
|
+
case CoerceType.Time:
|
|
3527
|
+
return Coerce.time(value);
|
|
3528
|
+
case CoerceType.Object:
|
|
3529
|
+
return Coerce.object(value);
|
|
3530
|
+
case CoerceType.Uint8Array:
|
|
3531
|
+
return Coerce.uint8Array(value);
|
|
3532
|
+
default:
|
|
3533
|
+
return value;
|
|
2833
3534
|
}
|
|
2834
|
-
return obj;
|
|
2835
3535
|
}
|
|
3536
|
+
}
|
|
3537
|
+
|
|
3538
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3539
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3540
|
+
/**
|
|
3541
|
+
* Class to help with filenames.
|
|
3542
|
+
*/
|
|
3543
|
+
class FilenameHelper {
|
|
2836
3544
|
/**
|
|
2837
|
-
*
|
|
2838
|
-
* @param
|
|
2839
|
-
* @
|
|
2840
|
-
* @returns The partial object.
|
|
3545
|
+
* Replaces any unsafe characters in the filename.
|
|
3546
|
+
* @param filename The filename to make safe.
|
|
3547
|
+
* @returns The safe filename.
|
|
2841
3548
|
*/
|
|
2842
|
-
static
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
delete result[key];
|
|
2847
|
-
}
|
|
2848
|
-
return result;
|
|
3549
|
+
static safeFilename(filename) {
|
|
3550
|
+
let safe = Coerce.string(filename);
|
|
3551
|
+
if (Is.empty(safe)) {
|
|
3552
|
+
return "";
|
|
2849
3553
|
}
|
|
2850
|
-
|
|
3554
|
+
// Common non filename characters
|
|
3555
|
+
safe = safe.replace(/["*/:<>?\\|]/g, "_");
|
|
3556
|
+
// Windows non filename characters
|
|
3557
|
+
safe = safe.replace(/^(con|prn|aux|nul|com\d|lpt\d)$/i, "_");
|
|
3558
|
+
// Control characters
|
|
3559
|
+
safe = safe.replace(/[\u0000-\u001F\u0080-\u009F]/g, "_");
|
|
3560
|
+
// Relative paths
|
|
3561
|
+
safe = safe.replace(/^\.+/, "_");
|
|
3562
|
+
// Trailing periods
|
|
3563
|
+
safe = safe.replace(/\.+$/, "");
|
|
3564
|
+
return safe;
|
|
2851
3565
|
}
|
|
2852
3566
|
}
|
|
2853
3567
|
|
|
@@ -2869,6 +3583,32 @@ class RandomHelper {
|
|
|
2869
3583
|
}
|
|
2870
3584
|
}
|
|
2871
3585
|
|
|
3586
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3587
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3588
|
+
/**
|
|
3589
|
+
* Class to help with uint8 arrays.
|
|
3590
|
+
*/
|
|
3591
|
+
class Uint8ArrayHelper {
|
|
3592
|
+
/**
|
|
3593
|
+
* Concatenate multiple arrays.
|
|
3594
|
+
* @param arrays The array to concatenate.
|
|
3595
|
+
* @returns The combined array.
|
|
3596
|
+
*/
|
|
3597
|
+
static concat(arrays) {
|
|
3598
|
+
let totalLength = 0;
|
|
3599
|
+
for (const array of arrays) {
|
|
3600
|
+
totalLength += array.length;
|
|
3601
|
+
}
|
|
3602
|
+
const concatBytes = new Uint8Array(totalLength);
|
|
3603
|
+
let offset = 0;
|
|
3604
|
+
for (const array of arrays) {
|
|
3605
|
+
concatBytes.set(array, offset);
|
|
3606
|
+
offset += array.length;
|
|
3607
|
+
}
|
|
3608
|
+
return concatBytes;
|
|
3609
|
+
}
|
|
3610
|
+
}
|
|
3611
|
+
|
|
2872
3612
|
// Copyright 2024 IOTA Stiftung.
|
|
2873
3613
|
// SPDX-License-Identifier: Apache-2.0.
|
|
2874
3614
|
/**
|
|
@@ -2881,7 +3621,7 @@ const CompressionType = {
|
|
|
2881
3621
|
*/
|
|
2882
3622
|
Gzip: "gzip",
|
|
2883
3623
|
/**
|
|
2884
|
-
*
|
|
3624
|
+
* Deflate.
|
|
2885
3625
|
*/
|
|
2886
3626
|
Deflate: "deflate"
|
|
2887
3627
|
};
|
|
@@ -3332,29 +4072,93 @@ class Urn {
|
|
|
3332
4072
|
* Cache the results from asynchronous requests.
|
|
3333
4073
|
*/
|
|
3334
4074
|
class AsyncCache {
|
|
3335
|
-
/**
|
|
3336
|
-
* Cache for the fetch requests.
|
|
3337
|
-
* @internal
|
|
3338
|
-
*/
|
|
3339
|
-
static _cache = {};
|
|
3340
4075
|
/**
|
|
3341
4076
|
* Execute an async request and cache the result.
|
|
3342
4077
|
* @param key The key for the entry in the cache.
|
|
3343
4078
|
* @param ttlMs The TTL of the entry in the cache.
|
|
3344
4079
|
* @param requestMethod The method to call if not cached.
|
|
4080
|
+
* @param cacheFailures Cache failure results, defaults to false.
|
|
3345
4081
|
* @returns The response.
|
|
3346
4082
|
*/
|
|
3347
|
-
static exec(key, ttlMs, requestMethod) {
|
|
4083
|
+
static exec(key, ttlMs, requestMethod, cacheFailures) {
|
|
3348
4084
|
const cacheEnabled = Is.integer(ttlMs) && ttlMs >= 0;
|
|
3349
4085
|
if (cacheEnabled) {
|
|
3350
4086
|
AsyncCache.cleanupExpired();
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
4087
|
+
const cache = AsyncCache.getSharedCache();
|
|
4088
|
+
// Do we have a cache entry for the key
|
|
4089
|
+
if (cache[key]) {
|
|
4090
|
+
if (!Is.empty(cache[key].result)) {
|
|
4091
|
+
// If the cache has already resulted in a value, resolve it
|
|
4092
|
+
return Promise.resolve(cache[key].result);
|
|
4093
|
+
}
|
|
4094
|
+
else if (!Is.empty(cache[key].error)) {
|
|
4095
|
+
// If the cache has already resulted in an error, reject it
|
|
4096
|
+
return Promise.reject(cache[key].error);
|
|
4097
|
+
}
|
|
4098
|
+
// Otherwise create a promise to return and store the resolver
|
|
4099
|
+
// and rejector in the cache entry, so that we can call then
|
|
4100
|
+
// when the request is done
|
|
4101
|
+
let storedResolve;
|
|
4102
|
+
let storedReject;
|
|
4103
|
+
const wait = new Promise((resolve, reject) => {
|
|
4104
|
+
storedResolve = resolve;
|
|
4105
|
+
storedReject = reject;
|
|
4106
|
+
});
|
|
4107
|
+
if (!Is.empty(storedResolve) && !Is.empty(storedReject)) {
|
|
4108
|
+
cache[key].promiseQueue.push({
|
|
4109
|
+
requestMethod,
|
|
4110
|
+
resolve: storedResolve,
|
|
4111
|
+
reject: storedReject
|
|
4112
|
+
});
|
|
4113
|
+
}
|
|
4114
|
+
return wait;
|
|
3356
4115
|
}
|
|
3357
|
-
|
|
4116
|
+
// If we don't have a cache entry, create a new one
|
|
4117
|
+
cache[key] = {
|
|
4118
|
+
promiseQueue: [],
|
|
4119
|
+
expires: ttlMs === 0 ? 0 : Date.now() + ttlMs
|
|
4120
|
+
};
|
|
4121
|
+
// Return a promise that wraps the original request method
|
|
4122
|
+
// so that we can store any results or errors in the cache
|
|
4123
|
+
return new Promise((resolve, reject) => {
|
|
4124
|
+
// Call the request method and store the result
|
|
4125
|
+
requestMethod()
|
|
4126
|
+
// eslint-disable-next-line promise/prefer-await-to-then
|
|
4127
|
+
.then(res => {
|
|
4128
|
+
// If the request was successful, store the result
|
|
4129
|
+
cache[key].result = res;
|
|
4130
|
+
// and resolve both this promise and all the waiters
|
|
4131
|
+
resolve(res);
|
|
4132
|
+
for (const wait of cache[key].promiseQueue) {
|
|
4133
|
+
wait.resolve(res);
|
|
4134
|
+
}
|
|
4135
|
+
return res;
|
|
4136
|
+
})
|
|
4137
|
+
// eslint-disable-next-line promise/prefer-await-to-then
|
|
4138
|
+
.catch((err) => {
|
|
4139
|
+
// Reject the promise
|
|
4140
|
+
reject(err);
|
|
4141
|
+
// Handle the waiters based on the cacheFailures flag
|
|
4142
|
+
if (cacheFailures ?? false) {
|
|
4143
|
+
// If we are caching failures, store the error and reject the waiters
|
|
4144
|
+
cache[key].error = err;
|
|
4145
|
+
for (const wait of cache[key].promiseQueue) {
|
|
4146
|
+
wait.reject(err);
|
|
4147
|
+
}
|
|
4148
|
+
// Clear the waiters so we don't call them again
|
|
4149
|
+
cache[key].promiseQueue = [];
|
|
4150
|
+
}
|
|
4151
|
+
else {
|
|
4152
|
+
// If not caching failures for any queued requests we
|
|
4153
|
+
// have no value to either resolve or reject, so we
|
|
4154
|
+
// just resolve with the original request method
|
|
4155
|
+
for (const wait of cache[key].promiseQueue) {
|
|
4156
|
+
wait.resolve(wait.requestMethod());
|
|
4157
|
+
}
|
|
4158
|
+
delete cache[key];
|
|
4159
|
+
}
|
|
4160
|
+
});
|
|
4161
|
+
});
|
|
3358
4162
|
}
|
|
3359
4163
|
}
|
|
3360
4164
|
/**
|
|
@@ -3363,41 +4167,80 @@ class AsyncCache {
|
|
|
3363
4167
|
* @returns The item from the cache if it exists.
|
|
3364
4168
|
*/
|
|
3365
4169
|
static async get(key) {
|
|
3366
|
-
|
|
4170
|
+
const cache = AsyncCache.getSharedCache();
|
|
4171
|
+
if (!Is.empty(cache[key].result)) {
|
|
4172
|
+
// If the cache has already resulted in a value, resolve it
|
|
4173
|
+
return cache[key].result;
|
|
4174
|
+
}
|
|
4175
|
+
else if (!Is.empty(cache[key].error)) {
|
|
4176
|
+
// If the cache has already resulted in an error, reject it
|
|
4177
|
+
throw cache[key].error;
|
|
4178
|
+
}
|
|
4179
|
+
}
|
|
4180
|
+
/**
|
|
4181
|
+
* Set an entry into the cache.
|
|
4182
|
+
* @param key The key to set in the cache.
|
|
4183
|
+
* @param value The value to set in the cache.
|
|
4184
|
+
* @param ttlMs The TTL of the entry in the cache in ms, defaults to 1s.
|
|
4185
|
+
* @returns Nothing.
|
|
4186
|
+
*/
|
|
4187
|
+
static async set(key, value, ttlMs) {
|
|
4188
|
+
const cache = AsyncCache.getSharedCache();
|
|
4189
|
+
cache[key] = {
|
|
4190
|
+
result: value,
|
|
4191
|
+
promiseQueue: [],
|
|
4192
|
+
expires: Date.now() + (ttlMs ?? 1000)
|
|
4193
|
+
};
|
|
3367
4194
|
}
|
|
3368
4195
|
/**
|
|
3369
4196
|
* Remove an entry from the cache.
|
|
3370
4197
|
* @param key The key to remove from the cache.
|
|
3371
4198
|
*/
|
|
3372
4199
|
static remove(key) {
|
|
3373
|
-
|
|
4200
|
+
const cache = AsyncCache.getSharedCache();
|
|
4201
|
+
delete cache[key];
|
|
3374
4202
|
}
|
|
3375
4203
|
/**
|
|
3376
4204
|
* Clear the cache.
|
|
3377
4205
|
* @param prefix Optional prefix to clear only entries with that prefix.
|
|
3378
4206
|
*/
|
|
3379
4207
|
static clearCache(prefix) {
|
|
4208
|
+
const cache = AsyncCache.getSharedCache();
|
|
3380
4209
|
if (Is.stringValue(prefix)) {
|
|
3381
|
-
for (const entry in
|
|
4210
|
+
for (const entry in cache) {
|
|
3382
4211
|
if (entry.startsWith(prefix)) {
|
|
3383
|
-
delete
|
|
4212
|
+
delete cache[entry];
|
|
3384
4213
|
}
|
|
3385
4214
|
}
|
|
3386
4215
|
}
|
|
3387
4216
|
else {
|
|
3388
|
-
|
|
4217
|
+
SharedStore.set("asyncCache", {});
|
|
3389
4218
|
}
|
|
3390
4219
|
}
|
|
3391
4220
|
/**
|
|
3392
4221
|
* Perform a cleanup of the expired entries in the cache.
|
|
3393
4222
|
*/
|
|
3394
4223
|
static cleanupExpired() {
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
4224
|
+
const cache = AsyncCache.getSharedCache();
|
|
4225
|
+
for (const entry in cache) {
|
|
4226
|
+
if (cache[entry].expires > 0 && cache[entry].expires < Date.now()) {
|
|
4227
|
+
delete cache[entry];
|
|
3398
4228
|
}
|
|
3399
4229
|
}
|
|
3400
4230
|
}
|
|
4231
|
+
/**
|
|
4232
|
+
* Get the shared cache.
|
|
4233
|
+
* @returns The shared cache.
|
|
4234
|
+
* @internal
|
|
4235
|
+
*/
|
|
4236
|
+
static getSharedCache() {
|
|
4237
|
+
let sharedCache = SharedStore.get("asyncCache");
|
|
4238
|
+
if (Is.undefined(sharedCache)) {
|
|
4239
|
+
sharedCache = {};
|
|
4240
|
+
SharedStore.set("asyncCache", sharedCache);
|
|
4241
|
+
}
|
|
4242
|
+
return sharedCache;
|
|
4243
|
+
}
|
|
3401
4244
|
}
|
|
3402
4245
|
|
|
3403
4246
|
/**
|
|
@@ -3419,15 +4262,14 @@ class Compression {
|
|
|
3419
4262
|
Guards.uint8Array(Compression._CLASS_NAME, "bytes", bytes);
|
|
3420
4263
|
Guards.arrayOneOf(Compression._CLASS_NAME, "type", type, Object.values(CompressionType));
|
|
3421
4264
|
const blob = new Blob([bytes]);
|
|
3422
|
-
const
|
|
3423
|
-
const
|
|
3424
|
-
const compressedBlob = await new Response(
|
|
3425
|
-
const
|
|
3426
|
-
const compressedBytes = new Uint8Array(ab);
|
|
4265
|
+
const compressionStream = new CompressionStream(type);
|
|
4266
|
+
const compressionPipe = blob.stream().pipeThrough(compressionStream);
|
|
4267
|
+
const compressedBlob = await new Response(compressionPipe).blob();
|
|
4268
|
+
const compressedBytes = new Uint8Array(await compressedBlob.arrayBuffer());
|
|
3427
4269
|
// GZIP header contains a byte which specifies the OS the
|
|
3428
4270
|
// compression was performed on. We set this to 3 (Unix) to ensure
|
|
3429
4271
|
// that we produce consistent results.
|
|
3430
|
-
if (type ===
|
|
4272
|
+
if (type === CompressionType.Gzip && compressedBytes.length >= 10) {
|
|
3431
4273
|
compressedBytes[9] = 3;
|
|
3432
4274
|
}
|
|
3433
4275
|
return compressedBytes;
|
|
@@ -3442,11 +4284,10 @@ class Compression {
|
|
|
3442
4284
|
Guards.uint8Array(Compression._CLASS_NAME, "compressedBytes", compressedBytes);
|
|
3443
4285
|
Guards.arrayOneOf(Compression._CLASS_NAME, "type", type, Object.values(CompressionType));
|
|
3444
4286
|
const blob = new Blob([compressedBytes]);
|
|
3445
|
-
const
|
|
3446
|
-
const
|
|
3447
|
-
const decompressedBlob = await new Response(
|
|
3448
|
-
|
|
3449
|
-
return new Uint8Array(ab);
|
|
4287
|
+
const decompressionStream = new DecompressionStream(type);
|
|
4288
|
+
const decompressionPipe = blob.stream().pipeThrough(decompressionStream);
|
|
4289
|
+
const decompressedBlob = await new Response(decompressionPipe).blob();
|
|
4290
|
+
return new Uint8Array(await decompressedBlob.bytes());
|
|
3450
4291
|
}
|
|
3451
4292
|
}
|
|
3452
4293
|
|
|
@@ -3504,6 +4345,7 @@ class Validation {
|
|
|
3504
4345
|
* @param options Additional options for the validation.
|
|
3505
4346
|
* @param options.minLength The minimum length of the string.
|
|
3506
4347
|
* @param options.maxLength The maximum length of the string.
|
|
4348
|
+
* @param options.format Specific format to check.
|
|
3507
4349
|
* @returns True if the value is a valid string.
|
|
3508
4350
|
*/
|
|
3509
4351
|
static string(property, value, failures, fieldNameResource, options) {
|
|
@@ -3522,6 +4364,47 @@ class Validation {
|
|
|
3522
4364
|
const maxLimitDefined = Is.integer(maxLength);
|
|
3523
4365
|
const belowMin = minLimitDefined && value.length < minLength;
|
|
3524
4366
|
const aboveMax = maxLimitDefined && value.length > maxLength;
|
|
4367
|
+
if (options?.format === "base58" && !Is.stringBase58(value)) {
|
|
4368
|
+
failures.push({
|
|
4369
|
+
property,
|
|
4370
|
+
reason: "validation.beTextBase58",
|
|
4371
|
+
properties: {
|
|
4372
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
4373
|
+
value
|
|
4374
|
+
}
|
|
4375
|
+
});
|
|
4376
|
+
}
|
|
4377
|
+
else if (options?.format === "base64" && !Is.stringBase64(value)) {
|
|
4378
|
+
failures.push({
|
|
4379
|
+
property,
|
|
4380
|
+
reason: "validation.beTextBase64",
|
|
4381
|
+
properties: {
|
|
4382
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
4383
|
+
value
|
|
4384
|
+
}
|
|
4385
|
+
});
|
|
4386
|
+
}
|
|
4387
|
+
else if (options?.format === "hex" && !Is.stringHex(value)) {
|
|
4388
|
+
failures.push({
|
|
4389
|
+
property,
|
|
4390
|
+
reason: "validation.beTextHex",
|
|
4391
|
+
properties: {
|
|
4392
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
4393
|
+
value
|
|
4394
|
+
}
|
|
4395
|
+
});
|
|
4396
|
+
}
|
|
4397
|
+
else if (Is.regexp(options?.format) && !options.format.test(value)) {
|
|
4398
|
+
failures.push({
|
|
4399
|
+
property,
|
|
4400
|
+
reason: "validation.beTextRegExp",
|
|
4401
|
+
properties: {
|
|
4402
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
4403
|
+
value,
|
|
4404
|
+
format: options?.format
|
|
4405
|
+
}
|
|
4406
|
+
});
|
|
4407
|
+
}
|
|
3525
4408
|
if (minLimitDefined && maxLimitDefined && (belowMin || aboveMax)) {
|
|
3526
4409
|
failures.push({
|
|
3527
4410
|
property,
|
|
@@ -4181,4 +5064,4 @@ class Validation {
|
|
|
4181
5064
|
}
|
|
4182
5065
|
}
|
|
4183
5066
|
|
|
4184
|
-
export { AlreadyExistsError, ArrayHelper, AsyncCache, Base32, Base64, Base64Url, BaseError, BitString, Coerce, ComponentFactory, Compression, CompressionType, ConflictError, Converter, ErrorHelper, Factory, FilenameHelper, GeneralError, GuardError, Guards, HexHelper, I18n, Is, JsonHelper, NotFoundError, NotImplementedError, NotSupportedError, ObjectHelper, RandomHelper, StringHelper, UnauthorizedError, UnprocessableError, Url, Urn, Validation, ValidationError };
|
|
5067
|
+
export { AlreadyExistsError, ArrayHelper, AsyncCache, Base32, Base58, Base64, Base64Url, BaseError, BitString, Coerce, CoerceType, ComponentFactory, Compression, CompressionType, ConflictError, Converter, EnvHelper, ErrorHelper, Factory, FilenameHelper, GeneralError, GuardError, Guards, HexHelper, I18n, Is, JsonHelper, NotFoundError, NotImplementedError, NotSupportedError, ObjectHelper, RandomHelper, SharedStore, StringHelper, Uint8ArrayHelper, UnauthorizedError, UnprocessableError, Url, Urn, Validation, ValidationError };
|