@twin.org/core 0.0.1-next.9 → 0.0.2-next.10
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 +2266 -1470
- package/dist/esm/index.mjs +2263 -1471
- package/dist/types/errors/alreadyExistsError.d.ts +2 -2
- package/dist/types/errors/baseError.d.ts +25 -5
- package/dist/types/errors/conflictError.d.ts +2 -2
- package/dist/types/errors/generalError.d.ts +2 -2
- package/dist/types/errors/notFoundError.d.ts +2 -2
- package/dist/types/errors/notSupportedError.d.ts +2 -2
- package/dist/types/errors/unauthorizedError.d.ts +2 -2
- package/dist/types/errors/unprocessableError.d.ts +2 -2
- package/dist/types/factories/factory.d.ts +21 -2
- 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 +5 -4
- 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 +6 -6
- package/dist/types/models/IError.d.ts +2 -2
- 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 +18 -0
- package/dist/types/utils/sharedStore.d.ts +23 -0
- package/dist/types/utils/validation.d.ts +2 -0
- package/docs/changelog.md +442 -1
- package/docs/reference/classes/AlreadyExistsError.md +166 -32
- 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 +160 -34
- 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 +169 -33
- package/docs/reference/classes/Converter.md +72 -28
- package/docs/reference/classes/EnvHelper.md +45 -0
- package/docs/reference/classes/ErrorHelper.md +22 -10
- package/docs/reference/classes/Factory.md +96 -18
- package/docs/reference/classes/FilenameHelper.md +6 -4
- package/docs/reference/classes/GeneralError.md +164 -32
- package/docs/reference/classes/GuardError.md +168 -32
- 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 +207 -51
- package/docs/reference/classes/JsonHelper.md +146 -10
- package/docs/reference/classes/NotFoundError.md +166 -32
- package/docs/reference/classes/NotImplementedError.md +159 -29
- package/docs/reference/classes/NotSupportedError.md +163 -31
- 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 +163 -31
- package/docs/reference/classes/UnprocessableError.md +164 -32
- 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 +162 -30
- package/docs/reference/index.md +7 -0
- package/docs/reference/interfaces/IComponent.md +21 -11
- package/docs/reference/interfaces/IError.md +4 -4
- 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 +2 -2
- package/locales/en.json +14 -1
- package/package.json +7 -7
package/dist/esm/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { IntlMessageFormat } from 'intl-messageformat';
|
|
2
1
|
import { createPatch, applyPatch } from 'rfc6902';
|
|
2
|
+
import { IntlMessageFormat } from 'intl-messageformat';
|
|
3
3
|
|
|
4
4
|
// Copyright 2024 IOTA Stiftung.
|
|
5
5
|
// SPDX-License-Identifier: Apache-2.0.
|
|
@@ -111,7 +111,12 @@ class Is {
|
|
|
111
111
|
}
|
|
112
112
|
try {
|
|
113
113
|
const json = JSON.parse(value);
|
|
114
|
-
return
|
|
114
|
+
return (Is.object(json) ||
|
|
115
|
+
Is.array(json) ||
|
|
116
|
+
Is.string(json) ||
|
|
117
|
+
Is.number(json) ||
|
|
118
|
+
Is.boolean(json) ||
|
|
119
|
+
Is.null(json));
|
|
115
120
|
}
|
|
116
121
|
catch {
|
|
117
122
|
return false;
|
|
@@ -135,7 +140,17 @@ class Is {
|
|
|
135
140
|
static stringBase64Url(value) {
|
|
136
141
|
return (Is.stringValue(value) &&
|
|
137
142
|
// eslint-disable-next-line unicorn/better-regex
|
|
138
|
-
/^(
|
|
143
|
+
/^([A-Za-z0-9-_])*$/.test(value));
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Is the value a base58 string.
|
|
147
|
+
* @param value The value to test.
|
|
148
|
+
* @returns True if the value is a base58 string.
|
|
149
|
+
*/
|
|
150
|
+
static stringBase58(value) {
|
|
151
|
+
return (Is.stringValue(value) &&
|
|
152
|
+
// eslint-disable-next-line unicorn/better-regex
|
|
153
|
+
/^[A-HJ-NP-Za-km-z1-9]*$/.test(value));
|
|
139
154
|
}
|
|
140
155
|
/**
|
|
141
156
|
* Is the value a hex string.
|
|
@@ -349,6 +364,27 @@ class Is {
|
|
|
349
364
|
static promise(value) {
|
|
350
365
|
return value instanceof Promise;
|
|
351
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* Is the value a regexp.
|
|
369
|
+
* @param value The value to test.
|
|
370
|
+
* @returns True if the value is a regexp.
|
|
371
|
+
*/
|
|
372
|
+
static regexp(value) {
|
|
373
|
+
return value instanceof RegExp;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Is the provided object a class constructor.
|
|
377
|
+
* @param obj The object to check.
|
|
378
|
+
* @returns True if the object is a class, false otherwise.
|
|
379
|
+
*/
|
|
380
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
381
|
+
static class(obj) {
|
|
382
|
+
if (typeof obj !== "function") {
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
const str = Function.prototype.toString.call(obj);
|
|
386
|
+
return /^class\s/.test(str);
|
|
387
|
+
}
|
|
352
388
|
}
|
|
353
389
|
|
|
354
390
|
// Copyright 2024 IOTA Stiftung.
|
|
@@ -606,31 +642,33 @@ class BaseError extends Error {
|
|
|
606
642
|
*/
|
|
607
643
|
properties;
|
|
608
644
|
/**
|
|
609
|
-
* The
|
|
645
|
+
* The cause of the error.
|
|
610
646
|
*/
|
|
611
|
-
|
|
647
|
+
cause;
|
|
612
648
|
/**
|
|
613
649
|
* Create a new instance of BaseError.
|
|
614
650
|
* @param name The name of the error.
|
|
615
651
|
* @param source The source of the error.
|
|
616
652
|
* @param message The message as a code.
|
|
617
653
|
* @param properties Any additional information for the error.
|
|
618
|
-
* @param
|
|
654
|
+
* @param cause The cause of error if we have wrapped another error.
|
|
619
655
|
*/
|
|
620
|
-
constructor(name, source, message, properties,
|
|
656
|
+
constructor(name, source, message, properties, cause) {
|
|
621
657
|
super(message);
|
|
622
658
|
this.name = name;
|
|
623
659
|
this.source = source;
|
|
660
|
+
this.cause = Is.notEmpty(cause) ? BaseError.fromError(cause).toJsonObject(true) : undefined;
|
|
661
|
+
this.properties = properties;
|
|
624
662
|
// If the message is camel case but has no namespace then prefix it
|
|
625
|
-
// with the source name in camel case
|
|
663
|
+
// with the source name in camel case.
|
|
626
664
|
if (Is.stringValue(source) &&
|
|
627
665
|
Is.stringValue(message) &&
|
|
628
666
|
!message.includes(".") &&
|
|
667
|
+
// This comparison checks that it is most likely a camel case name
|
|
668
|
+
// and not a free text error with a dot in it
|
|
629
669
|
StringHelper.camelCase(message) === message) {
|
|
630
670
|
this.message = `${StringHelper.camelCase(source)}.${message}`;
|
|
631
671
|
}
|
|
632
|
-
this.properties = properties;
|
|
633
|
-
this.inner = inner ? BaseError.fromError(inner).toJsonObject() : undefined;
|
|
634
672
|
}
|
|
635
673
|
/**
|
|
636
674
|
* Construct an error from an existing one.
|
|
@@ -642,9 +680,12 @@ class BaseError extends Error {
|
|
|
642
680
|
let message;
|
|
643
681
|
let source;
|
|
644
682
|
let properties;
|
|
645
|
-
let
|
|
683
|
+
let cause;
|
|
646
684
|
let stack;
|
|
647
|
-
if (Is.object(err)) {
|
|
685
|
+
if (Is.object(err) && Is.stringValue(err.error)) {
|
|
686
|
+
message = err.error;
|
|
687
|
+
}
|
|
688
|
+
else if (Is.object(err)) {
|
|
648
689
|
if (Is.stringValue(err.name)) {
|
|
649
690
|
name = err.name;
|
|
650
691
|
}
|
|
@@ -657,23 +698,24 @@ class BaseError extends Error {
|
|
|
657
698
|
if (Is.notEmpty(err.properties)) {
|
|
658
699
|
properties = err.properties;
|
|
659
700
|
}
|
|
660
|
-
if (
|
|
661
|
-
|
|
701
|
+
if (BaseError.isAggregateError(err)) {
|
|
702
|
+
properties ??= {};
|
|
703
|
+
properties.errors = err.errors;
|
|
704
|
+
}
|
|
705
|
+
if (Is.notEmpty(err.cause)) {
|
|
706
|
+
cause = err.cause;
|
|
662
707
|
}
|
|
663
708
|
if (Is.notEmpty(err.stack)) {
|
|
664
709
|
stack = err.stack;
|
|
665
710
|
}
|
|
666
711
|
}
|
|
667
|
-
else if (Is.object(err) && Is.stringValue(err.error)) {
|
|
668
|
-
message = err.error;
|
|
669
|
-
}
|
|
670
712
|
else if (Is.stringValue(err)) {
|
|
671
713
|
message = err;
|
|
672
714
|
}
|
|
673
715
|
else {
|
|
674
716
|
message = JSON.stringify(err);
|
|
675
717
|
}
|
|
676
|
-
const baseError = new BaseError(name, source ?? "", message ?? "", properties,
|
|
718
|
+
const baseError = new BaseError(name, source ?? "", message ?? "", properties, cause);
|
|
677
719
|
baseError.stack = stack;
|
|
678
720
|
return baseError;
|
|
679
721
|
}
|
|
@@ -684,12 +726,12 @@ class BaseError extends Error {
|
|
|
684
726
|
*/
|
|
685
727
|
static flatten(err) {
|
|
686
728
|
const flattened = [];
|
|
687
|
-
let e = BaseError.fromError(err).toJsonObject();
|
|
729
|
+
let e = BaseError.fromError(err).toJsonObject(true);
|
|
688
730
|
while (e) {
|
|
689
|
-
const
|
|
690
|
-
e.
|
|
731
|
+
const cause = e.cause;
|
|
732
|
+
e.cause = undefined;
|
|
691
733
|
flattened.push(e);
|
|
692
|
-
e =
|
|
734
|
+
e = cause;
|
|
693
735
|
}
|
|
694
736
|
return flattened;
|
|
695
737
|
}
|
|
@@ -704,8 +746,8 @@ class BaseError extends Error {
|
|
|
704
746
|
first = errors[0];
|
|
705
747
|
let current = first;
|
|
706
748
|
for (let i = 1; i < errors.length; i++) {
|
|
707
|
-
current.
|
|
708
|
-
current = current.
|
|
749
|
+
current.cause = errors[i];
|
|
750
|
+
current = current.cause;
|
|
709
751
|
}
|
|
710
752
|
}
|
|
711
753
|
return first;
|
|
@@ -778,11 +820,43 @@ class BaseError extends Error {
|
|
|
778
820
|
static someErrorCode(error, code) {
|
|
779
821
|
return BaseError.flatten(error).some(e => BaseError.isErrorCode(e, code));
|
|
780
822
|
}
|
|
823
|
+
/**
|
|
824
|
+
* Is the error empty, i.e. does it have no message, source, properties, or cause?
|
|
825
|
+
* @param err The error to check for being empty.
|
|
826
|
+
* @returns True if the error is empty.
|
|
827
|
+
*/
|
|
828
|
+
static isEmpty(err) {
|
|
829
|
+
return (!Is.stringValue(err.message) &&
|
|
830
|
+
!Is.stringValue(err.source) &&
|
|
831
|
+
!Is.objectValue(err.properties) &&
|
|
832
|
+
Is.empty(err.cause));
|
|
833
|
+
}
|
|
834
|
+
/**
|
|
835
|
+
* Is the error an aggregate error.
|
|
836
|
+
* @param err The error to check for being an aggregate error.
|
|
837
|
+
* @returns True if the error is an aggregate error.
|
|
838
|
+
*/
|
|
839
|
+
static isAggregateError(err) {
|
|
840
|
+
return err instanceof AggregateError;
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* Convert the aggregate error to an array of errors.
|
|
844
|
+
* @param err The error to convert.
|
|
845
|
+
* @param includeStackTrace Whether to include the error stack in the model, defaults to false.
|
|
846
|
+
* @returns The array of errors.
|
|
847
|
+
*/
|
|
848
|
+
static fromAggregate(err, includeStackTrace) {
|
|
849
|
+
if (BaseError.isAggregateError(err)) {
|
|
850
|
+
return err.errors.map(e => BaseError.fromError(e).toJsonObject(includeStackTrace));
|
|
851
|
+
}
|
|
852
|
+
return [BaseError.fromError(err).toJsonObject(includeStackTrace)];
|
|
853
|
+
}
|
|
781
854
|
/**
|
|
782
855
|
* Serialize the error to the error model.
|
|
856
|
+
* @param includeStackTrace Whether to include the error stack in the model, defaults to false.
|
|
783
857
|
* @returns The error model.
|
|
784
858
|
*/
|
|
785
|
-
toJsonObject() {
|
|
859
|
+
toJsonObject(includeStackTrace) {
|
|
786
860
|
const err = {};
|
|
787
861
|
if (Is.stringValue(this.name)) {
|
|
788
862
|
err.name = this.name;
|
|
@@ -796,11 +870,11 @@ class BaseError extends Error {
|
|
|
796
870
|
if (Is.object(this.properties)) {
|
|
797
871
|
err.properties = this.properties;
|
|
798
872
|
}
|
|
799
|
-
if (Is.stringValue(this.stack)) {
|
|
873
|
+
if ((includeStackTrace ?? false) && Is.stringValue(this.stack)) {
|
|
800
874
|
err.stack = this.stack;
|
|
801
875
|
}
|
|
802
|
-
if (Is.notEmpty(this.
|
|
803
|
-
err.
|
|
876
|
+
if (Is.notEmpty(this.cause)) {
|
|
877
|
+
err.cause = BaseError.fromError(this.cause).toJsonObject(includeStackTrace);
|
|
804
878
|
}
|
|
805
879
|
return err;
|
|
806
880
|
}
|
|
@@ -819,176 +893,614 @@ class GeneralError extends BaseError {
|
|
|
819
893
|
* @param source The source of the error.
|
|
820
894
|
* @param message The message as a code.
|
|
821
895
|
* @param properties Any additional information for the error.
|
|
822
|
-
* @param
|
|
896
|
+
* @param cause The cause of the error if we have wrapped another error.
|
|
823
897
|
*/
|
|
824
|
-
constructor(source, message, properties,
|
|
825
|
-
super(GeneralError.CLASS_NAME, source, message, properties,
|
|
898
|
+
constructor(source, message, properties, cause) {
|
|
899
|
+
super(GeneralError.CLASS_NAME, source, message, properties, cause);
|
|
826
900
|
}
|
|
827
901
|
}
|
|
828
902
|
|
|
829
|
-
// Copyright 2024 IOTA Stiftung.
|
|
830
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
831
|
-
/* eslint-disable no-bitwise */
|
|
832
903
|
/**
|
|
833
|
-
* Class to
|
|
904
|
+
* Class to handle errors which are triggered by data guards.
|
|
834
905
|
*/
|
|
835
|
-
class
|
|
906
|
+
class GuardError extends BaseError {
|
|
836
907
|
/**
|
|
837
908
|
* Runtime name for the class.
|
|
838
|
-
* @internal
|
|
839
909
|
*/
|
|
840
|
-
static
|
|
910
|
+
static CLASS_NAME = "GuardError";
|
|
841
911
|
/**
|
|
842
|
-
*
|
|
843
|
-
* @
|
|
912
|
+
* Create a new instance of GuardError.
|
|
913
|
+
* @param source The source of the error.
|
|
914
|
+
* @param message The message as a code.
|
|
915
|
+
* @param propertyName The property which triggered the guard error for the item.
|
|
916
|
+
* @param propertyValue The property value which triggered the guard error for the item.
|
|
917
|
+
* @param propertyOptions The property options which might be allowed.
|
|
844
918
|
*/
|
|
845
|
-
|
|
919
|
+
constructor(source, message, propertyName, propertyValue, propertyOptions) {
|
|
920
|
+
super(GuardError.CLASS_NAME, source, message, {
|
|
921
|
+
property: propertyName ?? "property",
|
|
922
|
+
value: Is.undefined(propertyValue) ? "undefined" : propertyValue,
|
|
923
|
+
options: propertyOptions
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
/**
|
|
929
|
+
* Class to help with arrays.
|
|
930
|
+
*/
|
|
931
|
+
class ArrayHelper {
|
|
846
932
|
/**
|
|
847
|
-
*
|
|
848
|
-
* @param
|
|
849
|
-
* @
|
|
850
|
-
* @
|
|
933
|
+
* Do the two arrays match.
|
|
934
|
+
* @param arr1 The first array.
|
|
935
|
+
* @param arr2 The second array.
|
|
936
|
+
* @returns True if both arrays are empty of have the same values.
|
|
851
937
|
*/
|
|
852
|
-
static
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
value = (value << 5) | idx;
|
|
866
|
-
bits += 5;
|
|
867
|
-
if (bits >= 8) {
|
|
868
|
-
output[index++] = (value >>> (bits - 8)) & 255;
|
|
869
|
-
bits -= 8;
|
|
938
|
+
static matches(arr1, arr2) {
|
|
939
|
+
if (Is.empty(arr1) && Is.empty(arr2)) {
|
|
940
|
+
return true;
|
|
941
|
+
}
|
|
942
|
+
if (!((Is.array(arr1) && Is.array(arr2)) || (Is.typedArray(arr1) && Is.typedArray(arr2)))) {
|
|
943
|
+
return false;
|
|
944
|
+
}
|
|
945
|
+
if (arr1.length !== arr2.length) {
|
|
946
|
+
return false;
|
|
947
|
+
}
|
|
948
|
+
for (let i = 0; i < arr1.length; i++) {
|
|
949
|
+
if (arr1[i] !== arr2[i]) {
|
|
950
|
+
return false;
|
|
870
951
|
}
|
|
871
952
|
}
|
|
872
|
-
return
|
|
953
|
+
return true;
|
|
873
954
|
}
|
|
874
955
|
/**
|
|
875
|
-
* Convert
|
|
876
|
-
* @param
|
|
877
|
-
* @returns The
|
|
956
|
+
* Convert an object or array to an array.
|
|
957
|
+
* @param value The object or array to convert.
|
|
958
|
+
* @returns The array.
|
|
878
959
|
*/
|
|
879
|
-
static
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
let output = "";
|
|
883
|
-
for (let i = 0; i < bytes.byteLength; i++) {
|
|
884
|
-
value = (value << 8) | bytes[i];
|
|
885
|
-
bits += 8;
|
|
886
|
-
while (bits >= 5) {
|
|
887
|
-
output += Base32._ALPHABET[(value >>> (bits - 5)) & 31];
|
|
888
|
-
bits -= 5;
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
if (bits > 0) {
|
|
892
|
-
output += Base32._ALPHABET[(value << (5 - bits)) & 31];
|
|
960
|
+
static fromObjectOrArray(value) {
|
|
961
|
+
if (Is.empty(value)) {
|
|
962
|
+
return undefined;
|
|
893
963
|
}
|
|
894
|
-
|
|
895
|
-
|
|
964
|
+
if (Is.array(value)) {
|
|
965
|
+
return value;
|
|
896
966
|
}
|
|
897
|
-
return
|
|
967
|
+
return [value];
|
|
898
968
|
}
|
|
899
969
|
}
|
|
900
970
|
|
|
971
|
+
// Copyright 2024 IOTA Stiftung.
|
|
972
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
901
973
|
/**
|
|
902
|
-
* Class to
|
|
974
|
+
* Class to handle guard operations for parameters.
|
|
903
975
|
*/
|
|
904
|
-
class
|
|
905
|
-
/**
|
|
906
|
-
* Runtime name for the class.
|
|
907
|
-
* @internal
|
|
908
|
-
*/
|
|
909
|
-
static _CLASS_NAME = "Base58";
|
|
976
|
+
class Guards {
|
|
910
977
|
/**
|
|
911
|
-
*
|
|
912
|
-
* @
|
|
978
|
+
* Is the property defined.
|
|
979
|
+
* @param source The source of the error.
|
|
980
|
+
* @param property The name of the property.
|
|
981
|
+
* @param value The value to test.
|
|
982
|
+
* @throws GuardError If the value does not match the assertion.
|
|
913
983
|
*/
|
|
914
|
-
static
|
|
915
|
-
|
|
916
|
-
|
|
984
|
+
static defined(source, property, value) {
|
|
985
|
+
if (Is.undefined(value)) {
|
|
986
|
+
throw new GuardError(source, "guard.undefined", property, value);
|
|
987
|
+
}
|
|
988
|
+
}
|
|
917
989
|
/**
|
|
918
|
-
*
|
|
919
|
-
* @
|
|
990
|
+
* Is the property a string.
|
|
991
|
+
* @param source The source of the error.
|
|
992
|
+
* @param property The name of the property.
|
|
993
|
+
* @param value The value to test.
|
|
994
|
+
* @throws GuardError If the value does not match the assertion.
|
|
920
995
|
*/
|
|
921
|
-
static
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
34, 35, 36, 37, 38, 39, 40, 41, 42, 43, -1, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
|
|
927
|
-
57, -1, -1, -1, -1, -1
|
|
928
|
-
];
|
|
996
|
+
static string(source, property, value) {
|
|
997
|
+
if (!Is.string(value)) {
|
|
998
|
+
throw new GuardError(source, "guard.string", property, value);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
929
1001
|
/**
|
|
930
|
-
*
|
|
931
|
-
* @param
|
|
932
|
-
* @
|
|
933
|
-
* @
|
|
1002
|
+
* Is the property a string with a value.
|
|
1003
|
+
* @param source The source of the error.
|
|
1004
|
+
* @param property The name of the property.
|
|
1005
|
+
* @param value The value to test.
|
|
1006
|
+
* @throws GuardError If the value does not match the assertion.
|
|
934
1007
|
*/
|
|
935
|
-
static
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
if (base58[i] !== "1") {
|
|
939
|
-
break;
|
|
940
|
-
}
|
|
941
|
-
zeroes += 1;
|
|
942
|
-
}
|
|
943
|
-
const size = Math.trunc((base58.length * 733) / 1000) + 1;
|
|
944
|
-
const b256 = new Uint8Array(size).fill(0);
|
|
945
|
-
let length = 0;
|
|
946
|
-
for (let i = zeroes; i < base58.length; i++) {
|
|
947
|
-
const ch = base58.charCodeAt(i);
|
|
948
|
-
if (ch & 0xff80) {
|
|
949
|
-
throw new GeneralError(Base58._CLASS_NAME, "invalidCharacter", { invalidCharacter: ch });
|
|
950
|
-
}
|
|
951
|
-
const val = Base58._ALPHABET_REVERSE[ch];
|
|
952
|
-
if (val === -1) {
|
|
953
|
-
throw new GeneralError(Base58._CLASS_NAME, "invalidCharacter", { invalidCharacter: ch });
|
|
954
|
-
}
|
|
955
|
-
let carry = val;
|
|
956
|
-
let j = 0;
|
|
957
|
-
for (let k = size - 1; k >= 0; k--, j++) {
|
|
958
|
-
if (carry === 0 && j >= length) {
|
|
959
|
-
break;
|
|
960
|
-
}
|
|
961
|
-
carry += b256[k] * 58;
|
|
962
|
-
b256[k] = carry;
|
|
963
|
-
carry >>>= 8;
|
|
964
|
-
}
|
|
965
|
-
length = j;
|
|
1008
|
+
static stringValue(source, property, value) {
|
|
1009
|
+
if (!Is.string(value)) {
|
|
1010
|
+
throw new GuardError(source, "guard.string", property, value);
|
|
966
1011
|
}
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
for (j = 0; j < zeroes; j++) {
|
|
970
|
-
out[j] = 0;
|
|
1012
|
+
if (value.length === 0) {
|
|
1013
|
+
throw new GuardError(source, "guard.stringEmpty", property, value);
|
|
971
1014
|
}
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1015
|
+
}
|
|
1016
|
+
/**
|
|
1017
|
+
* Is the property a JSON value.
|
|
1018
|
+
* @param source The source of the error.
|
|
1019
|
+
* @param property The name of the property.
|
|
1020
|
+
* @param value The value to test.
|
|
1021
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1022
|
+
*/
|
|
1023
|
+
static json(source, property, value) {
|
|
1024
|
+
if (!Is.json(value)) {
|
|
1025
|
+
throw new GuardError(source, "guard.stringJson", property, value);
|
|
975
1026
|
}
|
|
976
|
-
return out;
|
|
977
1027
|
}
|
|
978
1028
|
/**
|
|
979
|
-
*
|
|
980
|
-
* @param
|
|
981
|
-
* @
|
|
1029
|
+
* Is the property a base64 string.
|
|
1030
|
+
* @param source The source of the error.
|
|
1031
|
+
* @param property The name of the property.
|
|
1032
|
+
* @param value The value to test.
|
|
1033
|
+
* @throws GuardError If the value does not match the assertion.
|
|
982
1034
|
*/
|
|
983
|
-
static
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
if (bytes[i] !== 0) {
|
|
987
|
-
break;
|
|
988
|
-
}
|
|
989
|
-
zeroes += 1;
|
|
1035
|
+
static stringBase64(source, property, value) {
|
|
1036
|
+
if (!Is.stringBase64(value)) {
|
|
1037
|
+
throw new GuardError(source, "guard.base64", property, value);
|
|
990
1038
|
}
|
|
991
|
-
|
|
1039
|
+
}
|
|
1040
|
+
/**
|
|
1041
|
+
* Is the property a base64 url string.
|
|
1042
|
+
* @param source The source of the error.
|
|
1043
|
+
* @param property The name of the property.
|
|
1044
|
+
* @param value The value to test.
|
|
1045
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1046
|
+
*/
|
|
1047
|
+
static stringBase64Url(source, property, value) {
|
|
1048
|
+
if (!Is.stringBase64Url(value)) {
|
|
1049
|
+
throw new GuardError(source, "guard.base64Url", property, value);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Is the property a base58 string.
|
|
1054
|
+
* @param source The source of the error.
|
|
1055
|
+
* @param property The name of the property.
|
|
1056
|
+
* @param value The value to test.
|
|
1057
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1058
|
+
*/
|
|
1059
|
+
static stringBase58(source, property, value) {
|
|
1060
|
+
if (!Is.stringBase58(value)) {
|
|
1061
|
+
throw new GuardError(source, "guard.base58", property, value);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
/**
|
|
1065
|
+
* Is the property a string with a hex value.
|
|
1066
|
+
* @param source The source of the error.
|
|
1067
|
+
* @param property The name of the property.
|
|
1068
|
+
* @param value The value to test.
|
|
1069
|
+
* @param allowPrefix Allow the hex to have the 0x prefix.
|
|
1070
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1071
|
+
*/
|
|
1072
|
+
static stringHex(source, property, value, allowPrefix = false) {
|
|
1073
|
+
Guards.stringValue(source, property, value);
|
|
1074
|
+
if (!HexHelper.isHex(value, allowPrefix)) {
|
|
1075
|
+
throw new GuardError(source, "guard.stringHex", property, value);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
/**
|
|
1079
|
+
* Is the property a string with a hex value with fixed length.
|
|
1080
|
+
* @param source The source of the error.
|
|
1081
|
+
* @param property The name of the property.
|
|
1082
|
+
* @param value The value to test.
|
|
1083
|
+
* @param length The length of the string to match.
|
|
1084
|
+
* @param allowPrefix Allow the hex to have the 0x prefix.
|
|
1085
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1086
|
+
*/
|
|
1087
|
+
static stringHexLength(source, property, value, length, allowPrefix = false) {
|
|
1088
|
+
Guards.stringHex(source, property, value, allowPrefix);
|
|
1089
|
+
if (HexHelper.stripPrefix(value).length !== length) {
|
|
1090
|
+
throw new GuardError(source, "guard.stringHexLength", property, value.length, length.toString());
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
/**
|
|
1094
|
+
* Is the property a number.
|
|
1095
|
+
* @param source The source of the error.
|
|
1096
|
+
* @param property The name of the property.
|
|
1097
|
+
* @param value The value to test.
|
|
1098
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1099
|
+
*/
|
|
1100
|
+
static number(source, property, value) {
|
|
1101
|
+
if (!Is.number(value)) {
|
|
1102
|
+
throw new GuardError(source, "guard.number", property, value);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
/**
|
|
1106
|
+
* Is the property an integer.
|
|
1107
|
+
* @param source The source of the error.
|
|
1108
|
+
* @param property The name of the property.
|
|
1109
|
+
* @param value The value to test.
|
|
1110
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1111
|
+
*/
|
|
1112
|
+
static integer(source, property, value) {
|
|
1113
|
+
if (!Is.integer(value)) {
|
|
1114
|
+
throw new GuardError(source, "guard.integer", property, value);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
/**
|
|
1118
|
+
* Is the property a bigint.
|
|
1119
|
+
* @param source The source of the error.
|
|
1120
|
+
* @param property The name of the property.
|
|
1121
|
+
* @param value The value to test.
|
|
1122
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1123
|
+
*/
|
|
1124
|
+
static bigint(source, property, value) {
|
|
1125
|
+
if (!Is.bigint(value)) {
|
|
1126
|
+
throw new GuardError(source, "guard.bigint", property, value);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
/**
|
|
1130
|
+
* Is the property a boolean.
|
|
1131
|
+
* @param source The source of the error.
|
|
1132
|
+
* @param property The name of the property.
|
|
1133
|
+
* @param value The value to test.
|
|
1134
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1135
|
+
*/
|
|
1136
|
+
static boolean(source, property, value) {
|
|
1137
|
+
if (!Is.boolean(value)) {
|
|
1138
|
+
throw new GuardError(source, "guard.boolean", property, value);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
/**
|
|
1142
|
+
* Is the property a date.
|
|
1143
|
+
* @param source The source of the error.
|
|
1144
|
+
* @param property The name of the property.
|
|
1145
|
+
* @param value The value to test.
|
|
1146
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1147
|
+
*/
|
|
1148
|
+
static date(source, property, value) {
|
|
1149
|
+
if (!Is.date(value)) {
|
|
1150
|
+
throw new GuardError(source, "guard.date", property, value);
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
/**
|
|
1154
|
+
* Is the property a timestamp in milliseconds.
|
|
1155
|
+
* @param source The source of the error.
|
|
1156
|
+
* @param property The name of the property.
|
|
1157
|
+
* @param value The value to test.
|
|
1158
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1159
|
+
*/
|
|
1160
|
+
static timestampMilliseconds(source, property, value) {
|
|
1161
|
+
if (!Is.timestampMilliseconds(value)) {
|
|
1162
|
+
throw new GuardError(source, "guard.timestampMilliseconds", property, value);
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* Is the property a timestamp in seconds.
|
|
1167
|
+
* @param source The source of the error.
|
|
1168
|
+
* @param property The name of the property.
|
|
1169
|
+
* @param value The value to test.
|
|
1170
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1171
|
+
*/
|
|
1172
|
+
static timestampSeconds(source, property, value) {
|
|
1173
|
+
if (!Is.timestampSeconds(value)) {
|
|
1174
|
+
throw new GuardError(source, "guard.timestampSeconds", property, value);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
/**
|
|
1178
|
+
* Is the property an object.
|
|
1179
|
+
* @param source The source of the error.
|
|
1180
|
+
* @param property The name of the property.
|
|
1181
|
+
* @param value The value to test.
|
|
1182
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1183
|
+
*/
|
|
1184
|
+
static object(source, property, value) {
|
|
1185
|
+
if (Is.undefined(value)) {
|
|
1186
|
+
throw new GuardError(source, "guard.objectUndefined", property, value);
|
|
1187
|
+
}
|
|
1188
|
+
if (!Is.object(value)) {
|
|
1189
|
+
throw new GuardError(source, "guard.object", property, value);
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
/**
|
|
1193
|
+
* Is the property is an object with at least one property.
|
|
1194
|
+
* @param source The source of the error.
|
|
1195
|
+
* @param property The name of the property.
|
|
1196
|
+
* @param value The value to test.
|
|
1197
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1198
|
+
*/
|
|
1199
|
+
static objectValue(source, property, value) {
|
|
1200
|
+
if (Is.undefined(value)) {
|
|
1201
|
+
throw new GuardError(source, "guard.objectUndefined", property, value);
|
|
1202
|
+
}
|
|
1203
|
+
if (!Is.object(value)) {
|
|
1204
|
+
throw new GuardError(source, "guard.object", property, value);
|
|
1205
|
+
}
|
|
1206
|
+
if (Object.keys(value || {}).length === 0) {
|
|
1207
|
+
throw new GuardError(source, "guard.objectValue", property, value);
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
/**
|
|
1211
|
+
* Is the property is an array.
|
|
1212
|
+
* @param source The source of the error.
|
|
1213
|
+
* @param property The name of the property.
|
|
1214
|
+
* @param value The value to test.
|
|
1215
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1216
|
+
*/
|
|
1217
|
+
static array(source, property, value) {
|
|
1218
|
+
if (!Is.array(value)) {
|
|
1219
|
+
throw new GuardError(source, "guard.array", property, value);
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
/**
|
|
1223
|
+
* Is the property is an array with at least one item.
|
|
1224
|
+
* @param source The source of the error.
|
|
1225
|
+
* @param property The name of the property.
|
|
1226
|
+
* @param value The value to test.
|
|
1227
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1228
|
+
*/
|
|
1229
|
+
static arrayValue(source, property, value) {
|
|
1230
|
+
if (!Is.array(value)) {
|
|
1231
|
+
throw new GuardError(source, "guard.array", property, value);
|
|
1232
|
+
}
|
|
1233
|
+
if (value.length === 0) {
|
|
1234
|
+
throw new GuardError(source, "guard.arrayValue", property, value);
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1238
|
+
* Is the property one of a list of items.
|
|
1239
|
+
* @param source The source of the error.
|
|
1240
|
+
* @param property The name of the property.
|
|
1241
|
+
* @param value The value to test.
|
|
1242
|
+
* @param options The options the value must be one of.
|
|
1243
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1244
|
+
*/
|
|
1245
|
+
static arrayOneOf(source, property, value, options) {
|
|
1246
|
+
if (!Is.array(options)) {
|
|
1247
|
+
throw new GuardError(source, "guard.array", property, value);
|
|
1248
|
+
}
|
|
1249
|
+
if (!options.includes(value)) {
|
|
1250
|
+
throw new GuardError(source, "guard.arrayOneOf", property, value, options.join(", "));
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
/**
|
|
1254
|
+
* Does the array start with the specified data.
|
|
1255
|
+
* @param source The source of the error.
|
|
1256
|
+
* @param property The name of the property.
|
|
1257
|
+
* @param value The value to test.
|
|
1258
|
+
* @param startValues The values that must start the array.
|
|
1259
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1260
|
+
*/
|
|
1261
|
+
static arrayStartsWith(source, property, value, startValues) {
|
|
1262
|
+
if (!Is.arrayValue(value)) {
|
|
1263
|
+
throw new GuardError(source, "guard.array", property, value);
|
|
1264
|
+
}
|
|
1265
|
+
const startValuesArray = ArrayHelper.fromObjectOrArray(startValues);
|
|
1266
|
+
if (!Is.arrayValue(startValuesArray)) {
|
|
1267
|
+
throw new GuardError(source, "guard.array", property, startValuesArray);
|
|
1268
|
+
}
|
|
1269
|
+
for (let i = 0; i < startValuesArray.length; i++) {
|
|
1270
|
+
if (value[i] !== startValuesArray[i]) {
|
|
1271
|
+
throw new GuardError(source, "guard.arrayStartsWith", property, value, startValuesArray.join(", "));
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
/**
|
|
1276
|
+
* Does the array end with the specified data.
|
|
1277
|
+
* @param source The source of the error.
|
|
1278
|
+
* @param property The name of the property.
|
|
1279
|
+
* @param value The value to test.
|
|
1280
|
+
* @param endValues The values that must end the array.
|
|
1281
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1282
|
+
*/
|
|
1283
|
+
static arrayEndsWith(source, property, value, endValues) {
|
|
1284
|
+
if (!Is.arrayValue(value)) {
|
|
1285
|
+
throw new GuardError(source, "guard.array", property, value);
|
|
1286
|
+
}
|
|
1287
|
+
const endValuesArray = ArrayHelper.fromObjectOrArray(endValues);
|
|
1288
|
+
if (!Is.arrayValue(endValuesArray)) {
|
|
1289
|
+
throw new GuardError(source, "guard.array", property, endValuesArray);
|
|
1290
|
+
}
|
|
1291
|
+
for (let i = 0; i < endValuesArray.length; i++) {
|
|
1292
|
+
if (value[value.length - i - 1] !== endValuesArray[endValuesArray.length - i - 1]) {
|
|
1293
|
+
throw new GuardError(source, "guard.arrayEndsWith", property, value, endValuesArray.join(", "));
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
/**
|
|
1298
|
+
* Is the property a Uint8Array.
|
|
1299
|
+
* @param source The source of the error.
|
|
1300
|
+
* @param property The name of the property.
|
|
1301
|
+
* @param value The value to test.
|
|
1302
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1303
|
+
*/
|
|
1304
|
+
static uint8Array(source, property, value) {
|
|
1305
|
+
if (!Is.uint8Array(value)) {
|
|
1306
|
+
throw new GuardError(source, "guard.uint8Array", property, value);
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
/**
|
|
1310
|
+
* Is the property a function.
|
|
1311
|
+
* @param source The source of the error.
|
|
1312
|
+
* @param property The name of the property.
|
|
1313
|
+
* @param value The value to test.
|
|
1314
|
+
* @returns True if the value is a function.
|
|
1315
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1316
|
+
*/
|
|
1317
|
+
static function(source, property, value) {
|
|
1318
|
+
if (!Is.function(value)) {
|
|
1319
|
+
throw new GuardError(source, "guard.function", property, value);
|
|
1320
|
+
}
|
|
1321
|
+
return true;
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* Is the property a string formatted as an email address.
|
|
1325
|
+
* @param source The source of the error.
|
|
1326
|
+
* @param property The name of the property.
|
|
1327
|
+
* @param value The value to test.
|
|
1328
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1329
|
+
*/
|
|
1330
|
+
static email(source, property, value) {
|
|
1331
|
+
if (!Is.email(value)) {
|
|
1332
|
+
throw new GuardError(source, "guard.email", property, value);
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
// Copyright 2024 IOTA Stiftung.
|
|
1338
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
1339
|
+
/* eslint-disable no-bitwise */
|
|
1340
|
+
/**
|
|
1341
|
+
* Class to help with base63 Encoding/Decoding.
|
|
1342
|
+
*/
|
|
1343
|
+
class Base32 {
|
|
1344
|
+
/**
|
|
1345
|
+
* Runtime name for the class.
|
|
1346
|
+
* @internal
|
|
1347
|
+
*/
|
|
1348
|
+
static _CLASS_NAME = "Base32";
|
|
1349
|
+
/**
|
|
1350
|
+
* Alphabet table for encoding.
|
|
1351
|
+
* @internal
|
|
1352
|
+
*/
|
|
1353
|
+
static _ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
1354
|
+
/**
|
|
1355
|
+
* Convert the base 32 string to a byte array.
|
|
1356
|
+
* @param base32 The base32 string to convert.
|
|
1357
|
+
* @returns The byte array.
|
|
1358
|
+
* @throws If the input string contains a character not in the Base32 alphabet.
|
|
1359
|
+
*/
|
|
1360
|
+
static decode(base32) {
|
|
1361
|
+
Guards.string(Base32._CLASS_NAME, "base32", base32);
|
|
1362
|
+
let bits = 0;
|
|
1363
|
+
let value = 0;
|
|
1364
|
+
base32 = base32.replace(/=+$/, "");
|
|
1365
|
+
let index = 0;
|
|
1366
|
+
const output = new Uint8Array(Math.trunc((base32.length * 5) / 8));
|
|
1367
|
+
for (let i = 0; i < base32.length; i++) {
|
|
1368
|
+
const idx = Base32._ALPHABET.indexOf(base32[i]);
|
|
1369
|
+
if (idx === -1) {
|
|
1370
|
+
throw new GeneralError(Base32._CLASS_NAME, "invalidCharacter", {
|
|
1371
|
+
invalidCharacter: base32[i]
|
|
1372
|
+
});
|
|
1373
|
+
}
|
|
1374
|
+
value = (value << 5) | idx;
|
|
1375
|
+
bits += 5;
|
|
1376
|
+
if (bits >= 8) {
|
|
1377
|
+
output[index++] = (value >>> (bits - 8)) & 255;
|
|
1378
|
+
bits -= 8;
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
return output;
|
|
1382
|
+
}
|
|
1383
|
+
/**
|
|
1384
|
+
* Convert a byte array to base 32.
|
|
1385
|
+
* @param bytes The byte array to convert.
|
|
1386
|
+
* @returns The data as base32 string.
|
|
1387
|
+
*/
|
|
1388
|
+
static encode(bytes) {
|
|
1389
|
+
Guards.uint8Array(Base32._CLASS_NAME, "bytes", bytes);
|
|
1390
|
+
let bits = 0;
|
|
1391
|
+
let value = 0;
|
|
1392
|
+
let output = "";
|
|
1393
|
+
for (let i = 0; i < bytes.byteLength; i++) {
|
|
1394
|
+
value = (value << 8) | bytes[i];
|
|
1395
|
+
bits += 8;
|
|
1396
|
+
while (bits >= 5) {
|
|
1397
|
+
output += Base32._ALPHABET[(value >>> (bits - 5)) & 31];
|
|
1398
|
+
bits -= 5;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
if (bits > 0) {
|
|
1402
|
+
output += Base32._ALPHABET[(value << (5 - bits)) & 31];
|
|
1403
|
+
}
|
|
1404
|
+
while (output.length % 8 !== 0) {
|
|
1405
|
+
output += "=";
|
|
1406
|
+
}
|
|
1407
|
+
return output;
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
/**
|
|
1412
|
+
* Class to help with base58 Encoding/Decoding.
|
|
1413
|
+
*/
|
|
1414
|
+
class Base58 {
|
|
1415
|
+
/**
|
|
1416
|
+
* Runtime name for the class.
|
|
1417
|
+
* @internal
|
|
1418
|
+
*/
|
|
1419
|
+
static _CLASS_NAME = "Base58";
|
|
1420
|
+
/**
|
|
1421
|
+
* Alphabet table for encoding.
|
|
1422
|
+
* @internal
|
|
1423
|
+
*/
|
|
1424
|
+
static _ALPHABET =
|
|
1425
|
+
// cspell:disable-next-line
|
|
1426
|
+
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
1427
|
+
/**
|
|
1428
|
+
* Reverse map for decoding.
|
|
1429
|
+
* @internal
|
|
1430
|
+
*/
|
|
1431
|
+
static _ALPHABET_REVERSE = [
|
|
1432
|
+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
1433
|
+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
1434
|
+
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, 9, 10, 11, 12, 13, 14, 15, 16, -1,
|
|
1435
|
+
17, 18, 19, 20, 21, -1, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1, -1, 33,
|
|
1436
|
+
34, 35, 36, 37, 38, 39, 40, 41, 42, 43, -1, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
|
|
1437
|
+
57, -1, -1, -1, -1, -1
|
|
1438
|
+
];
|
|
1439
|
+
/**
|
|
1440
|
+
* Convert the base 58 string to a byte array.
|
|
1441
|
+
* @param base58 The base58 string to convert.
|
|
1442
|
+
* @returns The byte array.
|
|
1443
|
+
* @throws If the input string contains a character not in the Base58 alphabet.
|
|
1444
|
+
*/
|
|
1445
|
+
static decode(base58) {
|
|
1446
|
+
Guards.string(Base58._CLASS_NAME, "base58", base58);
|
|
1447
|
+
let zeroes = 0;
|
|
1448
|
+
for (let i = 0; i < base58.length; i++) {
|
|
1449
|
+
if (base58[i] !== "1") {
|
|
1450
|
+
break;
|
|
1451
|
+
}
|
|
1452
|
+
zeroes += 1;
|
|
1453
|
+
}
|
|
1454
|
+
const size = Math.trunc((base58.length * 733) / 1000) + 1;
|
|
1455
|
+
const b256 = new Uint8Array(size).fill(0);
|
|
1456
|
+
let length = 0;
|
|
1457
|
+
for (let i = zeroes; i < base58.length; i++) {
|
|
1458
|
+
const ch = base58.charCodeAt(i);
|
|
1459
|
+
if (ch & 0xff80) {
|
|
1460
|
+
throw new GeneralError(Base58._CLASS_NAME, "invalidCharacter", { invalidCharacter: ch });
|
|
1461
|
+
}
|
|
1462
|
+
const val = Base58._ALPHABET_REVERSE[ch];
|
|
1463
|
+
if (val === -1) {
|
|
1464
|
+
throw new GeneralError(Base58._CLASS_NAME, "invalidCharacter", { invalidCharacter: ch });
|
|
1465
|
+
}
|
|
1466
|
+
let carry = val;
|
|
1467
|
+
let j = 0;
|
|
1468
|
+
for (let k = size - 1; k >= 0; k--, j++) {
|
|
1469
|
+
if (carry === 0 && j >= length) {
|
|
1470
|
+
break;
|
|
1471
|
+
}
|
|
1472
|
+
carry += b256[k] * 58;
|
|
1473
|
+
b256[k] = carry;
|
|
1474
|
+
carry >>>= 8;
|
|
1475
|
+
}
|
|
1476
|
+
length = j;
|
|
1477
|
+
}
|
|
1478
|
+
const out = new Uint8Array(zeroes + length);
|
|
1479
|
+
let j;
|
|
1480
|
+
for (j = 0; j < zeroes; j++) {
|
|
1481
|
+
out[j] = 0;
|
|
1482
|
+
}
|
|
1483
|
+
let i = size - length;
|
|
1484
|
+
while (i < size) {
|
|
1485
|
+
out[j++] = b256[i++];
|
|
1486
|
+
}
|
|
1487
|
+
return out;
|
|
1488
|
+
}
|
|
1489
|
+
/**
|
|
1490
|
+
* Convert a byte array to base 58.
|
|
1491
|
+
* @param bytes The byte array to encode.
|
|
1492
|
+
* @returns The data as base58 string.
|
|
1493
|
+
*/
|
|
1494
|
+
static encode(bytes) {
|
|
1495
|
+
Guards.uint8Array(Base58._CLASS_NAME, "bytes", bytes);
|
|
1496
|
+
let zeroes = 0;
|
|
1497
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
1498
|
+
if (bytes[i] !== 0) {
|
|
1499
|
+
break;
|
|
1500
|
+
}
|
|
1501
|
+
zeroes += 1;
|
|
1502
|
+
}
|
|
1503
|
+
const size = Math.trunc(((bytes.length - zeroes) * 138) / 100) + 1;
|
|
992
1504
|
const b58 = new Uint8Array(size).fill(0);
|
|
993
1505
|
let length = 0;
|
|
994
1506
|
for (let i = zeroes; i < bytes.length; i++) {
|
|
@@ -1125,6 +1637,7 @@ class Base64 {
|
|
|
1125
1637
|
* @returns The byte array.
|
|
1126
1638
|
*/
|
|
1127
1639
|
static decode(base64) {
|
|
1640
|
+
Guards.string(Base64._CLASS_NAME, "base64", base64);
|
|
1128
1641
|
let tmp;
|
|
1129
1642
|
const lens = Base64.getLengths(base64);
|
|
1130
1643
|
const validLen = lens[0];
|
|
@@ -1166,6 +1679,7 @@ class Base64 {
|
|
|
1166
1679
|
* @returns The data as base64 string.
|
|
1167
1680
|
*/
|
|
1168
1681
|
static encode(bytes) {
|
|
1682
|
+
Guards.uint8Array(Base64._CLASS_NAME, "bytes", bytes);
|
|
1169
1683
|
let tmp;
|
|
1170
1684
|
const len = bytes.length;
|
|
1171
1685
|
const extraBytes = len % 3; // if we have 1 byte left, pad 2 bytes
|
|
@@ -1173,7 +1687,7 @@ class Base64 {
|
|
|
1173
1687
|
const maxChunkLength = 16383; // must be multiple of 3
|
|
1174
1688
|
// go through the array every three bytes, we'll deal with trailing stuff later
|
|
1175
1689
|
for (let i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
|
|
1176
|
-
parts.push(Base64.encodeChunk(bytes, i, i + maxChunkLength
|
|
1690
|
+
parts.push(Base64.encodeChunk(bytes, i, Math.min(i + maxChunkLength, len2)));
|
|
1177
1691
|
}
|
|
1178
1692
|
// pad the end with zeros, but make sure to not forget the extra bytes
|
|
1179
1693
|
if (extraBytes === 1) {
|
|
@@ -1247,522 +1761,251 @@ class Base64 {
|
|
|
1247
1761
|
}
|
|
1248
1762
|
}
|
|
1249
1763
|
|
|
1250
|
-
// Copyright 2024 IOTA Stiftung.
|
|
1251
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
1252
1764
|
/**
|
|
1253
1765
|
* Class to help with base64 URL Encoding/Decoding.
|
|
1254
1766
|
* https://www.rfc-editor.org/rfc/rfc4648#section-5.
|
|
1255
1767
|
*/
|
|
1256
1768
|
class Base64Url {
|
|
1257
|
-
/**
|
|
1258
|
-
* Convert the base 64 string to a byte array.
|
|
1259
|
-
* @param base64Url The base64 url string to convert.
|
|
1260
|
-
* @returns The byte array.
|
|
1261
|
-
*/
|
|
1262
|
-
static decode(base64Url) {
|
|
1263
|
-
let base64 = base64Url;
|
|
1264
|
-
// Base 64 url can have padding removed, so add it back if it is missing.
|
|
1265
|
-
if (base64.length > 0 && !base64.endsWith("=")) {
|
|
1266
|
-
const placeHoldersLen = 4 - (base64.length % 4);
|
|
1267
|
-
if (placeHoldersLen > 0 && placeHoldersLen < 4) {
|
|
1268
|
-
base64 = base64.padEnd(base64.length + placeHoldersLen, "=");
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
base64 = base64.replace(/-/g, "+").replace(/_/g, "/");
|
|
1272
|
-
return Base64.decode(base64);
|
|
1273
|
-
}
|
|
1274
|
-
/**
|
|
1275
|
-
* Convert a byte array to base 64 url.
|
|
1276
|
-
* @param bytes The byte array to convert.
|
|
1277
|
-
* @returns The data as base64 url string.
|
|
1278
|
-
*/
|
|
1279
|
-
static encode(bytes) {
|
|
1280
|
-
const base64 = Base64.encode(bytes);
|
|
1281
|
-
// Base 64 url can have padding removed, so remove it.
|
|
1282
|
-
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
1283
|
-
}
|
|
1284
|
-
}
|
|
1285
|
-
|
|
1286
|
-
/**
|
|
1287
|
-
* Class to handle errors which are triggered by data already existing.
|
|
1288
|
-
*/
|
|
1289
|
-
class AlreadyExistsError extends BaseError {
|
|
1290
|
-
/**
|
|
1291
|
-
* Runtime name for the class.
|
|
1292
|
-
*/
|
|
1293
|
-
static CLASS_NAME = "AlreadyExistsError";
|
|
1294
|
-
/**
|
|
1295
|
-
* Create a new instance of AlreadyExistsError.
|
|
1296
|
-
* @param source The source of the error.
|
|
1297
|
-
* @param message The message as a code.
|
|
1298
|
-
* @param existingId The id for the item.
|
|
1299
|
-
* @param inner The inner error if we have wrapped another error.
|
|
1300
|
-
*/
|
|
1301
|
-
constructor(source, message, existingId, inner) {
|
|
1302
|
-
super(AlreadyExistsError.CLASS_NAME, source, message, { existingId }, inner);
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
|
|
1306
|
-
/**
|
|
1307
|
-
* Class to handle errors which are triggered by conflicting data.
|
|
1308
|
-
*/
|
|
1309
|
-
class ConflictError extends BaseError {
|
|
1310
|
-
/**
|
|
1311
|
-
* Runtime name for the class.
|
|
1312
|
-
*/
|
|
1313
|
-
static CLASS_NAME = "ConflictError";
|
|
1314
|
-
/**
|
|
1315
|
-
* Create a new instance of ConflictError.
|
|
1316
|
-
* @param source The source of the error.
|
|
1317
|
-
* @param message The message as a code.
|
|
1318
|
-
* @param conflictId The id that has conflicts.
|
|
1319
|
-
* @param conflicts The conflicts that occurred.
|
|
1320
|
-
* @param inner The inner error if we have wrapped another error.
|
|
1321
|
-
*/
|
|
1322
|
-
constructor(source, message, conflictId, conflicts, inner) {
|
|
1323
|
-
super(ConflictError.CLASS_NAME, source, message, { conflictId, conflicts }, inner);
|
|
1324
|
-
}
|
|
1325
|
-
}
|
|
1326
|
-
|
|
1327
|
-
/**
|
|
1328
|
-
* Class to handle errors which are triggered by data guards.
|
|
1329
|
-
*/
|
|
1330
|
-
class GuardError extends BaseError {
|
|
1331
|
-
/**
|
|
1332
|
-
* Runtime name for the class.
|
|
1333
|
-
*/
|
|
1334
|
-
static CLASS_NAME = "GuardError";
|
|
1335
|
-
/**
|
|
1336
|
-
* Create a new instance of GuardError.
|
|
1337
|
-
* @param source The source of the error.
|
|
1338
|
-
* @param message The message as a code.
|
|
1339
|
-
* @param propertyName The property which triggered the guard error for the item.
|
|
1340
|
-
* @param propertyValue The property value which triggered the guard error for the item.
|
|
1341
|
-
* @param propertyOptions The property options which might be allowed.
|
|
1342
|
-
*/
|
|
1343
|
-
constructor(source, message, propertyName, propertyValue, propertyOptions) {
|
|
1344
|
-
super(GuardError.CLASS_NAME, source, message, {
|
|
1345
|
-
property: propertyName ?? "property",
|
|
1346
|
-
value: Is.undefined(propertyValue) ? "undefined" : propertyValue,
|
|
1347
|
-
options: propertyOptions
|
|
1348
|
-
});
|
|
1349
|
-
}
|
|
1350
|
-
}
|
|
1351
|
-
|
|
1352
|
-
/**
|
|
1353
|
-
* Class to handle errors which are triggered by data not being found.
|
|
1354
|
-
*/
|
|
1355
|
-
class NotFoundError extends BaseError {
|
|
1356
|
-
/**
|
|
1357
|
-
* Runtime name for the class.
|
|
1358
|
-
*/
|
|
1359
|
-
static CLASS_NAME = "NotFoundError";
|
|
1360
|
-
/**
|
|
1361
|
-
* Create a new instance of NotFoundError.
|
|
1362
|
-
* @param source The source of the error.
|
|
1363
|
-
* @param message The message as a code.
|
|
1364
|
-
* @param notFoundId The id for the item.
|
|
1365
|
-
* @param inner The inner error if we have wrapped another error.
|
|
1366
|
-
*/
|
|
1367
|
-
constructor(source, message, notFoundId, inner) {
|
|
1368
|
-
super(NotFoundError.CLASS_NAME, source, message, { notFoundId }, inner);
|
|
1369
|
-
}
|
|
1370
|
-
}
|
|
1371
|
-
|
|
1372
|
-
/**
|
|
1373
|
-
* Class to handle errors.
|
|
1374
|
-
*/
|
|
1375
|
-
class NotImplementedError extends BaseError {
|
|
1376
|
-
/**
|
|
1377
|
-
* Runtime name for the class.
|
|
1378
|
-
*/
|
|
1379
|
-
static CLASS_NAME = "NotImplementedError";
|
|
1380
|
-
/**
|
|
1381
|
-
* Create a new instance of NotImplementedError.
|
|
1382
|
-
* @param source The source of the error.
|
|
1383
|
-
* @param method The method for the error.
|
|
1384
|
-
*/
|
|
1385
|
-
constructor(source, method) {
|
|
1386
|
-
super(NotImplementedError.CLASS_NAME, source, "common.notImplementedMethod", {
|
|
1387
|
-
method
|
|
1388
|
-
});
|
|
1389
|
-
}
|
|
1390
|
-
}
|
|
1391
|
-
|
|
1392
|
-
/**
|
|
1393
|
-
* Class to handle errors when a feature is unsupported.
|
|
1394
|
-
*/
|
|
1395
|
-
class NotSupportedError extends BaseError {
|
|
1396
|
-
/**
|
|
1397
|
-
* Runtime name for the class.
|
|
1398
|
-
*/
|
|
1399
|
-
static CLASS_NAME = "NotSupportedError";
|
|
1400
|
-
/**
|
|
1401
|
-
* Create a new instance of NotSupportedError.
|
|
1402
|
-
* @param source The source of the error.
|
|
1403
|
-
* @param message The message as a code.
|
|
1404
|
-
* @param inner The inner error if we have wrapped another error.
|
|
1405
|
-
*/
|
|
1406
|
-
constructor(source, message, inner) {
|
|
1407
|
-
super(NotSupportedError.CLASS_NAME, source, message, undefined, inner);
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
|
-
|
|
1411
|
-
/**
|
|
1412
|
-
* Class to handle errors which are triggered by access not being unauthorized.
|
|
1413
|
-
*/
|
|
1414
|
-
class UnauthorizedError extends BaseError {
|
|
1415
|
-
/**
|
|
1416
|
-
* Runtime name for the class.
|
|
1417
|
-
*/
|
|
1418
|
-
static CLASS_NAME = "UnauthorizedError";
|
|
1419
|
-
/**
|
|
1420
|
-
* Create a new instance of UnauthorizedError.
|
|
1421
|
-
* @param source The source of the error.
|
|
1422
|
-
* @param message The message as a code.
|
|
1423
|
-
* @param inner The inner error if we have wrapped another error.
|
|
1424
|
-
*/
|
|
1425
|
-
constructor(source, message, inner) {
|
|
1426
|
-
super(UnauthorizedError.CLASS_NAME, source, message, undefined, inner);
|
|
1427
|
-
}
|
|
1428
|
-
}
|
|
1429
|
-
|
|
1430
|
-
/**
|
|
1431
|
-
* Class to handle errors when some data can not be processed.
|
|
1432
|
-
*/
|
|
1433
|
-
class UnprocessableError extends BaseError {
|
|
1434
1769
|
/**
|
|
1435
1770
|
* Runtime name for the class.
|
|
1771
|
+
* @internal
|
|
1436
1772
|
*/
|
|
1437
|
-
static
|
|
1438
|
-
/**
|
|
1439
|
-
* Create a new instance of UnprocessableError.
|
|
1440
|
-
* @param source The source of the error.
|
|
1441
|
-
* @param message The message as a code.
|
|
1442
|
-
* @param properties Any additional information for the error.
|
|
1443
|
-
* @param inner The inner error if we have wrapped another error.
|
|
1444
|
-
*/
|
|
1445
|
-
constructor(source, message, properties, inner) {
|
|
1446
|
-
super(UnprocessableError.CLASS_NAME, source, message, properties, inner);
|
|
1447
|
-
}
|
|
1448
|
-
}
|
|
1449
|
-
|
|
1450
|
-
/**
|
|
1451
|
-
* Class to handle errors which are triggered by entity validation.
|
|
1452
|
-
*/
|
|
1453
|
-
class ValidationError extends BaseError {
|
|
1454
|
-
/**
|
|
1455
|
-
* Runtime name for the class.s
|
|
1456
|
-
*/
|
|
1457
|
-
static CLASS_NAME = "ValidationError";
|
|
1458
|
-
/**
|
|
1459
|
-
* Create a new instance of ValidationError.
|
|
1460
|
-
* @param source The source of the error.
|
|
1461
|
-
* @param validationObject The object that failed validation.
|
|
1462
|
-
* @param validationFailures The validation failures.
|
|
1463
|
-
*/
|
|
1464
|
-
constructor(source, validationObject, validationFailures) {
|
|
1465
|
-
super(ValidationError.CLASS_NAME, source, "common.validation", {
|
|
1466
|
-
validationObject,
|
|
1467
|
-
validationFailures
|
|
1468
|
-
});
|
|
1469
|
-
}
|
|
1470
|
-
}
|
|
1471
|
-
|
|
1472
|
-
// Copyright 2024 IOTA Stiftung.
|
|
1473
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
1474
|
-
/**
|
|
1475
|
-
* Class to handle guard operations for parameters.
|
|
1476
|
-
*/
|
|
1477
|
-
class Guards {
|
|
1478
|
-
/**
|
|
1479
|
-
* Is the property defined.
|
|
1480
|
-
* @param source The source of the error.
|
|
1481
|
-
* @param property The name of the property.
|
|
1482
|
-
* @param value The value to test.
|
|
1483
|
-
* @throws GuardError If the value does not match the assertion.
|
|
1484
|
-
*/
|
|
1485
|
-
static defined(source, property, value) {
|
|
1486
|
-
if (Is.undefined(value)) {
|
|
1487
|
-
throw new GuardError(source, "guard.undefined", property, value);
|
|
1488
|
-
}
|
|
1489
|
-
}
|
|
1490
|
-
/**
|
|
1491
|
-
* Is the property a string.
|
|
1492
|
-
* @param source The source of the error.
|
|
1493
|
-
* @param property The name of the property.
|
|
1494
|
-
* @param value The value to test.
|
|
1495
|
-
* @throws GuardError If the value does not match the assertion.
|
|
1496
|
-
*/
|
|
1497
|
-
static string(source, property, value) {
|
|
1498
|
-
if (!Is.string(value)) {
|
|
1499
|
-
throw new GuardError(source, "guard.string", property, value);
|
|
1500
|
-
}
|
|
1501
|
-
}
|
|
1502
|
-
/**
|
|
1503
|
-
* Is the property a string with a value.
|
|
1504
|
-
* @param source The source of the error.
|
|
1505
|
-
* @param property The name of the property.
|
|
1506
|
-
* @param value The value to test.
|
|
1507
|
-
* @throws GuardError If the value does not match the assertion.
|
|
1508
|
-
*/
|
|
1509
|
-
static stringValue(source, property, value) {
|
|
1510
|
-
if (!Is.string(value)) {
|
|
1511
|
-
throw new GuardError(source, "guard.string", property, value);
|
|
1512
|
-
}
|
|
1513
|
-
if (value.length === 0) {
|
|
1514
|
-
throw new GuardError(source, "guard.stringEmpty", property, value);
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1773
|
+
static _CLASS_NAME = "Base64";
|
|
1517
1774
|
/**
|
|
1518
|
-
*
|
|
1519
|
-
* @param
|
|
1520
|
-
* @
|
|
1521
|
-
* @param value The value to test.
|
|
1522
|
-
* @throws GuardError If the value does not match the assertion.
|
|
1775
|
+
* Convert the base 64 string to a byte array.
|
|
1776
|
+
* @param base64Url The base64 url string to convert.
|
|
1777
|
+
* @returns The byte array.
|
|
1523
1778
|
*/
|
|
1524
|
-
static
|
|
1525
|
-
|
|
1526
|
-
|
|
1779
|
+
static decode(base64Url) {
|
|
1780
|
+
Guards.string(Base64Url._CLASS_NAME, "base64Url", base64Url);
|
|
1781
|
+
let base64 = base64Url;
|
|
1782
|
+
// Base 64 url can have padding removed, so add it back if it is missing.
|
|
1783
|
+
if (base64.length > 0 && !base64.endsWith("=")) {
|
|
1784
|
+
const placeHoldersLen = 4 - (base64.length % 4);
|
|
1785
|
+
if (placeHoldersLen > 0 && placeHoldersLen < 4) {
|
|
1786
|
+
base64 = base64.padEnd(base64.length + placeHoldersLen, "=");
|
|
1787
|
+
}
|
|
1527
1788
|
}
|
|
1789
|
+
base64 = base64.replace(/-/g, "+").replace(/_/g, "/");
|
|
1790
|
+
return Base64.decode(base64);
|
|
1528
1791
|
}
|
|
1529
1792
|
/**
|
|
1530
|
-
*
|
|
1531
|
-
* @param
|
|
1532
|
-
* @
|
|
1533
|
-
* @param value The value to test.
|
|
1534
|
-
* @throws GuardError If the value does not match the assertion.
|
|
1793
|
+
* Convert a byte array to base 64 url.
|
|
1794
|
+
* @param bytes The byte array to convert.
|
|
1795
|
+
* @returns The data as base64 url string.
|
|
1535
1796
|
*/
|
|
1536
|
-
static
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1797
|
+
static encode(bytes) {
|
|
1798
|
+
Guards.uint8Array(Base64Url._CLASS_NAME, "bytes", bytes);
|
|
1799
|
+
const base64 = Base64.encode(bytes);
|
|
1800
|
+
// Base 64 url can have padding removed, so remove it.
|
|
1801
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
1540
1802
|
}
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
/**
|
|
1806
|
+
* Class to handle errors which are triggered by data already existing.
|
|
1807
|
+
*/
|
|
1808
|
+
class AlreadyExistsError extends BaseError {
|
|
1541
1809
|
/**
|
|
1542
|
-
*
|
|
1543
|
-
* @param source The source of the error.
|
|
1544
|
-
* @param property The name of the property.
|
|
1545
|
-
* @param value The value to test.
|
|
1546
|
-
* @param allowPrefix Allow the hex to have the 0x prefix.
|
|
1547
|
-
* @throws GuardError If the value does not match the assertion.
|
|
1810
|
+
* Runtime name for the class.
|
|
1548
1811
|
*/
|
|
1549
|
-
static
|
|
1550
|
-
Guards.stringValue(source, property, value);
|
|
1551
|
-
if (!HexHelper.isHex(value, allowPrefix)) {
|
|
1552
|
-
throw new GuardError(source, "guard.stringHex", property, value);
|
|
1553
|
-
}
|
|
1554
|
-
}
|
|
1812
|
+
static CLASS_NAME = "AlreadyExistsError";
|
|
1555
1813
|
/**
|
|
1556
|
-
*
|
|
1814
|
+
* Create a new instance of AlreadyExistsError.
|
|
1557
1815
|
* @param source The source of the error.
|
|
1558
|
-
* @param
|
|
1559
|
-
* @param
|
|
1560
|
-
* @param
|
|
1561
|
-
* @param allowPrefix Allow the hex to have the 0x prefix.
|
|
1562
|
-
* @throws GuardError If the value does not match the assertion.
|
|
1816
|
+
* @param message The message as a code.
|
|
1817
|
+
* @param existingId The id for the item.
|
|
1818
|
+
* @param cause The cause of the error if we have wrapped another error.
|
|
1563
1819
|
*/
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
if (HexHelper.stripPrefix(value).length !== length) {
|
|
1567
|
-
throw new GuardError(source, "guard.stringHexLength", property, value.length, length.toString());
|
|
1568
|
-
}
|
|
1820
|
+
constructor(source, message, existingId, cause) {
|
|
1821
|
+
super(AlreadyExistsError.CLASS_NAME, source, message, { existingId });
|
|
1569
1822
|
}
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
/**
|
|
1826
|
+
* Class to handle errors which are triggered by conflicting data.
|
|
1827
|
+
*/
|
|
1828
|
+
class ConflictError extends BaseError {
|
|
1570
1829
|
/**
|
|
1571
|
-
*
|
|
1572
|
-
* @param source The source of the error.
|
|
1573
|
-
* @param property The name of the property.
|
|
1574
|
-
* @param value The value to test.
|
|
1575
|
-
* @throws GuardError If the value does not match the assertion.
|
|
1830
|
+
* Runtime name for the class.
|
|
1576
1831
|
*/
|
|
1577
|
-
static
|
|
1578
|
-
if (!Is.number(value)) {
|
|
1579
|
-
throw new GuardError(source, "guard.number", property, value);
|
|
1580
|
-
}
|
|
1581
|
-
}
|
|
1832
|
+
static CLASS_NAME = "ConflictError";
|
|
1582
1833
|
/**
|
|
1583
|
-
*
|
|
1834
|
+
* Create a new instance of ConflictError.
|
|
1584
1835
|
* @param source The source of the error.
|
|
1585
|
-
* @param
|
|
1586
|
-
* @param
|
|
1587
|
-
* @
|
|
1836
|
+
* @param message The message as a code.
|
|
1837
|
+
* @param conflictId The id that has conflicts.
|
|
1838
|
+
* @param conflicts The conflicts that occurred.
|
|
1839
|
+
* @param cause The cause or the error if we have wrapped another error.
|
|
1588
1840
|
*/
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
throw new GuardError(source, "guard.integer", property, value);
|
|
1592
|
-
}
|
|
1841
|
+
constructor(source, message, conflictId, conflicts, cause) {
|
|
1842
|
+
super(ConflictError.CLASS_NAME, source, message, { conflictId, conflicts }, cause);
|
|
1593
1843
|
}
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
/**
|
|
1847
|
+
* Class to handle errors which are triggered by data not being found.
|
|
1848
|
+
*/
|
|
1849
|
+
class NotFoundError extends BaseError {
|
|
1594
1850
|
/**
|
|
1595
|
-
*
|
|
1596
|
-
* @param source The source of the error.
|
|
1597
|
-
* @param property The name of the property.
|
|
1598
|
-
* @param value The value to test.
|
|
1599
|
-
* @throws GuardError If the value does not match the assertion.
|
|
1851
|
+
* Runtime name for the class.
|
|
1600
1852
|
*/
|
|
1601
|
-
static
|
|
1602
|
-
if (!Is.bigint(value)) {
|
|
1603
|
-
throw new GuardError(source, "guard.bigint", property, value);
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
1853
|
+
static CLASS_NAME = "NotFoundError";
|
|
1606
1854
|
/**
|
|
1607
|
-
*
|
|
1855
|
+
* Create a new instance of NotFoundError.
|
|
1608
1856
|
* @param source The source of the error.
|
|
1609
|
-
* @param
|
|
1610
|
-
* @param
|
|
1611
|
-
* @
|
|
1857
|
+
* @param message The message as a code.
|
|
1858
|
+
* @param notFoundId The id for the item.
|
|
1859
|
+
* @param cause The cause of the error if we have wrapped another error.
|
|
1612
1860
|
*/
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
throw new GuardError(source, "guard.boolean", property, value);
|
|
1616
|
-
}
|
|
1861
|
+
constructor(source, message, notFoundId, cause) {
|
|
1862
|
+
super(NotFoundError.CLASS_NAME, source, message, { notFoundId }, cause);
|
|
1617
1863
|
}
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
/**
|
|
1867
|
+
* Class to handle errors.
|
|
1868
|
+
*/
|
|
1869
|
+
class NotImplementedError extends BaseError {
|
|
1618
1870
|
/**
|
|
1619
|
-
*
|
|
1620
|
-
* @param source The source of the error.
|
|
1621
|
-
* @param property The name of the property.
|
|
1622
|
-
* @param value The value to test.
|
|
1623
|
-
* @throws GuardError If the value does not match the assertion.
|
|
1871
|
+
* Runtime name for the class.
|
|
1624
1872
|
*/
|
|
1625
|
-
static
|
|
1626
|
-
if (!Is.date(value)) {
|
|
1627
|
-
throw new GuardError(source, "guard.date", property, value);
|
|
1628
|
-
}
|
|
1629
|
-
}
|
|
1873
|
+
static CLASS_NAME = "NotImplementedError";
|
|
1630
1874
|
/**
|
|
1631
|
-
*
|
|
1875
|
+
* Create a new instance of NotImplementedError.
|
|
1632
1876
|
* @param source The source of the error.
|
|
1633
|
-
* @param
|
|
1634
|
-
* @param value The value to test.
|
|
1635
|
-
* @throws GuardError If the value does not match the assertion.
|
|
1877
|
+
* @param method The method for the error.
|
|
1636
1878
|
*/
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
}
|
|
1879
|
+
constructor(source, method) {
|
|
1880
|
+
super(NotImplementedError.CLASS_NAME, source, "common.notImplementedMethod", {
|
|
1881
|
+
method
|
|
1882
|
+
});
|
|
1641
1883
|
}
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
/**
|
|
1887
|
+
* Class to handle errors when a feature is unsupported.
|
|
1888
|
+
*/
|
|
1889
|
+
class NotSupportedError extends BaseError {
|
|
1642
1890
|
/**
|
|
1643
|
-
*
|
|
1644
|
-
* @param source The source of the error.
|
|
1645
|
-
* @param property The name of the property.
|
|
1646
|
-
* @param value The value to test.
|
|
1647
|
-
* @throws GuardError If the value does not match the assertion.
|
|
1891
|
+
* Runtime name for the class.
|
|
1648
1892
|
*/
|
|
1649
|
-
static
|
|
1650
|
-
if (!Is.timestampSeconds(value)) {
|
|
1651
|
-
throw new GuardError(source, "guard.timestampSeconds", property, value);
|
|
1652
|
-
}
|
|
1653
|
-
}
|
|
1893
|
+
static CLASS_NAME = "NotSupportedError";
|
|
1654
1894
|
/**
|
|
1655
|
-
*
|
|
1895
|
+
* Create a new instance of NotSupportedError.
|
|
1656
1896
|
* @param source The source of the error.
|
|
1657
|
-
* @param
|
|
1658
|
-
* @param
|
|
1659
|
-
* @throws GuardError If the value does not match the assertion.
|
|
1897
|
+
* @param message The message as a code.
|
|
1898
|
+
* @param cause The cause of the error if we have wrapped another error.
|
|
1660
1899
|
*/
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
throw new GuardError(source, "guard.objectUndefined", property, value);
|
|
1664
|
-
}
|
|
1665
|
-
if (!Is.object(value)) {
|
|
1666
|
-
throw new GuardError(source, "guard.object", property, value);
|
|
1667
|
-
}
|
|
1900
|
+
constructor(source, message, cause) {
|
|
1901
|
+
super(NotSupportedError.CLASS_NAME, source, message, undefined, cause);
|
|
1668
1902
|
}
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
/**
|
|
1906
|
+
* Class to handle errors which are triggered by access not being unauthorized.
|
|
1907
|
+
*/
|
|
1908
|
+
class UnauthorizedError extends BaseError {
|
|
1669
1909
|
/**
|
|
1670
|
-
*
|
|
1671
|
-
* @param source The source of the error.
|
|
1672
|
-
* @param property The name of the property.
|
|
1673
|
-
* @param value The value to test.
|
|
1674
|
-
* @throws GuardError If the value does not match the assertion.
|
|
1910
|
+
* Runtime name for the class.
|
|
1675
1911
|
*/
|
|
1676
|
-
static
|
|
1677
|
-
if (Is.undefined(value)) {
|
|
1678
|
-
throw new GuardError(source, "guard.objectUndefined", property, value);
|
|
1679
|
-
}
|
|
1680
|
-
if (!Is.object(value)) {
|
|
1681
|
-
throw new GuardError(source, "guard.object", property, value);
|
|
1682
|
-
}
|
|
1683
|
-
if (Object.keys(value || {}).length === 0) {
|
|
1684
|
-
throw new GuardError(source, "guard.objectValue", property, value);
|
|
1685
|
-
}
|
|
1686
|
-
}
|
|
1912
|
+
static CLASS_NAME = "UnauthorizedError";
|
|
1687
1913
|
/**
|
|
1688
|
-
*
|
|
1914
|
+
* Create a new instance of UnauthorizedError.
|
|
1689
1915
|
* @param source The source of the error.
|
|
1690
|
-
* @param
|
|
1691
|
-
* @param
|
|
1692
|
-
* @throws GuardError If the value does not match the assertion.
|
|
1916
|
+
* @param message The message as a code.
|
|
1917
|
+
* @param cause The cause of the error if we have wrapped another error.
|
|
1693
1918
|
*/
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
throw new GuardError(source, "guard.array", property, value);
|
|
1697
|
-
}
|
|
1919
|
+
constructor(source, message, cause) {
|
|
1920
|
+
super(UnauthorizedError.CLASS_NAME, source, message, undefined, cause);
|
|
1698
1921
|
}
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
/**
|
|
1925
|
+
* Class to handle errors when some data can not be processed.
|
|
1926
|
+
*/
|
|
1927
|
+
class UnprocessableError extends BaseError {
|
|
1699
1928
|
/**
|
|
1700
|
-
*
|
|
1929
|
+
* Runtime name for the class.
|
|
1930
|
+
*/
|
|
1931
|
+
static CLASS_NAME = "UnprocessableError";
|
|
1932
|
+
/**
|
|
1933
|
+
* Create a new instance of UnprocessableError.
|
|
1701
1934
|
* @param source The source of the error.
|
|
1702
|
-
* @param
|
|
1703
|
-
* @param
|
|
1704
|
-
* @
|
|
1935
|
+
* @param message The message as a code.
|
|
1936
|
+
* @param properties Any additional information for the error.
|
|
1937
|
+
* @param cause The cause of the error if we have wrapped another error.
|
|
1705
1938
|
*/
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
throw new GuardError(source, "guard.array", property, value);
|
|
1709
|
-
}
|
|
1710
|
-
if (value.length === 0) {
|
|
1711
|
-
throw new GuardError(source, "guard.arrayValue", property, value);
|
|
1712
|
-
}
|
|
1939
|
+
constructor(source, message, properties, cause) {
|
|
1940
|
+
super(UnprocessableError.CLASS_NAME, source, message, properties, cause);
|
|
1713
1941
|
}
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
/**
|
|
1945
|
+
* Class to handle errors which are triggered by entity validation.
|
|
1946
|
+
*/
|
|
1947
|
+
class ValidationError extends BaseError {
|
|
1714
1948
|
/**
|
|
1715
|
-
*
|
|
1949
|
+
* Runtime name for the class.s
|
|
1950
|
+
*/
|
|
1951
|
+
static CLASS_NAME = "ValidationError";
|
|
1952
|
+
/**
|
|
1953
|
+
* Create a new instance of ValidationError.
|
|
1716
1954
|
* @param source The source of the error.
|
|
1717
|
-
* @param
|
|
1718
|
-
* @param
|
|
1719
|
-
* @param options The options the value must be one of.
|
|
1720
|
-
* @throws GuardError If the value does not match the assertion.
|
|
1955
|
+
* @param validationObject The object that failed validation.
|
|
1956
|
+
* @param validationFailures The validation failures.
|
|
1721
1957
|
*/
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
throw new GuardError(source, "guard.arrayOneOf", property, value, options.join(", "));
|
|
1728
|
-
}
|
|
1958
|
+
constructor(source, validationObject, validationFailures) {
|
|
1959
|
+
super(ValidationError.CLASS_NAME, source, "common.validation", {
|
|
1960
|
+
validationObject,
|
|
1961
|
+
validationFailures
|
|
1962
|
+
});
|
|
1729
1963
|
}
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
// Copyright 2024 IOTA Stiftung.
|
|
1967
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
1968
|
+
/**
|
|
1969
|
+
* Provide a store for shared objects which can be accesses through multiple
|
|
1970
|
+
* instance loads of a packages.
|
|
1971
|
+
*/
|
|
1972
|
+
class SharedStore {
|
|
1730
1973
|
/**
|
|
1731
|
-
*
|
|
1732
|
-
* @param
|
|
1733
|
-
* @
|
|
1734
|
-
* @param value The value to test.
|
|
1735
|
-
* @throws GuardError If the value does not match the assertion.
|
|
1974
|
+
* Get a property from the shared store.
|
|
1975
|
+
* @param prop The name of the property to get.
|
|
1976
|
+
* @returns The property if it exists.
|
|
1736
1977
|
*/
|
|
1737
|
-
static
|
|
1738
|
-
|
|
1739
|
-
|
|
1978
|
+
static get(prop) {
|
|
1979
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1980
|
+
const shared = globalThis.__TWIN_SHARED__;
|
|
1981
|
+
if (Is.undefined(shared)) {
|
|
1982
|
+
return;
|
|
1740
1983
|
}
|
|
1984
|
+
return shared[prop];
|
|
1741
1985
|
}
|
|
1742
1986
|
/**
|
|
1743
|
-
*
|
|
1744
|
-
* @param
|
|
1745
|
-
* @param
|
|
1746
|
-
* @param value The value to test.
|
|
1747
|
-
* @returns True if the value is a function.
|
|
1748
|
-
* @throws GuardError If the value does not match the assertion.
|
|
1987
|
+
* Set the property in the shared store.
|
|
1988
|
+
* @param prop The name of the property to set.
|
|
1989
|
+
* @param value The value to set.
|
|
1749
1990
|
*/
|
|
1750
|
-
static
|
|
1751
|
-
|
|
1752
|
-
|
|
1991
|
+
static set(prop, value) {
|
|
1992
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1993
|
+
if (Is.undefined(globalThis.__TWIN_SHARED__)) {
|
|
1994
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1995
|
+
globalThis.__TWIN_SHARED__ = {};
|
|
1753
1996
|
}
|
|
1754
|
-
|
|
1997
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1998
|
+
globalThis.__TWIN_SHARED__[prop] = value;
|
|
1755
1999
|
}
|
|
1756
2000
|
/**
|
|
1757
|
-
*
|
|
1758
|
-
* @param
|
|
1759
|
-
* @param property The name of the property.
|
|
1760
|
-
* @param value The value to test.
|
|
1761
|
-
* @throws GuardError If the value does not match the assertion.
|
|
2001
|
+
* Remove a property from the shared store.
|
|
2002
|
+
* @param prop The name of the property to remove.
|
|
1762
2003
|
*/
|
|
1763
|
-
static
|
|
1764
|
-
|
|
1765
|
-
|
|
2004
|
+
static remove(prop) {
|
|
2005
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2006
|
+
const shared = globalThis.__TWIN_SHARED__;
|
|
2007
|
+
if (!Is.undefined(shared)) {
|
|
2008
|
+
delete shared[prop];
|
|
1766
2009
|
}
|
|
1767
2010
|
}
|
|
1768
2011
|
}
|
|
@@ -1776,11 +2019,6 @@ class Factory {
|
|
|
1776
2019
|
* @internal
|
|
1777
2020
|
*/
|
|
1778
2021
|
static _CLASS_NAME = "Factory";
|
|
1779
|
-
/**
|
|
1780
|
-
* Store all the created factories.
|
|
1781
|
-
* @internal
|
|
1782
|
-
*/
|
|
1783
|
-
static _factories = {};
|
|
1784
2022
|
/**
|
|
1785
2023
|
* Type name for the instances.
|
|
1786
2024
|
* @internal
|
|
@@ -1834,10 +2072,41 @@ class Factory {
|
|
|
1834
2072
|
* @returns The factory instance.
|
|
1835
2073
|
*/
|
|
1836
2074
|
static createFactory(typeName, autoInstance = false, matcher) {
|
|
1837
|
-
|
|
1838
|
-
|
|
2075
|
+
const factories = Factory.getFactories();
|
|
2076
|
+
if (Is.undefined(factories[typeName])) {
|
|
2077
|
+
factories[typeName] = new Factory(typeName, autoInstance, matcher);
|
|
2078
|
+
}
|
|
2079
|
+
return factories[typeName];
|
|
2080
|
+
}
|
|
2081
|
+
/**
|
|
2082
|
+
* Get all the factories.
|
|
2083
|
+
* @returns All the factories.
|
|
2084
|
+
*/
|
|
2085
|
+
static getFactories() {
|
|
2086
|
+
let factories = SharedStore.get("factories");
|
|
2087
|
+
if (Is.undefined(factories)) {
|
|
2088
|
+
factories = {};
|
|
2089
|
+
SharedStore.set("factories", factories);
|
|
2090
|
+
}
|
|
2091
|
+
return factories;
|
|
2092
|
+
}
|
|
2093
|
+
/**
|
|
2094
|
+
* Reset all the factories, which removes any created instances, but not the registrations.
|
|
2095
|
+
*/
|
|
2096
|
+
static resetFactories() {
|
|
2097
|
+
const factories = Factory.getFactories();
|
|
2098
|
+
for (const typeName in factories) {
|
|
2099
|
+
factories[typeName].reset();
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
/**
|
|
2103
|
+
* Clear all the factories, which removes anything registered with the factories.
|
|
2104
|
+
*/
|
|
2105
|
+
static clearFactories() {
|
|
2106
|
+
const factories = Factory.getFactories();
|
|
2107
|
+
for (const typeName in factories) {
|
|
2108
|
+
factories[typeName].clear();
|
|
1839
2109
|
}
|
|
1840
|
-
return Factory._factories[typeName];
|
|
1841
2110
|
}
|
|
1842
2111
|
/**
|
|
1843
2112
|
* Register a new generator.
|
|
@@ -1883,6 +2152,7 @@ class Factory {
|
|
|
1883
2152
|
* @throws GeneralError if no item exists to get.
|
|
1884
2153
|
*/
|
|
1885
2154
|
get(name) {
|
|
2155
|
+
Guards.stringValue(Factory._CLASS_NAME, "name", name);
|
|
1886
2156
|
const instance = this.getIfExists(name);
|
|
1887
2157
|
if (!instance) {
|
|
1888
2158
|
throw new GeneralError(Factory._CLASS_NAME, "noGet", {
|
|
@@ -1898,6 +2168,9 @@ class Factory {
|
|
|
1898
2168
|
* @returns An instance of the item or undefined if it does not exist.
|
|
1899
2169
|
*/
|
|
1900
2170
|
getIfExists(name) {
|
|
2171
|
+
if (Is.empty(name)) {
|
|
2172
|
+
return;
|
|
2173
|
+
}
|
|
1901
2174
|
Guards.stringValue(Factory._CLASS_NAME, "name", name);
|
|
1902
2175
|
const matchName = this._matcher(Object.keys(this._generators), name);
|
|
1903
2176
|
if (Is.stringValue(matchName) && this._generators[matchName]) {
|
|
@@ -1910,7 +2183,7 @@ class Factory {
|
|
|
1910
2183
|
}
|
|
1911
2184
|
}
|
|
1912
2185
|
/**
|
|
1913
|
-
*
|
|
2186
|
+
* Remove all the instances and leave the generators intact.
|
|
1914
2187
|
*/
|
|
1915
2188
|
reset() {
|
|
1916
2189
|
for (const name in this._generators) {
|
|
@@ -1918,6 +2191,14 @@ class Factory {
|
|
|
1918
2191
|
}
|
|
1919
2192
|
this._instances = {};
|
|
1920
2193
|
}
|
|
2194
|
+
/**
|
|
2195
|
+
* Remove all the instances and the generators.
|
|
2196
|
+
*/
|
|
2197
|
+
clear() {
|
|
2198
|
+
this._instances = {};
|
|
2199
|
+
this._generators = {};
|
|
2200
|
+
this._orderCounter = 0;
|
|
2201
|
+
}
|
|
1921
2202
|
/**
|
|
1922
2203
|
* Get all the instances as a map.
|
|
1923
2204
|
* @returns The instances as a map.
|
|
@@ -1951,1060 +2232,1402 @@ class Factory {
|
|
|
1951
2232
|
order: this._generators[generator].order
|
|
1952
2233
|
});
|
|
1953
2234
|
}
|
|
1954
|
-
return orderedNames.sort((a, b) => a.order - b.order).map(o => o.name);
|
|
2235
|
+
return orderedNames.sort((a, b) => a.order - b.order).map(o => o.name);
|
|
2236
|
+
}
|
|
2237
|
+
/**
|
|
2238
|
+
* Does the factory contain the name.
|
|
2239
|
+
* @param name The name of the instance to find.
|
|
2240
|
+
* @returns True if the factory has a matching name.
|
|
2241
|
+
*/
|
|
2242
|
+
hasName(name) {
|
|
2243
|
+
Guards.stringValue(Factory._CLASS_NAME, "name", name);
|
|
2244
|
+
return Is.stringValue(this._matcher(Object.keys(this._generators), name));
|
|
2245
|
+
}
|
|
2246
|
+
/**
|
|
2247
|
+
* Remove any instances of the given name.
|
|
2248
|
+
* @param name The name of the instances to remove.
|
|
2249
|
+
* @internal
|
|
2250
|
+
*/
|
|
2251
|
+
removeInstance(name) {
|
|
2252
|
+
delete this._instances[name];
|
|
2253
|
+
}
|
|
2254
|
+
/**
|
|
2255
|
+
* Match the requested name to the generator name.
|
|
2256
|
+
* @param names The list of names for all the generators.
|
|
2257
|
+
* @param name The name to match.
|
|
2258
|
+
* @returns The matched name or undefined if no match.
|
|
2259
|
+
* @internal
|
|
2260
|
+
*/
|
|
2261
|
+
defaultMatcher(names, name) {
|
|
2262
|
+
return this._generators[name] ? name : undefined;
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2267
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
2268
|
+
/**
|
|
2269
|
+
* Factory for creating implementation of component types.
|
|
2270
|
+
*/
|
|
2271
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
2272
|
+
const ComponentFactory = Factory.createFactory("component");
|
|
2273
|
+
|
|
2274
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2275
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
2276
|
+
/* eslint-disable no-bitwise */
|
|
2277
|
+
/**
|
|
2278
|
+
* Convert arrays to and from different formats.
|
|
2279
|
+
*/
|
|
2280
|
+
class Converter {
|
|
2281
|
+
/**
|
|
2282
|
+
* Lookup table for encoding.
|
|
2283
|
+
* @internal
|
|
2284
|
+
*/
|
|
2285
|
+
static _ENCODE_LOOKUP;
|
|
2286
|
+
/**
|
|
2287
|
+
* Lookup table for decoding.
|
|
2288
|
+
* @internal
|
|
2289
|
+
*/
|
|
2290
|
+
static _DECODE_LOOKUP;
|
|
2291
|
+
/**
|
|
2292
|
+
* Encode a raw array to UTF8 string.
|
|
2293
|
+
* @param array The bytes to encode.
|
|
2294
|
+
* @param startIndex The index to start in the bytes.
|
|
2295
|
+
* @param length The length of bytes to read.
|
|
2296
|
+
* @returns The array formatted as UTF8.
|
|
2297
|
+
*/
|
|
2298
|
+
static bytesToUtf8(array, startIndex, length) {
|
|
2299
|
+
const start = startIndex ?? 0;
|
|
2300
|
+
const len = length ?? array.length;
|
|
2301
|
+
let str = "";
|
|
2302
|
+
for (let i = start; i < start + len; i++) {
|
|
2303
|
+
const value = array[i];
|
|
2304
|
+
if (value < 0x80) {
|
|
2305
|
+
str += String.fromCharCode(value);
|
|
2306
|
+
}
|
|
2307
|
+
else if (value > 0xbf && value < 0xe0) {
|
|
2308
|
+
str += String.fromCharCode(((value & 0x1f) << 6) | (array[i + 1] & 0x3f));
|
|
2309
|
+
i += 1;
|
|
2310
|
+
}
|
|
2311
|
+
else if (value > 0xdf && value < 0xf0) {
|
|
2312
|
+
str += String.fromCharCode(((value & 0x0f) << 12) | ((array[i + 1] & 0x3f) << 6) | (array[i + 2] & 0x3f));
|
|
2313
|
+
i += 2;
|
|
2314
|
+
}
|
|
2315
|
+
else {
|
|
2316
|
+
// surrogate pair
|
|
2317
|
+
const charCode = (((value & 0x07) << 18) |
|
|
2318
|
+
((array[i + 1] & 0x3f) << 12) |
|
|
2319
|
+
((array[i + 2] & 0x3f) << 6) |
|
|
2320
|
+
(array[i + 3] & 0x3f)) -
|
|
2321
|
+
0x010000;
|
|
2322
|
+
str += String.fromCharCode((charCode >> 10) | 0xd800, (charCode & 0x03ff) | 0xdc00);
|
|
2323
|
+
i += 3;
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
return str;
|
|
2327
|
+
}
|
|
2328
|
+
/**
|
|
2329
|
+
* Convert a UTF8 string to raw array.
|
|
2330
|
+
* @param utf8 The text to decode.
|
|
2331
|
+
* @returns The array.
|
|
2332
|
+
*/
|
|
2333
|
+
static utf8ToBytes(utf8) {
|
|
2334
|
+
const bytes = [];
|
|
2335
|
+
for (let i = 0; i < utf8.length; i++) {
|
|
2336
|
+
let charCode = utf8.charCodeAt(i);
|
|
2337
|
+
if (charCode < 0x80) {
|
|
2338
|
+
bytes.push(charCode);
|
|
2339
|
+
}
|
|
2340
|
+
else if (charCode < 0x800) {
|
|
2341
|
+
bytes.push(0xc0 | (charCode >> 6), 0x80 | (charCode & 0x3f));
|
|
2342
|
+
}
|
|
2343
|
+
else if (charCode < 0xd800 || charCode >= 0xe000) {
|
|
2344
|
+
bytes.push(0xe0 | (charCode >> 12), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f));
|
|
2345
|
+
}
|
|
2346
|
+
else {
|
|
2347
|
+
// surrogate pair
|
|
2348
|
+
i++;
|
|
2349
|
+
// UTF-16 encodes 0x10000-0x10FFFF by
|
|
2350
|
+
// subtracting 0x10000 and splitting the
|
|
2351
|
+
// 20 bits of 0x0-0xFFFFF into two halves
|
|
2352
|
+
charCode = 0x10000 + (((charCode & 0x3ff) << 10) | (utf8.charCodeAt(i) & 0x3ff));
|
|
2353
|
+
bytes.push(0xf0 | (charCode >> 18), 0x80 | ((charCode >> 12) & 0x3f), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f));
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
return Uint8Array.from(bytes);
|
|
2357
|
+
}
|
|
2358
|
+
/**
|
|
2359
|
+
* Encode a raw array to hex string.
|
|
2360
|
+
* @param array The bytes to encode.
|
|
2361
|
+
* @param includePrefix Include the 0x prefix on the returned hex.
|
|
2362
|
+
* @param startIndex The index to start in the bytes.
|
|
2363
|
+
* @param length The length of bytes to read.
|
|
2364
|
+
* @param reverse Reverse the combine direction.
|
|
2365
|
+
* @returns The array formatted as hex.
|
|
2366
|
+
*/
|
|
2367
|
+
static bytesToHex(array, includePrefix = false, startIndex, length, reverse) {
|
|
2368
|
+
let hex = "";
|
|
2369
|
+
this.buildHexLookups();
|
|
2370
|
+
if (Converter._ENCODE_LOOKUP) {
|
|
2371
|
+
const len = length ?? array.length;
|
|
2372
|
+
const start = startIndex ?? 0;
|
|
2373
|
+
if (reverse) {
|
|
2374
|
+
for (let i = 0; i < len; i++) {
|
|
2375
|
+
hex = Converter._ENCODE_LOOKUP[array[start + i]] + hex;
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
else {
|
|
2379
|
+
for (let i = 0; i < len; i++) {
|
|
2380
|
+
hex += Converter._ENCODE_LOOKUP[array[start + i]];
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
}
|
|
2384
|
+
return includePrefix ? HexHelper.addPrefix(hex) : hex;
|
|
2385
|
+
}
|
|
2386
|
+
/**
|
|
2387
|
+
* Decode a hex string to raw array.
|
|
2388
|
+
* @param hex The hex to decode.
|
|
2389
|
+
* @param reverse Store the characters in reverse.
|
|
2390
|
+
* @returns The array.
|
|
2391
|
+
*/
|
|
2392
|
+
static hexToBytes(hex, reverse) {
|
|
2393
|
+
const strippedHex = HexHelper.stripPrefix(hex);
|
|
2394
|
+
const sizeof = strippedHex.length >> 1;
|
|
2395
|
+
const length = sizeof << 1;
|
|
2396
|
+
const array = new Uint8Array(sizeof);
|
|
2397
|
+
this.buildHexLookups();
|
|
2398
|
+
if (Converter._DECODE_LOOKUP) {
|
|
2399
|
+
let i = 0;
|
|
2400
|
+
let n = 0;
|
|
2401
|
+
while (i < length) {
|
|
2402
|
+
array[n++] =
|
|
2403
|
+
(Converter._DECODE_LOOKUP[strippedHex.charCodeAt(i++)] << 4) |
|
|
2404
|
+
Converter._DECODE_LOOKUP[strippedHex.charCodeAt(i++)];
|
|
2405
|
+
}
|
|
2406
|
+
if (reverse) {
|
|
2407
|
+
array.reverse();
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
return array;
|
|
1955
2411
|
}
|
|
1956
2412
|
/**
|
|
1957
|
-
*
|
|
1958
|
-
* @param
|
|
1959
|
-
* @
|
|
2413
|
+
* Convert the UTF8 to hex.
|
|
2414
|
+
* @param utf8 The text to convert.
|
|
2415
|
+
* @param includePrefix Include the 0x prefix on the returned hex.
|
|
2416
|
+
* @returns The hex version of the bytes.
|
|
1960
2417
|
*/
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
return
|
|
2418
|
+
static utf8ToHex(utf8, includePrefix = false) {
|
|
2419
|
+
const hex = Converter.bytesToHex(Converter.utf8ToBytes(utf8));
|
|
2420
|
+
return includePrefix ? HexHelper.addPrefix(hex) : hex;
|
|
1964
2421
|
}
|
|
1965
2422
|
/**
|
|
1966
|
-
*
|
|
1967
|
-
* @param
|
|
1968
|
-
* @
|
|
2423
|
+
* Convert the hex text to text.
|
|
2424
|
+
* @param hex The hex to convert.
|
|
2425
|
+
* @returns The UTF8 version of the bytes.
|
|
1969
2426
|
*/
|
|
1970
|
-
|
|
1971
|
-
|
|
2427
|
+
static hexToUtf8(hex) {
|
|
2428
|
+
return Converter.bytesToUtf8(Converter.hexToBytes(HexHelper.stripPrefix(hex)));
|
|
1972
2429
|
}
|
|
1973
2430
|
/**
|
|
1974
|
-
*
|
|
1975
|
-
* @param
|
|
1976
|
-
* @
|
|
1977
|
-
* @returns The matched name or undefined if no match.
|
|
1978
|
-
* @internal
|
|
2431
|
+
* Convert bytes to binary string.
|
|
2432
|
+
* @param bytes The bytes to convert.
|
|
2433
|
+
* @returns A binary string of the bytes.
|
|
1979
2434
|
*/
|
|
1980
|
-
|
|
1981
|
-
|
|
2435
|
+
static bytesToBinary(bytes) {
|
|
2436
|
+
const b = [];
|
|
2437
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2438
|
+
b.push(bytes[i].toString(2).padStart(8, "0"));
|
|
2439
|
+
}
|
|
2440
|
+
return b.join("");
|
|
1982
2441
|
}
|
|
1983
|
-
}
|
|
1984
|
-
|
|
1985
|
-
// Copyright 2024 IOTA Stiftung.
|
|
1986
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
1987
|
-
/**
|
|
1988
|
-
* Factory for creating implementation of component types.
|
|
1989
|
-
*/
|
|
1990
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1991
|
-
const ComponentFactory = Factory.createFactory("component");
|
|
1992
|
-
|
|
1993
|
-
// Copyright 2024 IOTA Stiftung.
|
|
1994
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
1995
|
-
/**
|
|
1996
|
-
* Class to help with arrays.
|
|
1997
|
-
*/
|
|
1998
|
-
class ArrayHelper {
|
|
1999
2442
|
/**
|
|
2000
|
-
*
|
|
2001
|
-
* @param
|
|
2002
|
-
* @
|
|
2003
|
-
* @returns True if both arrays are empty of have the same values.
|
|
2443
|
+
* Convert a binary string to bytes.
|
|
2444
|
+
* @param binary The binary string.
|
|
2445
|
+
* @returns The bytes.
|
|
2004
2446
|
*/
|
|
2005
|
-
static
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
if (!((Is.array(arr1) && Is.array(arr2)) || (Is.typedArray(arr1) && Is.typedArray(arr2)))) {
|
|
2010
|
-
return false;
|
|
2011
|
-
}
|
|
2012
|
-
if (arr1.length !== arr2.length) {
|
|
2013
|
-
return false;
|
|
2014
|
-
}
|
|
2015
|
-
for (let i = 0; i < arr1.length; i++) {
|
|
2016
|
-
if (arr1[i] !== arr2[i]) {
|
|
2017
|
-
return false;
|
|
2018
|
-
}
|
|
2447
|
+
static binaryToBytes(binary) {
|
|
2448
|
+
const bytes = new Uint8Array(Math.ceil(binary.length / 8));
|
|
2449
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2450
|
+
bytes[i] = Number.parseInt(binary.slice(i * 8, (i + 1) * 8), 2);
|
|
2019
2451
|
}
|
|
2020
|
-
return
|
|
2452
|
+
return bytes;
|
|
2021
2453
|
}
|
|
2022
|
-
}
|
|
2023
|
-
|
|
2024
|
-
// Copyright 2024 IOTA Stiftung.
|
|
2025
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
2026
|
-
/**
|
|
2027
|
-
* Class to perform internationalization.
|
|
2028
|
-
*/
|
|
2029
|
-
class I18n {
|
|
2030
|
-
/**
|
|
2031
|
-
* The default translation.
|
|
2032
|
-
*/
|
|
2033
|
-
static DEFAULT_LOCALE = "en";
|
|
2034
|
-
/**
|
|
2035
|
-
* Dictionaries for lookups.
|
|
2036
|
-
* @internal
|
|
2037
|
-
*/
|
|
2038
|
-
static _localeDictionaries = {};
|
|
2039
|
-
/**
|
|
2040
|
-
* The current locale.
|
|
2041
|
-
* @internal
|
|
2042
|
-
*/
|
|
2043
|
-
static _currentLocale = I18n.DEFAULT_LOCALE;
|
|
2044
|
-
/**
|
|
2045
|
-
* Change handler for the locale being updated.
|
|
2046
|
-
* @internal
|
|
2047
|
-
*/
|
|
2048
|
-
static _localeChangedHandlers = {};
|
|
2049
2454
|
/**
|
|
2050
|
-
*
|
|
2051
|
-
* @
|
|
2455
|
+
* Convert bytes to base64 string.
|
|
2456
|
+
* @param bytes The bytes to convert.
|
|
2457
|
+
* @returns A base64 string of the bytes.
|
|
2052
2458
|
*/
|
|
2053
|
-
static
|
|
2459
|
+
static bytesToBase64(bytes) {
|
|
2460
|
+
return Base64.encode(bytes);
|
|
2461
|
+
}
|
|
2054
2462
|
/**
|
|
2055
|
-
*
|
|
2056
|
-
* @param
|
|
2463
|
+
* Convert a base64 string to bytes.
|
|
2464
|
+
* @param base64 The base64 string.
|
|
2465
|
+
* @returns The bytes.
|
|
2057
2466
|
*/
|
|
2058
|
-
static
|
|
2059
|
-
|
|
2060
|
-
for (const callback in I18n._localeChangedHandlers) {
|
|
2061
|
-
I18n._localeChangedHandlers[callback](I18n._currentLocale);
|
|
2062
|
-
}
|
|
2467
|
+
static base64ToBytes(base64) {
|
|
2468
|
+
return Base64.decode(base64);
|
|
2063
2469
|
}
|
|
2064
2470
|
/**
|
|
2065
|
-
*
|
|
2066
|
-
* @
|
|
2471
|
+
* Convert bytes to base64 url string.
|
|
2472
|
+
* @param bytes The bytes to convert.
|
|
2473
|
+
* @returns A base64 url string of the bytes.
|
|
2067
2474
|
*/
|
|
2068
|
-
static
|
|
2069
|
-
return
|
|
2475
|
+
static bytesToBase64Url(bytes) {
|
|
2476
|
+
return Base64Url.encode(bytes);
|
|
2070
2477
|
}
|
|
2071
2478
|
/**
|
|
2072
|
-
*
|
|
2073
|
-
* @param
|
|
2074
|
-
* @
|
|
2479
|
+
* Convert a base64 url string to bytes.
|
|
2480
|
+
* @param base64Url The base64 url string.
|
|
2481
|
+
* @returns The bytes.
|
|
2075
2482
|
*/
|
|
2076
|
-
static
|
|
2077
|
-
|
|
2078
|
-
I18n.flattenTranslationKeys(dictionary, "", mergedKeys);
|
|
2079
|
-
I18n._localeDictionaries[locale] = mergedKeys;
|
|
2080
|
-
for (const callback in I18n._dictionaryChangedHandlers) {
|
|
2081
|
-
I18n._dictionaryChangedHandlers[callback](I18n._currentLocale);
|
|
2082
|
-
}
|
|
2483
|
+
static base64UrlToBytes(base64Url) {
|
|
2484
|
+
return Base64Url.decode(base64Url);
|
|
2083
2485
|
}
|
|
2084
2486
|
/**
|
|
2085
|
-
*
|
|
2086
|
-
* @param
|
|
2087
|
-
* @returns
|
|
2487
|
+
* Convert bytes to base58 string.
|
|
2488
|
+
* @param bytes The bytes to convert.
|
|
2489
|
+
* @returns A base58 string of the bytes.
|
|
2088
2490
|
*/
|
|
2089
|
-
static
|
|
2090
|
-
return
|
|
2491
|
+
static bytesToBase58(bytes) {
|
|
2492
|
+
return Base58.encode(bytes);
|
|
2091
2493
|
}
|
|
2092
2494
|
/**
|
|
2093
|
-
*
|
|
2094
|
-
* @
|
|
2495
|
+
* Convert a base58 string to bytes.
|
|
2496
|
+
* @param base58 The base58 string.
|
|
2497
|
+
* @returns The bytes.
|
|
2095
2498
|
*/
|
|
2096
|
-
static
|
|
2097
|
-
return
|
|
2499
|
+
static base58ToBytes(base58) {
|
|
2500
|
+
return Base58.decode(base58);
|
|
2098
2501
|
}
|
|
2099
2502
|
/**
|
|
2100
|
-
*
|
|
2101
|
-
* @
|
|
2102
|
-
* @param handler The handler to add.
|
|
2503
|
+
* Build the static lookup tables.
|
|
2504
|
+
* @internal
|
|
2103
2505
|
*/
|
|
2104
|
-
static
|
|
2105
|
-
|
|
2506
|
+
static buildHexLookups() {
|
|
2507
|
+
if (!Converter._ENCODE_LOOKUP || !Converter._DECODE_LOOKUP) {
|
|
2508
|
+
const alphabet = "0123456789abcdef";
|
|
2509
|
+
Converter._ENCODE_LOOKUP = [];
|
|
2510
|
+
Converter._DECODE_LOOKUP = [];
|
|
2511
|
+
for (let i = 0; i < 256; i++) {
|
|
2512
|
+
Converter._ENCODE_LOOKUP[i] = alphabet[(i >> 4) & 0xf] + alphabet[i & 0xf];
|
|
2513
|
+
if (i < 16) {
|
|
2514
|
+
if (i < 10) {
|
|
2515
|
+
Converter._DECODE_LOOKUP[0x30 + i] = i;
|
|
2516
|
+
}
|
|
2517
|
+
else {
|
|
2518
|
+
Converter._DECODE_LOOKUP[0x61 - 10 + i] = i;
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2106
2523
|
}
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2526
|
+
/**
|
|
2527
|
+
* Helpers methods for JSON objects.
|
|
2528
|
+
*/
|
|
2529
|
+
class JsonHelper {
|
|
2107
2530
|
/**
|
|
2108
|
-
*
|
|
2109
|
-
* @
|
|
2531
|
+
* Runtime name for the class.
|
|
2532
|
+
* @internal
|
|
2110
2533
|
*/
|
|
2111
|
-
static
|
|
2112
|
-
delete I18n._localeChangedHandlers[id];
|
|
2113
|
-
}
|
|
2534
|
+
static _CLASS_NAME = "JsonHelper";
|
|
2114
2535
|
/**
|
|
2115
|
-
*
|
|
2116
|
-
*
|
|
2117
|
-
* @param
|
|
2536
|
+
* Serializes in canonical format.
|
|
2537
|
+
* Based on https://www.rfc-editor.org/rfc/rfc8785.
|
|
2538
|
+
* @param object The object to be serialized.
|
|
2539
|
+
* @returns The serialized object.
|
|
2118
2540
|
*/
|
|
2119
|
-
static
|
|
2120
|
-
|
|
2541
|
+
static canonicalize(object) {
|
|
2542
|
+
const buffer = [];
|
|
2543
|
+
if (object === null ||
|
|
2544
|
+
typeof object !== "object" ||
|
|
2545
|
+
("toJSON" in object && object.toJSON instanceof Function)) {
|
|
2546
|
+
// Primitive data type
|
|
2547
|
+
buffer.push(JSON.stringify(object));
|
|
2548
|
+
}
|
|
2549
|
+
else if (Array.isArray(object)) {
|
|
2550
|
+
// Array maintain element order
|
|
2551
|
+
const parts = [];
|
|
2552
|
+
for (const element of object) {
|
|
2553
|
+
if (element === undefined) {
|
|
2554
|
+
parts.push("null");
|
|
2555
|
+
}
|
|
2556
|
+
else {
|
|
2557
|
+
parts.push(JsonHelper.canonicalize(element));
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
2560
|
+
buffer.push(`[${parts.join(",")}]`);
|
|
2561
|
+
}
|
|
2562
|
+
else {
|
|
2563
|
+
// Object sort properties
|
|
2564
|
+
const props = [];
|
|
2565
|
+
const keys = Object.keys(object).sort();
|
|
2566
|
+
const o = object;
|
|
2567
|
+
for (const key of keys) {
|
|
2568
|
+
if (o[key] !== undefined) {
|
|
2569
|
+
props.push(`${JSON.stringify(key)}:${JsonHelper.canonicalize(o[key])}`);
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
buffer.push(`{${props.join(",")}}`);
|
|
2573
|
+
}
|
|
2574
|
+
return buffer.join("");
|
|
2121
2575
|
}
|
|
2122
2576
|
/**
|
|
2123
|
-
*
|
|
2124
|
-
*
|
|
2577
|
+
* Creates a RFC 6902 diff set.
|
|
2578
|
+
* Based on https://www.rfc-editor.org/rfc/rfc6902.
|
|
2579
|
+
* @param object1 The first object.
|
|
2580
|
+
* @param object2 The second object.
|
|
2581
|
+
* @returns The list of patches.
|
|
2125
2582
|
*/
|
|
2126
|
-
static
|
|
2127
|
-
|
|
2583
|
+
static diff(object1, object2) {
|
|
2584
|
+
const operations = createPatch(object1, object2);
|
|
2585
|
+
return operations;
|
|
2128
2586
|
}
|
|
2129
2587
|
/**
|
|
2130
|
-
*
|
|
2131
|
-
*
|
|
2132
|
-
* @param
|
|
2133
|
-
* @param
|
|
2134
|
-
* @returns The
|
|
2588
|
+
* Applies a RFC 6902 diff set to an object.
|
|
2589
|
+
* Based on https://www.rfc-editor.org/rfc/rfc6902.
|
|
2590
|
+
* @param object The object to patch.
|
|
2591
|
+
* @param patches The second object.
|
|
2592
|
+
* @returns The updated object.
|
|
2593
|
+
* @throws GeneralError if the patch fails.
|
|
2135
2594
|
*/
|
|
2136
|
-
static
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
}
|
|
2144
|
-
if (!I18n._localeDictionaries[cl][key]) {
|
|
2145
|
-
return `!!Missing ${cl}.${key}`;
|
|
2146
|
-
}
|
|
2147
|
-
if (I18n._currentLocale === "debug-k") {
|
|
2148
|
-
return key;
|
|
2149
|
-
}
|
|
2150
|
-
let ret = new IntlMessageFormat(I18n._localeDictionaries[cl][key], cl).format(values);
|
|
2151
|
-
if (I18n._currentLocale === "debug-x") {
|
|
2152
|
-
ret = ret.replace(/[a-z]/g, "x").replace(/[A-Z]/g, "x").replace(/\d/g, "n");
|
|
2595
|
+
static patch(object, patches) {
|
|
2596
|
+
const clone = ObjectHelper.clone(object);
|
|
2597
|
+
const result = applyPatch(clone, patches);
|
|
2598
|
+
for (let i = 0; i < result.length; i++) {
|
|
2599
|
+
if (!Is.empty(result[i])) {
|
|
2600
|
+
throw new GeneralError(JsonHelper._CLASS_NAME, "failedPatch", { index: i }, result[i]);
|
|
2601
|
+
}
|
|
2153
2602
|
}
|
|
2154
|
-
return
|
|
2603
|
+
return clone;
|
|
2155
2604
|
}
|
|
2156
2605
|
/**
|
|
2157
|
-
*
|
|
2158
|
-
* @param
|
|
2159
|
-
* @
|
|
2606
|
+
* Stringify the JSON with support for extended data types date/bigint/uint8array.
|
|
2607
|
+
* @param object The object to stringify.
|
|
2608
|
+
* @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
|
|
2609
|
+
* @returns The stringified object.
|
|
2160
2610
|
*/
|
|
2161
|
-
|
|
2162
|
-
|
|
2611
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2612
|
+
static stringifyEx(object, space) {
|
|
2613
|
+
// We want to keep the 'this' intact for the replacer
|
|
2614
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
2615
|
+
return JSON.stringify(object, JsonHelper.stringifyExReplacer, space);
|
|
2163
2616
|
}
|
|
2164
2617
|
/**
|
|
2165
|
-
*
|
|
2166
|
-
* @param
|
|
2167
|
-
* @
|
|
2168
|
-
* @param mergedKeys The merged keys dictionary to populate.
|
|
2169
|
-
* @internal
|
|
2618
|
+
* Parse the JSON string with support for extended data types date/bigint/uint8array.
|
|
2619
|
+
* @param json The object to pause.
|
|
2620
|
+
* @returns The object.
|
|
2170
2621
|
*/
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2622
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2623
|
+
static parseEx(json) {
|
|
2624
|
+
// We want to keep the 'this' intact for the reviver
|
|
2625
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
2626
|
+
return JSON.parse(json, JsonHelper.parseExReviver);
|
|
2627
|
+
}
|
|
2628
|
+
/**
|
|
2629
|
+
* Replacer function to handle extended data types.
|
|
2630
|
+
* @param this The object.
|
|
2631
|
+
* @param key The key.
|
|
2632
|
+
* @param value The value.
|
|
2633
|
+
* @returns The value.
|
|
2634
|
+
*/
|
|
2635
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2636
|
+
static stringifyExReplacer(key, value) {
|
|
2637
|
+
const rawValue = this[key];
|
|
2638
|
+
if (Is.bigint(rawValue)) {
|
|
2639
|
+
return {
|
|
2640
|
+
"@ext": "bigint",
|
|
2641
|
+
value: rawValue.toString()
|
|
2642
|
+
};
|
|
2643
|
+
}
|
|
2644
|
+
else if (Is.date(rawValue)) {
|
|
2645
|
+
return {
|
|
2646
|
+
"@ext": "date",
|
|
2647
|
+
value: rawValue.getTime()
|
|
2648
|
+
};
|
|
2649
|
+
}
|
|
2650
|
+
else if (Is.uint8Array(rawValue)) {
|
|
2651
|
+
return {
|
|
2652
|
+
"@ext": "uint8array",
|
|
2653
|
+
value: Converter.bytesToBase64(rawValue)
|
|
2654
|
+
};
|
|
2655
|
+
}
|
|
2656
|
+
return value;
|
|
2657
|
+
}
|
|
2658
|
+
/**
|
|
2659
|
+
* Reviver function to handle extended data types.
|
|
2660
|
+
* @param this The object.
|
|
2661
|
+
* @param key The key.
|
|
2662
|
+
* @param value The value.
|
|
2663
|
+
* @returns The value.
|
|
2664
|
+
*/
|
|
2665
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2666
|
+
static parseExReviver(key, value) {
|
|
2667
|
+
if (Is.object(value)) {
|
|
2668
|
+
if (value["@ext"] === "bigint") {
|
|
2669
|
+
return BigInt(value.value);
|
|
2177
2670
|
}
|
|
2178
|
-
else if (
|
|
2179
|
-
|
|
2671
|
+
else if (value["@ext"] === "date") {
|
|
2672
|
+
return new Date(value.value);
|
|
2673
|
+
}
|
|
2674
|
+
else if (value["@ext"] === "uint8array") {
|
|
2675
|
+
return Converter.base64ToBytes(value.value);
|
|
2180
2676
|
}
|
|
2181
2677
|
}
|
|
2678
|
+
return value;
|
|
2182
2679
|
}
|
|
2183
2680
|
}
|
|
2184
2681
|
|
|
2185
|
-
// Copyright 2024 IOTA Stiftung.
|
|
2186
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
2187
2682
|
/**
|
|
2188
|
-
*
|
|
2683
|
+
* Class to help with objects.
|
|
2189
2684
|
*/
|
|
2190
|
-
class
|
|
2685
|
+
class ObjectHelper {
|
|
2191
2686
|
/**
|
|
2192
|
-
*
|
|
2193
|
-
* @
|
|
2194
|
-
* @returns The error formatted including any inner errors.
|
|
2687
|
+
* Runtime name for the class.
|
|
2688
|
+
* @internal
|
|
2195
2689
|
*/
|
|
2196
|
-
static
|
|
2197
|
-
return ErrorHelper.localizeErrors(error).map(e => e.message);
|
|
2198
|
-
}
|
|
2690
|
+
static _CLASS_NAME = "ObjectHelper";
|
|
2199
2691
|
/**
|
|
2200
|
-
*
|
|
2201
|
-
* @param
|
|
2202
|
-
* @
|
|
2692
|
+
* Convert an object to bytes.
|
|
2693
|
+
* @param obj The object to convert.
|
|
2694
|
+
* @param format Format the JSON content.
|
|
2695
|
+
* @returns The object as bytes.
|
|
2203
2696
|
*/
|
|
2204
|
-
static
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
const errors = BaseError.flatten(error);
|
|
2208
|
-
for (const err of errors) {
|
|
2209
|
-
const errorNameKey = `errorNames.${StringHelper.camelCase(err.name)}`;
|
|
2210
|
-
const errorMessageKey = `error.${err.message}`;
|
|
2211
|
-
// If there is no error message then it is probably
|
|
2212
|
-
// from a 3rd party lib, so don't format it just display
|
|
2213
|
-
const hasErrorName = I18n.hasMessage(errorNameKey);
|
|
2214
|
-
const hasErrorMessage = I18n.hasMessage(errorMessageKey);
|
|
2215
|
-
const localizedError = {
|
|
2216
|
-
name: I18n.formatMessage(hasErrorName ? errorNameKey : "errorNames.error"),
|
|
2217
|
-
message: hasErrorMessage
|
|
2218
|
-
? I18n.formatMessage(errorMessageKey, err.properties)
|
|
2219
|
-
: err.message
|
|
2220
|
-
};
|
|
2221
|
-
if (Is.stringValue(err.source)) {
|
|
2222
|
-
localizedError.source = err.source;
|
|
2223
|
-
}
|
|
2224
|
-
if (Is.stringValue(err.stack)) {
|
|
2225
|
-
// Remove the first line from the stack traces as they
|
|
2226
|
-
// just have the error type and message duplicated
|
|
2227
|
-
const lines = err.stack.split("\n");
|
|
2228
|
-
lines.shift();
|
|
2229
|
-
localizedError.stack = lines.join("\n");
|
|
2230
|
-
}
|
|
2231
|
-
const additional = ErrorHelper.formatValidationErrors(err);
|
|
2232
|
-
if (Is.stringValue(additional)) {
|
|
2233
|
-
localizedError.additional = additional;
|
|
2234
|
-
}
|
|
2235
|
-
formattedErrors.push(localizedError);
|
|
2236
|
-
}
|
|
2697
|
+
static toBytes(obj, format = false) {
|
|
2698
|
+
if (obj === undefined) {
|
|
2699
|
+
return new Uint8Array();
|
|
2237
2700
|
}
|
|
2238
|
-
|
|
2701
|
+
const json = format ? JSON.stringify(obj, undefined, "\t") : JSON.stringify(obj);
|
|
2702
|
+
return Converter.utf8ToBytes(json);
|
|
2239
2703
|
}
|
|
2240
2704
|
/**
|
|
2241
|
-
*
|
|
2242
|
-
* @param
|
|
2243
|
-
* @returns The
|
|
2705
|
+
* Convert a bytes to an object.
|
|
2706
|
+
* @param bytes The bytes to convert to an object.
|
|
2707
|
+
* @returns The object.
|
|
2708
|
+
* @throws GeneralError if there was an error parsing the JSON.
|
|
2244
2709
|
*/
|
|
2245
|
-
static
|
|
2246
|
-
if (Is.
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
const
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
: errorI18n;
|
|
2256
|
-
let v = `${validationFailure.property}: ${errorMessage}`;
|
|
2257
|
-
if (Is.object(validationFailure.properties) &&
|
|
2258
|
-
Is.notEmpty(validationFailure.properties.value)) {
|
|
2259
|
-
v += ` = ${JSON.stringify(validationFailure.properties.value)}`;
|
|
2260
|
-
}
|
|
2261
|
-
validationErrors.push(v);
|
|
2262
|
-
}
|
|
2263
|
-
return validationErrors.join("\n");
|
|
2710
|
+
static fromBytes(bytes) {
|
|
2711
|
+
if (Is.empty(bytes) || bytes.length === 0) {
|
|
2712
|
+
return undefined;
|
|
2713
|
+
}
|
|
2714
|
+
try {
|
|
2715
|
+
const utf8 = Converter.bytesToUtf8(bytes);
|
|
2716
|
+
return JSON.parse(utf8);
|
|
2717
|
+
}
|
|
2718
|
+
catch (err) {
|
|
2719
|
+
throw new GeneralError(ObjectHelper._CLASS_NAME, "failedBytesToJSON", undefined, err);
|
|
2264
2720
|
}
|
|
2265
2721
|
}
|
|
2266
|
-
}
|
|
2267
|
-
|
|
2268
|
-
// Copyright 2024 IOTA Stiftung.
|
|
2269
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
2270
|
-
/**
|
|
2271
|
-
* Coerce an object from one type to another.
|
|
2272
|
-
*/
|
|
2273
|
-
class Coerce {
|
|
2274
2722
|
/**
|
|
2275
|
-
*
|
|
2276
|
-
* @param
|
|
2277
|
-
* @
|
|
2278
|
-
* @returns The value if it can be coerced.
|
|
2723
|
+
* Make a deep clone of an object.
|
|
2724
|
+
* @param obj The object to clone.
|
|
2725
|
+
* @returns The objects clone.
|
|
2279
2726
|
*/
|
|
2280
|
-
static
|
|
2281
|
-
if (Is.undefined(
|
|
2282
|
-
return
|
|
2283
|
-
}
|
|
2284
|
-
if (Is.string(value)) {
|
|
2285
|
-
return value;
|
|
2727
|
+
static clone(obj) {
|
|
2728
|
+
if (Is.undefined(obj)) {
|
|
2729
|
+
return undefined;
|
|
2286
2730
|
}
|
|
2287
|
-
|
|
2288
|
-
|
|
2731
|
+
return structuredClone(obj);
|
|
2732
|
+
}
|
|
2733
|
+
/**
|
|
2734
|
+
* Deep merge objects.
|
|
2735
|
+
* @param obj1 The first object to merge.
|
|
2736
|
+
* @param obj2 The second object to merge.
|
|
2737
|
+
* @returns The combined deep merge of the objects.
|
|
2738
|
+
*/
|
|
2739
|
+
static merge(obj1, obj2) {
|
|
2740
|
+
if (Is.empty(obj1)) {
|
|
2741
|
+
return ObjectHelper.clone(obj2);
|
|
2289
2742
|
}
|
|
2290
|
-
if (Is.
|
|
2291
|
-
return
|
|
2743
|
+
if (Is.empty(obj2)) {
|
|
2744
|
+
return ObjectHelper.clone(obj1);
|
|
2292
2745
|
}
|
|
2293
|
-
|
|
2294
|
-
|
|
2746
|
+
const obj1Clone = ObjectHelper.clone(obj1);
|
|
2747
|
+
if (Is.object(obj1Clone) && Is.object(obj2)) {
|
|
2748
|
+
const keys = Object.keys(obj2);
|
|
2749
|
+
for (const key of keys) {
|
|
2750
|
+
if (Is.object(obj1Clone[key]) && Is.object(obj2[key])) {
|
|
2751
|
+
ObjectHelper.propertySet(obj1Clone, key, ObjectHelper.merge(obj1Clone[key], obj2[key]));
|
|
2752
|
+
}
|
|
2753
|
+
else {
|
|
2754
|
+
ObjectHelper.propertySet(obj1Clone, key, obj2[key]);
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2295
2757
|
}
|
|
2758
|
+
return obj1Clone;
|
|
2296
2759
|
}
|
|
2297
2760
|
/**
|
|
2298
|
-
*
|
|
2299
|
-
* @param
|
|
2300
|
-
* @
|
|
2301
|
-
* @
|
|
2761
|
+
* Does one object equal another.
|
|
2762
|
+
* @param obj1 The first object to compare.
|
|
2763
|
+
* @param obj2 The second object to compare.
|
|
2764
|
+
* @param strictPropertyOrder Should the properties be in the same order, defaults to true.
|
|
2765
|
+
* @returns True is the objects are equal.
|
|
2302
2766
|
*/
|
|
2303
|
-
static
|
|
2304
|
-
if (
|
|
2305
|
-
return
|
|
2306
|
-
}
|
|
2307
|
-
if (Is.number(value)) {
|
|
2308
|
-
return value;
|
|
2767
|
+
static equal(obj1, obj2, strictPropertyOrder) {
|
|
2768
|
+
if (strictPropertyOrder ?? true) {
|
|
2769
|
+
return JSON.stringify(obj1) === JSON.stringify(obj2);
|
|
2309
2770
|
}
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2771
|
+
return JsonHelper.canonicalize(obj1) === JsonHelper.canonicalize(obj2);
|
|
2772
|
+
}
|
|
2773
|
+
/**
|
|
2774
|
+
* Get the property of an unknown object.
|
|
2775
|
+
* @param obj The object to get the property from.
|
|
2776
|
+
* @param property The property to get, can be separated by dots for nested path.
|
|
2777
|
+
* @returns The property.
|
|
2778
|
+
*/
|
|
2779
|
+
static propertyGet(obj, property) {
|
|
2780
|
+
const pathParts = property.split(".");
|
|
2781
|
+
let pathValue = obj;
|
|
2782
|
+
for (const pathPart of pathParts) {
|
|
2783
|
+
// Is the path part numeric i.e. an array index.
|
|
2784
|
+
const arrayMatch = /^(\d+)$/.exec(pathPart);
|
|
2785
|
+
if (arrayMatch) {
|
|
2786
|
+
const arrayIndex = Number.parseInt(arrayMatch[1], 10);
|
|
2787
|
+
if (Is.arrayValue(pathValue) && arrayIndex < pathValue.length) {
|
|
2788
|
+
// There is no prop name so this is a direct array index on the current object
|
|
2789
|
+
pathValue = pathValue[arrayIndex];
|
|
2790
|
+
}
|
|
2791
|
+
else {
|
|
2792
|
+
// Array index for non array object so return
|
|
2793
|
+
return undefined;
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
else if (Is.object(pathValue)) {
|
|
2797
|
+
// No array part in path so assume object sub property
|
|
2798
|
+
pathValue = pathValue[pathPart];
|
|
2799
|
+
}
|
|
2800
|
+
else {
|
|
2801
|
+
return undefined;
|
|
2314
2802
|
}
|
|
2315
2803
|
}
|
|
2316
|
-
|
|
2317
|
-
|
|
2804
|
+
return pathValue;
|
|
2805
|
+
}
|
|
2806
|
+
/**
|
|
2807
|
+
* Set the property of an unknown object.
|
|
2808
|
+
* @param obj The object to set the property from.
|
|
2809
|
+
* @param property The property to set.
|
|
2810
|
+
* @param value The value to set.
|
|
2811
|
+
* @throws GeneralError if the property target is not an object.
|
|
2812
|
+
*/
|
|
2813
|
+
static propertySet(obj, property, value) {
|
|
2814
|
+
const pathParts = property.split(".");
|
|
2815
|
+
let pathValue = obj;
|
|
2816
|
+
let parentObj;
|
|
2817
|
+
for (let i = 0; i < pathParts.length; i++) {
|
|
2818
|
+
const pathPart = pathParts[i];
|
|
2819
|
+
// Is the path part numeric i.e. an array index.
|
|
2820
|
+
const arrayMatch = /^(\d+)$/.exec(pathPart);
|
|
2821
|
+
const arrayIndex = arrayMatch ? Number.parseInt(arrayMatch[1], 10) : -1;
|
|
2822
|
+
if (i === pathParts.length - 1) {
|
|
2823
|
+
// Last part of path so set the value
|
|
2824
|
+
if (arrayIndex >= 0) {
|
|
2825
|
+
if (Is.array(pathValue)) {
|
|
2826
|
+
pathValue[arrayIndex] = value;
|
|
2827
|
+
}
|
|
2828
|
+
else if (Is.object(pathValue)) {
|
|
2829
|
+
pathValue[arrayIndex] = value;
|
|
2830
|
+
}
|
|
2831
|
+
else {
|
|
2832
|
+
throw new GeneralError(ObjectHelper._CLASS_NAME, "cannotSetArrayIndex", {
|
|
2833
|
+
property,
|
|
2834
|
+
index: arrayIndex
|
|
2835
|
+
});
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
else if (Is.object(pathValue)) {
|
|
2839
|
+
pathValue[pathPart] = value;
|
|
2840
|
+
}
|
|
2841
|
+
else {
|
|
2842
|
+
throw new GeneralError(ObjectHelper._CLASS_NAME, "cannotSetProperty", { property });
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2845
|
+
else {
|
|
2846
|
+
parentObj = pathValue;
|
|
2847
|
+
if (Is.object(pathValue)) {
|
|
2848
|
+
pathValue = pathValue[pathPart];
|
|
2849
|
+
}
|
|
2850
|
+
else if (Is.array(pathValue)) {
|
|
2851
|
+
pathValue = pathValue[arrayIndex];
|
|
2852
|
+
}
|
|
2853
|
+
if (Is.empty(pathValue)) {
|
|
2854
|
+
const nextArrayMatch = /^(\d+)$/.exec(pathParts[i + 1]);
|
|
2855
|
+
const nextArrayIndex = nextArrayMatch ? Number.parseInt(nextArrayMatch[1], 10) : -1;
|
|
2856
|
+
if (nextArrayIndex >= 0) {
|
|
2857
|
+
pathValue = [];
|
|
2858
|
+
}
|
|
2859
|
+
else {
|
|
2860
|
+
pathValue = {};
|
|
2861
|
+
}
|
|
2862
|
+
if (Is.object(parentObj)) {
|
|
2863
|
+
parentObj[pathPart] = pathValue;
|
|
2864
|
+
}
|
|
2865
|
+
else if (Is.array(parentObj)) {
|
|
2866
|
+
parentObj[arrayIndex] = pathValue;
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2318
2870
|
}
|
|
2319
|
-
|
|
2320
|
-
|
|
2871
|
+
}
|
|
2872
|
+
/**
|
|
2873
|
+
* Delete the property of an unknown object.
|
|
2874
|
+
* @param obj The object to set the property from.
|
|
2875
|
+
* @param property The property to set
|
|
2876
|
+
*/
|
|
2877
|
+
static propertyDelete(obj, property) {
|
|
2878
|
+
if (Is.object(obj)) {
|
|
2879
|
+
delete obj[property];
|
|
2321
2880
|
}
|
|
2322
2881
|
}
|
|
2323
2882
|
/**
|
|
2324
|
-
*
|
|
2325
|
-
* @param
|
|
2326
|
-
* @
|
|
2327
|
-
* @
|
|
2883
|
+
* Extract a property from the object, providing alternative names.
|
|
2884
|
+
* @param obj The object to extract from.
|
|
2885
|
+
* @param propertyNames The possible names for the property.
|
|
2886
|
+
* @param removeProperties Remove the properties from the object, defaults to true.
|
|
2887
|
+
* @returns The property if available.
|
|
2328
2888
|
*/
|
|
2329
|
-
static
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
}
|
|
2339
|
-
if (Is.string(value)) {
|
|
2340
|
-
const parsed = Number.parseFloat(value);
|
|
2341
|
-
if (Is.integer(parsed)) {
|
|
2342
|
-
return BigInt(parsed);
|
|
2889
|
+
static extractProperty(obj, propertyNames, removeProperties = true) {
|
|
2890
|
+
let retVal;
|
|
2891
|
+
if (Is.object(obj)) {
|
|
2892
|
+
const names = Is.string(propertyNames) ? [propertyNames] : propertyNames;
|
|
2893
|
+
for (const prop of names) {
|
|
2894
|
+
retVal ??= ObjectHelper.propertyGet(obj, prop);
|
|
2895
|
+
if (removeProperties) {
|
|
2896
|
+
ObjectHelper.propertyDelete(obj, prop);
|
|
2897
|
+
}
|
|
2343
2898
|
}
|
|
2344
2899
|
}
|
|
2345
|
-
|
|
2346
|
-
return value ? 1n : 0n;
|
|
2347
|
-
}
|
|
2900
|
+
return retVal;
|
|
2348
2901
|
}
|
|
2349
2902
|
/**
|
|
2350
|
-
*
|
|
2351
|
-
* @param
|
|
2352
|
-
* @
|
|
2353
|
-
* @returns The
|
|
2903
|
+
* Pick a subset of properties from an object.
|
|
2904
|
+
* @param obj The object to pick the properties from.
|
|
2905
|
+
* @param keys The property keys to pick.
|
|
2906
|
+
* @returns The partial object.
|
|
2354
2907
|
*/
|
|
2355
|
-
static
|
|
2356
|
-
if (Is.
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
return value;
|
|
2361
|
-
}
|
|
2362
|
-
if (Is.number(value)) {
|
|
2363
|
-
// eslint-disable-next-line no-unneeded-ternary
|
|
2364
|
-
return value ? true : false;
|
|
2365
|
-
}
|
|
2366
|
-
if (Is.string(value)) {
|
|
2367
|
-
if (/true/i.test(value)) {
|
|
2368
|
-
return true;
|
|
2369
|
-
}
|
|
2370
|
-
if (/false/i.test(value)) {
|
|
2371
|
-
return false;
|
|
2908
|
+
static pick(obj, keys) {
|
|
2909
|
+
if (Is.object(obj) && Is.arrayValue(keys)) {
|
|
2910
|
+
const result = {};
|
|
2911
|
+
for (const key of keys) {
|
|
2912
|
+
result[key] = obj[key];
|
|
2372
2913
|
}
|
|
2914
|
+
return result;
|
|
2373
2915
|
}
|
|
2916
|
+
return obj;
|
|
2374
2917
|
}
|
|
2375
2918
|
/**
|
|
2376
|
-
*
|
|
2377
|
-
* @param
|
|
2378
|
-
* @
|
|
2379
|
-
* @returns The
|
|
2919
|
+
* Omit a subset of properties from an object.
|
|
2920
|
+
* @param obj The object to omit the properties from.
|
|
2921
|
+
* @param keys The property keys to omit.
|
|
2922
|
+
* @returns The partial object.
|
|
2380
2923
|
*/
|
|
2381
|
-
static
|
|
2382
|
-
if (Is.
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
return value;
|
|
2387
|
-
}
|
|
2388
|
-
if (Is.number(value)) {
|
|
2389
|
-
return new Date(value);
|
|
2390
|
-
}
|
|
2391
|
-
if (Is.string(value)) {
|
|
2392
|
-
const dt = new Date(value);
|
|
2393
|
-
if (!Number.isNaN(dt.getTime())) {
|
|
2394
|
-
const utc = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate());
|
|
2395
|
-
return new Date(utc);
|
|
2924
|
+
static omit(obj, keys) {
|
|
2925
|
+
if (Is.object(obj) && Is.arrayValue(keys)) {
|
|
2926
|
+
const result = { ...obj };
|
|
2927
|
+
for (const key of keys) {
|
|
2928
|
+
delete result[key];
|
|
2396
2929
|
}
|
|
2930
|
+
return result;
|
|
2397
2931
|
}
|
|
2932
|
+
return obj;
|
|
2398
2933
|
}
|
|
2399
2934
|
/**
|
|
2400
|
-
*
|
|
2401
|
-
* @param
|
|
2402
|
-
* @
|
|
2403
|
-
* @returns The value if it can be coerced.
|
|
2935
|
+
* Converter the non JSON primitives to extended types.
|
|
2936
|
+
* @param obj The object to convert.
|
|
2937
|
+
* @returns The object with extended properties.
|
|
2404
2938
|
*/
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
if (Is.date(value)) {
|
|
2410
|
-
return value;
|
|
2411
|
-
}
|
|
2412
|
-
if (Is.number(value)) {
|
|
2413
|
-
return new Date(value);
|
|
2414
|
-
}
|
|
2415
|
-
if (Is.string(value)) {
|
|
2416
|
-
const dt = new Date(value);
|
|
2417
|
-
if (!Number.isNaN(dt.getTime())) {
|
|
2418
|
-
const utc = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate(), dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
|
|
2419
|
-
return new Date(utc);
|
|
2420
|
-
}
|
|
2421
|
-
}
|
|
2939
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2940
|
+
static toExtended(obj) {
|
|
2941
|
+
const jsonExtended = JsonHelper.stringifyEx(obj);
|
|
2942
|
+
return JSON.parse(jsonExtended);
|
|
2422
2943
|
}
|
|
2423
2944
|
/**
|
|
2424
|
-
*
|
|
2425
|
-
* @param
|
|
2426
|
-
* @
|
|
2427
|
-
* @returns The value if it can be coerced.
|
|
2945
|
+
* Converter the extended types to non JSON primitives.
|
|
2946
|
+
* @param obj The object to convert.
|
|
2947
|
+
* @returns The object with regular properties.
|
|
2428
2948
|
*/
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
if (Is.date(value)) {
|
|
2434
|
-
return value;
|
|
2435
|
-
}
|
|
2436
|
-
if (Is.number(value)) {
|
|
2437
|
-
const dt = new Date(value);
|
|
2438
|
-
dt.setFullYear(1970, 0, 1);
|
|
2439
|
-
return dt;
|
|
2440
|
-
}
|
|
2441
|
-
if (Is.string(value)) {
|
|
2442
|
-
const dt = new Date(value);
|
|
2443
|
-
if (!Number.isNaN(dt.getTime())) {
|
|
2444
|
-
const utc = Date.UTC(1970, 0, 1, dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
|
|
2445
|
-
return new Date(utc);
|
|
2446
|
-
}
|
|
2447
|
-
}
|
|
2949
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2950
|
+
static fromExtended(obj) {
|
|
2951
|
+
const jsonExtended = JsonHelper.stringifyEx(obj);
|
|
2952
|
+
return JsonHelper.parseEx(jsonExtended);
|
|
2448
2953
|
}
|
|
2449
2954
|
/**
|
|
2450
|
-
*
|
|
2451
|
-
* @param
|
|
2452
|
-
* @
|
|
2453
|
-
* @
|
|
2955
|
+
* Remove empty properties from an object.
|
|
2956
|
+
* @param obj The object to remove the empty properties from.
|
|
2957
|
+
* @param options The options for the removal.
|
|
2958
|
+
* @param options.removeUndefined Remove undefined properties, defaults to true.
|
|
2959
|
+
* @param options.removeNull Remove null properties, defaults to false.
|
|
2960
|
+
* @returns The object with empty properties removed.
|
|
2454
2961
|
*/
|
|
2455
|
-
static
|
|
2456
|
-
if (Is.
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2962
|
+
static removeEmptyProperties(obj, options) {
|
|
2963
|
+
if (Is.object(obj)) {
|
|
2964
|
+
const removeUndefined = options?.removeUndefined ?? true;
|
|
2965
|
+
const removeNull = options?.removeNull ?? false;
|
|
2966
|
+
const newObj = {};
|
|
2967
|
+
const keys = Object.keys(obj);
|
|
2968
|
+
for (const key of keys) {
|
|
2969
|
+
if (!((removeUndefined && Is.undefined(obj[key])) || (removeNull && Is.null(obj[key])))) {
|
|
2970
|
+
newObj[key] = ObjectHelper.removeEmptyProperties(obj[key], options);
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
return newObj;
|
|
2461
2974
|
}
|
|
2462
|
-
if (Is.
|
|
2463
|
-
|
|
2464
|
-
|
|
2975
|
+
else if (Is.array(obj)) {
|
|
2976
|
+
const arr = [];
|
|
2977
|
+
for (const element of obj) {
|
|
2978
|
+
arr.push(ObjectHelper.removeEmptyProperties(element, options));
|
|
2465
2979
|
}
|
|
2466
|
-
|
|
2980
|
+
return arr;
|
|
2467
2981
|
}
|
|
2982
|
+
return obj;
|
|
2468
2983
|
}
|
|
2469
2984
|
}
|
|
2470
2985
|
|
|
2471
2986
|
// Copyright 2024 IOTA Stiftung.
|
|
2472
2987
|
// SPDX-License-Identifier: Apache-2.0.
|
|
2473
2988
|
/**
|
|
2474
|
-
*
|
|
2989
|
+
* Environment variable helper.
|
|
2475
2990
|
*/
|
|
2476
|
-
class
|
|
2477
|
-
/**
|
|
2478
|
-
*
|
|
2479
|
-
* @param
|
|
2480
|
-
* @
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2991
|
+
class EnvHelper {
|
|
2992
|
+
/**
|
|
2993
|
+
* Get the environment variable as an object with camel cased names.
|
|
2994
|
+
* @param envVars The environment variables.
|
|
2995
|
+
* @param prefix The prefix of the environment variables, if not provided gets all.
|
|
2996
|
+
* @returns The object with camel cased names.
|
|
2997
|
+
*/
|
|
2998
|
+
static envToJson(envVars, prefix) {
|
|
2999
|
+
const result = {};
|
|
3000
|
+
if (!Is.empty(envVars)) {
|
|
3001
|
+
if (Is.empty(prefix)) {
|
|
3002
|
+
for (const envVar in envVars) {
|
|
3003
|
+
if (Is.stringValue(envVars[envVar])) {
|
|
3004
|
+
const camelCaseName = StringHelper.camelCase(envVar.toLowerCase());
|
|
3005
|
+
ObjectHelper.propertySet(result, camelCaseName, envVars[envVar]);
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
3009
|
+
else {
|
|
3010
|
+
for (const envVar in envVars) {
|
|
3011
|
+
if (envVar.startsWith(prefix) && Is.stringValue(envVars[envVar])) {
|
|
3012
|
+
const camelCaseName = StringHelper.camelCase(envVar.replace(prefix, "").toLowerCase());
|
|
3013
|
+
ObjectHelper.propertySet(result, camelCaseName, envVars[envVar]);
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
2486
3017
|
}
|
|
2487
|
-
|
|
2488
|
-
safe = safe.replace(/["*/:<>?\\|]/g, "_");
|
|
2489
|
-
// Windows non filename characters
|
|
2490
|
-
safe = safe.replace(/^(con|prn|aux|nul|com\d|lpt\d)$/i, "_");
|
|
2491
|
-
// Control characters
|
|
2492
|
-
safe = safe.replace(/[\u0000-\u001F\u0080-\u009F]/g, "_");
|
|
2493
|
-
// Relative paths
|
|
2494
|
-
safe = safe.replace(/^\.+/, "_");
|
|
2495
|
-
// Trailing periods
|
|
2496
|
-
safe = safe.replace(/\.+$/, "");
|
|
2497
|
-
return safe;
|
|
3018
|
+
return result;
|
|
2498
3019
|
}
|
|
2499
3020
|
}
|
|
2500
3021
|
|
|
2501
3022
|
// Copyright 2024 IOTA Stiftung.
|
|
2502
3023
|
// SPDX-License-Identifier: Apache-2.0.
|
|
2503
3024
|
/**
|
|
2504
|
-
*
|
|
3025
|
+
* Class to perform internationalization.
|
|
2505
3026
|
*/
|
|
2506
|
-
class
|
|
3027
|
+
class I18n {
|
|
2507
3028
|
/**
|
|
2508
|
-
*
|
|
2509
|
-
* Based on https://www.rfc-editor.org/rfc/rfc8785.
|
|
2510
|
-
* @param object The object to be serialized.
|
|
2511
|
-
* @returns The serialized object.
|
|
3029
|
+
* The default translation.
|
|
2512
3030
|
*/
|
|
2513
|
-
static
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
const parts = [];
|
|
2524
|
-
for (const element of object) {
|
|
2525
|
-
if (element === undefined) {
|
|
2526
|
-
parts.push("null");
|
|
2527
|
-
}
|
|
2528
|
-
else {
|
|
2529
|
-
parts.push(JsonHelper.canonicalize(element));
|
|
2530
|
-
}
|
|
2531
|
-
}
|
|
2532
|
-
buffer.push(`[${parts.join(",")}]`);
|
|
3031
|
+
static DEFAULT_LOCALE = "en";
|
|
3032
|
+
/**
|
|
3033
|
+
* Set the locale.
|
|
3034
|
+
* @param locale The new locale.
|
|
3035
|
+
*/
|
|
3036
|
+
static setLocale(locale) {
|
|
3037
|
+
const i18nShared = I18n.getI18nShared();
|
|
3038
|
+
i18nShared.currentLocale = locale;
|
|
3039
|
+
for (const callback in i18nShared.localeChangedHandlers) {
|
|
3040
|
+
i18nShared.localeChangedHandlers[callback](i18nShared.currentLocale);
|
|
2533
3041
|
}
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
3042
|
+
}
|
|
3043
|
+
/**
|
|
3044
|
+
* Get the locale.
|
|
3045
|
+
* @returns The current locale.
|
|
3046
|
+
*/
|
|
3047
|
+
static getLocale() {
|
|
3048
|
+
const i18nShared = I18n.getI18nShared();
|
|
3049
|
+
return i18nShared.currentLocale;
|
|
3050
|
+
}
|
|
3051
|
+
/**
|
|
3052
|
+
* Add a locale dictionary.
|
|
3053
|
+
* @param locale The locale.
|
|
3054
|
+
* @param dictionary The dictionary to add.
|
|
3055
|
+
*/
|
|
3056
|
+
static addDictionary(locale, dictionary) {
|
|
3057
|
+
const i18nShared = I18n.getI18nShared();
|
|
3058
|
+
const mergedKeys = {};
|
|
3059
|
+
I18n.flattenTranslationKeys(dictionary, "", mergedKeys);
|
|
3060
|
+
i18nShared.localeDictionaries[locale] = mergedKeys;
|
|
3061
|
+
for (const callback in i18nShared.dictionaryChangedHandlers) {
|
|
3062
|
+
i18nShared.dictionaryChangedHandlers[callback](i18nShared.currentLocale);
|
|
2545
3063
|
}
|
|
2546
|
-
return buffer.join("");
|
|
2547
3064
|
}
|
|
2548
3065
|
/**
|
|
2549
|
-
*
|
|
2550
|
-
*
|
|
2551
|
-
* @
|
|
2552
|
-
* @param object2 The second object.
|
|
2553
|
-
* @returns The list of patches.
|
|
3066
|
+
* Get a locale dictionary.
|
|
3067
|
+
* @param locale The locale.
|
|
3068
|
+
* @returns The dictionary of undefined if it does not exist.
|
|
2554
3069
|
*/
|
|
2555
|
-
static
|
|
2556
|
-
const
|
|
2557
|
-
return
|
|
3070
|
+
static getDictionary(locale) {
|
|
3071
|
+
const i18nShared = I18n.getI18nShared();
|
|
3072
|
+
return i18nShared.localeDictionaries[locale];
|
|
2558
3073
|
}
|
|
2559
3074
|
/**
|
|
2560
|
-
*
|
|
2561
|
-
*
|
|
2562
|
-
* @param object The object to patch.
|
|
2563
|
-
* @param patches The second object.
|
|
2564
|
-
* @returns The updated object.
|
|
3075
|
+
* Get all the locale dictionaries.
|
|
3076
|
+
* @returns The dictionaries.
|
|
2565
3077
|
*/
|
|
2566
|
-
static
|
|
2567
|
-
|
|
3078
|
+
static getAllDictionaries() {
|
|
3079
|
+
const i18nShared = I18n.getI18nShared();
|
|
3080
|
+
return i18nShared.localeDictionaries;
|
|
2568
3081
|
}
|
|
2569
|
-
}
|
|
2570
|
-
|
|
2571
|
-
// Copyright 2024 IOTA Stiftung.
|
|
2572
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
2573
|
-
/* eslint-disable no-bitwise */
|
|
2574
|
-
/**
|
|
2575
|
-
* Convert arrays to and from different formats.
|
|
2576
|
-
*/
|
|
2577
|
-
class Converter {
|
|
2578
3082
|
/**
|
|
2579
|
-
*
|
|
2580
|
-
* @
|
|
3083
|
+
* Add a locale changed handler.
|
|
3084
|
+
* @param id The id of the handler.
|
|
3085
|
+
* @param handler The handler to add.
|
|
2581
3086
|
*/
|
|
2582
|
-
static
|
|
3087
|
+
static addLocaleHandler(id, handler) {
|
|
3088
|
+
const i18nShared = I18n.getI18nShared();
|
|
3089
|
+
i18nShared.localeChangedHandlers[id] = handler;
|
|
3090
|
+
}
|
|
2583
3091
|
/**
|
|
2584
|
-
*
|
|
2585
|
-
* @
|
|
3092
|
+
* Remove a locale changed handler.
|
|
3093
|
+
* @param id The id of the handler.
|
|
2586
3094
|
*/
|
|
2587
|
-
static
|
|
3095
|
+
static removeLocaleHandler(id) {
|
|
3096
|
+
const i18nShared = I18n.getI18nShared();
|
|
3097
|
+
delete i18nShared.localeChangedHandlers[id];
|
|
3098
|
+
}
|
|
2588
3099
|
/**
|
|
2589
|
-
*
|
|
2590
|
-
* @param
|
|
2591
|
-
* @param
|
|
2592
|
-
* @param length The length of bytes to read.
|
|
2593
|
-
* @returns The array formatted as UTF8.
|
|
3100
|
+
* Add a dictionary changed handler.
|
|
3101
|
+
* @param id The id of the handler.
|
|
3102
|
+
* @param handler The handler to add.
|
|
2594
3103
|
*/
|
|
2595
|
-
static
|
|
2596
|
-
const
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
str += String.fromCharCode((charCode >> 10) | 0xd800, (charCode & 0x03ff) | 0xdc00);
|
|
2620
|
-
i += 3;
|
|
2621
|
-
}
|
|
3104
|
+
static addDictionaryHandler(id, handler) {
|
|
3105
|
+
const i18nShared = I18n.getI18nShared();
|
|
3106
|
+
i18nShared.dictionaryChangedHandlers[id] = handler;
|
|
3107
|
+
}
|
|
3108
|
+
/**
|
|
3109
|
+
* Remove a dictionary changed handler.
|
|
3110
|
+
* @param id The id of the handler.
|
|
3111
|
+
*/
|
|
3112
|
+
static removeDictionaryHandler(id) {
|
|
3113
|
+
const i18nShared = I18n.getI18nShared();
|
|
3114
|
+
delete i18nShared.dictionaryChangedHandlers[id];
|
|
3115
|
+
}
|
|
3116
|
+
/**
|
|
3117
|
+
* Format a message.
|
|
3118
|
+
* @param key The key of the message to format.
|
|
3119
|
+
* @param values The values to substitute into the message.
|
|
3120
|
+
* @param overrideLocale Override the locale.
|
|
3121
|
+
* @returns The formatted string.
|
|
3122
|
+
*/
|
|
3123
|
+
static formatMessage(key, values, overrideLocale) {
|
|
3124
|
+
const i18nShared = I18n.getI18nShared();
|
|
3125
|
+
let cl = overrideLocale ?? i18nShared.currentLocale;
|
|
3126
|
+
if (cl.startsWith("debug-")) {
|
|
3127
|
+
cl = I18n.DEFAULT_LOCALE;
|
|
2622
3128
|
}
|
|
2623
|
-
|
|
3129
|
+
if (!i18nShared.localeDictionaries[cl]) {
|
|
3130
|
+
return `!!Missing ${cl}`;
|
|
3131
|
+
}
|
|
3132
|
+
if (!i18nShared.localeDictionaries[cl][key]) {
|
|
3133
|
+
return `!!Missing ${cl}.${key}`;
|
|
3134
|
+
}
|
|
3135
|
+
if (i18nShared.currentLocale === "debug-k") {
|
|
3136
|
+
return key;
|
|
3137
|
+
}
|
|
3138
|
+
let ret = new IntlMessageFormat(i18nShared.localeDictionaries[cl][key], cl).format(values);
|
|
3139
|
+
if (i18nShared.currentLocale === "debug-x") {
|
|
3140
|
+
ret = ret.replace(/[a-z]/g, "x").replace(/[A-Z]/g, "x").replace(/\d/g, "n");
|
|
3141
|
+
}
|
|
3142
|
+
return ret;
|
|
2624
3143
|
}
|
|
2625
3144
|
/**
|
|
2626
|
-
*
|
|
2627
|
-
* @param
|
|
2628
|
-
* @returns
|
|
3145
|
+
* Check if the dictionaries have a message for the given key.
|
|
3146
|
+
* @param key The key to check for existence.
|
|
3147
|
+
* @returns True if the key exists.
|
|
2629
3148
|
*/
|
|
2630
|
-
static
|
|
2631
|
-
const
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
3149
|
+
static hasMessage(key) {
|
|
3150
|
+
const i18nShared = I18n.getI18nShared();
|
|
3151
|
+
return Is.string(i18nShared.localeDictionaries[i18nShared.currentLocale]?.[key]);
|
|
3152
|
+
}
|
|
3153
|
+
/**
|
|
3154
|
+
* Flatten the translation property paths for faster lookup.
|
|
3155
|
+
* @param translation The translation to merge.
|
|
3156
|
+
* @param propertyPath The current root path.
|
|
3157
|
+
* @param mergedKeys The merged keys dictionary to populate.
|
|
3158
|
+
* @internal
|
|
3159
|
+
*/
|
|
3160
|
+
static flattenTranslationKeys(translation, propertyPath, mergedKeys) {
|
|
3161
|
+
for (const key in translation) {
|
|
3162
|
+
const val = translation[key];
|
|
3163
|
+
const mergedPath = propertyPath.length > 0 ? `${propertyPath}.${key}` : key;
|
|
3164
|
+
if (Is.string(val)) {
|
|
3165
|
+
mergedKeys[mergedPath] = val;
|
|
2642
3166
|
}
|
|
2643
|
-
else {
|
|
2644
|
-
|
|
2645
|
-
i++;
|
|
2646
|
-
// UTF-16 encodes 0x10000-0x10FFFF by
|
|
2647
|
-
// subtracting 0x10000 and splitting the
|
|
2648
|
-
// 20 bits of 0x0-0xFFFFF into two halves
|
|
2649
|
-
charCode = 0x10000 + (((charCode & 0x3ff) << 10) | (utf8.charCodeAt(i) & 0x3ff));
|
|
2650
|
-
bytes.push(0xf0 | (charCode >> 18), 0x80 | ((charCode >> 12) & 0x3f), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f));
|
|
3167
|
+
else if (Is.object(val)) {
|
|
3168
|
+
I18n.flattenTranslationKeys(val, mergedPath, mergedKeys);
|
|
2651
3169
|
}
|
|
2652
3170
|
}
|
|
2653
|
-
return Uint8Array.from(bytes);
|
|
2654
3171
|
}
|
|
2655
3172
|
/**
|
|
2656
|
-
*
|
|
2657
|
-
* @
|
|
2658
|
-
* @
|
|
2659
|
-
* @param startIndex The index to start in the bytes.
|
|
2660
|
-
* @param length The length of bytes to read.
|
|
2661
|
-
* @param reverse Reverse the combine direction.
|
|
2662
|
-
* @returns The array formatted as hex.
|
|
3173
|
+
* Get the I18n shared data.
|
|
3174
|
+
* @returns The I18n shared data.
|
|
3175
|
+
* @internal
|
|
2663
3176
|
*/
|
|
2664
|
-
static
|
|
2665
|
-
let
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
3177
|
+
static getI18nShared() {
|
|
3178
|
+
let i18nShared = SharedStore.get("i18n");
|
|
3179
|
+
if (Is.undefined(i18nShared)) {
|
|
3180
|
+
i18nShared = {
|
|
3181
|
+
localeDictionaries: {},
|
|
3182
|
+
currentLocale: I18n.DEFAULT_LOCALE,
|
|
3183
|
+
localeChangedHandlers: {},
|
|
3184
|
+
dictionaryChangedHandlers: {}
|
|
3185
|
+
};
|
|
3186
|
+
SharedStore.set("i18n", i18nShared);
|
|
3187
|
+
}
|
|
3188
|
+
return i18nShared;
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
|
|
3192
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3193
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3194
|
+
/**
|
|
3195
|
+
* Error helper functions.
|
|
3196
|
+
*/
|
|
3197
|
+
class ErrorHelper {
|
|
3198
|
+
/**
|
|
3199
|
+
* Format Errors and returns just their messages.
|
|
3200
|
+
* @param error The error to format.
|
|
3201
|
+
* @param includeDetails Whether to include error details, defaults to false.
|
|
3202
|
+
* @returns The error formatted including any causes errors.
|
|
3203
|
+
*/
|
|
3204
|
+
static formatErrors(error, includeDetails) {
|
|
3205
|
+
const localizedErrors = ErrorHelper.localizeErrors(error);
|
|
3206
|
+
if (includeDetails ?? false) {
|
|
3207
|
+
const output = [];
|
|
3208
|
+
for (const err of localizedErrors) {
|
|
3209
|
+
let detailedError = err.message;
|
|
3210
|
+
if (Is.stringValue(err.stack)) {
|
|
3211
|
+
detailedError += `\n${err.stack}`;
|
|
2673
3212
|
}
|
|
3213
|
+
output.push(detailedError);
|
|
2674
3214
|
}
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
3215
|
+
return output;
|
|
3216
|
+
}
|
|
3217
|
+
return localizedErrors.map(e => e.message);
|
|
3218
|
+
}
|
|
3219
|
+
/**
|
|
3220
|
+
* Localize the content of an error and any causes.
|
|
3221
|
+
* @param error The error to format.
|
|
3222
|
+
* @returns The localized version of the errors flattened.
|
|
3223
|
+
*/
|
|
3224
|
+
static localizeErrors(error) {
|
|
3225
|
+
const formattedErrors = [];
|
|
3226
|
+
if (Is.notEmpty(error)) {
|
|
3227
|
+
const errors = BaseError.flatten(error);
|
|
3228
|
+
for (const err of errors) {
|
|
3229
|
+
const errorNameKey = `errorNames.${StringHelper.camelCase(err.name)}`;
|
|
3230
|
+
const errorMessageKey = `error.${err.message}`;
|
|
3231
|
+
// If there is no error message then it is probably
|
|
3232
|
+
// from a 3rd party lib, so don't format it just display
|
|
3233
|
+
const hasErrorName = I18n.hasMessage(errorNameKey);
|
|
3234
|
+
const hasErrorMessage = I18n.hasMessage(errorMessageKey);
|
|
3235
|
+
const localizedError = {
|
|
3236
|
+
name: I18n.formatMessage(hasErrorName ? errorNameKey : "errorNames.error"),
|
|
3237
|
+
message: hasErrorMessage
|
|
3238
|
+
? I18n.formatMessage(errorMessageKey, err.properties)
|
|
3239
|
+
: err.message
|
|
3240
|
+
};
|
|
3241
|
+
if (Is.stringValue(err.source)) {
|
|
3242
|
+
localizedError.source = err.source;
|
|
3243
|
+
}
|
|
3244
|
+
if (Is.stringValue(err.stack)) {
|
|
3245
|
+
// Remove the first line from the stack traces as they
|
|
3246
|
+
// just have the error type and message duplicated
|
|
3247
|
+
const lines = err.stack.split("\n");
|
|
3248
|
+
lines.shift();
|
|
3249
|
+
localizedError.stack = lines.join("\n");
|
|
3250
|
+
}
|
|
3251
|
+
const additional = ErrorHelper.formatValidationErrors(err);
|
|
3252
|
+
if (Is.stringValue(additional)) {
|
|
3253
|
+
localizedError.additional = additional;
|
|
2678
3254
|
}
|
|
3255
|
+
formattedErrors.push(localizedError);
|
|
2679
3256
|
}
|
|
2680
3257
|
}
|
|
2681
|
-
return
|
|
3258
|
+
return formattedErrors;
|
|
2682
3259
|
}
|
|
2683
3260
|
/**
|
|
2684
|
-
*
|
|
2685
|
-
* @param
|
|
2686
|
-
* @
|
|
2687
|
-
* @returns The array.
|
|
3261
|
+
* Localize the content of an error and any causes.
|
|
3262
|
+
* @param error The error to format.
|
|
3263
|
+
* @returns The localized version of the errors flattened.
|
|
2688
3264
|
*/
|
|
2689
|
-
static
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
3265
|
+
static formatValidationErrors(error) {
|
|
3266
|
+
if (Is.object(error.properties) &&
|
|
3267
|
+
Object.keys(error.properties).length > 0 &&
|
|
3268
|
+
Is.object(error.properties) &&
|
|
3269
|
+
Is.arrayValue(error.properties.validationFailures)) {
|
|
3270
|
+
const validationErrors = [];
|
|
3271
|
+
for (const validationFailure of error.properties.validationFailures) {
|
|
3272
|
+
const errorI18n = `error.${validationFailure.reason}`;
|
|
3273
|
+
const errorMessage = I18n.hasMessage(errorI18n)
|
|
3274
|
+
? I18n.formatMessage(errorI18n, validationFailure.properties)
|
|
3275
|
+
: errorI18n;
|
|
3276
|
+
let v = `${validationFailure.property}: ${errorMessage}`;
|
|
3277
|
+
if (Is.object(validationFailure.properties) &&
|
|
3278
|
+
Is.notEmpty(validationFailure.properties.value)) {
|
|
3279
|
+
v += ` = ${JSON.stringify(validationFailure.properties.value)}`;
|
|
3280
|
+
}
|
|
3281
|
+
validationErrors.push(v);
|
|
2705
3282
|
}
|
|
3283
|
+
return validationErrors.join("\n");
|
|
2706
3284
|
}
|
|
2707
|
-
return array;
|
|
2708
|
-
}
|
|
2709
|
-
/**
|
|
2710
|
-
* Convert the UTF8 to hex.
|
|
2711
|
-
* @param utf8 The text to convert.
|
|
2712
|
-
* @param includePrefix Include the 0x prefix on the returned hex.
|
|
2713
|
-
* @returns The hex version of the bytes.
|
|
2714
|
-
*/
|
|
2715
|
-
static utf8ToHex(utf8, includePrefix = false) {
|
|
2716
|
-
const hex = Converter.bytesToHex(Converter.utf8ToBytes(utf8));
|
|
2717
|
-
return includePrefix ? HexHelper.addPrefix(hex) : hex;
|
|
2718
3285
|
}
|
|
3286
|
+
}
|
|
3287
|
+
|
|
3288
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3289
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3290
|
+
/**
|
|
3291
|
+
* The types the extracted data can be coerced to.
|
|
3292
|
+
*/
|
|
3293
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
3294
|
+
const CoerceType = {
|
|
2719
3295
|
/**
|
|
2720
|
-
*
|
|
2721
|
-
* @param hex The hex to convert.
|
|
2722
|
-
* @returns The UTF8 version of the bytes.
|
|
3296
|
+
* String.
|
|
2723
3297
|
*/
|
|
2724
|
-
|
|
2725
|
-
return Converter.bytesToUtf8(Converter.hexToBytes(HexHelper.stripPrefix(hex)));
|
|
2726
|
-
}
|
|
3298
|
+
String: "string",
|
|
2727
3299
|
/**
|
|
2728
|
-
*
|
|
2729
|
-
* @param bytes The bytes to convert.
|
|
2730
|
-
* @returns A binary string of the bytes.
|
|
3300
|
+
* Number.
|
|
2731
3301
|
*/
|
|
2732
|
-
|
|
2733
|
-
const b = [];
|
|
2734
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
2735
|
-
b.push(bytes[i].toString(2).padStart(8, "0"));
|
|
2736
|
-
}
|
|
2737
|
-
return b.join("");
|
|
2738
|
-
}
|
|
3302
|
+
Number: "number",
|
|
2739
3303
|
/**
|
|
2740
|
-
*
|
|
2741
|
-
* @param binary The binary string.
|
|
2742
|
-
* @returns The bytes.
|
|
3304
|
+
* Integer.
|
|
2743
3305
|
*/
|
|
2744
|
-
|
|
2745
|
-
const bytes = new Uint8Array(Math.ceil(binary.length / 8));
|
|
2746
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
2747
|
-
bytes[i] = Number.parseInt(binary.slice(i * 8, (i + 1) * 8), 2);
|
|
2748
|
-
}
|
|
2749
|
-
return bytes;
|
|
2750
|
-
}
|
|
3306
|
+
Integer: "integer",
|
|
2751
3307
|
/**
|
|
2752
|
-
*
|
|
2753
|
-
* @param bytes The bytes to convert.
|
|
2754
|
-
* @returns A base64 string of the bytes.
|
|
3308
|
+
* Boolean.
|
|
2755
3309
|
*/
|
|
2756
|
-
|
|
2757
|
-
return Base64.encode(bytes);
|
|
2758
|
-
}
|
|
3310
|
+
Boolean: "boolean",
|
|
2759
3311
|
/**
|
|
2760
|
-
*
|
|
2761
|
-
* @param base64 The base64 string.
|
|
2762
|
-
* @returns The bytes.
|
|
3312
|
+
* Big Integer.
|
|
2763
3313
|
*/
|
|
2764
|
-
|
|
2765
|
-
return Base64.decode(base64);
|
|
2766
|
-
}
|
|
3314
|
+
BigInt: "bigint",
|
|
2767
3315
|
/**
|
|
2768
|
-
*
|
|
2769
|
-
* @param bytes The bytes to convert.
|
|
2770
|
-
* @returns A base64 url string of the bytes.
|
|
3316
|
+
* Date.
|
|
2771
3317
|
*/
|
|
2772
|
-
|
|
2773
|
-
return Base64Url.encode(bytes);
|
|
2774
|
-
}
|
|
3318
|
+
Date: "date",
|
|
2775
3319
|
/**
|
|
2776
|
-
*
|
|
2777
|
-
* @param base64Url The base64 url string.
|
|
2778
|
-
* @returns The bytes.
|
|
3320
|
+
* Date Time.
|
|
2779
3321
|
*/
|
|
2780
|
-
|
|
2781
|
-
return Base64Url.decode(base64Url);
|
|
2782
|
-
}
|
|
3322
|
+
DateTime: "datetime",
|
|
2783
3323
|
/**
|
|
2784
|
-
*
|
|
2785
|
-
* @param bytes The bytes to convert.
|
|
2786
|
-
* @returns A base58 string of the bytes.
|
|
3324
|
+
* Time.
|
|
2787
3325
|
*/
|
|
2788
|
-
|
|
2789
|
-
return Base58.encode(bytes);
|
|
2790
|
-
}
|
|
3326
|
+
Time: "time",
|
|
2791
3327
|
/**
|
|
2792
|
-
*
|
|
2793
|
-
* @param base58 The base58 string.
|
|
2794
|
-
* @returns The bytes.
|
|
3328
|
+
* Object.
|
|
2795
3329
|
*/
|
|
2796
|
-
|
|
2797
|
-
return Base58.decode(base58);
|
|
2798
|
-
}
|
|
3330
|
+
Object: "object",
|
|
2799
3331
|
/**
|
|
2800
|
-
*
|
|
2801
|
-
* @internal
|
|
3332
|
+
* Uint8Array.
|
|
2802
3333
|
*/
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
const alphabet = "0123456789abcdef";
|
|
2806
|
-
Converter._ENCODE_LOOKUP = [];
|
|
2807
|
-
Converter._DECODE_LOOKUP = [];
|
|
2808
|
-
for (let i = 0; i < 256; i++) {
|
|
2809
|
-
Converter._ENCODE_LOOKUP[i] = alphabet[(i >> 4) & 0xf] + alphabet[i & 0xf];
|
|
2810
|
-
if (i < 16) {
|
|
2811
|
-
if (i < 10) {
|
|
2812
|
-
Converter._DECODE_LOOKUP[0x30 + i] = i;
|
|
2813
|
-
}
|
|
2814
|
-
else {
|
|
2815
|
-
Converter._DECODE_LOOKUP[0x61 - 10 + i] = i;
|
|
2816
|
-
}
|
|
2817
|
-
}
|
|
2818
|
-
}
|
|
2819
|
-
}
|
|
2820
|
-
}
|
|
2821
|
-
}
|
|
3334
|
+
Uint8Array: "uint8array"
|
|
3335
|
+
};
|
|
2822
3336
|
|
|
3337
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3338
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
2823
3339
|
/**
|
|
2824
|
-
*
|
|
3340
|
+
* Coerce an object from one type to another.
|
|
2825
3341
|
*/
|
|
2826
|
-
class
|
|
2827
|
-
/**
|
|
2828
|
-
* Runtime name for the class.
|
|
2829
|
-
* @internal
|
|
2830
|
-
*/
|
|
2831
|
-
static _CLASS_NAME = "ObjectHelper";
|
|
3342
|
+
class Coerce {
|
|
2832
3343
|
/**
|
|
2833
|
-
*
|
|
2834
|
-
* @param
|
|
2835
|
-
* @
|
|
2836
|
-
* @returns The
|
|
3344
|
+
* Coerce the value to a string.
|
|
3345
|
+
* @param value The value to coerce.
|
|
3346
|
+
* @throws TypeError If the value can not be coerced.
|
|
3347
|
+
* @returns The value if it can be coerced.
|
|
2837
3348
|
*/
|
|
2838
|
-
static
|
|
2839
|
-
if (
|
|
2840
|
-
return
|
|
3349
|
+
static string(value) {
|
|
3350
|
+
if (Is.undefined(value)) {
|
|
3351
|
+
return value;
|
|
3352
|
+
}
|
|
3353
|
+
if (Is.string(value)) {
|
|
3354
|
+
return value;
|
|
3355
|
+
}
|
|
3356
|
+
if (Is.number(value)) {
|
|
3357
|
+
return value.toString();
|
|
3358
|
+
}
|
|
3359
|
+
if (Is.boolean(value)) {
|
|
3360
|
+
return value ? "true" : "false";
|
|
3361
|
+
}
|
|
3362
|
+
if (Is.date(value)) {
|
|
3363
|
+
return value.toISOString();
|
|
2841
3364
|
}
|
|
2842
|
-
const json = format ? JSON.stringify(obj, undefined, "\t") : JSON.stringify(obj);
|
|
2843
|
-
return Converter.utf8ToBytes(json);
|
|
2844
3365
|
}
|
|
2845
3366
|
/**
|
|
2846
|
-
*
|
|
2847
|
-
* @param
|
|
2848
|
-
* @
|
|
2849
|
-
* @
|
|
3367
|
+
* Coerce the value to a number.
|
|
3368
|
+
* @param value The value to coerce.
|
|
3369
|
+
* @throws TypeError If the value can not be coerced.
|
|
3370
|
+
* @returns The value if it can be coerced.
|
|
2850
3371
|
*/
|
|
2851
|
-
static
|
|
2852
|
-
if (Is.
|
|
2853
|
-
return
|
|
3372
|
+
static number(value) {
|
|
3373
|
+
if (Is.undefined(value)) {
|
|
3374
|
+
return value;
|
|
2854
3375
|
}
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
return JSON.parse(utf8);
|
|
3376
|
+
if (Is.number(value)) {
|
|
3377
|
+
return value;
|
|
2858
3378
|
}
|
|
2859
|
-
|
|
2860
|
-
|
|
3379
|
+
if (Is.string(value)) {
|
|
3380
|
+
const parsed = Number.parseFloat(value);
|
|
3381
|
+
if (Is.number(parsed)) {
|
|
3382
|
+
return parsed;
|
|
3383
|
+
}
|
|
3384
|
+
}
|
|
3385
|
+
if (Is.boolean(value)) {
|
|
3386
|
+
return value ? 1 : 0;
|
|
3387
|
+
}
|
|
3388
|
+
if (Is.date(value)) {
|
|
3389
|
+
return value.getTime();
|
|
2861
3390
|
}
|
|
2862
3391
|
}
|
|
2863
3392
|
/**
|
|
2864
|
-
*
|
|
2865
|
-
* @param
|
|
2866
|
-
* @
|
|
3393
|
+
* Coerce the value to an integer.
|
|
3394
|
+
* @param value The value to coerce.
|
|
3395
|
+
* @throws TypeError If the value can not be coerced.
|
|
3396
|
+
* @returns The value if it can be coerced.
|
|
2867
3397
|
*/
|
|
2868
|
-
static
|
|
2869
|
-
|
|
2870
|
-
|
|
3398
|
+
static integer(value) {
|
|
3399
|
+
const num = Coerce.number(value);
|
|
3400
|
+
if (!Is.undefined(num)) {
|
|
3401
|
+
return Math.trunc(num);
|
|
2871
3402
|
}
|
|
2872
|
-
return structuredClone(obj);
|
|
2873
3403
|
}
|
|
2874
3404
|
/**
|
|
2875
|
-
*
|
|
2876
|
-
* @param
|
|
2877
|
-
* @
|
|
2878
|
-
* @returns The
|
|
3405
|
+
* Coerce the value to a bigint.
|
|
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.
|
|
2879
3409
|
*/
|
|
2880
|
-
static
|
|
2881
|
-
if (Is.
|
|
2882
|
-
return
|
|
3410
|
+
static bigint(value) {
|
|
3411
|
+
if (Is.undefined(value)) {
|
|
3412
|
+
return value;
|
|
2883
3413
|
}
|
|
2884
|
-
if (Is.
|
|
2885
|
-
return
|
|
3414
|
+
if (Is.bigint(value)) {
|
|
3415
|
+
return value;
|
|
2886
3416
|
}
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
3417
|
+
if (Is.number(value)) {
|
|
3418
|
+
return BigInt(value);
|
|
3419
|
+
}
|
|
3420
|
+
if (Is.string(value)) {
|
|
3421
|
+
const parsed = Number.parseFloat(value);
|
|
3422
|
+
if (Is.integer(parsed)) {
|
|
3423
|
+
return BigInt(parsed);
|
|
3424
|
+
}
|
|
3425
|
+
}
|
|
3426
|
+
if (Is.boolean(value)) {
|
|
3427
|
+
return value ? 1n : 0n;
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
/**
|
|
3431
|
+
* Coerce the value to a boolean.
|
|
3432
|
+
* @param value The value to coerce.
|
|
3433
|
+
* @throws TypeError If the value can not be coerced.
|
|
3434
|
+
* @returns The value if it can be coerced.
|
|
3435
|
+
*/
|
|
3436
|
+
static boolean(value) {
|
|
3437
|
+
if (Is.undefined(value)) {
|
|
3438
|
+
return value;
|
|
3439
|
+
}
|
|
3440
|
+
if (Is.boolean(value)) {
|
|
3441
|
+
return value;
|
|
3442
|
+
}
|
|
3443
|
+
if (Is.number(value)) {
|
|
3444
|
+
// eslint-disable-next-line no-unneeded-ternary
|
|
3445
|
+
return value ? true : false;
|
|
3446
|
+
}
|
|
3447
|
+
if (Is.string(value)) {
|
|
3448
|
+
if (/true/i.test(value)) {
|
|
3449
|
+
return true;
|
|
3450
|
+
}
|
|
3451
|
+
if (/false/i.test(value)) {
|
|
3452
|
+
return false;
|
|
2897
3453
|
}
|
|
2898
3454
|
}
|
|
2899
|
-
return obj1Clone;
|
|
2900
3455
|
}
|
|
2901
3456
|
/**
|
|
2902
|
-
*
|
|
2903
|
-
* @param
|
|
2904
|
-
* @
|
|
2905
|
-
* @
|
|
2906
|
-
* @returns True is the objects are equal.
|
|
3457
|
+
* Coerce the value to a date.
|
|
3458
|
+
* @param value The value to coerce.
|
|
3459
|
+
* @throws TypeError If the value can not be coerced.
|
|
3460
|
+
* @returns The value if it can be coerced.
|
|
2907
3461
|
*/
|
|
2908
|
-
static
|
|
2909
|
-
if (
|
|
2910
|
-
return
|
|
3462
|
+
static date(value) {
|
|
3463
|
+
if (Is.undefined(value)) {
|
|
3464
|
+
return value;
|
|
3465
|
+
}
|
|
3466
|
+
if (Is.date(value)) {
|
|
3467
|
+
return value;
|
|
3468
|
+
}
|
|
3469
|
+
if (Is.number(value)) {
|
|
3470
|
+
return new Date(value);
|
|
3471
|
+
}
|
|
3472
|
+
if (Is.string(value)) {
|
|
3473
|
+
const dt = new Date(value);
|
|
3474
|
+
if (!Number.isNaN(dt.getTime())) {
|
|
3475
|
+
const utc = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate());
|
|
3476
|
+
return new Date(utc);
|
|
3477
|
+
}
|
|
2911
3478
|
}
|
|
2912
|
-
return JsonHelper.canonicalize(obj1) === JsonHelper.canonicalize(obj2);
|
|
2913
3479
|
}
|
|
2914
3480
|
/**
|
|
2915
|
-
*
|
|
2916
|
-
* @param
|
|
2917
|
-
* @
|
|
2918
|
-
* @returns The
|
|
3481
|
+
* Coerce the value to a date/time.
|
|
3482
|
+
* @param value The value to coerce.
|
|
3483
|
+
* @throws TypeError If the value can not be coerced.
|
|
3484
|
+
* @returns The value if it can be coerced.
|
|
2919
3485
|
*/
|
|
2920
|
-
static
|
|
2921
|
-
if (
|
|
2922
|
-
const parts = property.split(".");
|
|
2923
|
-
let value = obj;
|
|
2924
|
-
for (const part of parts) {
|
|
2925
|
-
if (Is.object(value)) {
|
|
2926
|
-
value = value[part];
|
|
2927
|
-
}
|
|
2928
|
-
else {
|
|
2929
|
-
return undefined;
|
|
2930
|
-
}
|
|
2931
|
-
}
|
|
3486
|
+
static dateTime(value) {
|
|
3487
|
+
if (Is.undefined(value)) {
|
|
2932
3488
|
return value;
|
|
2933
3489
|
}
|
|
2934
|
-
|
|
3490
|
+
if (Is.date(value)) {
|
|
3491
|
+
return value;
|
|
3492
|
+
}
|
|
3493
|
+
if (Is.number(value)) {
|
|
3494
|
+
return new Date(value);
|
|
3495
|
+
}
|
|
3496
|
+
if (Is.string(value)) {
|
|
3497
|
+
const dt = new Date(value);
|
|
3498
|
+
if (!Number.isNaN(dt.getTime())) {
|
|
3499
|
+
const utc = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate(), dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
|
|
3500
|
+
return new Date(utc);
|
|
3501
|
+
}
|
|
3502
|
+
}
|
|
2935
3503
|
}
|
|
2936
3504
|
/**
|
|
2937
|
-
*
|
|
2938
|
-
* @param
|
|
2939
|
-
* @
|
|
2940
|
-
* @
|
|
3505
|
+
* Coerce the value to a time.
|
|
3506
|
+
* @param value The value to coerce.
|
|
3507
|
+
* @throws TypeError If the value can not be coerced.
|
|
3508
|
+
* @returns The value if it can be coerced.
|
|
2941
3509
|
*/
|
|
2942
|
-
static
|
|
2943
|
-
if (Is.
|
|
2944
|
-
|
|
3510
|
+
static time(value) {
|
|
3511
|
+
if (Is.undefined(value)) {
|
|
3512
|
+
return value;
|
|
3513
|
+
}
|
|
3514
|
+
if (Is.date(value)) {
|
|
3515
|
+
return value;
|
|
3516
|
+
}
|
|
3517
|
+
if (Is.number(value)) {
|
|
3518
|
+
const dt = new Date(value);
|
|
3519
|
+
dt.setFullYear(1970, 0, 1);
|
|
3520
|
+
return dt;
|
|
3521
|
+
}
|
|
3522
|
+
if (Is.string(value)) {
|
|
3523
|
+
const dt = new Date(value);
|
|
3524
|
+
if (!Number.isNaN(dt.getTime())) {
|
|
3525
|
+
const utc = Date.UTC(1970, 0, 1, dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
|
|
3526
|
+
return new Date(utc);
|
|
3527
|
+
}
|
|
2945
3528
|
}
|
|
2946
3529
|
}
|
|
2947
3530
|
/**
|
|
2948
|
-
*
|
|
2949
|
-
* @param
|
|
2950
|
-
* @
|
|
3531
|
+
* Coerce the value to an object.
|
|
3532
|
+
* @param value The value to coerce.
|
|
3533
|
+
* @throws TypeError If the value can not be coerced.
|
|
3534
|
+
* @returns The value if it can be coerced.
|
|
2951
3535
|
*/
|
|
2952
|
-
static
|
|
2953
|
-
if (Is.
|
|
2954
|
-
|
|
3536
|
+
static object(value) {
|
|
3537
|
+
if (Is.undefined(value)) {
|
|
3538
|
+
return value;
|
|
3539
|
+
}
|
|
3540
|
+
if (Is.object(value)) {
|
|
3541
|
+
return value;
|
|
3542
|
+
}
|
|
3543
|
+
if (Is.stringValue(value)) {
|
|
3544
|
+
try {
|
|
3545
|
+
return JSON.parse(value);
|
|
3546
|
+
}
|
|
3547
|
+
catch { }
|
|
2955
3548
|
}
|
|
2956
3549
|
}
|
|
2957
3550
|
/**
|
|
2958
|
-
*
|
|
2959
|
-
* @param
|
|
2960
|
-
* @
|
|
2961
|
-
* @
|
|
2962
|
-
* @returns The property if available.
|
|
3551
|
+
* Coerce the value to a Uint8Array.
|
|
3552
|
+
* @param value The value to coerce.
|
|
3553
|
+
* @throws TypeError If the value can not be coerced.
|
|
3554
|
+
* @returns The value if it can be coerced.
|
|
2963
3555
|
*/
|
|
2964
|
-
static
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
3556
|
+
static uint8Array(value) {
|
|
3557
|
+
if (Is.undefined(value)) {
|
|
3558
|
+
return value;
|
|
3559
|
+
}
|
|
3560
|
+
if (Is.string(value)) {
|
|
3561
|
+
if (Is.stringHex(value.toLowerCase(), true)) {
|
|
3562
|
+
return Converter.hexToBytes(value.toLowerCase());
|
|
3563
|
+
}
|
|
3564
|
+
if (Is.stringBase64(value)) {
|
|
3565
|
+
return Converter.base64ToBytes(value);
|
|
2973
3566
|
}
|
|
2974
3567
|
}
|
|
2975
|
-
return retVal;
|
|
2976
3568
|
}
|
|
2977
3569
|
/**
|
|
2978
|
-
*
|
|
2979
|
-
* @param
|
|
2980
|
-
* @param
|
|
2981
|
-
* @returns The
|
|
2982
|
-
*/
|
|
2983
|
-
static
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
3570
|
+
* Coerces a value based on the coercion type.
|
|
3571
|
+
* @param value The value to coerce.
|
|
3572
|
+
* @param type The coercion type to perform.
|
|
3573
|
+
* @returns The coerced value.
|
|
3574
|
+
*/
|
|
3575
|
+
static byType(value, type) {
|
|
3576
|
+
// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
|
|
3577
|
+
switch (type) {
|
|
3578
|
+
case CoerceType.String:
|
|
3579
|
+
return Coerce.string(value);
|
|
3580
|
+
case CoerceType.Number:
|
|
3581
|
+
return Coerce.number(value);
|
|
3582
|
+
case CoerceType.Integer:
|
|
3583
|
+
return Coerce.integer(value);
|
|
3584
|
+
case CoerceType.BigInt:
|
|
3585
|
+
return Coerce.bigint(value);
|
|
3586
|
+
case CoerceType.Boolean:
|
|
3587
|
+
return Coerce.boolean(value);
|
|
3588
|
+
case CoerceType.Date:
|
|
3589
|
+
return Coerce.date(value);
|
|
3590
|
+
case CoerceType.DateTime:
|
|
3591
|
+
return Coerce.dateTime(value);
|
|
3592
|
+
case CoerceType.Time:
|
|
3593
|
+
return Coerce.time(value);
|
|
3594
|
+
case CoerceType.Object:
|
|
3595
|
+
return Coerce.object(value);
|
|
3596
|
+
case CoerceType.Uint8Array:
|
|
3597
|
+
return Coerce.uint8Array(value);
|
|
3598
|
+
default:
|
|
3599
|
+
return value;
|
|
2990
3600
|
}
|
|
2991
|
-
return obj;
|
|
2992
3601
|
}
|
|
3602
|
+
}
|
|
3603
|
+
|
|
3604
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3605
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3606
|
+
/**
|
|
3607
|
+
* Class to help with filenames.
|
|
3608
|
+
*/
|
|
3609
|
+
class FilenameHelper {
|
|
2993
3610
|
/**
|
|
2994
|
-
*
|
|
2995
|
-
* @param
|
|
2996
|
-
* @
|
|
2997
|
-
* @returns The partial object.
|
|
3611
|
+
* Replaces any unsafe characters in the filename.
|
|
3612
|
+
* @param filename The filename to make safe.
|
|
3613
|
+
* @returns The safe filename.
|
|
2998
3614
|
*/
|
|
2999
|
-
static
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
delete result[key];
|
|
3004
|
-
}
|
|
3005
|
-
return result;
|
|
3615
|
+
static safeFilename(filename) {
|
|
3616
|
+
let safe = Coerce.string(filename);
|
|
3617
|
+
if (Is.empty(safe)) {
|
|
3618
|
+
return "";
|
|
3006
3619
|
}
|
|
3007
|
-
|
|
3620
|
+
// Common non filename characters
|
|
3621
|
+
safe = safe.replace(/["*/:<>?\\|]/g, "_");
|
|
3622
|
+
// Windows non filename characters
|
|
3623
|
+
safe = safe.replace(/^(con|prn|aux|nul|com\d|lpt\d)$/i, "_");
|
|
3624
|
+
// Control characters
|
|
3625
|
+
safe = safe.replace(/[\u0000-\u001F\u0080-\u009F]/g, "_");
|
|
3626
|
+
// Relative paths
|
|
3627
|
+
safe = safe.replace(/^\.+/, "_");
|
|
3628
|
+
// Trailing periods
|
|
3629
|
+
safe = safe.replace(/\.+$/, "");
|
|
3630
|
+
return safe;
|
|
3008
3631
|
}
|
|
3009
3632
|
}
|
|
3010
3633
|
|
|
@@ -3026,6 +3649,32 @@ class RandomHelper {
|
|
|
3026
3649
|
}
|
|
3027
3650
|
}
|
|
3028
3651
|
|
|
3652
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3653
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3654
|
+
/**
|
|
3655
|
+
* Class to help with uint8 arrays.
|
|
3656
|
+
*/
|
|
3657
|
+
class Uint8ArrayHelper {
|
|
3658
|
+
/**
|
|
3659
|
+
* Concatenate multiple arrays.
|
|
3660
|
+
* @param arrays The array to concatenate.
|
|
3661
|
+
* @returns The combined array.
|
|
3662
|
+
*/
|
|
3663
|
+
static concat(arrays) {
|
|
3664
|
+
let totalLength = 0;
|
|
3665
|
+
for (const array of arrays) {
|
|
3666
|
+
totalLength += array.length;
|
|
3667
|
+
}
|
|
3668
|
+
const concatBytes = new Uint8Array(totalLength);
|
|
3669
|
+
let offset = 0;
|
|
3670
|
+
for (const array of arrays) {
|
|
3671
|
+
concatBytes.set(array, offset);
|
|
3672
|
+
offset += array.length;
|
|
3673
|
+
}
|
|
3674
|
+
return concatBytes;
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
|
|
3029
3678
|
// Copyright 2024 IOTA Stiftung.
|
|
3030
3679
|
// SPDX-License-Identifier: Apache-2.0.
|
|
3031
3680
|
/**
|
|
@@ -3038,7 +3687,7 @@ const CompressionType = {
|
|
|
3038
3687
|
*/
|
|
3039
3688
|
Gzip: "gzip",
|
|
3040
3689
|
/**
|
|
3041
|
-
*
|
|
3690
|
+
* Deflate.
|
|
3042
3691
|
*/
|
|
3043
3692
|
Deflate: "deflate"
|
|
3044
3693
|
};
|
|
@@ -3489,29 +4138,93 @@ class Urn {
|
|
|
3489
4138
|
* Cache the results from asynchronous requests.
|
|
3490
4139
|
*/
|
|
3491
4140
|
class AsyncCache {
|
|
3492
|
-
/**
|
|
3493
|
-
* Cache for the fetch requests.
|
|
3494
|
-
* @internal
|
|
3495
|
-
*/
|
|
3496
|
-
static _cache = {};
|
|
3497
4141
|
/**
|
|
3498
4142
|
* Execute an async request and cache the result.
|
|
3499
4143
|
* @param key The key for the entry in the cache.
|
|
3500
4144
|
* @param ttlMs The TTL of the entry in the cache.
|
|
3501
4145
|
* @param requestMethod The method to call if not cached.
|
|
4146
|
+
* @param cacheFailures Cache failure results, defaults to false.
|
|
3502
4147
|
* @returns The response.
|
|
3503
4148
|
*/
|
|
3504
|
-
static exec(key, ttlMs, requestMethod) {
|
|
4149
|
+
static exec(key, ttlMs, requestMethod, cacheFailures) {
|
|
3505
4150
|
const cacheEnabled = Is.integer(ttlMs) && ttlMs >= 0;
|
|
3506
4151
|
if (cacheEnabled) {
|
|
3507
4152
|
AsyncCache.cleanupExpired();
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
4153
|
+
const cache = AsyncCache.getSharedCache();
|
|
4154
|
+
// Do we have a cache entry for the key
|
|
4155
|
+
if (cache[key]) {
|
|
4156
|
+
if (!Is.empty(cache[key].result)) {
|
|
4157
|
+
// If the cache has already resulted in a value, resolve it
|
|
4158
|
+
return Promise.resolve(cache[key].result);
|
|
4159
|
+
}
|
|
4160
|
+
else if (!Is.empty(cache[key].error)) {
|
|
4161
|
+
// If the cache has already resulted in an error, reject it
|
|
4162
|
+
return Promise.reject(cache[key].error);
|
|
4163
|
+
}
|
|
4164
|
+
// Otherwise create a promise to return and store the resolver
|
|
4165
|
+
// and rejector in the cache entry, so that we can call then
|
|
4166
|
+
// when the request is done
|
|
4167
|
+
let storedResolve;
|
|
4168
|
+
let storedReject;
|
|
4169
|
+
const wait = new Promise((resolve, reject) => {
|
|
4170
|
+
storedResolve = resolve;
|
|
4171
|
+
storedReject = reject;
|
|
4172
|
+
});
|
|
4173
|
+
if (!Is.empty(storedResolve) && !Is.empty(storedReject)) {
|
|
4174
|
+
cache[key].promiseQueue.push({
|
|
4175
|
+
requestMethod,
|
|
4176
|
+
resolve: storedResolve,
|
|
4177
|
+
reject: storedReject
|
|
4178
|
+
});
|
|
4179
|
+
}
|
|
4180
|
+
return wait;
|
|
3513
4181
|
}
|
|
3514
|
-
|
|
4182
|
+
// If we don't have a cache entry, create a new one
|
|
4183
|
+
cache[key] = {
|
|
4184
|
+
promiseQueue: [],
|
|
4185
|
+
expires: ttlMs === 0 ? 0 : Date.now() + ttlMs
|
|
4186
|
+
};
|
|
4187
|
+
// Return a promise that wraps the original request method
|
|
4188
|
+
// so that we can store any results or errors in the cache
|
|
4189
|
+
return new Promise((resolve, reject) => {
|
|
4190
|
+
// Call the request method and store the result
|
|
4191
|
+
requestMethod()
|
|
4192
|
+
// eslint-disable-next-line promise/prefer-await-to-then
|
|
4193
|
+
.then(res => {
|
|
4194
|
+
// If the request was successful, store the result
|
|
4195
|
+
cache[key].result = res;
|
|
4196
|
+
// and resolve both this promise and all the waiters
|
|
4197
|
+
resolve(res);
|
|
4198
|
+
for (const wait of cache[key].promiseQueue) {
|
|
4199
|
+
wait.resolve(res);
|
|
4200
|
+
}
|
|
4201
|
+
return res;
|
|
4202
|
+
})
|
|
4203
|
+
// eslint-disable-next-line promise/prefer-await-to-then
|
|
4204
|
+
.catch((err) => {
|
|
4205
|
+
// Reject the promise
|
|
4206
|
+
reject(err);
|
|
4207
|
+
// Handle the waiters based on the cacheFailures flag
|
|
4208
|
+
if (cacheFailures ?? false) {
|
|
4209
|
+
// If we are caching failures, store the error and reject the waiters
|
|
4210
|
+
cache[key].error = err;
|
|
4211
|
+
for (const wait of cache[key].promiseQueue) {
|
|
4212
|
+
wait.reject(err);
|
|
4213
|
+
}
|
|
4214
|
+
// Clear the waiters so we don't call them again
|
|
4215
|
+
cache[key].promiseQueue = [];
|
|
4216
|
+
}
|
|
4217
|
+
else {
|
|
4218
|
+
// If not caching failures for any queued requests we
|
|
4219
|
+
// have no value to either resolve or reject, so we
|
|
4220
|
+
// just resolve with the original request method
|
|
4221
|
+
for (const wait of cache[key].promiseQueue) {
|
|
4222
|
+
wait.resolve(wait.requestMethod());
|
|
4223
|
+
}
|
|
4224
|
+
delete cache[key];
|
|
4225
|
+
}
|
|
4226
|
+
});
|
|
4227
|
+
});
|
|
3515
4228
|
}
|
|
3516
4229
|
}
|
|
3517
4230
|
/**
|
|
@@ -3520,41 +4233,80 @@ class AsyncCache {
|
|
|
3520
4233
|
* @returns The item from the cache if it exists.
|
|
3521
4234
|
*/
|
|
3522
4235
|
static async get(key) {
|
|
3523
|
-
|
|
4236
|
+
const cache = AsyncCache.getSharedCache();
|
|
4237
|
+
if (!Is.empty(cache[key].result)) {
|
|
4238
|
+
// If the cache has already resulted in a value, resolve it
|
|
4239
|
+
return cache[key].result;
|
|
4240
|
+
}
|
|
4241
|
+
else if (!Is.empty(cache[key].error)) {
|
|
4242
|
+
// If the cache has already resulted in an error, reject it
|
|
4243
|
+
throw cache[key].error;
|
|
4244
|
+
}
|
|
4245
|
+
}
|
|
4246
|
+
/**
|
|
4247
|
+
* Set an entry into the cache.
|
|
4248
|
+
* @param key The key to set in the cache.
|
|
4249
|
+
* @param value The value to set in the cache.
|
|
4250
|
+
* @param ttlMs The TTL of the entry in the cache in ms, defaults to 1s.
|
|
4251
|
+
* @returns Nothing.
|
|
4252
|
+
*/
|
|
4253
|
+
static async set(key, value, ttlMs) {
|
|
4254
|
+
const cache = AsyncCache.getSharedCache();
|
|
4255
|
+
cache[key] = {
|
|
4256
|
+
result: value,
|
|
4257
|
+
promiseQueue: [],
|
|
4258
|
+
expires: Date.now() + (ttlMs ?? 1000)
|
|
4259
|
+
};
|
|
3524
4260
|
}
|
|
3525
4261
|
/**
|
|
3526
4262
|
* Remove an entry from the cache.
|
|
3527
4263
|
* @param key The key to remove from the cache.
|
|
3528
4264
|
*/
|
|
3529
4265
|
static remove(key) {
|
|
3530
|
-
|
|
4266
|
+
const cache = AsyncCache.getSharedCache();
|
|
4267
|
+
delete cache[key];
|
|
3531
4268
|
}
|
|
3532
4269
|
/**
|
|
3533
4270
|
* Clear the cache.
|
|
3534
4271
|
* @param prefix Optional prefix to clear only entries with that prefix.
|
|
3535
4272
|
*/
|
|
3536
4273
|
static clearCache(prefix) {
|
|
4274
|
+
const cache = AsyncCache.getSharedCache();
|
|
3537
4275
|
if (Is.stringValue(prefix)) {
|
|
3538
|
-
for (const entry in
|
|
4276
|
+
for (const entry in cache) {
|
|
3539
4277
|
if (entry.startsWith(prefix)) {
|
|
3540
|
-
delete
|
|
4278
|
+
delete cache[entry];
|
|
3541
4279
|
}
|
|
3542
4280
|
}
|
|
3543
4281
|
}
|
|
3544
4282
|
else {
|
|
3545
|
-
|
|
4283
|
+
SharedStore.set("asyncCache", {});
|
|
3546
4284
|
}
|
|
3547
4285
|
}
|
|
3548
4286
|
/**
|
|
3549
4287
|
* Perform a cleanup of the expired entries in the cache.
|
|
3550
4288
|
*/
|
|
3551
4289
|
static cleanupExpired() {
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
4290
|
+
const cache = AsyncCache.getSharedCache();
|
|
4291
|
+
for (const entry in cache) {
|
|
4292
|
+
if (cache[entry].expires > 0 && cache[entry].expires < Date.now()) {
|
|
4293
|
+
delete cache[entry];
|
|
3555
4294
|
}
|
|
3556
4295
|
}
|
|
3557
4296
|
}
|
|
4297
|
+
/**
|
|
4298
|
+
* Get the shared cache.
|
|
4299
|
+
* @returns The shared cache.
|
|
4300
|
+
* @internal
|
|
4301
|
+
*/
|
|
4302
|
+
static getSharedCache() {
|
|
4303
|
+
let sharedCache = SharedStore.get("asyncCache");
|
|
4304
|
+
if (Is.undefined(sharedCache)) {
|
|
4305
|
+
sharedCache = {};
|
|
4306
|
+
SharedStore.set("asyncCache", sharedCache);
|
|
4307
|
+
}
|
|
4308
|
+
return sharedCache;
|
|
4309
|
+
}
|
|
3558
4310
|
}
|
|
3559
4311
|
|
|
3560
4312
|
/**
|
|
@@ -3575,16 +4327,15 @@ class Compression {
|
|
|
3575
4327
|
static async compress(bytes, type) {
|
|
3576
4328
|
Guards.uint8Array(Compression._CLASS_NAME, "bytes", bytes);
|
|
3577
4329
|
Guards.arrayOneOf(Compression._CLASS_NAME, "type", type, Object.values(CompressionType));
|
|
3578
|
-
const blob = new Blob([bytes]);
|
|
3579
|
-
const
|
|
3580
|
-
const
|
|
3581
|
-
const compressedBlob = await new Response(
|
|
3582
|
-
const
|
|
3583
|
-
const compressedBytes = new Uint8Array(ab);
|
|
4330
|
+
const blob = new Blob([new Uint8Array(bytes)]);
|
|
4331
|
+
const compressionStream = new CompressionStream(type);
|
|
4332
|
+
const compressionPipe = blob.stream().pipeThrough(compressionStream);
|
|
4333
|
+
const compressedBlob = await new Response(compressionPipe).blob();
|
|
4334
|
+
const compressedBytes = new Uint8Array(await compressedBlob.arrayBuffer());
|
|
3584
4335
|
// GZIP header contains a byte which specifies the OS the
|
|
3585
4336
|
// compression was performed on. We set this to 3 (Unix) to ensure
|
|
3586
4337
|
// that we produce consistent results.
|
|
3587
|
-
if (type ===
|
|
4338
|
+
if (type === CompressionType.Gzip && compressedBytes.length >= 10) {
|
|
3588
4339
|
compressedBytes[9] = 3;
|
|
3589
4340
|
}
|
|
3590
4341
|
return compressedBytes;
|
|
@@ -3598,12 +4349,11 @@ class Compression {
|
|
|
3598
4349
|
static async decompress(compressedBytes, type) {
|
|
3599
4350
|
Guards.uint8Array(Compression._CLASS_NAME, "compressedBytes", compressedBytes);
|
|
3600
4351
|
Guards.arrayOneOf(Compression._CLASS_NAME, "type", type, Object.values(CompressionType));
|
|
3601
|
-
const blob = new Blob([compressedBytes]);
|
|
3602
|
-
const
|
|
3603
|
-
const
|
|
3604
|
-
const decompressedBlob = await new Response(
|
|
3605
|
-
|
|
3606
|
-
return new Uint8Array(ab);
|
|
4352
|
+
const blob = new Blob([new Uint8Array(compressedBytes)]);
|
|
4353
|
+
const decompressionStream = new DecompressionStream(type);
|
|
4354
|
+
const decompressionPipe = blob.stream().pipeThrough(decompressionStream);
|
|
4355
|
+
const decompressedBlob = await new Response(decompressionPipe).blob();
|
|
4356
|
+
return new Uint8Array(await decompressedBlob.bytes());
|
|
3607
4357
|
}
|
|
3608
4358
|
}
|
|
3609
4359
|
|
|
@@ -3661,6 +4411,7 @@ class Validation {
|
|
|
3661
4411
|
* @param options Additional options for the validation.
|
|
3662
4412
|
* @param options.minLength The minimum length of the string.
|
|
3663
4413
|
* @param options.maxLength The maximum length of the string.
|
|
4414
|
+
* @param options.format Specific format to check.
|
|
3664
4415
|
* @returns True if the value is a valid string.
|
|
3665
4416
|
*/
|
|
3666
4417
|
static string(property, value, failures, fieldNameResource, options) {
|
|
@@ -3679,6 +4430,47 @@ class Validation {
|
|
|
3679
4430
|
const maxLimitDefined = Is.integer(maxLength);
|
|
3680
4431
|
const belowMin = minLimitDefined && value.length < minLength;
|
|
3681
4432
|
const aboveMax = maxLimitDefined && value.length > maxLength;
|
|
4433
|
+
if (options?.format === "base58" && !Is.stringBase58(value)) {
|
|
4434
|
+
failures.push({
|
|
4435
|
+
property,
|
|
4436
|
+
reason: "validation.beTextBase58",
|
|
4437
|
+
properties: {
|
|
4438
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
4439
|
+
value
|
|
4440
|
+
}
|
|
4441
|
+
});
|
|
4442
|
+
}
|
|
4443
|
+
else if (options?.format === "base64" && !Is.stringBase64(value)) {
|
|
4444
|
+
failures.push({
|
|
4445
|
+
property,
|
|
4446
|
+
reason: "validation.beTextBase64",
|
|
4447
|
+
properties: {
|
|
4448
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
4449
|
+
value
|
|
4450
|
+
}
|
|
4451
|
+
});
|
|
4452
|
+
}
|
|
4453
|
+
else if (options?.format === "hex" && !Is.stringHex(value)) {
|
|
4454
|
+
failures.push({
|
|
4455
|
+
property,
|
|
4456
|
+
reason: "validation.beTextHex",
|
|
4457
|
+
properties: {
|
|
4458
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
4459
|
+
value
|
|
4460
|
+
}
|
|
4461
|
+
});
|
|
4462
|
+
}
|
|
4463
|
+
else if (Is.regexp(options?.format) && !options.format.test(value)) {
|
|
4464
|
+
failures.push({
|
|
4465
|
+
property,
|
|
4466
|
+
reason: "validation.beTextRegExp",
|
|
4467
|
+
properties: {
|
|
4468
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
4469
|
+
value,
|
|
4470
|
+
format: options?.format
|
|
4471
|
+
}
|
|
4472
|
+
});
|
|
4473
|
+
}
|
|
3682
4474
|
if (minLimitDefined && maxLimitDefined && (belowMin || aboveMax)) {
|
|
3683
4475
|
failures.push({
|
|
3684
4476
|
property,
|
|
@@ -4338,4 +5130,4 @@ class Validation {
|
|
|
4338
5130
|
}
|
|
4339
5131
|
}
|
|
4340
5132
|
|
|
4341
|
-
export { AlreadyExistsError, ArrayHelper, AsyncCache, Base32, Base58, Base64, Base64Url, BaseError, BitString, Coerce, ComponentFactory, Compression, CompressionType, ConflictError, Converter, ErrorHelper, Factory, FilenameHelper, GeneralError, GuardError, Guards, HexHelper, I18n, Is, JsonHelper, NotFoundError, NotImplementedError, NotSupportedError, ObjectHelper, RandomHelper, StringHelper, UnauthorizedError, UnprocessableError, Url, Urn, Validation, ValidationError };
|
|
5133
|
+
export { AlreadyExistsError, ArrayHelper, AsyncCache, Base32, Base58, Base64, Base64Url, BaseError, BitString, Coerce, CoerceType, ComponentFactory, Compression, CompressionType, ConflictError, Converter, EnvHelper, ErrorHelper, Factory, FilenameHelper, GeneralError, GuardError, Guards, HexHelper, I18n, Is, JsonHelper, NotFoundError, NotImplementedError, NotSupportedError, ObjectHelper, RandomHelper, SharedStore, StringHelper, Uint8ArrayHelper, UnauthorizedError, UnprocessableError, Url, Urn, Validation, ValidationError };
|