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