@twin.org/core 0.0.1-next.9 → 0.0.2-next.3
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 +1574 -833
- package/dist/esm/index.mjs +1571 -834
- package/dist/types/errors/baseError.d.ts +8 -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 +25 -0
- package/dist/types/helpers/uint8ArrayHelper.d.ts +11 -0
- package/dist/types/index.d.ts +6 -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/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 +323 -1
- package/docs/reference/classes/AlreadyExistsError.md +103 -27
- package/docs/reference/classes/ArrayHelper.md +71 -5
- package/docs/reference/classes/AsyncCache.md +75 -13
- package/docs/reference/classes/Base32.md +9 -5
- package/docs/reference/classes/Base58.md +9 -5
- package/docs/reference/classes/Base64.md +12 -6
- package/docs/reference/classes/Base64Url.md +9 -5
- package/docs/reference/classes/BaseError.md +101 -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 +106 -28
- package/docs/reference/classes/Converter.md +72 -28
- 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 +101 -27
- package/docs/reference/classes/GuardError.md +106 -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 +103 -27
- package/docs/reference/classes/NotImplementedError.md +97 -25
- package/docs/reference/classes/NotSupportedError.md +100 -26
- package/docs/reference/classes/ObjectHelper.md +197 -39
- 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 +100 -26
- package/docs/reference/classes/UnprocessableError.md +101 -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 +100 -26
- package/docs/reference/index.md +7 -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 +14 -1
- package/package.json +7 -7
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.
|
|
@@ -646,7 +669,10 @@ class BaseError extends Error {
|
|
|
646
669
|
let properties;
|
|
647
670
|
let inner;
|
|
648
671
|
let stack;
|
|
649
|
-
if (Is.object(err)) {
|
|
672
|
+
if (Is.object(err) && Is.stringValue(err.error)) {
|
|
673
|
+
message = err.error;
|
|
674
|
+
}
|
|
675
|
+
else if (Is.object(err)) {
|
|
650
676
|
if (Is.stringValue(err.name)) {
|
|
651
677
|
name = err.name;
|
|
652
678
|
}
|
|
@@ -666,9 +692,6 @@ class BaseError extends Error {
|
|
|
666
692
|
stack = err.stack;
|
|
667
693
|
}
|
|
668
694
|
}
|
|
669
|
-
else if (Is.object(err) && Is.stringValue(err.error)) {
|
|
670
|
-
message = err.error;
|
|
671
|
-
}
|
|
672
695
|
else if (Is.stringValue(err)) {
|
|
673
696
|
message = err;
|
|
674
697
|
}
|
|
@@ -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;
|
|
@@ -780,11 +803,23 @@ class BaseError extends Error {
|
|
|
780
803
|
static someErrorCode(error, code) {
|
|
781
804
|
return BaseError.flatten(error).some(e => BaseError.isErrorCode(e, code));
|
|
782
805
|
}
|
|
806
|
+
/**
|
|
807
|
+
* Is the error empty.
|
|
808
|
+
* @param err The error to check for being empty.
|
|
809
|
+
* @returns True if the error is empty.
|
|
810
|
+
*/
|
|
811
|
+
static isEmpty(err) {
|
|
812
|
+
return (!Is.stringValue(err.message) &&
|
|
813
|
+
!Is.stringValue(err.source) &&
|
|
814
|
+
!Is.objectValue(err.properties) &&
|
|
815
|
+
Is.empty(err.inner));
|
|
816
|
+
}
|
|
783
817
|
/**
|
|
784
818
|
* Serialize the error to the error model.
|
|
819
|
+
* @param includeStackTrace Whether to include the error stack in the model, defaults to false.
|
|
785
820
|
* @returns The error model.
|
|
786
821
|
*/
|
|
787
|
-
toJsonObject() {
|
|
822
|
+
toJsonObject(includeStackTrace) {
|
|
788
823
|
const err = {};
|
|
789
824
|
if (Is.stringValue(this.name)) {
|
|
790
825
|
err.name = this.name;
|
|
@@ -798,11 +833,11 @@ class BaseError extends Error {
|
|
|
798
833
|
if (Is.object(this.properties)) {
|
|
799
834
|
err.properties = this.properties;
|
|
800
835
|
}
|
|
801
|
-
if (Is.stringValue(this.stack)) {
|
|
836
|
+
if ((includeStackTrace ?? false) && Is.stringValue(this.stack)) {
|
|
802
837
|
err.stack = this.stack;
|
|
803
838
|
}
|
|
804
839
|
if (Is.notEmpty(this.inner)) {
|
|
805
|
-
err.inner = BaseError.fromError(this.inner).toJsonObject();
|
|
840
|
+
err.inner = BaseError.fromError(this.inner).toJsonObject(includeStackTrace);
|
|
806
841
|
}
|
|
807
842
|
return err;
|
|
808
843
|
}
|
|
@@ -1175,7 +1210,7 @@ class Base64 {
|
|
|
1175
1210
|
const maxChunkLength = 16383; // must be multiple of 3
|
|
1176
1211
|
// go through the array every three bytes, we'll deal with trailing stuff later
|
|
1177
1212
|
for (let i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
|
|
1178
|
-
parts.push(Base64.encodeChunk(bytes, i, i + maxChunkLength
|
|
1213
|
+
parts.push(Base64.encodeChunk(bytes, i, Math.min(i + maxChunkLength, len2)));
|
|
1179
1214
|
}
|
|
1180
1215
|
// pad the end with zeros, but make sure to not forget the extra bytes
|
|
1181
1216
|
if (extraBytes === 1) {
|
|
@@ -1471,6 +1506,49 @@ class ValidationError extends BaseError {
|
|
|
1471
1506
|
}
|
|
1472
1507
|
}
|
|
1473
1508
|
|
|
1509
|
+
/**
|
|
1510
|
+
* Class to help with arrays.
|
|
1511
|
+
*/
|
|
1512
|
+
class ArrayHelper {
|
|
1513
|
+
/**
|
|
1514
|
+
* Do the two arrays match.
|
|
1515
|
+
* @param arr1 The first array.
|
|
1516
|
+
* @param arr2 The second array.
|
|
1517
|
+
* @returns True if both arrays are empty of have the same values.
|
|
1518
|
+
*/
|
|
1519
|
+
static matches(arr1, arr2) {
|
|
1520
|
+
if (Is.empty(arr1) && Is.empty(arr2)) {
|
|
1521
|
+
return true;
|
|
1522
|
+
}
|
|
1523
|
+
if (!((Is.array(arr1) && Is.array(arr2)) || (Is.typedArray(arr1) && Is.typedArray(arr2)))) {
|
|
1524
|
+
return false;
|
|
1525
|
+
}
|
|
1526
|
+
if (arr1.length !== arr2.length) {
|
|
1527
|
+
return false;
|
|
1528
|
+
}
|
|
1529
|
+
for (let i = 0; i < arr1.length; i++) {
|
|
1530
|
+
if (arr1[i] !== arr2[i]) {
|
|
1531
|
+
return false;
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
return true;
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
* Convert an object or array to an array.
|
|
1538
|
+
* @param value The object or array to convert.
|
|
1539
|
+
* @returns The array.
|
|
1540
|
+
*/
|
|
1541
|
+
static fromObjectOrArray(value) {
|
|
1542
|
+
if (Is.empty(value)) {
|
|
1543
|
+
return undefined;
|
|
1544
|
+
}
|
|
1545
|
+
if (Is.array(value)) {
|
|
1546
|
+
return value;
|
|
1547
|
+
}
|
|
1548
|
+
return [value];
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1474
1552
|
// Copyright 2024 IOTA Stiftung.
|
|
1475
1553
|
// SPDX-License-Identifier: Apache-2.0.
|
|
1476
1554
|
/**
|
|
@@ -1516,6 +1594,18 @@ class Guards {
|
|
|
1516
1594
|
throw new GuardError(source, "guard.stringEmpty", property, value);
|
|
1517
1595
|
}
|
|
1518
1596
|
}
|
|
1597
|
+
/**
|
|
1598
|
+
* Is the property a JSON value.
|
|
1599
|
+
* @param source The source of the error.
|
|
1600
|
+
* @param property The name of the property.
|
|
1601
|
+
* @param value The value to test.
|
|
1602
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1603
|
+
*/
|
|
1604
|
+
static json(source, property, value) {
|
|
1605
|
+
if (!Is.json(value)) {
|
|
1606
|
+
throw new GuardError(source, "guard.stringJson", property, value);
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1519
1609
|
/**
|
|
1520
1610
|
* Is the property a base64 string.
|
|
1521
1611
|
* @param source The source of the error.
|
|
@@ -1540,6 +1630,18 @@ class Guards {
|
|
|
1540
1630
|
throw new GuardError(source, "guard.base64Url", property, value);
|
|
1541
1631
|
}
|
|
1542
1632
|
}
|
|
1633
|
+
/**
|
|
1634
|
+
* Is the property a base58 string.
|
|
1635
|
+
* @param source The source of the error.
|
|
1636
|
+
* @param property The name of the property.
|
|
1637
|
+
* @param value The value to test.
|
|
1638
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1639
|
+
*/
|
|
1640
|
+
static stringBase58(source, property, value) {
|
|
1641
|
+
if (!Is.stringBase58(value)) {
|
|
1642
|
+
throw new GuardError(source, "guard.base58", property, value);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1543
1645
|
/**
|
|
1544
1646
|
* Is the property a string with a hex value.
|
|
1545
1647
|
* @param source The source of the error.
|
|
@@ -1729,6 +1831,50 @@ class Guards {
|
|
|
1729
1831
|
throw new GuardError(source, "guard.arrayOneOf", property, value, options.join(", "));
|
|
1730
1832
|
}
|
|
1731
1833
|
}
|
|
1834
|
+
/**
|
|
1835
|
+
* Does the array start with the specified data.
|
|
1836
|
+
* @param source The source of the error.
|
|
1837
|
+
* @param property The name of the property.
|
|
1838
|
+
* @param value The value to test.
|
|
1839
|
+
* @param startValues The values that must start the array.
|
|
1840
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1841
|
+
*/
|
|
1842
|
+
static arrayStartsWith(source, property, value, startValues) {
|
|
1843
|
+
if (!Is.arrayValue(value)) {
|
|
1844
|
+
throw new GuardError(source, "guard.array", property, value);
|
|
1845
|
+
}
|
|
1846
|
+
const startValuesArray = ArrayHelper.fromObjectOrArray(startValues);
|
|
1847
|
+
if (!Is.arrayValue(startValuesArray)) {
|
|
1848
|
+
throw new GuardError(source, "guard.array", property, startValuesArray);
|
|
1849
|
+
}
|
|
1850
|
+
for (let i = 0; i < startValuesArray.length; i++) {
|
|
1851
|
+
if (value[i] !== startValuesArray[i]) {
|
|
1852
|
+
throw new GuardError(source, "guard.arrayStartsWith", property, value, startValuesArray.join(", "));
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
/**
|
|
1857
|
+
* Does the array end with the specified data.
|
|
1858
|
+
* @param source The source of the error.
|
|
1859
|
+
* @param property The name of the property.
|
|
1860
|
+
* @param value The value to test.
|
|
1861
|
+
* @param endValues The values that must end the array.
|
|
1862
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1863
|
+
*/
|
|
1864
|
+
static arrayEndsWith(source, property, value, endValues) {
|
|
1865
|
+
if (!Is.arrayValue(value)) {
|
|
1866
|
+
throw new GuardError(source, "guard.array", property, value);
|
|
1867
|
+
}
|
|
1868
|
+
const endValuesArray = ArrayHelper.fromObjectOrArray(endValues);
|
|
1869
|
+
if (!Is.arrayValue(endValuesArray)) {
|
|
1870
|
+
throw new GuardError(source, "guard.array", property, endValuesArray);
|
|
1871
|
+
}
|
|
1872
|
+
for (let i = 0; i < endValuesArray.length; i++) {
|
|
1873
|
+
if (value[value.length - i - 1] !== endValuesArray[endValuesArray.length - i - 1]) {
|
|
1874
|
+
throw new GuardError(source, "guard.arrayEndsWith", property, value, endValuesArray.join(", "));
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1732
1878
|
/**
|
|
1733
1879
|
* Is the property a Uint8Array.
|
|
1734
1880
|
* @param source The source of the error.
|
|
@@ -1769,6 +1915,53 @@ class Guards {
|
|
|
1769
1915
|
}
|
|
1770
1916
|
}
|
|
1771
1917
|
|
|
1918
|
+
// Copyright 2024 IOTA Stiftung.
|
|
1919
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
1920
|
+
/**
|
|
1921
|
+
* Provide a store for shared objects which can be accesses through multiple
|
|
1922
|
+
* instance loads of a packages.
|
|
1923
|
+
*/
|
|
1924
|
+
class SharedStore {
|
|
1925
|
+
/**
|
|
1926
|
+
* Get a property from the shared store.
|
|
1927
|
+
* @param prop The name of the property to get.
|
|
1928
|
+
* @returns The property if it exists.
|
|
1929
|
+
*/
|
|
1930
|
+
static get(prop) {
|
|
1931
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1932
|
+
const shared = globalThis.__TWIN_SHARED__;
|
|
1933
|
+
if (Is.undefined(shared)) {
|
|
1934
|
+
return;
|
|
1935
|
+
}
|
|
1936
|
+
return shared[prop];
|
|
1937
|
+
}
|
|
1938
|
+
/**
|
|
1939
|
+
* Set the property in the shared store.
|
|
1940
|
+
* @param prop The name of the property to set.
|
|
1941
|
+
* @param value The value to set.
|
|
1942
|
+
*/
|
|
1943
|
+
static set(prop, value) {
|
|
1944
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1945
|
+
if (Is.undefined(globalThis.__TWIN_SHARED__)) {
|
|
1946
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1947
|
+
globalThis.__TWIN_SHARED__ = {};
|
|
1948
|
+
}
|
|
1949
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1950
|
+
globalThis.__TWIN_SHARED__[prop] = value;
|
|
1951
|
+
}
|
|
1952
|
+
/**
|
|
1953
|
+
* Remove a property from the shared store.
|
|
1954
|
+
* @param prop The name of the property to remove.
|
|
1955
|
+
*/
|
|
1956
|
+
static remove(prop) {
|
|
1957
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1958
|
+
const shared = globalThis.__TWIN_SHARED__;
|
|
1959
|
+
if (!Is.undefined(shared)) {
|
|
1960
|
+
delete shared[prop];
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1772
1965
|
/**
|
|
1773
1966
|
* Factory for creating implementation of generic types.
|
|
1774
1967
|
*/
|
|
@@ -1778,11 +1971,6 @@ class Factory {
|
|
|
1778
1971
|
* @internal
|
|
1779
1972
|
*/
|
|
1780
1973
|
static _CLASS_NAME = "Factory";
|
|
1781
|
-
/**
|
|
1782
|
-
* Store all the created factories.
|
|
1783
|
-
* @internal
|
|
1784
|
-
*/
|
|
1785
|
-
static _factories = {};
|
|
1786
1974
|
/**
|
|
1787
1975
|
* Type name for the instances.
|
|
1788
1976
|
* @internal
|
|
@@ -1836,10 +2024,41 @@ class Factory {
|
|
|
1836
2024
|
* @returns The factory instance.
|
|
1837
2025
|
*/
|
|
1838
2026
|
static createFactory(typeName, autoInstance = false, matcher) {
|
|
1839
|
-
|
|
1840
|
-
|
|
2027
|
+
const factories = Factory.getFactories();
|
|
2028
|
+
if (Is.undefined(factories[typeName])) {
|
|
2029
|
+
factories[typeName] = new Factory(typeName, autoInstance, matcher);
|
|
2030
|
+
}
|
|
2031
|
+
return factories[typeName];
|
|
2032
|
+
}
|
|
2033
|
+
/**
|
|
2034
|
+
* Get all the factories.
|
|
2035
|
+
* @returns All the factories.
|
|
2036
|
+
*/
|
|
2037
|
+
static getFactories() {
|
|
2038
|
+
let factories = SharedStore.get("factories");
|
|
2039
|
+
if (Is.undefined(factories)) {
|
|
2040
|
+
factories = {};
|
|
2041
|
+
SharedStore.set("factories", factories);
|
|
2042
|
+
}
|
|
2043
|
+
return factories;
|
|
2044
|
+
}
|
|
2045
|
+
/**
|
|
2046
|
+
* Reset all the factories, which removes any created instances, but not the registrations.
|
|
2047
|
+
*/
|
|
2048
|
+
static resetFactories() {
|
|
2049
|
+
const factories = Factory.getFactories();
|
|
2050
|
+
for (const typeName in factories) {
|
|
2051
|
+
factories[typeName].reset();
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
/**
|
|
2055
|
+
* Clear all the factories, which removes anything registered with the factories.
|
|
2056
|
+
*/
|
|
2057
|
+
static clearFactories() {
|
|
2058
|
+
const factories = Factory.getFactories();
|
|
2059
|
+
for (const typeName in factories) {
|
|
2060
|
+
factories[typeName].clear();
|
|
1841
2061
|
}
|
|
1842
|
-
return Factory._factories[typeName];
|
|
1843
2062
|
}
|
|
1844
2063
|
/**
|
|
1845
2064
|
* Register a new generator.
|
|
@@ -1912,7 +2131,7 @@ class Factory {
|
|
|
1912
2131
|
}
|
|
1913
2132
|
}
|
|
1914
2133
|
/**
|
|
1915
|
-
*
|
|
2134
|
+
* Remove all the instances and leave the generators intact.
|
|
1916
2135
|
*/
|
|
1917
2136
|
reset() {
|
|
1918
2137
|
for (const name in this._generators) {
|
|
@@ -1920,6 +2139,14 @@ class Factory {
|
|
|
1920
2139
|
}
|
|
1921
2140
|
this._instances = {};
|
|
1922
2141
|
}
|
|
2142
|
+
/**
|
|
2143
|
+
* Remove all the instances and the generators.
|
|
2144
|
+
*/
|
|
2145
|
+
clear() {
|
|
2146
|
+
this._instances = {};
|
|
2147
|
+
this._generators = {};
|
|
2148
|
+
this._orderCounter = 0;
|
|
2149
|
+
}
|
|
1923
2150
|
/**
|
|
1924
2151
|
* Get all the instances as a map.
|
|
1925
2152
|
* @returns The instances as a map.
|
|
@@ -1994,518 +2221,265 @@ const ComponentFactory = Factory.createFactory("component");
|
|
|
1994
2221
|
|
|
1995
2222
|
// Copyright 2024 IOTA Stiftung.
|
|
1996
2223
|
// SPDX-License-Identifier: Apache-2.0.
|
|
2224
|
+
/* eslint-disable no-bitwise */
|
|
1997
2225
|
/**
|
|
1998
|
-
*
|
|
1999
|
-
*/
|
|
2000
|
-
class ArrayHelper {
|
|
2001
|
-
/**
|
|
2002
|
-
* Do the two arrays match.
|
|
2003
|
-
* @param arr1 The first array.
|
|
2004
|
-
* @param arr2 The second array.
|
|
2005
|
-
* @returns True if both arrays are empty of have the same values.
|
|
2006
|
-
*/
|
|
2007
|
-
static matches(arr1, arr2) {
|
|
2008
|
-
if (Is.empty(arr1) && Is.empty(arr2)) {
|
|
2009
|
-
return true;
|
|
2010
|
-
}
|
|
2011
|
-
if (!((Is.array(arr1) && Is.array(arr2)) || (Is.typedArray(arr1) && Is.typedArray(arr2)))) {
|
|
2012
|
-
return false;
|
|
2013
|
-
}
|
|
2014
|
-
if (arr1.length !== arr2.length) {
|
|
2015
|
-
return false;
|
|
2016
|
-
}
|
|
2017
|
-
for (let i = 0; i < arr1.length; i++) {
|
|
2018
|
-
if (arr1[i] !== arr2[i]) {
|
|
2019
|
-
return false;
|
|
2020
|
-
}
|
|
2021
|
-
}
|
|
2022
|
-
return true;
|
|
2023
|
-
}
|
|
2024
|
-
}
|
|
2025
|
-
|
|
2026
|
-
// Copyright 2024 IOTA Stiftung.
|
|
2027
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
2028
|
-
/**
|
|
2029
|
-
* Class to perform internationalization.
|
|
2226
|
+
* Convert arrays to and from different formats.
|
|
2030
2227
|
*/
|
|
2031
|
-
class
|
|
2032
|
-
/**
|
|
2033
|
-
* The default translation.
|
|
2034
|
-
*/
|
|
2035
|
-
static DEFAULT_LOCALE = "en";
|
|
2036
|
-
/**
|
|
2037
|
-
* Dictionaries for lookups.
|
|
2038
|
-
* @internal
|
|
2039
|
-
*/
|
|
2040
|
-
static _localeDictionaries = {};
|
|
2041
|
-
/**
|
|
2042
|
-
* The current locale.
|
|
2043
|
-
* @internal
|
|
2044
|
-
*/
|
|
2045
|
-
static _currentLocale = I18n.DEFAULT_LOCALE;
|
|
2228
|
+
class Converter {
|
|
2046
2229
|
/**
|
|
2047
|
-
*
|
|
2230
|
+
* Lookup table for encoding.
|
|
2048
2231
|
* @internal
|
|
2049
2232
|
*/
|
|
2050
|
-
static
|
|
2233
|
+
static _ENCODE_LOOKUP;
|
|
2051
2234
|
/**
|
|
2052
|
-
*
|
|
2235
|
+
* Lookup table for decoding.
|
|
2053
2236
|
* @internal
|
|
2054
2237
|
*/
|
|
2055
|
-
static
|
|
2238
|
+
static _DECODE_LOOKUP;
|
|
2056
2239
|
/**
|
|
2057
|
-
*
|
|
2058
|
-
* @param
|
|
2240
|
+
* Encode a raw array to UTF8 string.
|
|
2241
|
+
* @param array The bytes to encode.
|
|
2242
|
+
* @param startIndex The index to start in the bytes.
|
|
2243
|
+
* @param length The length of bytes to read.
|
|
2244
|
+
* @returns The array formatted as UTF8.
|
|
2059
2245
|
*/
|
|
2060
|
-
static
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2246
|
+
static bytesToUtf8(array, startIndex, length) {
|
|
2247
|
+
const start = startIndex ?? 0;
|
|
2248
|
+
const len = length ?? array.length;
|
|
2249
|
+
let str = "";
|
|
2250
|
+
for (let i = start; i < start + len; i++) {
|
|
2251
|
+
const value = array[i];
|
|
2252
|
+
if (value < 0x80) {
|
|
2253
|
+
str += String.fromCharCode(value);
|
|
2254
|
+
}
|
|
2255
|
+
else if (value > 0xbf && value < 0xe0) {
|
|
2256
|
+
str += String.fromCharCode(((value & 0x1f) << 6) | (array[i + 1] & 0x3f));
|
|
2257
|
+
i += 1;
|
|
2258
|
+
}
|
|
2259
|
+
else if (value > 0xdf && value < 0xf0) {
|
|
2260
|
+
str += String.fromCharCode(((value & 0x0f) << 12) | ((array[i + 1] & 0x3f) << 6) | (array[i + 2] & 0x3f));
|
|
2261
|
+
i += 2;
|
|
2262
|
+
}
|
|
2263
|
+
else {
|
|
2264
|
+
// surrogate pair
|
|
2265
|
+
const charCode = (((value & 0x07) << 18) |
|
|
2266
|
+
((array[i + 1] & 0x3f) << 12) |
|
|
2267
|
+
((array[i + 2] & 0x3f) << 6) |
|
|
2268
|
+
(array[i + 3] & 0x3f)) -
|
|
2269
|
+
0x010000;
|
|
2270
|
+
str += String.fromCharCode((charCode >> 10) | 0xd800, (charCode & 0x03ff) | 0xdc00);
|
|
2271
|
+
i += 3;
|
|
2272
|
+
}
|
|
2064
2273
|
}
|
|
2274
|
+
return str;
|
|
2065
2275
|
}
|
|
2066
2276
|
/**
|
|
2067
|
-
*
|
|
2068
|
-
* @
|
|
2277
|
+
* Convert a UTF8 string to raw array.
|
|
2278
|
+
* @param utf8 The text to decode.
|
|
2279
|
+
* @returns The array.
|
|
2069
2280
|
*/
|
|
2070
|
-
static
|
|
2071
|
-
|
|
2281
|
+
static utf8ToBytes(utf8) {
|
|
2282
|
+
const bytes = [];
|
|
2283
|
+
for (let i = 0; i < utf8.length; i++) {
|
|
2284
|
+
let charCode = utf8.charCodeAt(i);
|
|
2285
|
+
if (charCode < 0x80) {
|
|
2286
|
+
bytes.push(charCode);
|
|
2287
|
+
}
|
|
2288
|
+
else if (charCode < 0x800) {
|
|
2289
|
+
bytes.push(0xc0 | (charCode >> 6), 0x80 | (charCode & 0x3f));
|
|
2290
|
+
}
|
|
2291
|
+
else if (charCode < 0xd800 || charCode >= 0xe000) {
|
|
2292
|
+
bytes.push(0xe0 | (charCode >> 12), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f));
|
|
2293
|
+
}
|
|
2294
|
+
else {
|
|
2295
|
+
// surrogate pair
|
|
2296
|
+
i++;
|
|
2297
|
+
// UTF-16 encodes 0x10000-0x10FFFF by
|
|
2298
|
+
// subtracting 0x10000 and splitting the
|
|
2299
|
+
// 20 bits of 0x0-0xFFFFF into two halves
|
|
2300
|
+
charCode = 0x10000 + (((charCode & 0x3ff) << 10) | (utf8.charCodeAt(i) & 0x3ff));
|
|
2301
|
+
bytes.push(0xf0 | (charCode >> 18), 0x80 | ((charCode >> 12) & 0x3f), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f));
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
return Uint8Array.from(bytes);
|
|
2072
2305
|
}
|
|
2073
2306
|
/**
|
|
2074
|
-
*
|
|
2075
|
-
* @param
|
|
2076
|
-
* @param
|
|
2307
|
+
* Encode a raw array to hex string.
|
|
2308
|
+
* @param array The bytes to encode.
|
|
2309
|
+
* @param includePrefix Include the 0x prefix on the returned hex.
|
|
2310
|
+
* @param startIndex The index to start in the bytes.
|
|
2311
|
+
* @param length The length of bytes to read.
|
|
2312
|
+
* @param reverse Reverse the combine direction.
|
|
2313
|
+
* @returns The array formatted as hex.
|
|
2077
2314
|
*/
|
|
2078
|
-
static
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2315
|
+
static bytesToHex(array, includePrefix = false, startIndex, length, reverse) {
|
|
2316
|
+
let hex = "";
|
|
2317
|
+
this.buildHexLookups();
|
|
2318
|
+
if (Converter._ENCODE_LOOKUP) {
|
|
2319
|
+
const len = length ?? array.length;
|
|
2320
|
+
const start = startIndex ?? 0;
|
|
2321
|
+
if (reverse) {
|
|
2322
|
+
for (let i = 0; i < len; i++) {
|
|
2323
|
+
hex = Converter._ENCODE_LOOKUP[array[start + i]] + hex;
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
else {
|
|
2327
|
+
for (let i = 0; i < len; i++) {
|
|
2328
|
+
hex += Converter._ENCODE_LOOKUP[array[start + i]];
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2084
2331
|
}
|
|
2332
|
+
return includePrefix ? HexHelper.addPrefix(hex) : hex;
|
|
2085
2333
|
}
|
|
2086
2334
|
/**
|
|
2087
|
-
*
|
|
2088
|
-
* @param
|
|
2089
|
-
* @
|
|
2335
|
+
* Decode a hex string to raw array.
|
|
2336
|
+
* @param hex The hex to decode.
|
|
2337
|
+
* @param reverse Store the characters in reverse.
|
|
2338
|
+
* @returns The array.
|
|
2090
2339
|
*/
|
|
2091
|
-
static
|
|
2092
|
-
|
|
2340
|
+
static hexToBytes(hex, reverse) {
|
|
2341
|
+
const strippedHex = HexHelper.stripPrefix(hex);
|
|
2342
|
+
const sizeof = strippedHex.length >> 1;
|
|
2343
|
+
const length = sizeof << 1;
|
|
2344
|
+
const array = new Uint8Array(sizeof);
|
|
2345
|
+
this.buildHexLookups();
|
|
2346
|
+
if (Converter._DECODE_LOOKUP) {
|
|
2347
|
+
let i = 0;
|
|
2348
|
+
let n = 0;
|
|
2349
|
+
while (i < length) {
|
|
2350
|
+
array[n++] =
|
|
2351
|
+
(Converter._DECODE_LOOKUP[strippedHex.charCodeAt(i++)] << 4) |
|
|
2352
|
+
Converter._DECODE_LOOKUP[strippedHex.charCodeAt(i++)];
|
|
2353
|
+
}
|
|
2354
|
+
if (reverse) {
|
|
2355
|
+
array.reverse();
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
return array;
|
|
2093
2359
|
}
|
|
2094
2360
|
/**
|
|
2095
|
-
*
|
|
2096
|
-
* @
|
|
2361
|
+
* Convert the UTF8 to hex.
|
|
2362
|
+
* @param utf8 The text to convert.
|
|
2363
|
+
* @param includePrefix Include the 0x prefix on the returned hex.
|
|
2364
|
+
* @returns The hex version of the bytes.
|
|
2097
2365
|
*/
|
|
2098
|
-
static
|
|
2099
|
-
|
|
2366
|
+
static utf8ToHex(utf8, includePrefix = false) {
|
|
2367
|
+
const hex = Converter.bytesToHex(Converter.utf8ToBytes(utf8));
|
|
2368
|
+
return includePrefix ? HexHelper.addPrefix(hex) : hex;
|
|
2100
2369
|
}
|
|
2101
2370
|
/**
|
|
2102
|
-
*
|
|
2103
|
-
* @param
|
|
2104
|
-
* @
|
|
2371
|
+
* Convert the hex text to text.
|
|
2372
|
+
* @param hex The hex to convert.
|
|
2373
|
+
* @returns The UTF8 version of the bytes.
|
|
2105
2374
|
*/
|
|
2106
|
-
static
|
|
2107
|
-
|
|
2375
|
+
static hexToUtf8(hex) {
|
|
2376
|
+
return Converter.bytesToUtf8(Converter.hexToBytes(HexHelper.stripPrefix(hex)));
|
|
2108
2377
|
}
|
|
2109
2378
|
/**
|
|
2110
|
-
*
|
|
2111
|
-
* @param
|
|
2379
|
+
* Convert bytes to binary string.
|
|
2380
|
+
* @param bytes The bytes to convert.
|
|
2381
|
+
* @returns A binary string of the bytes.
|
|
2112
2382
|
*/
|
|
2113
|
-
static
|
|
2114
|
-
|
|
2383
|
+
static bytesToBinary(bytes) {
|
|
2384
|
+
const b = [];
|
|
2385
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2386
|
+
b.push(bytes[i].toString(2).padStart(8, "0"));
|
|
2387
|
+
}
|
|
2388
|
+
return b.join("");
|
|
2115
2389
|
}
|
|
2116
2390
|
/**
|
|
2117
|
-
*
|
|
2118
|
-
* @param
|
|
2119
|
-
* @
|
|
2391
|
+
* Convert a binary string to bytes.
|
|
2392
|
+
* @param binary The binary string.
|
|
2393
|
+
* @returns The bytes.
|
|
2120
2394
|
*/
|
|
2121
|
-
static
|
|
2122
|
-
|
|
2395
|
+
static binaryToBytes(binary) {
|
|
2396
|
+
const bytes = new Uint8Array(Math.ceil(binary.length / 8));
|
|
2397
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2398
|
+
bytes[i] = Number.parseInt(binary.slice(i * 8, (i + 1) * 8), 2);
|
|
2399
|
+
}
|
|
2400
|
+
return bytes;
|
|
2123
2401
|
}
|
|
2124
2402
|
/**
|
|
2125
|
-
*
|
|
2126
|
-
* @param
|
|
2403
|
+
* Convert bytes to base64 string.
|
|
2404
|
+
* @param bytes The bytes to convert.
|
|
2405
|
+
* @returns A base64 string of the bytes.
|
|
2127
2406
|
*/
|
|
2128
|
-
static
|
|
2129
|
-
|
|
2407
|
+
static bytesToBase64(bytes) {
|
|
2408
|
+
return Base64.encode(bytes);
|
|
2130
2409
|
}
|
|
2131
2410
|
/**
|
|
2132
|
-
*
|
|
2133
|
-
* @param
|
|
2134
|
-
* @
|
|
2135
|
-
* @param overrideLocale Override the locale.
|
|
2136
|
-
* @returns The formatted string.
|
|
2411
|
+
* Convert a base64 string to bytes.
|
|
2412
|
+
* @param base64 The base64 string.
|
|
2413
|
+
* @returns The bytes.
|
|
2137
2414
|
*/
|
|
2138
|
-
static
|
|
2139
|
-
|
|
2140
|
-
if (cl.startsWith("debug-")) {
|
|
2141
|
-
cl = I18n.DEFAULT_LOCALE;
|
|
2142
|
-
}
|
|
2143
|
-
if (!I18n._localeDictionaries[cl]) {
|
|
2144
|
-
return `!!Missing ${cl}`;
|
|
2145
|
-
}
|
|
2146
|
-
if (!I18n._localeDictionaries[cl][key]) {
|
|
2147
|
-
return `!!Missing ${cl}.${key}`;
|
|
2148
|
-
}
|
|
2149
|
-
if (I18n._currentLocale === "debug-k") {
|
|
2150
|
-
return key;
|
|
2151
|
-
}
|
|
2152
|
-
let ret = new intlMessageformat.IntlMessageFormat(I18n._localeDictionaries[cl][key], cl).format(values);
|
|
2153
|
-
if (I18n._currentLocale === "debug-x") {
|
|
2154
|
-
ret = ret.replace(/[a-z]/g, "x").replace(/[A-Z]/g, "x").replace(/\d/g, "n");
|
|
2155
|
-
}
|
|
2156
|
-
return ret;
|
|
2415
|
+
static base64ToBytes(base64) {
|
|
2416
|
+
return Base64.decode(base64);
|
|
2157
2417
|
}
|
|
2158
2418
|
/**
|
|
2159
|
-
*
|
|
2160
|
-
* @param
|
|
2161
|
-
* @returns
|
|
2419
|
+
* Convert bytes to base64 url string.
|
|
2420
|
+
* @param bytes The bytes to convert.
|
|
2421
|
+
* @returns A base64 url string of the bytes.
|
|
2162
2422
|
*/
|
|
2163
|
-
static
|
|
2164
|
-
return
|
|
2423
|
+
static bytesToBase64Url(bytes) {
|
|
2424
|
+
return Base64Url.encode(bytes);
|
|
2165
2425
|
}
|
|
2166
2426
|
/**
|
|
2167
|
-
*
|
|
2168
|
-
* @param
|
|
2169
|
-
* @
|
|
2170
|
-
* @param mergedKeys The merged keys dictionary to populate.
|
|
2171
|
-
* @internal
|
|
2427
|
+
* Convert a base64 url string to bytes.
|
|
2428
|
+
* @param base64Url The base64 url string.
|
|
2429
|
+
* @returns The bytes.
|
|
2172
2430
|
*/
|
|
2173
|
-
static
|
|
2174
|
-
|
|
2175
|
-
const val = translation[key];
|
|
2176
|
-
const mergedPath = propertyPath.length > 0 ? `${propertyPath}.${key}` : key;
|
|
2177
|
-
if (Is.string(val)) {
|
|
2178
|
-
mergedKeys[mergedPath] = val;
|
|
2179
|
-
}
|
|
2180
|
-
else if (Is.object(val)) {
|
|
2181
|
-
I18n.flattenTranslationKeys(val, mergedPath, mergedKeys);
|
|
2182
|
-
}
|
|
2183
|
-
}
|
|
2431
|
+
static base64UrlToBytes(base64Url) {
|
|
2432
|
+
return Base64Url.decode(base64Url);
|
|
2184
2433
|
}
|
|
2185
|
-
}
|
|
2186
|
-
|
|
2187
|
-
// Copyright 2024 IOTA Stiftung.
|
|
2188
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
2189
|
-
/**
|
|
2190
|
-
* Error helper functions.
|
|
2191
|
-
*/
|
|
2192
|
-
class ErrorHelper {
|
|
2193
2434
|
/**
|
|
2194
|
-
*
|
|
2195
|
-
* @param
|
|
2196
|
-
* @returns
|
|
2435
|
+
* Convert bytes to base58 string.
|
|
2436
|
+
* @param bytes The bytes to convert.
|
|
2437
|
+
* @returns A base58 string of the bytes.
|
|
2197
2438
|
*/
|
|
2198
|
-
static
|
|
2199
|
-
return
|
|
2439
|
+
static bytesToBase58(bytes) {
|
|
2440
|
+
return Base58.encode(bytes);
|
|
2200
2441
|
}
|
|
2201
2442
|
/**
|
|
2202
|
-
*
|
|
2203
|
-
* @param
|
|
2204
|
-
* @returns The
|
|
2443
|
+
* Convert a base58 string to bytes.
|
|
2444
|
+
* @param base58 The base58 string.
|
|
2445
|
+
* @returns The bytes.
|
|
2205
2446
|
*/
|
|
2206
|
-
static
|
|
2207
|
-
|
|
2208
|
-
if (Is.notEmpty(error)) {
|
|
2209
|
-
const errors = BaseError.flatten(error);
|
|
2210
|
-
for (const err of errors) {
|
|
2211
|
-
const errorNameKey = `errorNames.${StringHelper.camelCase(err.name)}`;
|
|
2212
|
-
const errorMessageKey = `error.${err.message}`;
|
|
2213
|
-
// If there is no error message then it is probably
|
|
2214
|
-
// from a 3rd party lib, so don't format it just display
|
|
2215
|
-
const hasErrorName = I18n.hasMessage(errorNameKey);
|
|
2216
|
-
const hasErrorMessage = I18n.hasMessage(errorMessageKey);
|
|
2217
|
-
const localizedError = {
|
|
2218
|
-
name: I18n.formatMessage(hasErrorName ? errorNameKey : "errorNames.error"),
|
|
2219
|
-
message: hasErrorMessage
|
|
2220
|
-
? I18n.formatMessage(errorMessageKey, err.properties)
|
|
2221
|
-
: err.message
|
|
2222
|
-
};
|
|
2223
|
-
if (Is.stringValue(err.source)) {
|
|
2224
|
-
localizedError.source = err.source;
|
|
2225
|
-
}
|
|
2226
|
-
if (Is.stringValue(err.stack)) {
|
|
2227
|
-
// Remove the first line from the stack traces as they
|
|
2228
|
-
// just have the error type and message duplicated
|
|
2229
|
-
const lines = err.stack.split("\n");
|
|
2230
|
-
lines.shift();
|
|
2231
|
-
localizedError.stack = lines.join("\n");
|
|
2232
|
-
}
|
|
2233
|
-
const additional = ErrorHelper.formatValidationErrors(err);
|
|
2234
|
-
if (Is.stringValue(additional)) {
|
|
2235
|
-
localizedError.additional = additional;
|
|
2236
|
-
}
|
|
2237
|
-
formattedErrors.push(localizedError);
|
|
2238
|
-
}
|
|
2239
|
-
}
|
|
2240
|
-
return formattedErrors;
|
|
2447
|
+
static base58ToBytes(base58) {
|
|
2448
|
+
return Base58.decode(base58);
|
|
2241
2449
|
}
|
|
2242
2450
|
/**
|
|
2243
|
-
*
|
|
2244
|
-
* @
|
|
2245
|
-
* @returns The localized version of the errors flattened.
|
|
2451
|
+
* Build the static lookup tables.
|
|
2452
|
+
* @internal
|
|
2246
2453
|
*/
|
|
2247
|
-
static
|
|
2248
|
-
if (
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
v += ` = ${JSON.stringify(validationFailure.properties.value)}`;
|
|
2454
|
+
static buildHexLookups() {
|
|
2455
|
+
if (!Converter._ENCODE_LOOKUP || !Converter._DECODE_LOOKUP) {
|
|
2456
|
+
const alphabet = "0123456789abcdef";
|
|
2457
|
+
Converter._ENCODE_LOOKUP = [];
|
|
2458
|
+
Converter._DECODE_LOOKUP = [];
|
|
2459
|
+
for (let i = 0; i < 256; i++) {
|
|
2460
|
+
Converter._ENCODE_LOOKUP[i] = alphabet[(i >> 4) & 0xf] + alphabet[i & 0xf];
|
|
2461
|
+
if (i < 16) {
|
|
2462
|
+
if (i < 10) {
|
|
2463
|
+
Converter._DECODE_LOOKUP[0x30 + i] = i;
|
|
2464
|
+
}
|
|
2465
|
+
else {
|
|
2466
|
+
Converter._DECODE_LOOKUP[0x61 - 10 + i] = i;
|
|
2467
|
+
}
|
|
2262
2468
|
}
|
|
2263
|
-
validationErrors.push(v);
|
|
2264
2469
|
}
|
|
2265
|
-
return validationErrors.join("\n");
|
|
2266
2470
|
}
|
|
2267
2471
|
}
|
|
2268
2472
|
}
|
|
2269
2473
|
|
|
2270
|
-
// Copyright 2024 IOTA Stiftung.
|
|
2271
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
2272
|
-
/**
|
|
2273
|
-
* Coerce an object from one type to another.
|
|
2274
|
-
*/
|
|
2275
|
-
class Coerce {
|
|
2276
|
-
/**
|
|
2277
|
-
* Coerce the value to a string.
|
|
2278
|
-
* @param value The value to coerce.
|
|
2279
|
-
* @throws TypeError If the value can not be coerced.
|
|
2280
|
-
* @returns The value if it can be coerced.
|
|
2281
|
-
*/
|
|
2282
|
-
static string(value) {
|
|
2283
|
-
if (Is.undefined(value)) {
|
|
2284
|
-
return value;
|
|
2285
|
-
}
|
|
2286
|
-
if (Is.string(value)) {
|
|
2287
|
-
return value;
|
|
2288
|
-
}
|
|
2289
|
-
if (Is.number(value)) {
|
|
2290
|
-
return value.toString();
|
|
2291
|
-
}
|
|
2292
|
-
if (Is.boolean(value)) {
|
|
2293
|
-
return value ? "true" : "false";
|
|
2294
|
-
}
|
|
2295
|
-
if (Is.date(value)) {
|
|
2296
|
-
return value.toISOString();
|
|
2297
|
-
}
|
|
2298
|
-
}
|
|
2299
|
-
/**
|
|
2300
|
-
* Coerce the value to a number.
|
|
2301
|
-
* @param value The value to coerce.
|
|
2302
|
-
* @throws TypeError If the value can not be coerced.
|
|
2303
|
-
* @returns The value if it can be coerced.
|
|
2304
|
-
*/
|
|
2305
|
-
static number(value) {
|
|
2306
|
-
if (Is.undefined(value)) {
|
|
2307
|
-
return value;
|
|
2308
|
-
}
|
|
2309
|
-
if (Is.number(value)) {
|
|
2310
|
-
return value;
|
|
2311
|
-
}
|
|
2312
|
-
if (Is.string(value)) {
|
|
2313
|
-
const parsed = Number.parseFloat(value);
|
|
2314
|
-
if (Is.number(parsed)) {
|
|
2315
|
-
return parsed;
|
|
2316
|
-
}
|
|
2317
|
-
}
|
|
2318
|
-
if (Is.boolean(value)) {
|
|
2319
|
-
return value ? 1 : 0;
|
|
2320
|
-
}
|
|
2321
|
-
if (Is.date(value)) {
|
|
2322
|
-
return value.getTime();
|
|
2323
|
-
}
|
|
2324
|
-
}
|
|
2325
|
-
/**
|
|
2326
|
-
* Coerce the value to a bigint.
|
|
2327
|
-
* @param value The value to coerce.
|
|
2328
|
-
* @throws TypeError If the value can not be coerced.
|
|
2329
|
-
* @returns The value if it can be coerced.
|
|
2330
|
-
*/
|
|
2331
|
-
static bigint(value) {
|
|
2332
|
-
if (Is.undefined(value)) {
|
|
2333
|
-
return value;
|
|
2334
|
-
}
|
|
2335
|
-
if (Is.bigint(value)) {
|
|
2336
|
-
return value;
|
|
2337
|
-
}
|
|
2338
|
-
if (Is.number(value)) {
|
|
2339
|
-
return BigInt(value);
|
|
2340
|
-
}
|
|
2341
|
-
if (Is.string(value)) {
|
|
2342
|
-
const parsed = Number.parseFloat(value);
|
|
2343
|
-
if (Is.integer(parsed)) {
|
|
2344
|
-
return BigInt(parsed);
|
|
2345
|
-
}
|
|
2346
|
-
}
|
|
2347
|
-
if (Is.boolean(value)) {
|
|
2348
|
-
return value ? 1n : 0n;
|
|
2349
|
-
}
|
|
2350
|
-
}
|
|
2351
|
-
/**
|
|
2352
|
-
* Coerce the value to a boolean.
|
|
2353
|
-
* @param value The value to coerce.
|
|
2354
|
-
* @throws TypeError If the value can not be coerced.
|
|
2355
|
-
* @returns The value if it can be coerced.
|
|
2356
|
-
*/
|
|
2357
|
-
static boolean(value) {
|
|
2358
|
-
if (Is.undefined(value)) {
|
|
2359
|
-
return value;
|
|
2360
|
-
}
|
|
2361
|
-
if (Is.boolean(value)) {
|
|
2362
|
-
return value;
|
|
2363
|
-
}
|
|
2364
|
-
if (Is.number(value)) {
|
|
2365
|
-
// eslint-disable-next-line no-unneeded-ternary
|
|
2366
|
-
return value ? true : false;
|
|
2367
|
-
}
|
|
2368
|
-
if (Is.string(value)) {
|
|
2369
|
-
if (/true/i.test(value)) {
|
|
2370
|
-
return true;
|
|
2371
|
-
}
|
|
2372
|
-
if (/false/i.test(value)) {
|
|
2373
|
-
return false;
|
|
2374
|
-
}
|
|
2375
|
-
}
|
|
2376
|
-
}
|
|
2377
|
-
/**
|
|
2378
|
-
* Coerce the value to a date.
|
|
2379
|
-
* @param value The value to coerce.
|
|
2380
|
-
* @throws TypeError If the value can not be coerced.
|
|
2381
|
-
* @returns The value if it can be coerced.
|
|
2382
|
-
*/
|
|
2383
|
-
static date(value) {
|
|
2384
|
-
if (Is.undefined(value)) {
|
|
2385
|
-
return value;
|
|
2386
|
-
}
|
|
2387
|
-
if (Is.date(value)) {
|
|
2388
|
-
return value;
|
|
2389
|
-
}
|
|
2390
|
-
if (Is.number(value)) {
|
|
2391
|
-
return new Date(value);
|
|
2392
|
-
}
|
|
2393
|
-
if (Is.string(value)) {
|
|
2394
|
-
const dt = new Date(value);
|
|
2395
|
-
if (!Number.isNaN(dt.getTime())) {
|
|
2396
|
-
const utc = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate());
|
|
2397
|
-
return new Date(utc);
|
|
2398
|
-
}
|
|
2399
|
-
}
|
|
2400
|
-
}
|
|
2401
|
-
/**
|
|
2402
|
-
* Coerce the value to a date/time.
|
|
2403
|
-
* @param value The value to coerce.
|
|
2404
|
-
* @throws TypeError If the value can not be coerced.
|
|
2405
|
-
* @returns The value if it can be coerced.
|
|
2406
|
-
*/
|
|
2407
|
-
static dateTime(value) {
|
|
2408
|
-
if (Is.undefined(value)) {
|
|
2409
|
-
return value;
|
|
2410
|
-
}
|
|
2411
|
-
if (Is.date(value)) {
|
|
2412
|
-
return value;
|
|
2413
|
-
}
|
|
2414
|
-
if (Is.number(value)) {
|
|
2415
|
-
return new Date(value);
|
|
2416
|
-
}
|
|
2417
|
-
if (Is.string(value)) {
|
|
2418
|
-
const dt = new Date(value);
|
|
2419
|
-
if (!Number.isNaN(dt.getTime())) {
|
|
2420
|
-
const utc = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate(), dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
|
|
2421
|
-
return new Date(utc);
|
|
2422
|
-
}
|
|
2423
|
-
}
|
|
2424
|
-
}
|
|
2425
|
-
/**
|
|
2426
|
-
* Coerce the value to a time.
|
|
2427
|
-
* @param value The value to coerce.
|
|
2428
|
-
* @throws TypeError If the value can not be coerced.
|
|
2429
|
-
* @returns The value if it can be coerced.
|
|
2430
|
-
*/
|
|
2431
|
-
static time(value) {
|
|
2432
|
-
if (Is.undefined(value)) {
|
|
2433
|
-
return value;
|
|
2434
|
-
}
|
|
2435
|
-
if (Is.date(value)) {
|
|
2436
|
-
return value;
|
|
2437
|
-
}
|
|
2438
|
-
if (Is.number(value)) {
|
|
2439
|
-
const dt = new Date(value);
|
|
2440
|
-
dt.setFullYear(1970, 0, 1);
|
|
2441
|
-
return dt;
|
|
2442
|
-
}
|
|
2443
|
-
if (Is.string(value)) {
|
|
2444
|
-
const dt = new Date(value);
|
|
2445
|
-
if (!Number.isNaN(dt.getTime())) {
|
|
2446
|
-
const utc = Date.UTC(1970, 0, 1, dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
|
|
2447
|
-
return new Date(utc);
|
|
2448
|
-
}
|
|
2449
|
-
}
|
|
2450
|
-
}
|
|
2451
|
-
/**
|
|
2452
|
-
* Coerce the value to an object.
|
|
2453
|
-
* @param value The value to coerce.
|
|
2454
|
-
* @throws TypeError If the value can not be coerced.
|
|
2455
|
-
* @returns The value if it can be coerced.
|
|
2456
|
-
*/
|
|
2457
|
-
static object(value) {
|
|
2458
|
-
if (Is.undefined(value)) {
|
|
2459
|
-
return value;
|
|
2460
|
-
}
|
|
2461
|
-
if (Is.object(value)) {
|
|
2462
|
-
return value;
|
|
2463
|
-
}
|
|
2464
|
-
if (Is.stringValue(value)) {
|
|
2465
|
-
try {
|
|
2466
|
-
return JSON.parse(value);
|
|
2467
|
-
}
|
|
2468
|
-
catch { }
|
|
2469
|
-
}
|
|
2470
|
-
}
|
|
2471
|
-
}
|
|
2472
|
-
|
|
2473
|
-
// Copyright 2024 IOTA Stiftung.
|
|
2474
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
2475
|
-
/**
|
|
2476
|
-
* Class to help with filenames.
|
|
2477
|
-
*/
|
|
2478
|
-
class FilenameHelper {
|
|
2479
|
-
/**
|
|
2480
|
-
* Replaces any unsafe characters in the filename.
|
|
2481
|
-
* @param filename The filename to make safe.
|
|
2482
|
-
* @returns The safe filename.
|
|
2483
|
-
*/
|
|
2484
|
-
static safeFilename(filename) {
|
|
2485
|
-
let safe = Coerce.string(filename);
|
|
2486
|
-
if (Is.empty(safe)) {
|
|
2487
|
-
return "";
|
|
2488
|
-
}
|
|
2489
|
-
// Common non filename characters
|
|
2490
|
-
safe = safe.replace(/["*/:<>?\\|]/g, "_");
|
|
2491
|
-
// Windows non filename characters
|
|
2492
|
-
safe = safe.replace(/^(con|prn|aux|nul|com\d|lpt\d)$/i, "_");
|
|
2493
|
-
// Control characters
|
|
2494
|
-
safe = safe.replace(/[\u0000-\u001F\u0080-\u009F]/g, "_");
|
|
2495
|
-
// Relative paths
|
|
2496
|
-
safe = safe.replace(/^\.+/, "_");
|
|
2497
|
-
// Trailing periods
|
|
2498
|
-
safe = safe.replace(/\.+$/, "");
|
|
2499
|
-
return safe;
|
|
2500
|
-
}
|
|
2501
|
-
}
|
|
2502
|
-
|
|
2503
|
-
// Copyright 2024 IOTA Stiftung.
|
|
2504
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
2505
2474
|
/**
|
|
2506
2475
|
* Helpers methods for JSON objects.
|
|
2507
2476
|
*/
|
|
2508
2477
|
class JsonHelper {
|
|
2478
|
+
/**
|
|
2479
|
+
* Runtime name for the class.
|
|
2480
|
+
* @internal
|
|
2481
|
+
*/
|
|
2482
|
+
static _CLASS_NAME = "JsonHelper";
|
|
2509
2483
|
/**
|
|
2510
2484
|
* Serializes in canonical format.
|
|
2511
2485
|
* Based on https://www.rfc-editor.org/rfc/rfc8785.
|
|
@@ -2564,449 +2538,1043 @@ class JsonHelper {
|
|
|
2564
2538
|
* @param object The object to patch.
|
|
2565
2539
|
* @param patches The second object.
|
|
2566
2540
|
* @returns The updated object.
|
|
2541
|
+
* @throws GeneralError if the patch fails.
|
|
2567
2542
|
*/
|
|
2568
2543
|
static patch(object, patches) {
|
|
2569
|
-
|
|
2544
|
+
const clone = ObjectHelper.clone(object);
|
|
2545
|
+
const result = rfc6902.applyPatch(clone, patches);
|
|
2546
|
+
for (let i = 0; i < result.length; i++) {
|
|
2547
|
+
if (!Is.empty(result[i])) {
|
|
2548
|
+
throw new GeneralError(JsonHelper._CLASS_NAME, "failedPatch", { index: i }, result[i]);
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
return clone;
|
|
2570
2552
|
}
|
|
2571
|
-
}
|
|
2572
|
-
|
|
2573
|
-
// Copyright 2024 IOTA Stiftung.
|
|
2574
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
2575
|
-
/* eslint-disable no-bitwise */
|
|
2576
|
-
/**
|
|
2577
|
-
* Convert arrays to and from different formats.
|
|
2578
|
-
*/
|
|
2579
|
-
class Converter {
|
|
2580
2553
|
/**
|
|
2581
|
-
*
|
|
2582
|
-
* @
|
|
2554
|
+
* Stringify the JSON with support for extended data types date/bigint/uint8array.
|
|
2555
|
+
* @param object The object to stringify.
|
|
2556
|
+
* @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
|
|
2557
|
+
* @returns The stringified object.
|
|
2583
2558
|
*/
|
|
2584
|
-
|
|
2559
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2560
|
+
static stringifyEx(object, space) {
|
|
2561
|
+
// We want to keep the 'this' intact for the replacer
|
|
2562
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
2563
|
+
return JSON.stringify(object, JsonHelper.stringifyExReplacer, space);
|
|
2564
|
+
}
|
|
2585
2565
|
/**
|
|
2586
|
-
*
|
|
2587
|
-
* @
|
|
2566
|
+
* Parse the JSON string with support for extended data types date/bigint/uint8array.
|
|
2567
|
+
* @param json The object to pause.
|
|
2568
|
+
* @returns The object.
|
|
2588
2569
|
*/
|
|
2589
|
-
|
|
2570
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2571
|
+
static parseEx(json) {
|
|
2572
|
+
// We want to keep the 'this' intact for the reviver
|
|
2573
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
2574
|
+
return JSON.parse(json, JsonHelper.parseExReviver);
|
|
2575
|
+
}
|
|
2576
|
+
/**
|
|
2577
|
+
* Replacer function to handle extended data types.
|
|
2578
|
+
* @param this The object.
|
|
2579
|
+
* @param key The key.
|
|
2580
|
+
* @param value The value.
|
|
2581
|
+
* @returns The value.
|
|
2582
|
+
*/
|
|
2583
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2584
|
+
static stringifyExReplacer(key, value) {
|
|
2585
|
+
const rawValue = this[key];
|
|
2586
|
+
if (Is.bigint(rawValue)) {
|
|
2587
|
+
return {
|
|
2588
|
+
"@ext": "bigint",
|
|
2589
|
+
value: rawValue.toString()
|
|
2590
|
+
};
|
|
2591
|
+
}
|
|
2592
|
+
else if (Is.date(rawValue)) {
|
|
2593
|
+
return {
|
|
2594
|
+
"@ext": "date",
|
|
2595
|
+
value: rawValue.getTime()
|
|
2596
|
+
};
|
|
2597
|
+
}
|
|
2598
|
+
else if (Is.uint8Array(rawValue)) {
|
|
2599
|
+
return {
|
|
2600
|
+
"@ext": "uint8array",
|
|
2601
|
+
value: Converter.bytesToBase64(rawValue)
|
|
2602
|
+
};
|
|
2603
|
+
}
|
|
2604
|
+
return value;
|
|
2605
|
+
}
|
|
2606
|
+
/**
|
|
2607
|
+
* Reviver function to handle extended data types.
|
|
2608
|
+
* @param this The object.
|
|
2609
|
+
* @param key The key.
|
|
2610
|
+
* @param value The value.
|
|
2611
|
+
* @returns The value.
|
|
2612
|
+
*/
|
|
2613
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2614
|
+
static parseExReviver(key, value) {
|
|
2615
|
+
if (Is.object(value)) {
|
|
2616
|
+
if (value["@ext"] === "bigint") {
|
|
2617
|
+
return BigInt(value.value);
|
|
2618
|
+
}
|
|
2619
|
+
else if (value["@ext"] === "date") {
|
|
2620
|
+
return new Date(value.value);
|
|
2621
|
+
}
|
|
2622
|
+
else if (value["@ext"] === "uint8array") {
|
|
2623
|
+
return Converter.base64ToBytes(value.value);
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
return value;
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2629
|
+
|
|
2630
|
+
/**
|
|
2631
|
+
* Class to help with objects.
|
|
2632
|
+
*/
|
|
2633
|
+
class ObjectHelper {
|
|
2590
2634
|
/**
|
|
2591
|
-
*
|
|
2592
|
-
* @
|
|
2593
|
-
* @param startIndex The index to start in the bytes.
|
|
2594
|
-
* @param length The length of bytes to read.
|
|
2595
|
-
* @returns The array formatted as UTF8.
|
|
2635
|
+
* Runtime name for the class.
|
|
2636
|
+
* @internal
|
|
2596
2637
|
*/
|
|
2597
|
-
static
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2638
|
+
static _CLASS_NAME = "ObjectHelper";
|
|
2639
|
+
/**
|
|
2640
|
+
* Convert an object to bytes.
|
|
2641
|
+
* @param obj The object to convert.
|
|
2642
|
+
* @param format Format the JSON content.
|
|
2643
|
+
* @returns The object as bytes.
|
|
2644
|
+
*/
|
|
2645
|
+
static toBytes(obj, format = false) {
|
|
2646
|
+
if (obj === undefined) {
|
|
2647
|
+
return new Uint8Array();
|
|
2648
|
+
}
|
|
2649
|
+
const json = format ? JSON.stringify(obj, undefined, "\t") : JSON.stringify(obj);
|
|
2650
|
+
return Converter.utf8ToBytes(json);
|
|
2651
|
+
}
|
|
2652
|
+
/**
|
|
2653
|
+
* Convert a bytes to an object.
|
|
2654
|
+
* @param bytes The bytes to convert to an object.
|
|
2655
|
+
* @returns The object.
|
|
2656
|
+
* @throws GeneralError if there was an error parsing the JSON.
|
|
2657
|
+
*/
|
|
2658
|
+
static fromBytes(bytes) {
|
|
2659
|
+
if (Is.empty(bytes) || bytes.length === 0) {
|
|
2660
|
+
return undefined;
|
|
2661
|
+
}
|
|
2662
|
+
try {
|
|
2663
|
+
const utf8 = Converter.bytesToUtf8(bytes);
|
|
2664
|
+
return JSON.parse(utf8);
|
|
2665
|
+
}
|
|
2666
|
+
catch (err) {
|
|
2667
|
+
throw new GeneralError(ObjectHelper._CLASS_NAME, "failedBytesToJSON", undefined, err);
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
/**
|
|
2671
|
+
* Make a deep clone of an object.
|
|
2672
|
+
* @param obj The object to clone.
|
|
2673
|
+
* @returns The objects clone.
|
|
2674
|
+
*/
|
|
2675
|
+
static clone(obj) {
|
|
2676
|
+
if (Is.undefined(obj)) {
|
|
2677
|
+
return undefined;
|
|
2678
|
+
}
|
|
2679
|
+
return structuredClone(obj);
|
|
2680
|
+
}
|
|
2681
|
+
/**
|
|
2682
|
+
* Deep merge objects.
|
|
2683
|
+
* @param obj1 The first object to merge.
|
|
2684
|
+
* @param obj2 The second object to merge.
|
|
2685
|
+
* @returns The combined deep merge of the objects.
|
|
2686
|
+
*/
|
|
2687
|
+
static merge(obj1, obj2) {
|
|
2688
|
+
if (Is.empty(obj1)) {
|
|
2689
|
+
return ObjectHelper.clone(obj2);
|
|
2690
|
+
}
|
|
2691
|
+
if (Is.empty(obj2)) {
|
|
2692
|
+
return ObjectHelper.clone(obj1);
|
|
2693
|
+
}
|
|
2694
|
+
const obj1Clone = ObjectHelper.clone(obj1);
|
|
2695
|
+
if (Is.object(obj1Clone) && Is.object(obj2)) {
|
|
2696
|
+
const keys = Object.keys(obj2);
|
|
2697
|
+
for (const key of keys) {
|
|
2698
|
+
if (Is.object(obj1Clone[key]) && Is.object(obj2[key])) {
|
|
2699
|
+
ObjectHelper.propertySet(obj1Clone, key, ObjectHelper.merge(obj1Clone[key], obj2[key]));
|
|
2700
|
+
}
|
|
2701
|
+
else {
|
|
2702
|
+
ObjectHelper.propertySet(obj1Clone, key, obj2[key]);
|
|
2703
|
+
}
|
|
2605
2704
|
}
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2705
|
+
}
|
|
2706
|
+
return obj1Clone;
|
|
2707
|
+
}
|
|
2708
|
+
/**
|
|
2709
|
+
* Does one object equal another.
|
|
2710
|
+
* @param obj1 The first object to compare.
|
|
2711
|
+
* @param obj2 The second object to compare.
|
|
2712
|
+
* @param strictPropertyOrder Should the properties be in the same order, defaults to true.
|
|
2713
|
+
* @returns True is the objects are equal.
|
|
2714
|
+
*/
|
|
2715
|
+
static equal(obj1, obj2, strictPropertyOrder) {
|
|
2716
|
+
if (strictPropertyOrder ?? true) {
|
|
2717
|
+
return JSON.stringify(obj1) === JSON.stringify(obj2);
|
|
2718
|
+
}
|
|
2719
|
+
return JsonHelper.canonicalize(obj1) === JsonHelper.canonicalize(obj2);
|
|
2720
|
+
}
|
|
2721
|
+
/**
|
|
2722
|
+
* Get the property of an unknown object.
|
|
2723
|
+
* @param obj The object to get the property from.
|
|
2724
|
+
* @param property The property to get, can be separated by dots for nested path.
|
|
2725
|
+
* @returns The property.
|
|
2726
|
+
*/
|
|
2727
|
+
static propertyGet(obj, property) {
|
|
2728
|
+
const pathParts = property.split(".");
|
|
2729
|
+
let pathValue = obj;
|
|
2730
|
+
for (const pathPart of pathParts) {
|
|
2731
|
+
// Is the path part numeric i.e. an array index.
|
|
2732
|
+
const arrayMatch = /^(\d+)$/.exec(pathPart);
|
|
2733
|
+
if (arrayMatch) {
|
|
2734
|
+
const arrayIndex = Number.parseInt(arrayMatch[1], 10);
|
|
2735
|
+
if (Is.arrayValue(pathValue) && arrayIndex < pathValue.length) {
|
|
2736
|
+
// There is no prop name so this is a direct array index on the current object
|
|
2737
|
+
pathValue = pathValue[arrayIndex];
|
|
2738
|
+
}
|
|
2739
|
+
else {
|
|
2740
|
+
// Array index for non array object so return
|
|
2741
|
+
return undefined;
|
|
2742
|
+
}
|
|
2609
2743
|
}
|
|
2610
|
-
else if (
|
|
2611
|
-
|
|
2612
|
-
|
|
2744
|
+
else if (Is.object(pathValue)) {
|
|
2745
|
+
// No array part in path so assume object sub property
|
|
2746
|
+
pathValue = pathValue[pathPart];
|
|
2613
2747
|
}
|
|
2614
2748
|
else {
|
|
2615
|
-
|
|
2616
|
-
const charCode = (((value & 0x07) << 18) |
|
|
2617
|
-
((array[i + 1] & 0x3f) << 12) |
|
|
2618
|
-
((array[i + 2] & 0x3f) << 6) |
|
|
2619
|
-
(array[i + 3] & 0x3f)) -
|
|
2620
|
-
0x010000;
|
|
2621
|
-
str += String.fromCharCode((charCode >> 10) | 0xd800, (charCode & 0x03ff) | 0xdc00);
|
|
2622
|
-
i += 3;
|
|
2749
|
+
return undefined;
|
|
2623
2750
|
}
|
|
2624
2751
|
}
|
|
2625
|
-
return
|
|
2752
|
+
return pathValue;
|
|
2626
2753
|
}
|
|
2627
2754
|
/**
|
|
2628
|
-
*
|
|
2629
|
-
* @param
|
|
2630
|
-
* @
|
|
2755
|
+
* Set the property of an unknown object.
|
|
2756
|
+
* @param obj The object to set the property from.
|
|
2757
|
+
* @param property The property to set.
|
|
2758
|
+
* @param value The value to set.
|
|
2759
|
+
* @throws GeneralError if the property target is not an object.
|
|
2631
2760
|
*/
|
|
2632
|
-
static
|
|
2633
|
-
const
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2761
|
+
static propertySet(obj, property, value) {
|
|
2762
|
+
const pathParts = property.split(".");
|
|
2763
|
+
let pathValue = obj;
|
|
2764
|
+
let parentObj;
|
|
2765
|
+
for (let i = 0; i < pathParts.length; i++) {
|
|
2766
|
+
const pathPart = pathParts[i];
|
|
2767
|
+
// Is the path part numeric i.e. an array index.
|
|
2768
|
+
const arrayMatch = /^(\d+)$/.exec(pathPart);
|
|
2769
|
+
const arrayIndex = arrayMatch ? Number.parseInt(arrayMatch[1], 10) : -1;
|
|
2770
|
+
if (i === pathParts.length - 1) {
|
|
2771
|
+
// Last part of path so set the value
|
|
2772
|
+
if (arrayIndex >= 0) {
|
|
2773
|
+
if (Is.array(pathValue)) {
|
|
2774
|
+
pathValue[arrayIndex] = value;
|
|
2775
|
+
}
|
|
2776
|
+
else if (Is.object(pathValue)) {
|
|
2777
|
+
pathValue[arrayIndex] = value;
|
|
2778
|
+
}
|
|
2779
|
+
else {
|
|
2780
|
+
throw new GeneralError(ObjectHelper._CLASS_NAME, "cannotSetArrayIndex", {
|
|
2781
|
+
property,
|
|
2782
|
+
index: arrayIndex
|
|
2783
|
+
});
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
else if (Is.object(pathValue)) {
|
|
2787
|
+
pathValue[pathPart] = value;
|
|
2788
|
+
}
|
|
2789
|
+
else {
|
|
2790
|
+
throw new GeneralError(ObjectHelper._CLASS_NAME, "cannotSetProperty", { property });
|
|
2791
|
+
}
|
|
2638
2792
|
}
|
|
2639
|
-
else
|
|
2640
|
-
|
|
2793
|
+
else {
|
|
2794
|
+
parentObj = pathValue;
|
|
2795
|
+
if (Is.object(pathValue)) {
|
|
2796
|
+
pathValue = pathValue[pathPart];
|
|
2797
|
+
}
|
|
2798
|
+
else if (Is.array(pathValue)) {
|
|
2799
|
+
pathValue = pathValue[arrayIndex];
|
|
2800
|
+
}
|
|
2801
|
+
if (Is.empty(pathValue)) {
|
|
2802
|
+
const nextArrayMatch = /^(\d+)$/.exec(pathParts[i + 1]);
|
|
2803
|
+
const nextArrayIndex = nextArrayMatch ? Number.parseInt(nextArrayMatch[1], 10) : -1;
|
|
2804
|
+
if (nextArrayIndex >= 0) {
|
|
2805
|
+
pathValue = [];
|
|
2806
|
+
}
|
|
2807
|
+
else {
|
|
2808
|
+
pathValue = {};
|
|
2809
|
+
}
|
|
2810
|
+
if (Is.object(parentObj)) {
|
|
2811
|
+
parentObj[pathPart] = pathValue;
|
|
2812
|
+
}
|
|
2813
|
+
else if (Is.array(parentObj)) {
|
|
2814
|
+
parentObj[arrayIndex] = pathValue;
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2641
2817
|
}
|
|
2642
|
-
|
|
2643
|
-
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
/**
|
|
2821
|
+
* Delete the property of an unknown object.
|
|
2822
|
+
* @param obj The object to set the property from.
|
|
2823
|
+
* @param property The property to set
|
|
2824
|
+
*/
|
|
2825
|
+
static propertyDelete(obj, property) {
|
|
2826
|
+
if (Is.object(obj)) {
|
|
2827
|
+
delete obj[property];
|
|
2828
|
+
}
|
|
2829
|
+
}
|
|
2830
|
+
/**
|
|
2831
|
+
* Extract a property from the object, providing alternative names.
|
|
2832
|
+
* @param obj The object to extract from.
|
|
2833
|
+
* @param propertyNames The possible names for the property.
|
|
2834
|
+
* @param removeProperties Remove the properties from the object, defaults to true.
|
|
2835
|
+
* @returns The property if available.
|
|
2836
|
+
*/
|
|
2837
|
+
static extractProperty(obj, propertyNames, removeProperties = true) {
|
|
2838
|
+
let retVal;
|
|
2839
|
+
if (Is.object(obj)) {
|
|
2840
|
+
const names = Is.string(propertyNames) ? [propertyNames] : propertyNames;
|
|
2841
|
+
for (const prop of names) {
|
|
2842
|
+
retVal ??= ObjectHelper.propertyGet(obj, prop);
|
|
2843
|
+
if (removeProperties) {
|
|
2844
|
+
ObjectHelper.propertyDelete(obj, prop);
|
|
2845
|
+
}
|
|
2644
2846
|
}
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2847
|
+
}
|
|
2848
|
+
return retVal;
|
|
2849
|
+
}
|
|
2850
|
+
/**
|
|
2851
|
+
* Pick a subset of properties from an object.
|
|
2852
|
+
* @param obj The object to pick the properties from.
|
|
2853
|
+
* @param keys The property keys to pick.
|
|
2854
|
+
* @returns The partial object.
|
|
2855
|
+
*/
|
|
2856
|
+
static pick(obj, keys) {
|
|
2857
|
+
if (Is.object(obj) && Is.arrayValue(keys)) {
|
|
2858
|
+
const result = {};
|
|
2859
|
+
for (const key of keys) {
|
|
2860
|
+
result[key] = obj[key];
|
|
2653
2861
|
}
|
|
2862
|
+
return result;
|
|
2654
2863
|
}
|
|
2655
|
-
return
|
|
2864
|
+
return obj;
|
|
2656
2865
|
}
|
|
2657
2866
|
/**
|
|
2658
|
-
*
|
|
2659
|
-
* @param
|
|
2660
|
-
* @param
|
|
2661
|
-
* @
|
|
2662
|
-
* @param length The length of bytes to read.
|
|
2663
|
-
* @param reverse Reverse the combine direction.
|
|
2664
|
-
* @returns The array formatted as hex.
|
|
2867
|
+
* Omit a subset of properties from an object.
|
|
2868
|
+
* @param obj The object to omit the properties from.
|
|
2869
|
+
* @param keys The property keys to omit.
|
|
2870
|
+
* @returns The partial object.
|
|
2665
2871
|
*/
|
|
2666
|
-
static
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2872
|
+
static omit(obj, keys) {
|
|
2873
|
+
if (Is.object(obj) && Is.arrayValue(keys)) {
|
|
2874
|
+
const result = { ...obj };
|
|
2875
|
+
for (const key of keys) {
|
|
2876
|
+
delete result[key];
|
|
2877
|
+
}
|
|
2878
|
+
return result;
|
|
2879
|
+
}
|
|
2880
|
+
return obj;
|
|
2881
|
+
}
|
|
2882
|
+
/**
|
|
2883
|
+
* Converter the non JSON primitives to extended types.
|
|
2884
|
+
* @param obj The object to convert.
|
|
2885
|
+
* @returns The object with extended properties.
|
|
2886
|
+
*/
|
|
2887
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2888
|
+
static toExtended(obj) {
|
|
2889
|
+
const jsonExtended = JsonHelper.stringifyEx(obj);
|
|
2890
|
+
return JSON.parse(jsonExtended);
|
|
2891
|
+
}
|
|
2892
|
+
/**
|
|
2893
|
+
* Converter the extended types to non JSON primitives.
|
|
2894
|
+
* @param obj The object to convert.
|
|
2895
|
+
* @returns The object with regular properties.
|
|
2896
|
+
*/
|
|
2897
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2898
|
+
static fromExtended(obj) {
|
|
2899
|
+
const jsonExtended = JsonHelper.stringifyEx(obj);
|
|
2900
|
+
return JsonHelper.parseEx(jsonExtended);
|
|
2901
|
+
}
|
|
2902
|
+
/**
|
|
2903
|
+
* Remove empty properties from an object.
|
|
2904
|
+
* @param obj The object to remove the empty properties from.
|
|
2905
|
+
* @param options The options for the removal.
|
|
2906
|
+
* @param options.removeUndefined Remove undefined properties, defaults to true.
|
|
2907
|
+
* @param options.removeNull Remove null properties, defaults to false.
|
|
2908
|
+
* @returns The object with empty properties removed.
|
|
2909
|
+
*/
|
|
2910
|
+
static removeEmptyProperties(obj, options) {
|
|
2911
|
+
if (Is.object(obj)) {
|
|
2912
|
+
const removeUndefined = options?.removeUndefined ?? true;
|
|
2913
|
+
const removeNull = options?.removeNull ?? false;
|
|
2914
|
+
const newObj = {};
|
|
2915
|
+
const keys = Object.keys(obj);
|
|
2916
|
+
for (const key of keys) {
|
|
2917
|
+
if (!((removeUndefined && Is.undefined(obj[key])) || (removeNull && Is.null(obj[key])))) {
|
|
2918
|
+
newObj[key] = ObjectHelper.removeEmptyProperties(obj[key], options);
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
return newObj;
|
|
2922
|
+
}
|
|
2923
|
+
else if (Is.array(obj)) {
|
|
2924
|
+
const arr = [];
|
|
2925
|
+
for (const element of obj) {
|
|
2926
|
+
arr.push(ObjectHelper.removeEmptyProperties(element, options));
|
|
2927
|
+
}
|
|
2928
|
+
return arr;
|
|
2929
|
+
}
|
|
2930
|
+
return obj;
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2934
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2935
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
2936
|
+
/**
|
|
2937
|
+
* Environment variable helper.
|
|
2938
|
+
*/
|
|
2939
|
+
class EnvHelper {
|
|
2940
|
+
/**
|
|
2941
|
+
* Get the environment variable as an object with camel cased names.
|
|
2942
|
+
* @param envVars The environment variables.
|
|
2943
|
+
* @param prefix The prefix of the environment variables, if not provided gets all.
|
|
2944
|
+
* @returns The object with camel cased names.
|
|
2945
|
+
*/
|
|
2946
|
+
static envToJson(envVars, prefix) {
|
|
2947
|
+
const result = {};
|
|
2948
|
+
if (!Is.empty(envVars)) {
|
|
2949
|
+
if (Is.empty(prefix)) {
|
|
2950
|
+
for (const envVar in envVars) {
|
|
2951
|
+
if (Is.stringValue(envVars[envVar])) {
|
|
2952
|
+
const camelCaseName = StringHelper.camelCase(envVar.toLowerCase());
|
|
2953
|
+
ObjectHelper.propertySet(result, camelCaseName, envVars[envVar]);
|
|
2954
|
+
}
|
|
2675
2955
|
}
|
|
2676
2956
|
}
|
|
2677
2957
|
else {
|
|
2678
|
-
for (
|
|
2679
|
-
|
|
2958
|
+
for (const envVar in envVars) {
|
|
2959
|
+
if (envVar.startsWith(prefix) && Is.stringValue(envVars[envVar])) {
|
|
2960
|
+
const camelCaseName = StringHelper.camelCase(envVar.replace(prefix, "").toLowerCase());
|
|
2961
|
+
ObjectHelper.propertySet(result, camelCaseName, envVars[envVar]);
|
|
2962
|
+
}
|
|
2680
2963
|
}
|
|
2681
2964
|
}
|
|
2682
2965
|
}
|
|
2683
|
-
return
|
|
2966
|
+
return result;
|
|
2684
2967
|
}
|
|
2968
|
+
}
|
|
2969
|
+
|
|
2970
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2971
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
2972
|
+
/**
|
|
2973
|
+
* Class to perform internationalization.
|
|
2974
|
+
*/
|
|
2975
|
+
class I18n {
|
|
2685
2976
|
/**
|
|
2686
|
-
*
|
|
2687
|
-
* @param hex The hex to decode.
|
|
2688
|
-
* @param reverse Store the characters in reverse.
|
|
2689
|
-
* @returns The array.
|
|
2977
|
+
* The default translation.
|
|
2690
2978
|
*/
|
|
2691
|
-
static
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2979
|
+
static DEFAULT_LOCALE = "en";
|
|
2980
|
+
/**
|
|
2981
|
+
* Set the locale.
|
|
2982
|
+
* @param locale The new locale.
|
|
2983
|
+
*/
|
|
2984
|
+
static setLocale(locale) {
|
|
2985
|
+
const i18nShared = I18n.getI18nShared();
|
|
2986
|
+
i18nShared.currentLocale = locale;
|
|
2987
|
+
for (const callback in i18nShared.localeChangedHandlers) {
|
|
2988
|
+
i18nShared.localeChangedHandlers[callback](i18nShared.currentLocale);
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
/**
|
|
2992
|
+
* Get the locale.
|
|
2993
|
+
* @returns The current locale.
|
|
2994
|
+
*/
|
|
2995
|
+
static getLocale() {
|
|
2996
|
+
const i18nShared = I18n.getI18nShared();
|
|
2997
|
+
return i18nShared.currentLocale;
|
|
2998
|
+
}
|
|
2999
|
+
/**
|
|
3000
|
+
* Add a locale dictionary.
|
|
3001
|
+
* @param locale The locale.
|
|
3002
|
+
* @param dictionary The dictionary to add.
|
|
3003
|
+
*/
|
|
3004
|
+
static addDictionary(locale, dictionary) {
|
|
3005
|
+
const i18nShared = I18n.getI18nShared();
|
|
3006
|
+
const mergedKeys = {};
|
|
3007
|
+
I18n.flattenTranslationKeys(dictionary, "", mergedKeys);
|
|
3008
|
+
i18nShared.localeDictionaries[locale] = mergedKeys;
|
|
3009
|
+
for (const callback in i18nShared.dictionaryChangedHandlers) {
|
|
3010
|
+
i18nShared.dictionaryChangedHandlers[callback](i18nShared.currentLocale);
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
3013
|
+
/**
|
|
3014
|
+
* Get a locale dictionary.
|
|
3015
|
+
* @param locale The locale.
|
|
3016
|
+
* @returns The dictionary of undefined if it does not exist.
|
|
3017
|
+
*/
|
|
3018
|
+
static getDictionary(locale) {
|
|
3019
|
+
const i18nShared = I18n.getI18nShared();
|
|
3020
|
+
return i18nShared.localeDictionaries[locale];
|
|
3021
|
+
}
|
|
3022
|
+
/**
|
|
3023
|
+
* Get all the locale dictionaries.
|
|
3024
|
+
* @returns The dictionaries.
|
|
3025
|
+
*/
|
|
3026
|
+
static getAllDictionaries() {
|
|
3027
|
+
const i18nShared = I18n.getI18nShared();
|
|
3028
|
+
return i18nShared.localeDictionaries;
|
|
3029
|
+
}
|
|
3030
|
+
/**
|
|
3031
|
+
* Add a locale changed handler.
|
|
3032
|
+
* @param id The id of the handler.
|
|
3033
|
+
* @param handler The handler to add.
|
|
3034
|
+
*/
|
|
3035
|
+
static addLocaleHandler(id, handler) {
|
|
3036
|
+
const i18nShared = I18n.getI18nShared();
|
|
3037
|
+
i18nShared.localeChangedHandlers[id] = handler;
|
|
3038
|
+
}
|
|
3039
|
+
/**
|
|
3040
|
+
* Remove a locale changed handler.
|
|
3041
|
+
* @param id The id of the handler.
|
|
3042
|
+
*/
|
|
3043
|
+
static removeLocaleHandler(id) {
|
|
3044
|
+
const i18nShared = I18n.getI18nShared();
|
|
3045
|
+
delete i18nShared.localeChangedHandlers[id];
|
|
3046
|
+
}
|
|
3047
|
+
/**
|
|
3048
|
+
* Add a dictionary changed handler.
|
|
3049
|
+
* @param id The id of the handler.
|
|
3050
|
+
* @param handler The handler to add.
|
|
3051
|
+
*/
|
|
3052
|
+
static addDictionaryHandler(id, handler) {
|
|
3053
|
+
const i18nShared = I18n.getI18nShared();
|
|
3054
|
+
i18nShared.dictionaryChangedHandlers[id] = handler;
|
|
3055
|
+
}
|
|
3056
|
+
/**
|
|
3057
|
+
* Remove a dictionary changed handler.
|
|
3058
|
+
* @param id The id of the handler.
|
|
3059
|
+
*/
|
|
3060
|
+
static removeDictionaryHandler(id) {
|
|
3061
|
+
const i18nShared = I18n.getI18nShared();
|
|
3062
|
+
delete i18nShared.dictionaryChangedHandlers[id];
|
|
3063
|
+
}
|
|
3064
|
+
/**
|
|
3065
|
+
* Format a message.
|
|
3066
|
+
* @param key The key of the message to format.
|
|
3067
|
+
* @param values The values to substitute into the message.
|
|
3068
|
+
* @param overrideLocale Override the locale.
|
|
3069
|
+
* @returns The formatted string.
|
|
3070
|
+
*/
|
|
3071
|
+
static formatMessage(key, values, overrideLocale) {
|
|
3072
|
+
const i18nShared = I18n.getI18nShared();
|
|
3073
|
+
let cl = overrideLocale ?? i18nShared.currentLocale;
|
|
3074
|
+
if (cl.startsWith("debug-")) {
|
|
3075
|
+
cl = I18n.DEFAULT_LOCALE;
|
|
3076
|
+
}
|
|
3077
|
+
if (!i18nShared.localeDictionaries[cl]) {
|
|
3078
|
+
return `!!Missing ${cl}`;
|
|
3079
|
+
}
|
|
3080
|
+
if (!i18nShared.localeDictionaries[cl][key]) {
|
|
3081
|
+
return `!!Missing ${cl}.${key}`;
|
|
3082
|
+
}
|
|
3083
|
+
if (i18nShared.currentLocale === "debug-k") {
|
|
3084
|
+
return key;
|
|
3085
|
+
}
|
|
3086
|
+
let ret = new intlMessageformat.IntlMessageFormat(i18nShared.localeDictionaries[cl][key], cl).format(values);
|
|
3087
|
+
if (i18nShared.currentLocale === "debug-x") {
|
|
3088
|
+
ret = ret.replace(/[a-z]/g, "x").replace(/[A-Z]/g, "x").replace(/\d/g, "n");
|
|
3089
|
+
}
|
|
3090
|
+
return ret;
|
|
3091
|
+
}
|
|
3092
|
+
/**
|
|
3093
|
+
* Check if the dictionaries have a message for the given key.
|
|
3094
|
+
* @param key The key to check for existence.
|
|
3095
|
+
* @returns True if the key exists.
|
|
3096
|
+
*/
|
|
3097
|
+
static hasMessage(key) {
|
|
3098
|
+
const i18nShared = I18n.getI18nShared();
|
|
3099
|
+
return Is.string(i18nShared.localeDictionaries[i18nShared.currentLocale]?.[key]);
|
|
3100
|
+
}
|
|
3101
|
+
/**
|
|
3102
|
+
* Flatten the translation property paths for faster lookup.
|
|
3103
|
+
* @param translation The translation to merge.
|
|
3104
|
+
* @param propertyPath The current root path.
|
|
3105
|
+
* @param mergedKeys The merged keys dictionary to populate.
|
|
3106
|
+
* @internal
|
|
3107
|
+
*/
|
|
3108
|
+
static flattenTranslationKeys(translation, propertyPath, mergedKeys) {
|
|
3109
|
+
for (const key in translation) {
|
|
3110
|
+
const val = translation[key];
|
|
3111
|
+
const mergedPath = propertyPath.length > 0 ? `${propertyPath}.${key}` : key;
|
|
3112
|
+
if (Is.string(val)) {
|
|
3113
|
+
mergedKeys[mergedPath] = val;
|
|
2704
3114
|
}
|
|
2705
|
-
if (
|
|
2706
|
-
|
|
3115
|
+
else if (Is.object(val)) {
|
|
3116
|
+
I18n.flattenTranslationKeys(val, mergedPath, mergedKeys);
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3120
|
+
/**
|
|
3121
|
+
* Get the I18n shared data.
|
|
3122
|
+
* @returns The I18n shared data.
|
|
3123
|
+
* @internal
|
|
3124
|
+
*/
|
|
3125
|
+
static getI18nShared() {
|
|
3126
|
+
let i18nShared = SharedStore.get("i18n");
|
|
3127
|
+
if (Is.undefined(i18nShared)) {
|
|
3128
|
+
i18nShared = {
|
|
3129
|
+
localeDictionaries: {},
|
|
3130
|
+
currentLocale: I18n.DEFAULT_LOCALE,
|
|
3131
|
+
localeChangedHandlers: {},
|
|
3132
|
+
dictionaryChangedHandlers: {}
|
|
3133
|
+
};
|
|
3134
|
+
SharedStore.set("i18n", i18nShared);
|
|
3135
|
+
}
|
|
3136
|
+
return i18nShared;
|
|
3137
|
+
}
|
|
3138
|
+
}
|
|
3139
|
+
|
|
3140
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3141
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3142
|
+
/**
|
|
3143
|
+
* Error helper functions.
|
|
3144
|
+
*/
|
|
3145
|
+
class ErrorHelper {
|
|
3146
|
+
/**
|
|
3147
|
+
* Format Errors and returns just their messages.
|
|
3148
|
+
* @param error The error to format.
|
|
3149
|
+
* @param includeDetails Whether to include error details, defaults to false.
|
|
3150
|
+
* @returns The error formatted including any inner errors.
|
|
3151
|
+
*/
|
|
3152
|
+
static formatErrors(error, includeDetails) {
|
|
3153
|
+
const localizedErrors = ErrorHelper.localizeErrors(error);
|
|
3154
|
+
if (includeDetails ?? false) {
|
|
3155
|
+
const output = [];
|
|
3156
|
+
for (const err of localizedErrors) {
|
|
3157
|
+
let detailedError = err.message;
|
|
3158
|
+
if (Is.stringValue(err.stack)) {
|
|
3159
|
+
detailedError += `\n${err.stack}`;
|
|
3160
|
+
}
|
|
3161
|
+
output.push(detailedError);
|
|
3162
|
+
}
|
|
3163
|
+
return output;
|
|
3164
|
+
}
|
|
3165
|
+
return localizedErrors.map(e => e.message);
|
|
3166
|
+
}
|
|
3167
|
+
/**
|
|
3168
|
+
* Localize the content of an error and any inner errors.
|
|
3169
|
+
* @param error The error to format.
|
|
3170
|
+
* @returns The localized version of the errors flattened.
|
|
3171
|
+
*/
|
|
3172
|
+
static localizeErrors(error) {
|
|
3173
|
+
const formattedErrors = [];
|
|
3174
|
+
if (Is.notEmpty(error)) {
|
|
3175
|
+
const errors = BaseError.flatten(error);
|
|
3176
|
+
for (const err of errors) {
|
|
3177
|
+
const errorNameKey = `errorNames.${StringHelper.camelCase(err.name)}`;
|
|
3178
|
+
const errorMessageKey = `error.${err.message}`;
|
|
3179
|
+
// If there is no error message then it is probably
|
|
3180
|
+
// from a 3rd party lib, so don't format it just display
|
|
3181
|
+
const hasErrorName = I18n.hasMessage(errorNameKey);
|
|
3182
|
+
const hasErrorMessage = I18n.hasMessage(errorMessageKey);
|
|
3183
|
+
const localizedError = {
|
|
3184
|
+
name: I18n.formatMessage(hasErrorName ? errorNameKey : "errorNames.error"),
|
|
3185
|
+
message: hasErrorMessage
|
|
3186
|
+
? I18n.formatMessage(errorMessageKey, err.properties)
|
|
3187
|
+
: err.message
|
|
3188
|
+
};
|
|
3189
|
+
if (Is.stringValue(err.source)) {
|
|
3190
|
+
localizedError.source = err.source;
|
|
3191
|
+
}
|
|
3192
|
+
if (Is.stringValue(err.stack)) {
|
|
3193
|
+
// Remove the first line from the stack traces as they
|
|
3194
|
+
// just have the error type and message duplicated
|
|
3195
|
+
const lines = err.stack.split("\n");
|
|
3196
|
+
lines.shift();
|
|
3197
|
+
localizedError.stack = lines.join("\n");
|
|
3198
|
+
}
|
|
3199
|
+
const additional = ErrorHelper.formatValidationErrors(err);
|
|
3200
|
+
if (Is.stringValue(additional)) {
|
|
3201
|
+
localizedError.additional = additional;
|
|
3202
|
+
}
|
|
3203
|
+
formattedErrors.push(localizedError);
|
|
3204
|
+
}
|
|
3205
|
+
}
|
|
3206
|
+
return formattedErrors;
|
|
3207
|
+
}
|
|
3208
|
+
/**
|
|
3209
|
+
* Localize the content of an error and any inner errors.
|
|
3210
|
+
* @param error The error to format.
|
|
3211
|
+
* @returns The localized version of the errors flattened.
|
|
3212
|
+
*/
|
|
3213
|
+
static formatValidationErrors(error) {
|
|
3214
|
+
if (Is.object(error.properties) &&
|
|
3215
|
+
Object.keys(error.properties).length > 0 &&
|
|
3216
|
+
Is.object(error.properties) &&
|
|
3217
|
+
Is.arrayValue(error.properties.validationFailures)) {
|
|
3218
|
+
const validationErrors = [];
|
|
3219
|
+
for (const validationFailure of error.properties.validationFailures) {
|
|
3220
|
+
const errorI18n = `error.${validationFailure.reason}`;
|
|
3221
|
+
const errorMessage = I18n.hasMessage(errorI18n)
|
|
3222
|
+
? I18n.formatMessage(errorI18n, validationFailure.properties)
|
|
3223
|
+
: errorI18n;
|
|
3224
|
+
let v = `${validationFailure.property}: ${errorMessage}`;
|
|
3225
|
+
if (Is.object(validationFailure.properties) &&
|
|
3226
|
+
Is.notEmpty(validationFailure.properties.value)) {
|
|
3227
|
+
v += ` = ${JSON.stringify(validationFailure.properties.value)}`;
|
|
3228
|
+
}
|
|
3229
|
+
validationErrors.push(v);
|
|
2707
3230
|
}
|
|
3231
|
+
return validationErrors.join("\n");
|
|
2708
3232
|
}
|
|
2709
|
-
return array;
|
|
2710
|
-
}
|
|
2711
|
-
/**
|
|
2712
|
-
* Convert the UTF8 to hex.
|
|
2713
|
-
* @param utf8 The text to convert.
|
|
2714
|
-
* @param includePrefix Include the 0x prefix on the returned hex.
|
|
2715
|
-
* @returns The hex version of the bytes.
|
|
2716
|
-
*/
|
|
2717
|
-
static utf8ToHex(utf8, includePrefix = false) {
|
|
2718
|
-
const hex = Converter.bytesToHex(Converter.utf8ToBytes(utf8));
|
|
2719
|
-
return includePrefix ? HexHelper.addPrefix(hex) : hex;
|
|
2720
3233
|
}
|
|
3234
|
+
}
|
|
3235
|
+
|
|
3236
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3237
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3238
|
+
/**
|
|
3239
|
+
* The types the extracted data can be coerced to.
|
|
3240
|
+
*/
|
|
3241
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
3242
|
+
const CoerceType = {
|
|
2721
3243
|
/**
|
|
2722
|
-
*
|
|
2723
|
-
* @param hex The hex to convert.
|
|
2724
|
-
* @returns The UTF8 version of the bytes.
|
|
3244
|
+
* String.
|
|
2725
3245
|
*/
|
|
2726
|
-
|
|
2727
|
-
return Converter.bytesToUtf8(Converter.hexToBytes(HexHelper.stripPrefix(hex)));
|
|
2728
|
-
}
|
|
3246
|
+
String: "string",
|
|
2729
3247
|
/**
|
|
2730
|
-
*
|
|
2731
|
-
* @param bytes The bytes to convert.
|
|
2732
|
-
* @returns A binary string of the bytes.
|
|
3248
|
+
* Number.
|
|
2733
3249
|
*/
|
|
2734
|
-
|
|
2735
|
-
const b = [];
|
|
2736
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
2737
|
-
b.push(bytes[i].toString(2).padStart(8, "0"));
|
|
2738
|
-
}
|
|
2739
|
-
return b.join("");
|
|
2740
|
-
}
|
|
3250
|
+
Number: "number",
|
|
2741
3251
|
/**
|
|
2742
|
-
*
|
|
2743
|
-
* @param binary The binary string.
|
|
2744
|
-
* @returns The bytes.
|
|
3252
|
+
* Integer.
|
|
2745
3253
|
*/
|
|
2746
|
-
|
|
2747
|
-
const bytes = new Uint8Array(Math.ceil(binary.length / 8));
|
|
2748
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
2749
|
-
bytes[i] = Number.parseInt(binary.slice(i * 8, (i + 1) * 8), 2);
|
|
2750
|
-
}
|
|
2751
|
-
return bytes;
|
|
2752
|
-
}
|
|
3254
|
+
Integer: "integer",
|
|
2753
3255
|
/**
|
|
2754
|
-
*
|
|
2755
|
-
* @param bytes The bytes to convert.
|
|
2756
|
-
* @returns A base64 string of the bytes.
|
|
3256
|
+
* Boolean.
|
|
2757
3257
|
*/
|
|
2758
|
-
|
|
2759
|
-
return Base64.encode(bytes);
|
|
2760
|
-
}
|
|
3258
|
+
Boolean: "boolean",
|
|
2761
3259
|
/**
|
|
2762
|
-
*
|
|
2763
|
-
* @param base64 The base64 string.
|
|
2764
|
-
* @returns The bytes.
|
|
3260
|
+
* Big Integer.
|
|
2765
3261
|
*/
|
|
2766
|
-
|
|
2767
|
-
return Base64.decode(base64);
|
|
2768
|
-
}
|
|
3262
|
+
BigInt: "bigint",
|
|
2769
3263
|
/**
|
|
2770
|
-
*
|
|
2771
|
-
* @param bytes The bytes to convert.
|
|
2772
|
-
* @returns A base64 url string of the bytes.
|
|
3264
|
+
* Date.
|
|
2773
3265
|
*/
|
|
2774
|
-
|
|
2775
|
-
return Base64Url.encode(bytes);
|
|
2776
|
-
}
|
|
3266
|
+
Date: "date",
|
|
2777
3267
|
/**
|
|
2778
|
-
*
|
|
2779
|
-
* @param base64Url The base64 url string.
|
|
2780
|
-
* @returns The bytes.
|
|
3268
|
+
* Date Time.
|
|
2781
3269
|
*/
|
|
2782
|
-
|
|
2783
|
-
return Base64Url.decode(base64Url);
|
|
2784
|
-
}
|
|
3270
|
+
DateTime: "datetime",
|
|
2785
3271
|
/**
|
|
2786
|
-
*
|
|
2787
|
-
* @param bytes The bytes to convert.
|
|
2788
|
-
* @returns A base58 string of the bytes.
|
|
3272
|
+
* Time.
|
|
2789
3273
|
*/
|
|
2790
|
-
|
|
2791
|
-
return Base58.encode(bytes);
|
|
2792
|
-
}
|
|
3274
|
+
Time: "time",
|
|
2793
3275
|
/**
|
|
2794
|
-
*
|
|
2795
|
-
* @param base58 The base58 string.
|
|
2796
|
-
* @returns The bytes.
|
|
3276
|
+
* Object.
|
|
2797
3277
|
*/
|
|
2798
|
-
|
|
2799
|
-
return Base58.decode(base58);
|
|
2800
|
-
}
|
|
3278
|
+
Object: "object",
|
|
2801
3279
|
/**
|
|
2802
|
-
*
|
|
2803
|
-
* @internal
|
|
3280
|
+
* Uint8Array.
|
|
2804
3281
|
*/
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
const alphabet = "0123456789abcdef";
|
|
2808
|
-
Converter._ENCODE_LOOKUP = [];
|
|
2809
|
-
Converter._DECODE_LOOKUP = [];
|
|
2810
|
-
for (let i = 0; i < 256; i++) {
|
|
2811
|
-
Converter._ENCODE_LOOKUP[i] = alphabet[(i >> 4) & 0xf] + alphabet[i & 0xf];
|
|
2812
|
-
if (i < 16) {
|
|
2813
|
-
if (i < 10) {
|
|
2814
|
-
Converter._DECODE_LOOKUP[0x30 + i] = i;
|
|
2815
|
-
}
|
|
2816
|
-
else {
|
|
2817
|
-
Converter._DECODE_LOOKUP[0x61 - 10 + i] = i;
|
|
2818
|
-
}
|
|
2819
|
-
}
|
|
2820
|
-
}
|
|
2821
|
-
}
|
|
2822
|
-
}
|
|
2823
|
-
}
|
|
3282
|
+
Uint8Array: "uint8array"
|
|
3283
|
+
};
|
|
2824
3284
|
|
|
3285
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3286
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
2825
3287
|
/**
|
|
2826
|
-
*
|
|
3288
|
+
* Coerce an object from one type to another.
|
|
2827
3289
|
*/
|
|
2828
|
-
class
|
|
2829
|
-
/**
|
|
2830
|
-
* Runtime name for the class.
|
|
2831
|
-
* @internal
|
|
2832
|
-
*/
|
|
2833
|
-
static _CLASS_NAME = "ObjectHelper";
|
|
3290
|
+
class Coerce {
|
|
2834
3291
|
/**
|
|
2835
|
-
*
|
|
2836
|
-
* @param
|
|
2837
|
-
* @
|
|
2838
|
-
* @returns The
|
|
3292
|
+
* Coerce the value to a string.
|
|
3293
|
+
* @param value The value to coerce.
|
|
3294
|
+
* @throws TypeError If the value can not be coerced.
|
|
3295
|
+
* @returns The value if it can be coerced.
|
|
2839
3296
|
*/
|
|
2840
|
-
static
|
|
2841
|
-
if (
|
|
2842
|
-
return
|
|
3297
|
+
static string(value) {
|
|
3298
|
+
if (Is.undefined(value)) {
|
|
3299
|
+
return value;
|
|
3300
|
+
}
|
|
3301
|
+
if (Is.string(value)) {
|
|
3302
|
+
return value;
|
|
3303
|
+
}
|
|
3304
|
+
if (Is.number(value)) {
|
|
3305
|
+
return value.toString();
|
|
3306
|
+
}
|
|
3307
|
+
if (Is.boolean(value)) {
|
|
3308
|
+
return value ? "true" : "false";
|
|
3309
|
+
}
|
|
3310
|
+
if (Is.date(value)) {
|
|
3311
|
+
return value.toISOString();
|
|
2843
3312
|
}
|
|
2844
|
-
const json = format ? JSON.stringify(obj, undefined, "\t") : JSON.stringify(obj);
|
|
2845
|
-
return Converter.utf8ToBytes(json);
|
|
2846
3313
|
}
|
|
2847
3314
|
/**
|
|
2848
|
-
*
|
|
2849
|
-
* @param
|
|
2850
|
-
* @
|
|
2851
|
-
* @
|
|
3315
|
+
* Coerce the value to a number.
|
|
3316
|
+
* @param value The value to coerce.
|
|
3317
|
+
* @throws TypeError If the value can not be coerced.
|
|
3318
|
+
* @returns The value if it can be coerced.
|
|
2852
3319
|
*/
|
|
2853
|
-
static
|
|
2854
|
-
if (Is.
|
|
2855
|
-
return
|
|
3320
|
+
static number(value) {
|
|
3321
|
+
if (Is.undefined(value)) {
|
|
3322
|
+
return value;
|
|
2856
3323
|
}
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
return JSON.parse(utf8);
|
|
3324
|
+
if (Is.number(value)) {
|
|
3325
|
+
return value;
|
|
2860
3326
|
}
|
|
2861
|
-
|
|
2862
|
-
|
|
3327
|
+
if (Is.string(value)) {
|
|
3328
|
+
const parsed = Number.parseFloat(value);
|
|
3329
|
+
if (Is.number(parsed)) {
|
|
3330
|
+
return parsed;
|
|
3331
|
+
}
|
|
3332
|
+
}
|
|
3333
|
+
if (Is.boolean(value)) {
|
|
3334
|
+
return value ? 1 : 0;
|
|
3335
|
+
}
|
|
3336
|
+
if (Is.date(value)) {
|
|
3337
|
+
return value.getTime();
|
|
2863
3338
|
}
|
|
2864
3339
|
}
|
|
2865
3340
|
/**
|
|
2866
|
-
*
|
|
2867
|
-
* @param
|
|
2868
|
-
* @
|
|
3341
|
+
* Coerce the value to an integer.
|
|
3342
|
+
* @param value The value to coerce.
|
|
3343
|
+
* @throws TypeError If the value can not be coerced.
|
|
3344
|
+
* @returns The value if it can be coerced.
|
|
2869
3345
|
*/
|
|
2870
|
-
static
|
|
2871
|
-
|
|
2872
|
-
|
|
3346
|
+
static integer(value) {
|
|
3347
|
+
const num = Coerce.number(value);
|
|
3348
|
+
if (!Is.undefined(num)) {
|
|
3349
|
+
return Math.trunc(num);
|
|
2873
3350
|
}
|
|
2874
|
-
return structuredClone(obj);
|
|
2875
3351
|
}
|
|
2876
3352
|
/**
|
|
2877
|
-
*
|
|
2878
|
-
* @param
|
|
2879
|
-
* @
|
|
2880
|
-
* @returns The
|
|
3353
|
+
* Coerce the value to a bigint.
|
|
3354
|
+
* @param value The value to coerce.
|
|
3355
|
+
* @throws TypeError If the value can not be coerced.
|
|
3356
|
+
* @returns The value if it can be coerced.
|
|
2881
3357
|
*/
|
|
2882
|
-
static
|
|
2883
|
-
if (Is.
|
|
2884
|
-
return
|
|
3358
|
+
static bigint(value) {
|
|
3359
|
+
if (Is.undefined(value)) {
|
|
3360
|
+
return value;
|
|
2885
3361
|
}
|
|
2886
|
-
if (Is.
|
|
2887
|
-
return
|
|
3362
|
+
if (Is.bigint(value)) {
|
|
3363
|
+
return value;
|
|
2888
3364
|
}
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
3365
|
+
if (Is.number(value)) {
|
|
3366
|
+
return BigInt(value);
|
|
3367
|
+
}
|
|
3368
|
+
if (Is.string(value)) {
|
|
3369
|
+
const parsed = Number.parseFloat(value);
|
|
3370
|
+
if (Is.integer(parsed)) {
|
|
3371
|
+
return BigInt(parsed);
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3374
|
+
if (Is.boolean(value)) {
|
|
3375
|
+
return value ? 1n : 0n;
|
|
3376
|
+
}
|
|
3377
|
+
}
|
|
3378
|
+
/**
|
|
3379
|
+
* Coerce the value to a boolean.
|
|
3380
|
+
* @param value The value to coerce.
|
|
3381
|
+
* @throws TypeError If the value can not be coerced.
|
|
3382
|
+
* @returns The value if it can be coerced.
|
|
3383
|
+
*/
|
|
3384
|
+
static boolean(value) {
|
|
3385
|
+
if (Is.undefined(value)) {
|
|
3386
|
+
return value;
|
|
3387
|
+
}
|
|
3388
|
+
if (Is.boolean(value)) {
|
|
3389
|
+
return value;
|
|
3390
|
+
}
|
|
3391
|
+
if (Is.number(value)) {
|
|
3392
|
+
// eslint-disable-next-line no-unneeded-ternary
|
|
3393
|
+
return value ? true : false;
|
|
3394
|
+
}
|
|
3395
|
+
if (Is.string(value)) {
|
|
3396
|
+
if (/true/i.test(value)) {
|
|
3397
|
+
return true;
|
|
3398
|
+
}
|
|
3399
|
+
if (/false/i.test(value)) {
|
|
3400
|
+
return false;
|
|
2899
3401
|
}
|
|
2900
3402
|
}
|
|
2901
|
-
return obj1Clone;
|
|
2902
3403
|
}
|
|
2903
3404
|
/**
|
|
2904
|
-
*
|
|
2905
|
-
* @param
|
|
2906
|
-
* @
|
|
2907
|
-
* @
|
|
2908
|
-
* @returns True is the objects are equal.
|
|
3405
|
+
* Coerce the value to a date.
|
|
3406
|
+
* @param value The value to coerce.
|
|
3407
|
+
* @throws TypeError If the value can not be coerced.
|
|
3408
|
+
* @returns The value if it can be coerced.
|
|
2909
3409
|
*/
|
|
2910
|
-
static
|
|
2911
|
-
if (
|
|
2912
|
-
return
|
|
3410
|
+
static date(value) {
|
|
3411
|
+
if (Is.undefined(value)) {
|
|
3412
|
+
return value;
|
|
3413
|
+
}
|
|
3414
|
+
if (Is.date(value)) {
|
|
3415
|
+
return value;
|
|
3416
|
+
}
|
|
3417
|
+
if (Is.number(value)) {
|
|
3418
|
+
return new Date(value);
|
|
3419
|
+
}
|
|
3420
|
+
if (Is.string(value)) {
|
|
3421
|
+
const dt = new Date(value);
|
|
3422
|
+
if (!Number.isNaN(dt.getTime())) {
|
|
3423
|
+
const utc = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate());
|
|
3424
|
+
return new Date(utc);
|
|
3425
|
+
}
|
|
2913
3426
|
}
|
|
2914
|
-
return JsonHelper.canonicalize(obj1) === JsonHelper.canonicalize(obj2);
|
|
2915
3427
|
}
|
|
2916
3428
|
/**
|
|
2917
|
-
*
|
|
2918
|
-
* @param
|
|
2919
|
-
* @
|
|
2920
|
-
* @returns The
|
|
3429
|
+
* Coerce the value to a date/time.
|
|
3430
|
+
* @param value The value to coerce.
|
|
3431
|
+
* @throws TypeError If the value can not be coerced.
|
|
3432
|
+
* @returns The value if it can be coerced.
|
|
2921
3433
|
*/
|
|
2922
|
-
static
|
|
2923
|
-
if (
|
|
2924
|
-
const parts = property.split(".");
|
|
2925
|
-
let value = obj;
|
|
2926
|
-
for (const part of parts) {
|
|
2927
|
-
if (Is.object(value)) {
|
|
2928
|
-
value = value[part];
|
|
2929
|
-
}
|
|
2930
|
-
else {
|
|
2931
|
-
return undefined;
|
|
2932
|
-
}
|
|
2933
|
-
}
|
|
3434
|
+
static dateTime(value) {
|
|
3435
|
+
if (Is.undefined(value)) {
|
|
2934
3436
|
return value;
|
|
2935
3437
|
}
|
|
2936
|
-
|
|
3438
|
+
if (Is.date(value)) {
|
|
3439
|
+
return value;
|
|
3440
|
+
}
|
|
3441
|
+
if (Is.number(value)) {
|
|
3442
|
+
return new Date(value);
|
|
3443
|
+
}
|
|
3444
|
+
if (Is.string(value)) {
|
|
3445
|
+
const dt = new Date(value);
|
|
3446
|
+
if (!Number.isNaN(dt.getTime())) {
|
|
3447
|
+
const utc = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate(), dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
|
|
3448
|
+
return new Date(utc);
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
2937
3451
|
}
|
|
2938
3452
|
/**
|
|
2939
|
-
*
|
|
2940
|
-
* @param
|
|
2941
|
-
* @
|
|
2942
|
-
* @
|
|
3453
|
+
* Coerce the value to a time.
|
|
3454
|
+
* @param value The value to coerce.
|
|
3455
|
+
* @throws TypeError If the value can not be coerced.
|
|
3456
|
+
* @returns The value if it can be coerced.
|
|
2943
3457
|
*/
|
|
2944
|
-
static
|
|
2945
|
-
if (Is.
|
|
2946
|
-
|
|
3458
|
+
static time(value) {
|
|
3459
|
+
if (Is.undefined(value)) {
|
|
3460
|
+
return value;
|
|
3461
|
+
}
|
|
3462
|
+
if (Is.date(value)) {
|
|
3463
|
+
return value;
|
|
3464
|
+
}
|
|
3465
|
+
if (Is.number(value)) {
|
|
3466
|
+
const dt = new Date(value);
|
|
3467
|
+
dt.setFullYear(1970, 0, 1);
|
|
3468
|
+
return dt;
|
|
3469
|
+
}
|
|
3470
|
+
if (Is.string(value)) {
|
|
3471
|
+
const dt = new Date(value);
|
|
3472
|
+
if (!Number.isNaN(dt.getTime())) {
|
|
3473
|
+
const utc = Date.UTC(1970, 0, 1, dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
|
|
3474
|
+
return new Date(utc);
|
|
3475
|
+
}
|
|
2947
3476
|
}
|
|
2948
3477
|
}
|
|
2949
3478
|
/**
|
|
2950
|
-
*
|
|
2951
|
-
* @param
|
|
2952
|
-
* @
|
|
3479
|
+
* Coerce the value to an object.
|
|
3480
|
+
* @param value The value to coerce.
|
|
3481
|
+
* @throws TypeError If the value can not be coerced.
|
|
3482
|
+
* @returns The value if it can be coerced.
|
|
2953
3483
|
*/
|
|
2954
|
-
static
|
|
2955
|
-
if (Is.
|
|
2956
|
-
|
|
3484
|
+
static object(value) {
|
|
3485
|
+
if (Is.undefined(value)) {
|
|
3486
|
+
return value;
|
|
3487
|
+
}
|
|
3488
|
+
if (Is.object(value)) {
|
|
3489
|
+
return value;
|
|
3490
|
+
}
|
|
3491
|
+
if (Is.stringValue(value)) {
|
|
3492
|
+
try {
|
|
3493
|
+
return JSON.parse(value);
|
|
3494
|
+
}
|
|
3495
|
+
catch { }
|
|
2957
3496
|
}
|
|
2958
3497
|
}
|
|
2959
3498
|
/**
|
|
2960
|
-
*
|
|
2961
|
-
* @param
|
|
2962
|
-
* @
|
|
2963
|
-
* @
|
|
2964
|
-
* @returns The property if available.
|
|
3499
|
+
* Coerce the value to a Uint8Array.
|
|
3500
|
+
* @param value The value to coerce.
|
|
3501
|
+
* @throws TypeError If the value can not be coerced.
|
|
3502
|
+
* @returns The value if it can be coerced.
|
|
2965
3503
|
*/
|
|
2966
|
-
static
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
3504
|
+
static uint8Array(value) {
|
|
3505
|
+
if (Is.undefined(value)) {
|
|
3506
|
+
return value;
|
|
3507
|
+
}
|
|
3508
|
+
if (Is.string(value)) {
|
|
3509
|
+
if (Is.stringHex(value.toLowerCase(), true)) {
|
|
3510
|
+
return Converter.hexToBytes(value.toLowerCase());
|
|
3511
|
+
}
|
|
3512
|
+
if (Is.stringBase64(value)) {
|
|
3513
|
+
return Converter.base64ToBytes(value);
|
|
2975
3514
|
}
|
|
2976
3515
|
}
|
|
2977
|
-
return retVal;
|
|
2978
3516
|
}
|
|
2979
3517
|
/**
|
|
2980
|
-
*
|
|
2981
|
-
* @param
|
|
2982
|
-
* @param
|
|
2983
|
-
* @returns The
|
|
2984
|
-
*/
|
|
2985
|
-
static
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
3518
|
+
* Coerces a value based on the coercion type.
|
|
3519
|
+
* @param value The value to coerce.
|
|
3520
|
+
* @param type The coercion type to perform.
|
|
3521
|
+
* @returns The coerced value.
|
|
3522
|
+
*/
|
|
3523
|
+
static byType(value, type) {
|
|
3524
|
+
switch (type) {
|
|
3525
|
+
case CoerceType.String:
|
|
3526
|
+
return Coerce.string(value);
|
|
3527
|
+
case CoerceType.Number:
|
|
3528
|
+
return Coerce.number(value);
|
|
3529
|
+
case CoerceType.Integer:
|
|
3530
|
+
return Coerce.integer(value);
|
|
3531
|
+
case CoerceType.BigInt:
|
|
3532
|
+
return Coerce.bigint(value);
|
|
3533
|
+
case CoerceType.Boolean:
|
|
3534
|
+
return Coerce.boolean(value);
|
|
3535
|
+
case CoerceType.Date:
|
|
3536
|
+
return Coerce.date(value);
|
|
3537
|
+
case CoerceType.DateTime:
|
|
3538
|
+
return Coerce.dateTime(value);
|
|
3539
|
+
case CoerceType.Time:
|
|
3540
|
+
return Coerce.time(value);
|
|
3541
|
+
case CoerceType.Object:
|
|
3542
|
+
return Coerce.object(value);
|
|
3543
|
+
case CoerceType.Uint8Array:
|
|
3544
|
+
return Coerce.uint8Array(value);
|
|
3545
|
+
default:
|
|
3546
|
+
return value;
|
|
2992
3547
|
}
|
|
2993
|
-
return obj;
|
|
2994
3548
|
}
|
|
3549
|
+
}
|
|
3550
|
+
|
|
3551
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3552
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3553
|
+
/**
|
|
3554
|
+
* Class to help with filenames.
|
|
3555
|
+
*/
|
|
3556
|
+
class FilenameHelper {
|
|
2995
3557
|
/**
|
|
2996
|
-
*
|
|
2997
|
-
* @param
|
|
2998
|
-
* @
|
|
2999
|
-
* @returns The partial object.
|
|
3558
|
+
* Replaces any unsafe characters in the filename.
|
|
3559
|
+
* @param filename The filename to make safe.
|
|
3560
|
+
* @returns The safe filename.
|
|
3000
3561
|
*/
|
|
3001
|
-
static
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
delete result[key];
|
|
3006
|
-
}
|
|
3007
|
-
return result;
|
|
3562
|
+
static safeFilename(filename) {
|
|
3563
|
+
let safe = Coerce.string(filename);
|
|
3564
|
+
if (Is.empty(safe)) {
|
|
3565
|
+
return "";
|
|
3008
3566
|
}
|
|
3009
|
-
|
|
3567
|
+
// Common non filename characters
|
|
3568
|
+
safe = safe.replace(/["*/:<>?\\|]/g, "_");
|
|
3569
|
+
// Windows non filename characters
|
|
3570
|
+
safe = safe.replace(/^(con|prn|aux|nul|com\d|lpt\d)$/i, "_");
|
|
3571
|
+
// Control characters
|
|
3572
|
+
safe = safe.replace(/[\u0000-\u001F\u0080-\u009F]/g, "_");
|
|
3573
|
+
// Relative paths
|
|
3574
|
+
safe = safe.replace(/^\.+/, "_");
|
|
3575
|
+
// Trailing periods
|
|
3576
|
+
safe = safe.replace(/\.+$/, "");
|
|
3577
|
+
return safe;
|
|
3010
3578
|
}
|
|
3011
3579
|
}
|
|
3012
3580
|
|
|
@@ -3028,6 +3596,32 @@ class RandomHelper {
|
|
|
3028
3596
|
}
|
|
3029
3597
|
}
|
|
3030
3598
|
|
|
3599
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3600
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3601
|
+
/**
|
|
3602
|
+
* Class to help with uint8 arrays.
|
|
3603
|
+
*/
|
|
3604
|
+
class Uint8ArrayHelper {
|
|
3605
|
+
/**
|
|
3606
|
+
* Concatenate multiple arrays.
|
|
3607
|
+
* @param arrays The array to concatenate.
|
|
3608
|
+
* @returns The combined array.
|
|
3609
|
+
*/
|
|
3610
|
+
static concat(arrays) {
|
|
3611
|
+
let totalLength = 0;
|
|
3612
|
+
for (const array of arrays) {
|
|
3613
|
+
totalLength += array.length;
|
|
3614
|
+
}
|
|
3615
|
+
const concatBytes = new Uint8Array(totalLength);
|
|
3616
|
+
let offset = 0;
|
|
3617
|
+
for (const array of arrays) {
|
|
3618
|
+
concatBytes.set(array, offset);
|
|
3619
|
+
offset += array.length;
|
|
3620
|
+
}
|
|
3621
|
+
return concatBytes;
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
|
|
3031
3625
|
// Copyright 2024 IOTA Stiftung.
|
|
3032
3626
|
// SPDX-License-Identifier: Apache-2.0.
|
|
3033
3627
|
/**
|
|
@@ -3040,7 +3634,7 @@ const CompressionType = {
|
|
|
3040
3634
|
*/
|
|
3041
3635
|
Gzip: "gzip",
|
|
3042
3636
|
/**
|
|
3043
|
-
*
|
|
3637
|
+
* Deflate.
|
|
3044
3638
|
*/
|
|
3045
3639
|
Deflate: "deflate"
|
|
3046
3640
|
};
|
|
@@ -3491,29 +4085,93 @@ class Urn {
|
|
|
3491
4085
|
* Cache the results from asynchronous requests.
|
|
3492
4086
|
*/
|
|
3493
4087
|
class AsyncCache {
|
|
3494
|
-
/**
|
|
3495
|
-
* Cache for the fetch requests.
|
|
3496
|
-
* @internal
|
|
3497
|
-
*/
|
|
3498
|
-
static _cache = {};
|
|
3499
4088
|
/**
|
|
3500
4089
|
* Execute an async request and cache the result.
|
|
3501
4090
|
* @param key The key for the entry in the cache.
|
|
3502
4091
|
* @param ttlMs The TTL of the entry in the cache.
|
|
3503
4092
|
* @param requestMethod The method to call if not cached.
|
|
4093
|
+
* @param cacheFailures Cache failure results, defaults to false.
|
|
3504
4094
|
* @returns The response.
|
|
3505
4095
|
*/
|
|
3506
|
-
static exec(key, ttlMs, requestMethod) {
|
|
4096
|
+
static exec(key, ttlMs, requestMethod, cacheFailures) {
|
|
3507
4097
|
const cacheEnabled = Is.integer(ttlMs) && ttlMs >= 0;
|
|
3508
4098
|
if (cacheEnabled) {
|
|
3509
4099
|
AsyncCache.cleanupExpired();
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
4100
|
+
const cache = AsyncCache.getSharedCache();
|
|
4101
|
+
// Do we have a cache entry for the key
|
|
4102
|
+
if (cache[key]) {
|
|
4103
|
+
if (!Is.empty(cache[key].result)) {
|
|
4104
|
+
// If the cache has already resulted in a value, resolve it
|
|
4105
|
+
return Promise.resolve(cache[key].result);
|
|
4106
|
+
}
|
|
4107
|
+
else if (!Is.empty(cache[key].error)) {
|
|
4108
|
+
// If the cache has already resulted in an error, reject it
|
|
4109
|
+
return Promise.reject(cache[key].error);
|
|
4110
|
+
}
|
|
4111
|
+
// Otherwise create a promise to return and store the resolver
|
|
4112
|
+
// and rejector in the cache entry, so that we can call then
|
|
4113
|
+
// when the request is done
|
|
4114
|
+
let storedResolve;
|
|
4115
|
+
let storedReject;
|
|
4116
|
+
const wait = new Promise((resolve, reject) => {
|
|
4117
|
+
storedResolve = resolve;
|
|
4118
|
+
storedReject = reject;
|
|
4119
|
+
});
|
|
4120
|
+
if (!Is.empty(storedResolve) && !Is.empty(storedReject)) {
|
|
4121
|
+
cache[key].promiseQueue.push({
|
|
4122
|
+
requestMethod,
|
|
4123
|
+
resolve: storedResolve,
|
|
4124
|
+
reject: storedReject
|
|
4125
|
+
});
|
|
4126
|
+
}
|
|
4127
|
+
return wait;
|
|
3515
4128
|
}
|
|
3516
|
-
|
|
4129
|
+
// If we don't have a cache entry, create a new one
|
|
4130
|
+
cache[key] = {
|
|
4131
|
+
promiseQueue: [],
|
|
4132
|
+
expires: ttlMs === 0 ? 0 : Date.now() + ttlMs
|
|
4133
|
+
};
|
|
4134
|
+
// Return a promise that wraps the original request method
|
|
4135
|
+
// so that we can store any results or errors in the cache
|
|
4136
|
+
return new Promise((resolve, reject) => {
|
|
4137
|
+
// Call the request method and store the result
|
|
4138
|
+
requestMethod()
|
|
4139
|
+
// eslint-disable-next-line promise/prefer-await-to-then
|
|
4140
|
+
.then(res => {
|
|
4141
|
+
// If the request was successful, store the result
|
|
4142
|
+
cache[key].result = res;
|
|
4143
|
+
// and resolve both this promise and all the waiters
|
|
4144
|
+
resolve(res);
|
|
4145
|
+
for (const wait of cache[key].promiseQueue) {
|
|
4146
|
+
wait.resolve(res);
|
|
4147
|
+
}
|
|
4148
|
+
return res;
|
|
4149
|
+
})
|
|
4150
|
+
// eslint-disable-next-line promise/prefer-await-to-then
|
|
4151
|
+
.catch((err) => {
|
|
4152
|
+
// Reject the promise
|
|
4153
|
+
reject(err);
|
|
4154
|
+
// Handle the waiters based on the cacheFailures flag
|
|
4155
|
+
if (cacheFailures ?? false) {
|
|
4156
|
+
// If we are caching failures, store the error and reject the waiters
|
|
4157
|
+
cache[key].error = err;
|
|
4158
|
+
for (const wait of cache[key].promiseQueue) {
|
|
4159
|
+
wait.reject(err);
|
|
4160
|
+
}
|
|
4161
|
+
// Clear the waiters so we don't call them again
|
|
4162
|
+
cache[key].promiseQueue = [];
|
|
4163
|
+
}
|
|
4164
|
+
else {
|
|
4165
|
+
// If not caching failures for any queued requests we
|
|
4166
|
+
// have no value to either resolve or reject, so we
|
|
4167
|
+
// just resolve with the original request method
|
|
4168
|
+
for (const wait of cache[key].promiseQueue) {
|
|
4169
|
+
wait.resolve(wait.requestMethod());
|
|
4170
|
+
}
|
|
4171
|
+
delete cache[key];
|
|
4172
|
+
}
|
|
4173
|
+
});
|
|
4174
|
+
});
|
|
3517
4175
|
}
|
|
3518
4176
|
}
|
|
3519
4177
|
/**
|
|
@@ -3522,41 +4180,80 @@ class AsyncCache {
|
|
|
3522
4180
|
* @returns The item from the cache if it exists.
|
|
3523
4181
|
*/
|
|
3524
4182
|
static async get(key) {
|
|
3525
|
-
|
|
4183
|
+
const cache = AsyncCache.getSharedCache();
|
|
4184
|
+
if (!Is.empty(cache[key].result)) {
|
|
4185
|
+
// If the cache has already resulted in a value, resolve it
|
|
4186
|
+
return cache[key].result;
|
|
4187
|
+
}
|
|
4188
|
+
else if (!Is.empty(cache[key].error)) {
|
|
4189
|
+
// If the cache has already resulted in an error, reject it
|
|
4190
|
+
throw cache[key].error;
|
|
4191
|
+
}
|
|
4192
|
+
}
|
|
4193
|
+
/**
|
|
4194
|
+
* Set an entry into the cache.
|
|
4195
|
+
* @param key The key to set in the cache.
|
|
4196
|
+
* @param value The value to set in the cache.
|
|
4197
|
+
* @param ttlMs The TTL of the entry in the cache in ms, defaults to 1s.
|
|
4198
|
+
* @returns Nothing.
|
|
4199
|
+
*/
|
|
4200
|
+
static async set(key, value, ttlMs) {
|
|
4201
|
+
const cache = AsyncCache.getSharedCache();
|
|
4202
|
+
cache[key] = {
|
|
4203
|
+
result: value,
|
|
4204
|
+
promiseQueue: [],
|
|
4205
|
+
expires: Date.now() + (ttlMs ?? 1000)
|
|
4206
|
+
};
|
|
3526
4207
|
}
|
|
3527
4208
|
/**
|
|
3528
4209
|
* Remove an entry from the cache.
|
|
3529
4210
|
* @param key The key to remove from the cache.
|
|
3530
4211
|
*/
|
|
3531
4212
|
static remove(key) {
|
|
3532
|
-
|
|
4213
|
+
const cache = AsyncCache.getSharedCache();
|
|
4214
|
+
delete cache[key];
|
|
3533
4215
|
}
|
|
3534
4216
|
/**
|
|
3535
4217
|
* Clear the cache.
|
|
3536
4218
|
* @param prefix Optional prefix to clear only entries with that prefix.
|
|
3537
4219
|
*/
|
|
3538
4220
|
static clearCache(prefix) {
|
|
4221
|
+
const cache = AsyncCache.getSharedCache();
|
|
3539
4222
|
if (Is.stringValue(prefix)) {
|
|
3540
|
-
for (const entry in
|
|
4223
|
+
for (const entry in cache) {
|
|
3541
4224
|
if (entry.startsWith(prefix)) {
|
|
3542
|
-
delete
|
|
4225
|
+
delete cache[entry];
|
|
3543
4226
|
}
|
|
3544
4227
|
}
|
|
3545
4228
|
}
|
|
3546
4229
|
else {
|
|
3547
|
-
|
|
4230
|
+
SharedStore.set("asyncCache", {});
|
|
3548
4231
|
}
|
|
3549
4232
|
}
|
|
3550
4233
|
/**
|
|
3551
4234
|
* Perform a cleanup of the expired entries in the cache.
|
|
3552
4235
|
*/
|
|
3553
4236
|
static cleanupExpired() {
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
4237
|
+
const cache = AsyncCache.getSharedCache();
|
|
4238
|
+
for (const entry in cache) {
|
|
4239
|
+
if (cache[entry].expires > 0 && cache[entry].expires < Date.now()) {
|
|
4240
|
+
delete cache[entry];
|
|
3557
4241
|
}
|
|
3558
4242
|
}
|
|
3559
4243
|
}
|
|
4244
|
+
/**
|
|
4245
|
+
* Get the shared cache.
|
|
4246
|
+
* @returns The shared cache.
|
|
4247
|
+
* @internal
|
|
4248
|
+
*/
|
|
4249
|
+
static getSharedCache() {
|
|
4250
|
+
let sharedCache = SharedStore.get("asyncCache");
|
|
4251
|
+
if (Is.undefined(sharedCache)) {
|
|
4252
|
+
sharedCache = {};
|
|
4253
|
+
SharedStore.set("asyncCache", sharedCache);
|
|
4254
|
+
}
|
|
4255
|
+
return sharedCache;
|
|
4256
|
+
}
|
|
3560
4257
|
}
|
|
3561
4258
|
|
|
3562
4259
|
/**
|
|
@@ -3578,15 +4275,14 @@ class Compression {
|
|
|
3578
4275
|
Guards.uint8Array(Compression._CLASS_NAME, "bytes", bytes);
|
|
3579
4276
|
Guards.arrayOneOf(Compression._CLASS_NAME, "type", type, Object.values(CompressionType));
|
|
3580
4277
|
const blob = new Blob([bytes]);
|
|
3581
|
-
const
|
|
3582
|
-
const
|
|
3583
|
-
const compressedBlob = await new Response(
|
|
3584
|
-
const
|
|
3585
|
-
const compressedBytes = new Uint8Array(ab);
|
|
4278
|
+
const compressionStream = new CompressionStream(type);
|
|
4279
|
+
const compressionPipe = blob.stream().pipeThrough(compressionStream);
|
|
4280
|
+
const compressedBlob = await new Response(compressionPipe).blob();
|
|
4281
|
+
const compressedBytes = new Uint8Array(await compressedBlob.arrayBuffer());
|
|
3586
4282
|
// GZIP header contains a byte which specifies the OS the
|
|
3587
4283
|
// compression was performed on. We set this to 3 (Unix) to ensure
|
|
3588
4284
|
// that we produce consistent results.
|
|
3589
|
-
if (type ===
|
|
4285
|
+
if (type === CompressionType.Gzip && compressedBytes.length >= 10) {
|
|
3590
4286
|
compressedBytes[9] = 3;
|
|
3591
4287
|
}
|
|
3592
4288
|
return compressedBytes;
|
|
@@ -3601,11 +4297,10 @@ class Compression {
|
|
|
3601
4297
|
Guards.uint8Array(Compression._CLASS_NAME, "compressedBytes", compressedBytes);
|
|
3602
4298
|
Guards.arrayOneOf(Compression._CLASS_NAME, "type", type, Object.values(CompressionType));
|
|
3603
4299
|
const blob = new Blob([compressedBytes]);
|
|
3604
|
-
const
|
|
3605
|
-
const
|
|
3606
|
-
const decompressedBlob = await new Response(
|
|
3607
|
-
|
|
3608
|
-
return new Uint8Array(ab);
|
|
4300
|
+
const decompressionStream = new DecompressionStream(type);
|
|
4301
|
+
const decompressionPipe = blob.stream().pipeThrough(decompressionStream);
|
|
4302
|
+
const decompressedBlob = await new Response(decompressionPipe).blob();
|
|
4303
|
+
return new Uint8Array(await decompressedBlob.bytes());
|
|
3609
4304
|
}
|
|
3610
4305
|
}
|
|
3611
4306
|
|
|
@@ -3663,6 +4358,7 @@ class Validation {
|
|
|
3663
4358
|
* @param options Additional options for the validation.
|
|
3664
4359
|
* @param options.minLength The minimum length of the string.
|
|
3665
4360
|
* @param options.maxLength The maximum length of the string.
|
|
4361
|
+
* @param options.format Specific format to check.
|
|
3666
4362
|
* @returns True if the value is a valid string.
|
|
3667
4363
|
*/
|
|
3668
4364
|
static string(property, value, failures, fieldNameResource, options) {
|
|
@@ -3681,6 +4377,47 @@ class Validation {
|
|
|
3681
4377
|
const maxLimitDefined = Is.integer(maxLength);
|
|
3682
4378
|
const belowMin = minLimitDefined && value.length < minLength;
|
|
3683
4379
|
const aboveMax = maxLimitDefined && value.length > maxLength;
|
|
4380
|
+
if (options?.format === "base58" && !Is.stringBase58(value)) {
|
|
4381
|
+
failures.push({
|
|
4382
|
+
property,
|
|
4383
|
+
reason: "validation.beTextBase58",
|
|
4384
|
+
properties: {
|
|
4385
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
4386
|
+
value
|
|
4387
|
+
}
|
|
4388
|
+
});
|
|
4389
|
+
}
|
|
4390
|
+
else if (options?.format === "base64" && !Is.stringBase64(value)) {
|
|
4391
|
+
failures.push({
|
|
4392
|
+
property,
|
|
4393
|
+
reason: "validation.beTextBase64",
|
|
4394
|
+
properties: {
|
|
4395
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
4396
|
+
value
|
|
4397
|
+
}
|
|
4398
|
+
});
|
|
4399
|
+
}
|
|
4400
|
+
else if (options?.format === "hex" && !Is.stringHex(value)) {
|
|
4401
|
+
failures.push({
|
|
4402
|
+
property,
|
|
4403
|
+
reason: "validation.beTextHex",
|
|
4404
|
+
properties: {
|
|
4405
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
4406
|
+
value
|
|
4407
|
+
}
|
|
4408
|
+
});
|
|
4409
|
+
}
|
|
4410
|
+
else if (Is.regexp(options?.format) && !options.format.test(value)) {
|
|
4411
|
+
failures.push({
|
|
4412
|
+
property,
|
|
4413
|
+
reason: "validation.beTextRegExp",
|
|
4414
|
+
properties: {
|
|
4415
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
4416
|
+
value,
|
|
4417
|
+
format: options?.format
|
|
4418
|
+
}
|
|
4419
|
+
});
|
|
4420
|
+
}
|
|
3684
4421
|
if (minLimitDefined && maxLimitDefined && (belowMin || aboveMax)) {
|
|
3685
4422
|
failures.push({
|
|
3686
4423
|
property,
|
|
@@ -4350,11 +5087,13 @@ exports.Base64Url = Base64Url;
|
|
|
4350
5087
|
exports.BaseError = BaseError;
|
|
4351
5088
|
exports.BitString = BitString;
|
|
4352
5089
|
exports.Coerce = Coerce;
|
|
5090
|
+
exports.CoerceType = CoerceType;
|
|
4353
5091
|
exports.ComponentFactory = ComponentFactory;
|
|
4354
5092
|
exports.Compression = Compression;
|
|
4355
5093
|
exports.CompressionType = CompressionType;
|
|
4356
5094
|
exports.ConflictError = ConflictError;
|
|
4357
5095
|
exports.Converter = Converter;
|
|
5096
|
+
exports.EnvHelper = EnvHelper;
|
|
4358
5097
|
exports.ErrorHelper = ErrorHelper;
|
|
4359
5098
|
exports.Factory = Factory;
|
|
4360
5099
|
exports.FilenameHelper = FilenameHelper;
|
|
@@ -4370,7 +5109,9 @@ exports.NotImplementedError = NotImplementedError;
|
|
|
4370
5109
|
exports.NotSupportedError = NotSupportedError;
|
|
4371
5110
|
exports.ObjectHelper = ObjectHelper;
|
|
4372
5111
|
exports.RandomHelper = RandomHelper;
|
|
5112
|
+
exports.SharedStore = SharedStore;
|
|
4373
5113
|
exports.StringHelper = StringHelper;
|
|
5114
|
+
exports.Uint8ArrayHelper = Uint8ArrayHelper;
|
|
4374
5115
|
exports.UnauthorizedError = UnauthorizedError;
|
|
4375
5116
|
exports.UnprocessableError = UnprocessableError;
|
|
4376
5117
|
exports.Url = Url;
|