@twin.org/core 0.0.1-next.1
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/LICENSE +201 -0
- package/README.md +29 -0
- package/dist/cjs/index.cjs +4221 -0
- package/dist/esm/index.mjs +4184 -0
- package/dist/types/encoding/base32.d.ts +18 -0
- package/dist/types/encoding/base64.d.ts +24 -0
- package/dist/types/encoding/base64Url.d.ts +18 -0
- package/dist/types/errors/alreadyExistsError.d.ts +18 -0
- package/dist/types/errors/baseError.d.ts +103 -0
- package/dist/types/errors/conflictError.d.ts +19 -0
- package/dist/types/errors/generalError.d.ts +20 -0
- package/dist/types/errors/guardError.d.ts +19 -0
- package/dist/types/errors/notFoundError.d.ts +18 -0
- package/dist/types/errors/notImplementedError.d.ts +16 -0
- package/dist/types/errors/notSupportedError.d.ts +17 -0
- package/dist/types/errors/unauthorizedError.d.ts +17 -0
- package/dist/types/errors/unprocessableError.d.ts +20 -0
- package/dist/types/errors/validationError.d.ts +18 -0
- package/dist/types/factories/componentFactory.d.ts +6 -0
- package/dist/types/factories/factory.d.ts +67 -0
- package/dist/types/helpers/arrayHelper.d.ts +12 -0
- package/dist/types/helpers/errorHelper.d.ts +24 -0
- package/dist/types/helpers/filenameHelper.d.ts +11 -0
- package/dist/types/helpers/hexHelper.d.ts +30 -0
- package/dist/types/helpers/jsonHelper.d.ts +29 -0
- package/dist/types/helpers/objectHelper.d.ts +74 -0
- package/dist/types/helpers/randomHelper.d.ts +11 -0
- package/dist/types/helpers/stringHelper.d.ts +90 -0
- package/dist/types/index.d.ts +46 -0
- package/dist/types/models/IComponent.d.ts +29 -0
- package/dist/types/models/IError.d.ts +31 -0
- package/dist/types/models/IKeyValue.d.ts +13 -0
- package/dist/types/models/ILabelledValue.d.ts +13 -0
- package/dist/types/models/ILocale.d.ts +13 -0
- package/dist/types/models/ILocaleDictionary.d.ts +6 -0
- package/dist/types/models/ILocalesIndex.d.ts +10 -0
- package/dist/types/models/IPatchOperation.d.ts +21 -0
- package/dist/types/models/IUrlParts.d.ts +29 -0
- package/dist/types/models/IValidationFailure.d.ts +23 -0
- package/dist/types/models/compressionType.d.ts +17 -0
- package/dist/types/types/bitString.d.ts +41 -0
- package/dist/types/types/url.d.ts +56 -0
- package/dist/types/types/urn.d.ts +93 -0
- package/dist/types/utils/asyncCache.d.ts +33 -0
- package/dist/types/utils/coerce.d.ts +61 -0
- package/dist/types/utils/compression.d.ts +20 -0
- package/dist/types/utils/converter.d.ts +85 -0
- package/dist/types/utils/guards.d.ts +190 -0
- package/dist/types/utils/i18n.d.ts +81 -0
- package/dist/types/utils/is.d.ts +205 -0
- package/dist/types/utils/validation.d.ts +245 -0
- package/docs/changelog.md +5 -0
- package/docs/examples.md +1 -0
- package/docs/reference/classes/AlreadyExistsError.md +375 -0
- package/docs/reference/classes/ArrayHelper.md +37 -0
- package/docs/reference/classes/AsyncCache.md +117 -0
- package/docs/reference/classes/Base32.md +57 -0
- package/docs/reference/classes/Base64.md +74 -0
- package/docs/reference/classes/Base64Url.md +54 -0
- package/docs/reference/classes/BaseError.md +344 -0
- package/docs/reference/classes/BitString.md +123 -0
- package/docs/reference/classes/Coerce.md +209 -0
- package/docs/reference/classes/Compression.md +61 -0
- package/docs/reference/classes/ConflictError.md +379 -0
- package/docs/reference/classes/Converter.md +285 -0
- package/docs/reference/classes/ErrorHelper.md +73 -0
- package/docs/reference/classes/Factory.md +221 -0
- package/docs/reference/classes/FilenameHelper.md +33 -0
- package/docs/reference/classes/GeneralError.md +375 -0
- package/docs/reference/classes/GuardError.md +379 -0
- package/docs/reference/classes/Guards.md +711 -0
- package/docs/reference/classes/HexHelper.md +97 -0
- package/docs/reference/classes/I18n.md +237 -0
- package/docs/reference/classes/Is.md +693 -0
- package/docs/reference/classes/JsonHelper.md +92 -0
- package/docs/reference/classes/NotFoundError.md +375 -0
- package/docs/reference/classes/NotImplementedError.md +367 -0
- package/docs/reference/classes/NotSupportedError.md +371 -0
- package/docs/reference/classes/ObjectHelper.md +287 -0
- package/docs/reference/classes/RandomHelper.md +33 -0
- package/docs/reference/classes/StringHelper.md +270 -0
- package/docs/reference/classes/UnauthorizedError.md +371 -0
- package/docs/reference/classes/UnprocessableError.md +375 -0
- package/docs/reference/classes/Url.md +167 -0
- package/docs/reference/classes/Urn.md +295 -0
- package/docs/reference/classes/Validation.md +821 -0
- package/docs/reference/classes/ValidationError.md +371 -0
- package/docs/reference/index.md +60 -0
- package/docs/reference/interfaces/IComponent.md +79 -0
- package/docs/reference/interfaces/IError.md +55 -0
- package/docs/reference/interfaces/IKeyValue.md +23 -0
- package/docs/reference/interfaces/ILabelledValue.md +23 -0
- package/docs/reference/interfaces/ILocale.md +19 -0
- package/docs/reference/interfaces/ILocaleDictionary.md +7 -0
- package/docs/reference/interfaces/ILocalesIndex.md +11 -0
- package/docs/reference/interfaces/IPatchOperation.md +35 -0
- package/docs/reference/interfaces/IUrlParts.md +51 -0
- package/docs/reference/interfaces/IValidationFailure.md +39 -0
- package/docs/reference/type-aliases/CompressionType.md +5 -0
- package/docs/reference/variables/ComponentFactory.md +5 -0
- package/docs/reference/variables/CompressionType.md +19 -0
- package/locales/en.json +98 -0
- package/package.json +65 -0
|
@@ -0,0 +1,4221 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var intlMessageformat = require('intl-messageformat');
|
|
4
|
+
var rfc6902 = require('rfc6902');
|
|
5
|
+
|
|
6
|
+
// Copyright 2024 IOTA Stiftung.
|
|
7
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
8
|
+
/**
|
|
9
|
+
* Helper methods for hex conversions.
|
|
10
|
+
*/
|
|
11
|
+
class HexHelper {
|
|
12
|
+
/**
|
|
13
|
+
* Strip the 0x prefix if it exists.
|
|
14
|
+
* @param hex The hex value to strip.
|
|
15
|
+
* @returns The stripped hex without the prefix.
|
|
16
|
+
*/
|
|
17
|
+
static stripPrefix(hex) {
|
|
18
|
+
return hex.replace(/^0x/, "");
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Add the 0x prefix if it does not exist.
|
|
22
|
+
* @param hex The hex value to add the prefix to.
|
|
23
|
+
* @returns The hex with the prefix.
|
|
24
|
+
*/
|
|
25
|
+
static addPrefix(hex) {
|
|
26
|
+
return HexHelper.hasPrefix(hex) ? hex : `0x${hex}`;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Does the hex string have the prefix.
|
|
30
|
+
* @param hex The hex value to check for the prefix.
|
|
31
|
+
* @returns True if the hex string has the prefix.
|
|
32
|
+
*/
|
|
33
|
+
static hasPrefix(hex) {
|
|
34
|
+
return hex.startsWith("0x");
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Is the data hex format.
|
|
38
|
+
* @param value The value to test.
|
|
39
|
+
* @param allowPrefix Allow the hex to have the 0x prefix.
|
|
40
|
+
* @returns True if the string is hex.
|
|
41
|
+
*/
|
|
42
|
+
static isHex(value, allowPrefix = false) {
|
|
43
|
+
const localHex = allowPrefix ? HexHelper.stripPrefix(value) : value;
|
|
44
|
+
if (localHex.length % 2 === 1) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
return /^[\da-f]+$/g.test(localHex);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Copyright 2024 IOTA Stiftung.
|
|
52
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
53
|
+
/**
|
|
54
|
+
* Class to check types of objects.
|
|
55
|
+
*/
|
|
56
|
+
class Is {
|
|
57
|
+
/**
|
|
58
|
+
* Is the property undefined.
|
|
59
|
+
* @param value The value to test.
|
|
60
|
+
* @returns True if the value is a empty.
|
|
61
|
+
*/
|
|
62
|
+
static undefined(value) {
|
|
63
|
+
return value === undefined;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Is the property null.
|
|
67
|
+
* @param value The value to test.
|
|
68
|
+
* @returns True if the value is a empty.
|
|
69
|
+
*/
|
|
70
|
+
static null(value) {
|
|
71
|
+
return value === null;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Is the property null or undefined.
|
|
75
|
+
* @param value The value to test.
|
|
76
|
+
* @returns True if the value is a empty.
|
|
77
|
+
*/
|
|
78
|
+
static empty(value) {
|
|
79
|
+
return value === null || value === undefined;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Is the property is not null or undefined.
|
|
83
|
+
* @param value The value to test.
|
|
84
|
+
* @returns True if the value is a not empty.
|
|
85
|
+
*/
|
|
86
|
+
static notEmpty(value) {
|
|
87
|
+
return value !== null && value !== undefined;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Is the value a string.
|
|
91
|
+
* @param value The value to test.
|
|
92
|
+
* @returns True if the value is a string.
|
|
93
|
+
*/
|
|
94
|
+
static string(value) {
|
|
95
|
+
return typeof value === "string";
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Is the value a string.
|
|
99
|
+
* @param value The value to test.
|
|
100
|
+
* @returns True if the value is a string.
|
|
101
|
+
*/
|
|
102
|
+
static stringValue(value) {
|
|
103
|
+
return Is.string(value) && value.trim().length > 0;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Is the value a JSON string.
|
|
107
|
+
* @param value The value to test.
|
|
108
|
+
* @returns True if the value is a JSON string.
|
|
109
|
+
*/
|
|
110
|
+
static json(value) {
|
|
111
|
+
if (!Is.stringValue(value)) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
const json = JSON.parse(value);
|
|
116
|
+
return typeof json === "object";
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Is the value a base64 string.
|
|
124
|
+
* @param value The value to test.
|
|
125
|
+
* @returns True if the value is a base64 string.
|
|
126
|
+
*/
|
|
127
|
+
static stringBase64(value) {
|
|
128
|
+
return (Is.stringValue(value) &&
|
|
129
|
+
// eslint-disable-next-line unicorn/better-regex
|
|
130
|
+
/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value));
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Is the value a base64 url string.
|
|
134
|
+
* @param value The value to test.
|
|
135
|
+
* @returns True if the value is a base64 string.
|
|
136
|
+
*/
|
|
137
|
+
static stringBase64Url(value) {
|
|
138
|
+
return (Is.stringValue(value) &&
|
|
139
|
+
// eslint-disable-next-line unicorn/better-regex
|
|
140
|
+
/^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2}==|[A-Za-z0-9-_]{3}=)?$/.test(value));
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Is the value a hex string.
|
|
144
|
+
* @param value The value to test.
|
|
145
|
+
* @param allowPrefix Allow the hex to have the 0x prefix.
|
|
146
|
+
* @returns True if the value is a hex string.
|
|
147
|
+
*/
|
|
148
|
+
static stringHex(value, allowPrefix = false) {
|
|
149
|
+
return Is.string(value) && HexHelper.isHex(value, allowPrefix);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Is the value a hex string of fixed length.
|
|
153
|
+
* @param value The value to test.
|
|
154
|
+
* @param length The length to test.
|
|
155
|
+
* @param allowPrefix Allow the hex to have the 0x prefix.
|
|
156
|
+
* @returns True if the value is a hex string of required length.
|
|
157
|
+
*/
|
|
158
|
+
static stringHexLength(value, length, allowPrefix = false) {
|
|
159
|
+
return Is.stringHex(value, allowPrefix) && value.length === length;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Is the value a number.
|
|
163
|
+
* @param value The value to test.
|
|
164
|
+
* @returns True if the value is a number.
|
|
165
|
+
*/
|
|
166
|
+
static number(value) {
|
|
167
|
+
return typeof value === "number" && Number.isFinite(value) && !Number.isNaN(value);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Is the value an integer.
|
|
171
|
+
* @param value The value to test.
|
|
172
|
+
* @returns True if the value is an integer.
|
|
173
|
+
*/
|
|
174
|
+
static integer(value) {
|
|
175
|
+
return Is.number(value) && Number.isInteger(value);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Is the value a big integer.
|
|
179
|
+
* @param value The value to test.
|
|
180
|
+
* @returns True if the value is a big integer.
|
|
181
|
+
*/
|
|
182
|
+
static bigint(value) {
|
|
183
|
+
return typeof value === "bigint";
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Is the value a boolean.
|
|
187
|
+
* @param value The value to test.
|
|
188
|
+
* @returns True if the value is a boolean.
|
|
189
|
+
*/
|
|
190
|
+
static boolean(value) {
|
|
191
|
+
return typeof value === "boolean";
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Is the value a date.
|
|
195
|
+
* @param value The value to test.
|
|
196
|
+
* @returns True if the value is a date.
|
|
197
|
+
*/
|
|
198
|
+
static date(value) {
|
|
199
|
+
return (Object.prototype.toString.call(value) === "[object Date]" &&
|
|
200
|
+
!Number.isNaN(value.getTime()));
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Is the value an empty date.
|
|
204
|
+
* @param value The value to test.
|
|
205
|
+
* @returns True if the value is an empty date.
|
|
206
|
+
*/
|
|
207
|
+
static dateEmpty(value) {
|
|
208
|
+
return (Object.prototype.toString.call(value) === "[object Date]" &&
|
|
209
|
+
Number.isNaN(value.getTime()));
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Is the value a date string.
|
|
213
|
+
* @param value The value to test.
|
|
214
|
+
* @returns True if the value is a string in ISO 8601 date format.
|
|
215
|
+
*/
|
|
216
|
+
static dateString(value) {
|
|
217
|
+
if (typeof value !== "string" || value.length === 0 || value.includes("T")) {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
return !Number.isNaN(Date.parse(value));
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Is the value a date string.
|
|
224
|
+
* @param value The value to test.
|
|
225
|
+
* @returns True if the value is a string in ISO 8601 date/time format.
|
|
226
|
+
*/
|
|
227
|
+
static dateTimeString(value) {
|
|
228
|
+
if (typeof value !== "string" || value.length === 0 || !value.includes("T")) {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
return !Number.isNaN(Date.parse(value));
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Is the value a time string.
|
|
235
|
+
* @param value The value to test.
|
|
236
|
+
* @returns True if the value is a string in ISO 8601 time format.
|
|
237
|
+
*/
|
|
238
|
+
static timeString(value) {
|
|
239
|
+
if (typeof value !== "string" || value.length === 0 || value.includes("T")) {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
return !Number.isNaN(Date.parse(`1970-01-01T${value}`));
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Is the value a timestamp in seconds.
|
|
246
|
+
* @param value The value to test.
|
|
247
|
+
* @returns True if the value is a date.
|
|
248
|
+
*/
|
|
249
|
+
static timestampSeconds(value) {
|
|
250
|
+
if (!Is.integer(value)) {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
return value.toString().length < 12;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Is the value a timestamp in milliseconds.
|
|
257
|
+
* @param value The value to test.
|
|
258
|
+
* @returns True if the value is a date.
|
|
259
|
+
*/
|
|
260
|
+
static timestampMilliseconds(value) {
|
|
261
|
+
if (!Is.integer(value)) {
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
return value.toString().length >= 12;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Is the value an object.
|
|
268
|
+
* @param value The value to test.
|
|
269
|
+
* @returns True if the value is a object.
|
|
270
|
+
*/
|
|
271
|
+
static object(value) {
|
|
272
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Is the value an object with at least one property.
|
|
276
|
+
* @param value The value to test.
|
|
277
|
+
* @returns True if the value is a object.
|
|
278
|
+
*/
|
|
279
|
+
static objectValue(value) {
|
|
280
|
+
return (typeof value === "object" &&
|
|
281
|
+
value !== null &&
|
|
282
|
+
!Array.isArray(value) &&
|
|
283
|
+
Object.keys(value).length > 0);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Is the value an array.
|
|
287
|
+
* @param value The value to test.
|
|
288
|
+
* @returns True if the value is an array.
|
|
289
|
+
*/
|
|
290
|
+
static array(value) {
|
|
291
|
+
return Array.isArray(value);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Is the value an array with at least one element.
|
|
295
|
+
* @param value The value to test.
|
|
296
|
+
* @returns True if the value is an array with at least one element.
|
|
297
|
+
*/
|
|
298
|
+
static arrayValue(value) {
|
|
299
|
+
return Array.isArray(value) && value.length > 0;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Is the value an array with at least one element.
|
|
303
|
+
* @param value The value to test.
|
|
304
|
+
* @param options The options the value must be one of.
|
|
305
|
+
* @returns True if the value is an element from the options array.
|
|
306
|
+
*/
|
|
307
|
+
static arrayOneOf(value, options) {
|
|
308
|
+
if (Is.empty(value) || !Is.array(options) || !options.includes(value)) {
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Is the value a Uint8Array.
|
|
315
|
+
* @param value The value to test.
|
|
316
|
+
* @returns True if the value is a Uint8Array.
|
|
317
|
+
*/
|
|
318
|
+
static uint8Array(value) {
|
|
319
|
+
return value instanceof Uint8Array;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Is the value a TypedArray.
|
|
323
|
+
* @param value The value to test.
|
|
324
|
+
* @returns True if the value is a TypedArray.
|
|
325
|
+
*/
|
|
326
|
+
static typedArray(value) {
|
|
327
|
+
return value instanceof Object.getPrototypeOf(Uint8Array);
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Is the property a function.
|
|
331
|
+
* @param value The value to test.
|
|
332
|
+
* @returns True if the value is a function.
|
|
333
|
+
*/
|
|
334
|
+
static function(value) {
|
|
335
|
+
return typeof value === "function";
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Is the value a string formatted as an email address.
|
|
339
|
+
* @param value The value to test.
|
|
340
|
+
* @returns True if the value is a string.
|
|
341
|
+
*/
|
|
342
|
+
static email(value) {
|
|
343
|
+
return (Is.stringValue(value) &&
|
|
344
|
+
/^[\w!#$%&'*+./=?^`{|}~-]+@[\dA-Za-z](?:[\dA-Za-z-]{0,61}[\dA-Za-z])?(?:\.[\dA-Za-z](?:[\dA-Za-z-]{0,61}[\dA-Za-z])?)*$/.test(value));
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Is the value a promise.
|
|
348
|
+
* @param value The value to test.
|
|
349
|
+
* @returns True if the value is a promise.
|
|
350
|
+
*/
|
|
351
|
+
static promise(value) {
|
|
352
|
+
return value instanceof Promise;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Copyright 2024 IOTA Stiftung.
|
|
357
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
358
|
+
/* eslint-disable no-continue */
|
|
359
|
+
/* eslint-disable no-bitwise */
|
|
360
|
+
/**
|
|
361
|
+
* Class to help with string.
|
|
362
|
+
*/
|
|
363
|
+
class StringHelper {
|
|
364
|
+
/**
|
|
365
|
+
* Trim trailing slashes from a string.
|
|
366
|
+
* @param value The value to trim.
|
|
367
|
+
* @returns The trimmed value.
|
|
368
|
+
*/
|
|
369
|
+
static trimTrailingSlashes(value) {
|
|
370
|
+
if (Is.stringValue(value)) {
|
|
371
|
+
return value.replace(/\/+$/, "");
|
|
372
|
+
}
|
|
373
|
+
return "";
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Trim leading slashes from a string.
|
|
377
|
+
* @param value The value to trim.
|
|
378
|
+
* @returns The trimmed value.
|
|
379
|
+
*/
|
|
380
|
+
static trimLeadingSlashes(value) {
|
|
381
|
+
if (Is.stringValue(value)) {
|
|
382
|
+
return value.replace(/^\/+/, "");
|
|
383
|
+
}
|
|
384
|
+
return "";
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Convert the input string to kebab case.
|
|
388
|
+
* @param input The input to convert.
|
|
389
|
+
* @param stripInterfacePrefix Strip interface prefixes.
|
|
390
|
+
* @returns The kebab case version of the input.
|
|
391
|
+
*/
|
|
392
|
+
static kebabCase(input, stripInterfacePrefix = true) {
|
|
393
|
+
if (Is.stringValue(input)) {
|
|
394
|
+
let output = input;
|
|
395
|
+
if (stripInterfacePrefix && /I[A-Z]/.test(output)) {
|
|
396
|
+
output = output.slice(1);
|
|
397
|
+
}
|
|
398
|
+
return StringHelper.words(output).join("-").toLowerCase();
|
|
399
|
+
}
|
|
400
|
+
return "";
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Convert the input string to snake case.
|
|
404
|
+
* @param input The input to convert.
|
|
405
|
+
* @param stripInterfacePrefix Strip interface prefixes.
|
|
406
|
+
* @returns The snake case version of the input.
|
|
407
|
+
*/
|
|
408
|
+
static snakeCase(input, stripInterfacePrefix = true) {
|
|
409
|
+
if (Is.stringValue(input)) {
|
|
410
|
+
let output = input;
|
|
411
|
+
if (stripInterfacePrefix && /I[A-Z]/.test(output)) {
|
|
412
|
+
output = output.slice(1);
|
|
413
|
+
}
|
|
414
|
+
return StringHelper.words(output).join("_").toLowerCase();
|
|
415
|
+
}
|
|
416
|
+
return "";
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Title case all the words.
|
|
420
|
+
* @param input The input to convert.
|
|
421
|
+
* @param stripInterfacePrefix Strip interface prefixes.
|
|
422
|
+
* @returns The title case version of the input.
|
|
423
|
+
*/
|
|
424
|
+
static titleCase(input, stripInterfacePrefix = true) {
|
|
425
|
+
if (Is.stringValue(input)) {
|
|
426
|
+
let output = input;
|
|
427
|
+
if (stripInterfacePrefix && /I[A-Z]/.test(output)) {
|
|
428
|
+
output = output.slice(1);
|
|
429
|
+
}
|
|
430
|
+
return StringHelper.words(output)
|
|
431
|
+
.map(w => `${w[0].toUpperCase()}${w.slice(1).toLowerCase()}`)
|
|
432
|
+
.join(" ");
|
|
433
|
+
}
|
|
434
|
+
return "";
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Pascal case all the words.
|
|
438
|
+
* @param input The input to convert.
|
|
439
|
+
* @param stripInterfacePrefix Strip interface prefixes.
|
|
440
|
+
* @returns The pascal case version of the input.
|
|
441
|
+
*/
|
|
442
|
+
static pascalCase(input, stripInterfacePrefix = true) {
|
|
443
|
+
if (Is.stringValue(input)) {
|
|
444
|
+
let output = input;
|
|
445
|
+
if (stripInterfacePrefix && /I[A-Z]/.test(output)) {
|
|
446
|
+
output = output.slice(1);
|
|
447
|
+
}
|
|
448
|
+
return StringHelper.words(output)
|
|
449
|
+
.map(w => `${w[0].toUpperCase()}${w.slice(1).toLowerCase()}`)
|
|
450
|
+
.join("");
|
|
451
|
+
}
|
|
452
|
+
return "";
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Camel case all the words.
|
|
456
|
+
* @param input The input to convert.
|
|
457
|
+
* @param stripInterfacePrefix Strip interface prefixes.
|
|
458
|
+
* @returns The camel case version of the input.
|
|
459
|
+
*/
|
|
460
|
+
static camelCase(input, stripInterfacePrefix = true) {
|
|
461
|
+
if (Is.stringValue(input)) {
|
|
462
|
+
let output = input;
|
|
463
|
+
if (stripInterfacePrefix && /I[A-Z]/.test(output)) {
|
|
464
|
+
output = output.slice(1);
|
|
465
|
+
}
|
|
466
|
+
const words = StringHelper.words(output);
|
|
467
|
+
return words.length === 0
|
|
468
|
+
? ""
|
|
469
|
+
: `${words[0].toLowerCase()}${words
|
|
470
|
+
.slice(1)
|
|
471
|
+
.map(w => `${w[0].toUpperCase()}${w.slice(1).toLowerCase()}`)
|
|
472
|
+
.join("")}`;
|
|
473
|
+
}
|
|
474
|
+
return "";
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Convert the words to a path.
|
|
478
|
+
* @param input The input to convert.
|
|
479
|
+
* @param stripInterfacePrefix Strip interface prefixes.
|
|
480
|
+
* @returns The path version of the input.
|
|
481
|
+
*/
|
|
482
|
+
static wordPath(input, stripInterfacePrefix = true) {
|
|
483
|
+
if (Is.stringValue(input)) {
|
|
484
|
+
let output = input;
|
|
485
|
+
if (stripInterfacePrefix && /I[A-Z]/.test(output)) {
|
|
486
|
+
output = output.slice(1);
|
|
487
|
+
}
|
|
488
|
+
const words = StringHelper.words(output);
|
|
489
|
+
return words.join("/").toLowerCase();
|
|
490
|
+
}
|
|
491
|
+
return "";
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Strip interface prefix if there is one.
|
|
495
|
+
* @param input The input to strip.
|
|
496
|
+
* @returns The input with any interface prefix stripped.
|
|
497
|
+
*/
|
|
498
|
+
static stripPrefix(input) {
|
|
499
|
+
if (Is.stringValue(input)) {
|
|
500
|
+
let output = input;
|
|
501
|
+
if (/I[A-Z]/.test(output)) {
|
|
502
|
+
output = output.slice(1);
|
|
503
|
+
}
|
|
504
|
+
return output;
|
|
505
|
+
}
|
|
506
|
+
return "";
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Split a string into words.
|
|
510
|
+
* @param input The input to split.
|
|
511
|
+
* @returns The string split into words.
|
|
512
|
+
*/
|
|
513
|
+
static words(input) {
|
|
514
|
+
if (!Is.stringValue(input)) {
|
|
515
|
+
return [];
|
|
516
|
+
}
|
|
517
|
+
return (input
|
|
518
|
+
.replace(/([A-Z])/g, " $1")
|
|
519
|
+
.trim()
|
|
520
|
+
.match(/[^\u0000-\u002F\u003A-\u0040\u005B-\u0060\u007B-\u007F]+/g) ?? []);
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Check if a Node.js Buffer or Uint8Array is UTF-8.
|
|
524
|
+
* Url https://tools.ietf.org/html/rfc3629
|
|
525
|
+
* Source https://github.com/hcodes/isutf8
|
|
526
|
+
* UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4.
|
|
527
|
+
* UTF8-1 = %x00-7F.
|
|
528
|
+
* UTF8-2 = %xC2-DF UTF8-tail.
|
|
529
|
+
* UTF8-3 = %xE0 %xA0-BF UTF8-tail.
|
|
530
|
+
* - %xE1-EC 2( UTF8-tail ).
|
|
531
|
+
* - %xED %x80-9F UTF8-tail.
|
|
532
|
+
* - %xEE-EF 2( UTF8-tail ).
|
|
533
|
+
* UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ).
|
|
534
|
+
* - %xF1-F3 3( UTF8-tail ).
|
|
535
|
+
* - %xF4 %x80-8F 2( UTF8-tail ).
|
|
536
|
+
* UTF8-tail = %x80-BF.
|
|
537
|
+
* @param data The data to check.
|
|
538
|
+
* @returns True if the data is utf8.
|
|
539
|
+
*/
|
|
540
|
+
static isUtf8(data) {
|
|
541
|
+
if (!Is.uint8Array(data)) {
|
|
542
|
+
return false;
|
|
543
|
+
}
|
|
544
|
+
let i = 0;
|
|
545
|
+
const len = data.length;
|
|
546
|
+
while (i < len) {
|
|
547
|
+
// UTF8-1 = %x00-7F
|
|
548
|
+
if (data[i] <= 0x7f) {
|
|
549
|
+
i++;
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
// UTF8-2 = %xC2-DF UTF8-tail
|
|
553
|
+
if (data[i] >= 0xc2 && data[i] <= 0xdf) {
|
|
554
|
+
// if(buf[i + 1] >= 0x80 && buf[i + 1] <= 0xBF) {
|
|
555
|
+
if (data[i + 1] >> 6 === 2) {
|
|
556
|
+
i += 2;
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
else {
|
|
560
|
+
return false;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
// UTF8-3 = %xE0 %xA0-BF UTF8-tail
|
|
564
|
+
// UTF8-3 = %xED %x80-9F UTF8-tail
|
|
565
|
+
if (((data[i] === 0xe0 && data[i + 1] >= 0xa0 && data[i + 1] <= 0xbf) ||
|
|
566
|
+
(data[i] === 0xed && data[i + 1] >= 0x80 && data[i + 1] <= 0x9f)) &&
|
|
567
|
+
data[i + 2] >> 6 === 2) {
|
|
568
|
+
i += 3;
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
// UTF8-3 = %xE1-EC 2( UTF8-tail )
|
|
572
|
+
// UTF8-3 = %xEE-EF 2( UTF8-tail )
|
|
573
|
+
if (((data[i] >= 0xe1 && data[i] <= 0xec) || (data[i] >= 0xee && data[i] <= 0xef)) &&
|
|
574
|
+
data[i + 1] >> 6 === 2 &&
|
|
575
|
+
data[i + 2] >> 6 === 2) {
|
|
576
|
+
i += 3;
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
579
|
+
// UTF8-4 = %xF0 %x90-BF 2( UTF8-tail )
|
|
580
|
+
// %xF1-F3 3( UTF8-tail )
|
|
581
|
+
// %xF4 %x80-8F 2( UTF8-tail )
|
|
582
|
+
if (((data[i] === 0xf0 && data[i + 1] >= 0x90 && data[i + 1] <= 0xbf) ||
|
|
583
|
+
(data[i] >= 0xf1 && data[i] <= 0xf3 && data[i + 1] >> 6 === 2) ||
|
|
584
|
+
(data[i] === 0xf4 && data[i + 1] >= 0x80 && data[i + 1] <= 0x8f)) &&
|
|
585
|
+
data[i + 2] >> 6 === 2 &&
|
|
586
|
+
data[i + 3] >> 6 === 2) {
|
|
587
|
+
i += 4;
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
return false;
|
|
591
|
+
}
|
|
592
|
+
return true;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// Copyright 2024 IOTA Stiftung.
|
|
597
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
598
|
+
/**
|
|
599
|
+
* Class to handle errors.
|
|
600
|
+
*/
|
|
601
|
+
class BaseError extends Error {
|
|
602
|
+
/**
|
|
603
|
+
* The source of the error.
|
|
604
|
+
*/
|
|
605
|
+
source;
|
|
606
|
+
/**
|
|
607
|
+
* Any additional information for the error.
|
|
608
|
+
*/
|
|
609
|
+
properties;
|
|
610
|
+
/**
|
|
611
|
+
* The inner error if there was one.
|
|
612
|
+
*/
|
|
613
|
+
inner;
|
|
614
|
+
/**
|
|
615
|
+
* Create a new instance of BaseError.
|
|
616
|
+
* @param name The name of the error.
|
|
617
|
+
* @param source The source of the error.
|
|
618
|
+
* @param message The message as a code.
|
|
619
|
+
* @param properties Any additional information for the error.
|
|
620
|
+
* @param inner The inner error if we have wrapped another error.
|
|
621
|
+
*/
|
|
622
|
+
constructor(name, source, message, properties, inner) {
|
|
623
|
+
super(message);
|
|
624
|
+
this.name = name;
|
|
625
|
+
this.source = source;
|
|
626
|
+
// If the message is camel case but has no namespace then prefix it
|
|
627
|
+
// with the source name in camel case
|
|
628
|
+
if (Is.stringValue(source) &&
|
|
629
|
+
Is.stringValue(message) &&
|
|
630
|
+
!message.includes(".") &&
|
|
631
|
+
StringHelper.camelCase(message) === message) {
|
|
632
|
+
this.message = `${StringHelper.camelCase(source)}.${message}`;
|
|
633
|
+
}
|
|
634
|
+
this.properties = properties;
|
|
635
|
+
this.inner = inner ? BaseError.fromError(inner).toJsonObject() : undefined;
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Construct an error from an existing one.
|
|
639
|
+
* @param err The existing error.
|
|
640
|
+
* @returns The new instance.
|
|
641
|
+
*/
|
|
642
|
+
static fromError(err) {
|
|
643
|
+
let name = "Base";
|
|
644
|
+
let message;
|
|
645
|
+
let source;
|
|
646
|
+
let properties;
|
|
647
|
+
let inner;
|
|
648
|
+
let stack;
|
|
649
|
+
if (Is.object(err)) {
|
|
650
|
+
if (Is.stringValue(err.name)) {
|
|
651
|
+
name = err.name;
|
|
652
|
+
}
|
|
653
|
+
if (Is.stringValue(err.source)) {
|
|
654
|
+
source = err.source;
|
|
655
|
+
}
|
|
656
|
+
if (Is.stringValue(err.message)) {
|
|
657
|
+
message = err.message;
|
|
658
|
+
}
|
|
659
|
+
if (Is.notEmpty(err.properties)) {
|
|
660
|
+
properties = err.properties;
|
|
661
|
+
}
|
|
662
|
+
if (Is.notEmpty(err.inner)) {
|
|
663
|
+
inner = err.inner;
|
|
664
|
+
}
|
|
665
|
+
if (Is.notEmpty(err.stack)) {
|
|
666
|
+
stack = err.stack;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
else if (Is.object(err) && Is.stringValue(err.error)) {
|
|
670
|
+
message = err.error;
|
|
671
|
+
}
|
|
672
|
+
else if (Is.stringValue(err)) {
|
|
673
|
+
message = err;
|
|
674
|
+
}
|
|
675
|
+
else {
|
|
676
|
+
message = JSON.stringify(err);
|
|
677
|
+
}
|
|
678
|
+
const baseError = new BaseError(name, source ?? "", message ?? "", properties, inner);
|
|
679
|
+
baseError.stack = stack;
|
|
680
|
+
return baseError;
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Flatten an error tree.
|
|
684
|
+
* @param err The starting error.
|
|
685
|
+
* @returns The list of all internal errors.
|
|
686
|
+
*/
|
|
687
|
+
static flatten(err) {
|
|
688
|
+
const flattened = [];
|
|
689
|
+
let e = BaseError.fromError(err).toJsonObject();
|
|
690
|
+
while (e) {
|
|
691
|
+
const inner = e.inner;
|
|
692
|
+
e.inner = undefined;
|
|
693
|
+
flattened.push(e);
|
|
694
|
+
e = inner;
|
|
695
|
+
}
|
|
696
|
+
return flattened;
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Expand an error tree.
|
|
700
|
+
* @param errors The list of errors to expand.
|
|
701
|
+
* @returns The first level error.
|
|
702
|
+
*/
|
|
703
|
+
static expand(errors) {
|
|
704
|
+
let first;
|
|
705
|
+
if (Is.arrayValue(errors)) {
|
|
706
|
+
first = errors[0];
|
|
707
|
+
let current = first;
|
|
708
|
+
for (let i = 1; i < errors.length; i++) {
|
|
709
|
+
current.inner = errors[i];
|
|
710
|
+
current = current.inner;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
return first;
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Test to see if the error has the specified error name.
|
|
717
|
+
* @param error The error to test.
|
|
718
|
+
* @param name The name to check for.
|
|
719
|
+
* @returns True if the error has the name.
|
|
720
|
+
*/
|
|
721
|
+
static isErrorName(error, name) {
|
|
722
|
+
return (Is.object(error) &&
|
|
723
|
+
(Is.string(name) ? error.name === name : name.test(error.name)));
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Test to see if the error has the specified error message.
|
|
727
|
+
* @param error The error to test.
|
|
728
|
+
* @param message The message to check for.
|
|
729
|
+
* @returns True if the error has the name.
|
|
730
|
+
*/
|
|
731
|
+
static isErrorMessage(error, message) {
|
|
732
|
+
return (Is.object(error) &&
|
|
733
|
+
(Is.string(message) ? error.message === message : message.test(error.message)));
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Test to see if the error has the specified error code.
|
|
737
|
+
* @param error The error to test.
|
|
738
|
+
* @param code The code to check for.
|
|
739
|
+
* @returns True if the error has the code.
|
|
740
|
+
*/
|
|
741
|
+
static isErrorCode(error, code) {
|
|
742
|
+
return (Is.object(error) &&
|
|
743
|
+
(Is.string(code) ? error.code === code : code.test(error.code)));
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Test to see if any of the errors or children have the given error name.
|
|
747
|
+
* @param error The error to test.
|
|
748
|
+
* @param name The name to check for.
|
|
749
|
+
* @returns True if the error has the name.
|
|
750
|
+
*/
|
|
751
|
+
static someErrorName(error, name) {
|
|
752
|
+
return BaseError.flatten(error).some(e => BaseError.isErrorName(e, name));
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Test to see if any of the errors or children have the given error message.
|
|
756
|
+
* @param error The error to test.
|
|
757
|
+
* @param message The message to check for.
|
|
758
|
+
* @returns True if the error has the name.
|
|
759
|
+
*/
|
|
760
|
+
static someErrorMessage(error, message) {
|
|
761
|
+
return BaseError.flatten(error).some(e => BaseError.isErrorMessage(e, message));
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* Test to see if any of the errors or children are from a specific class.
|
|
765
|
+
* @param error The error to test.
|
|
766
|
+
* @param cls The class to check for.
|
|
767
|
+
* @returns True if the error has the specific class.
|
|
768
|
+
*/
|
|
769
|
+
static someErrorClass(error, cls) {
|
|
770
|
+
const errorClass = StringHelper.camelCase(cls);
|
|
771
|
+
const regExp = new RegExp(`^${errorClass}\\.`);
|
|
772
|
+
return BaseError.flatten(error).some(e => BaseError.isErrorMessage(e, regExp));
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Test to see if any of the errors or children have the given error code.
|
|
776
|
+
* @param error The error to test.
|
|
777
|
+
* @param code The code to check for.
|
|
778
|
+
* @returns True if the error has the name.
|
|
779
|
+
*/
|
|
780
|
+
static someErrorCode(error, code) {
|
|
781
|
+
return BaseError.flatten(error).some(e => BaseError.isErrorCode(e, code));
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Serialize the error to the error model.
|
|
785
|
+
* @returns The error model.
|
|
786
|
+
*/
|
|
787
|
+
toJsonObject() {
|
|
788
|
+
const err = {};
|
|
789
|
+
if (Is.stringValue(this.name)) {
|
|
790
|
+
err.name = this.name;
|
|
791
|
+
}
|
|
792
|
+
if (Is.stringValue(this.source)) {
|
|
793
|
+
err.source = this.source;
|
|
794
|
+
}
|
|
795
|
+
if (Is.stringValue(this.message)) {
|
|
796
|
+
err.message = this.message;
|
|
797
|
+
}
|
|
798
|
+
if (Is.object(this.properties)) {
|
|
799
|
+
err.properties = this.properties;
|
|
800
|
+
}
|
|
801
|
+
if (Is.stringValue(this.stack)) {
|
|
802
|
+
err.stack = this.stack;
|
|
803
|
+
}
|
|
804
|
+
if (Is.notEmpty(this.inner)) {
|
|
805
|
+
err.inner = BaseError.fromError(this.inner).toJsonObject();
|
|
806
|
+
}
|
|
807
|
+
return err;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* Class to handle errors.
|
|
813
|
+
*/
|
|
814
|
+
class GeneralError extends BaseError {
|
|
815
|
+
/**
|
|
816
|
+
* Runtime name for the class.
|
|
817
|
+
*/
|
|
818
|
+
static CLASS_NAME = "GeneralError";
|
|
819
|
+
/**
|
|
820
|
+
* Create a new instance of GeneralError.
|
|
821
|
+
* @param source The source of the error.
|
|
822
|
+
* @param message The message as a code.
|
|
823
|
+
* @param properties Any additional information for the error.
|
|
824
|
+
* @param inner The inner error if we have wrapped another error.
|
|
825
|
+
*/
|
|
826
|
+
constructor(source, message, properties, inner) {
|
|
827
|
+
super(GeneralError.CLASS_NAME, source, message, properties, inner);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// Copyright 2024 IOTA Stiftung.
|
|
832
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
833
|
+
/* eslint-disable no-bitwise */
|
|
834
|
+
/**
|
|
835
|
+
* Class to help with base63 Encoding/Decoding.
|
|
836
|
+
*/
|
|
837
|
+
class Base32 {
|
|
838
|
+
/**
|
|
839
|
+
* Runtime name for the class.
|
|
840
|
+
* @internal
|
|
841
|
+
*/
|
|
842
|
+
static _CLASS_NAME = "Base32";
|
|
843
|
+
/**
|
|
844
|
+
* Alphabet table for encoding.
|
|
845
|
+
* @internal
|
|
846
|
+
*/
|
|
847
|
+
static _ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
848
|
+
/**
|
|
849
|
+
* Convert the base 32 string to a byte array.
|
|
850
|
+
* @param base32 The base32 string to convert.
|
|
851
|
+
* @returns The byte array.
|
|
852
|
+
* @throws If the input string contains a character not in the Base32 alphabet.
|
|
853
|
+
*/
|
|
854
|
+
static decode(base32) {
|
|
855
|
+
let bits = 0;
|
|
856
|
+
let value = 0;
|
|
857
|
+
base32 = base32.replace(/=+$/, "");
|
|
858
|
+
let index = 0;
|
|
859
|
+
const output = new Uint8Array(Math.trunc((base32.length * 5) / 8));
|
|
860
|
+
for (let i = 0; i < base32.length; i++) {
|
|
861
|
+
const idx = Base32._ALPHABET.indexOf(base32[i]);
|
|
862
|
+
if (idx === -1) {
|
|
863
|
+
throw new GeneralError(Base32._CLASS_NAME, "invalidCharacter", {
|
|
864
|
+
invalidCharacter: base32[i]
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
value = (value << 5) | idx;
|
|
868
|
+
bits += 5;
|
|
869
|
+
if (bits >= 8) {
|
|
870
|
+
output[index++] = (value >>> (bits - 8)) & 255;
|
|
871
|
+
bits -= 8;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
return output;
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Convert a byte array to base 32.
|
|
878
|
+
* @param bytes The byte array to convert.
|
|
879
|
+
* @returns The data as base32 string.
|
|
880
|
+
*/
|
|
881
|
+
static encode(bytes) {
|
|
882
|
+
let bits = 0;
|
|
883
|
+
let value = 0;
|
|
884
|
+
let output = "";
|
|
885
|
+
for (let i = 0; i < bytes.byteLength; i++) {
|
|
886
|
+
value = (value << 8) | bytes[i];
|
|
887
|
+
bits += 8;
|
|
888
|
+
while (bits >= 5) {
|
|
889
|
+
output += Base32._ALPHABET[(value >>> (bits - 5)) & 31];
|
|
890
|
+
bits -= 5;
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
if (bits > 0) {
|
|
894
|
+
output += Base32._ALPHABET[(value << (5 - bits)) & 31];
|
|
895
|
+
}
|
|
896
|
+
while (output.length % 8 !== 0) {
|
|
897
|
+
output += "=";
|
|
898
|
+
}
|
|
899
|
+
return output;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
// Copyright 2024 IOTA Stiftung.
|
|
904
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
905
|
+
/* eslint-disable no-bitwise */
|
|
906
|
+
/* eslint-disable no-mixed-operators */
|
|
907
|
+
/**
|
|
908
|
+
* Class to help with base64 Encoding/Decoding.
|
|
909
|
+
* Sourced from https://github.com/beatgammit/base64-js.
|
|
910
|
+
*/
|
|
911
|
+
class Base64 {
|
|
912
|
+
/**
|
|
913
|
+
* Runtime name for the class.
|
|
914
|
+
* @internal
|
|
915
|
+
*/
|
|
916
|
+
static _CLASS_NAME = "Base64";
|
|
917
|
+
/**
|
|
918
|
+
* Alphabet table for encoding.
|
|
919
|
+
* @internal
|
|
920
|
+
*/
|
|
921
|
+
static _LOOKUP = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
922
|
+
/**
|
|
923
|
+
* Alphabet table for decoding.
|
|
924
|
+
* @internal
|
|
925
|
+
*/
|
|
926
|
+
static _REVERSE_LOOKUP = {
|
|
927
|
+
"43": 62,
|
|
928
|
+
"45": 62,
|
|
929
|
+
"47": 63,
|
|
930
|
+
"48": 52,
|
|
931
|
+
"49": 53,
|
|
932
|
+
"50": 54,
|
|
933
|
+
"51": 55,
|
|
934
|
+
"52": 56,
|
|
935
|
+
"53": 57,
|
|
936
|
+
"54": 58,
|
|
937
|
+
"55": 59,
|
|
938
|
+
"56": 60,
|
|
939
|
+
"57": 61,
|
|
940
|
+
"65": 0,
|
|
941
|
+
"66": 1,
|
|
942
|
+
"67": 2,
|
|
943
|
+
"68": 3,
|
|
944
|
+
"69": 4,
|
|
945
|
+
"70": 5,
|
|
946
|
+
"71": 6,
|
|
947
|
+
"72": 7,
|
|
948
|
+
"73": 8,
|
|
949
|
+
"74": 9,
|
|
950
|
+
"75": 10,
|
|
951
|
+
"76": 11,
|
|
952
|
+
"77": 12,
|
|
953
|
+
"78": 13,
|
|
954
|
+
"79": 14,
|
|
955
|
+
"80": 15,
|
|
956
|
+
"81": 16,
|
|
957
|
+
"82": 17,
|
|
958
|
+
"83": 18,
|
|
959
|
+
"84": 19,
|
|
960
|
+
"85": 20,
|
|
961
|
+
"86": 21,
|
|
962
|
+
"87": 22,
|
|
963
|
+
"88": 23,
|
|
964
|
+
"89": 24,
|
|
965
|
+
"90": 25,
|
|
966
|
+
"95": 63,
|
|
967
|
+
"97": 26,
|
|
968
|
+
"98": 27,
|
|
969
|
+
"99": 28,
|
|
970
|
+
"100": 29,
|
|
971
|
+
"101": 30,
|
|
972
|
+
"102": 31,
|
|
973
|
+
"103": 32,
|
|
974
|
+
"104": 33,
|
|
975
|
+
"105": 34,
|
|
976
|
+
"106": 35,
|
|
977
|
+
"107": 36,
|
|
978
|
+
"108": 37,
|
|
979
|
+
"109": 38,
|
|
980
|
+
"110": 39,
|
|
981
|
+
"111": 40,
|
|
982
|
+
"112": 41,
|
|
983
|
+
"113": 42,
|
|
984
|
+
"114": 43,
|
|
985
|
+
"115": 44,
|
|
986
|
+
"116": 45,
|
|
987
|
+
"117": 46,
|
|
988
|
+
"118": 47,
|
|
989
|
+
"119": 48,
|
|
990
|
+
"120": 49,
|
|
991
|
+
"121": 50,
|
|
992
|
+
"122": 51
|
|
993
|
+
};
|
|
994
|
+
/**
|
|
995
|
+
* Get the byte length of the data.
|
|
996
|
+
* @param base64 The base64 string.
|
|
997
|
+
* @returns The byte length of the data.
|
|
998
|
+
*/
|
|
999
|
+
static byteLength(base64) {
|
|
1000
|
+
const lens = Base64.getLengths(base64);
|
|
1001
|
+
return Base64.calcByteLength(lens[0], lens[1]);
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Convert the base 64 string to a byte array.
|
|
1005
|
+
* @param base64 The base64 string to convert.
|
|
1006
|
+
* @returns The byte array.
|
|
1007
|
+
*/
|
|
1008
|
+
static decode(base64) {
|
|
1009
|
+
let tmp;
|
|
1010
|
+
const lens = Base64.getLengths(base64);
|
|
1011
|
+
const validLen = lens[0];
|
|
1012
|
+
const placeHoldersLen = lens[1];
|
|
1013
|
+
const arr = new Uint8Array(Base64.calcByteLength(validLen, placeHoldersLen));
|
|
1014
|
+
let curByte = 0;
|
|
1015
|
+
// if there are placeholders, only get up to the last complete 4 chars
|
|
1016
|
+
const len = placeHoldersLen > 0 ? validLen - 4 : validLen;
|
|
1017
|
+
let i;
|
|
1018
|
+
for (i = 0; i < len; i += 4) {
|
|
1019
|
+
tmp =
|
|
1020
|
+
(Base64._REVERSE_LOOKUP[base64.charCodeAt(i)] << 18) |
|
|
1021
|
+
(Base64._REVERSE_LOOKUP[base64.charCodeAt(i + 1)] << 12) |
|
|
1022
|
+
(Base64._REVERSE_LOOKUP[base64.charCodeAt(i + 2)] << 6) |
|
|
1023
|
+
Base64._REVERSE_LOOKUP[base64.charCodeAt(i + 3)];
|
|
1024
|
+
arr[curByte++] = (tmp >> 16) & 0xff;
|
|
1025
|
+
arr[curByte++] = (tmp >> 8) & 0xff;
|
|
1026
|
+
arr[curByte++] = tmp & 0xff;
|
|
1027
|
+
}
|
|
1028
|
+
if (placeHoldersLen === 2) {
|
|
1029
|
+
tmp =
|
|
1030
|
+
(Base64._REVERSE_LOOKUP[base64.charCodeAt(i)] << 2) |
|
|
1031
|
+
(Base64._REVERSE_LOOKUP[base64.charCodeAt(i + 1)] >> 4);
|
|
1032
|
+
arr[curByte++] = tmp & 0xff;
|
|
1033
|
+
}
|
|
1034
|
+
if (placeHoldersLen === 1) {
|
|
1035
|
+
tmp =
|
|
1036
|
+
(Base64._REVERSE_LOOKUP[base64.charCodeAt(i)] << 10) |
|
|
1037
|
+
(Base64._REVERSE_LOOKUP[base64.charCodeAt(i + 1)] << 4) |
|
|
1038
|
+
(Base64._REVERSE_LOOKUP[base64.charCodeAt(i + 2)] >> 2);
|
|
1039
|
+
arr[curByte++] = (tmp >> 8) & 0xff;
|
|
1040
|
+
arr[curByte++] = tmp & 0xff;
|
|
1041
|
+
}
|
|
1042
|
+
return arr;
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Convert a byte array to base 64.
|
|
1046
|
+
* @param bytes The byte array to convert.
|
|
1047
|
+
* @returns The data as base64 string.
|
|
1048
|
+
*/
|
|
1049
|
+
static encode(bytes) {
|
|
1050
|
+
let tmp;
|
|
1051
|
+
const len = bytes.length;
|
|
1052
|
+
const extraBytes = len % 3; // if we have 1 byte left, pad 2 bytes
|
|
1053
|
+
const parts = [];
|
|
1054
|
+
const maxChunkLength = 16383; // must be multiple of 3
|
|
1055
|
+
// go through the array every three bytes, we'll deal with trailing stuff later
|
|
1056
|
+
for (let i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
|
|
1057
|
+
parts.push(Base64.encodeChunk(bytes, i, i + maxChunkLength > len2 ? len2 : i + maxChunkLength));
|
|
1058
|
+
}
|
|
1059
|
+
// pad the end with zeros, but make sure to not forget the extra bytes
|
|
1060
|
+
if (extraBytes === 1) {
|
|
1061
|
+
tmp = bytes[len - 1];
|
|
1062
|
+
parts.push(`${Base64._LOOKUP[tmp >> 2] + Base64._LOOKUP[(tmp << 4) & 0x3f]}==`);
|
|
1063
|
+
}
|
|
1064
|
+
else if (extraBytes === 2) {
|
|
1065
|
+
tmp = (bytes[len - 2] << 8) + bytes[len - 1];
|
|
1066
|
+
parts.push(`${Base64._LOOKUP[tmp >> 10] + Base64._LOOKUP[(tmp >> 4) & 0x3f] + Base64._LOOKUP[(tmp << 2) & 0x3f]}=`);
|
|
1067
|
+
}
|
|
1068
|
+
return parts.join("");
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Calculate the byte length.
|
|
1072
|
+
* @param validLen The valid length.
|
|
1073
|
+
* @param placeHoldersLen The placeholder length.
|
|
1074
|
+
* @returns The length.
|
|
1075
|
+
* @internal
|
|
1076
|
+
*/
|
|
1077
|
+
static calcByteLength(validLen, placeHoldersLen) {
|
|
1078
|
+
return ((validLen + placeHoldersLen) * 3) / 4 - placeHoldersLen;
|
|
1079
|
+
}
|
|
1080
|
+
/**
|
|
1081
|
+
* Get the valid and placeholder lengths from a bas64 string.
|
|
1082
|
+
* @param base64 The base64 string.
|
|
1083
|
+
* @returns The lengths.
|
|
1084
|
+
* @internal
|
|
1085
|
+
*/
|
|
1086
|
+
static getLengths(base64) {
|
|
1087
|
+
const len = base64.length;
|
|
1088
|
+
if (len % 4 > 0) {
|
|
1089
|
+
throw new GeneralError(Base64._CLASS_NAME, "length4Multiple", { value: len });
|
|
1090
|
+
}
|
|
1091
|
+
// Trim off extra bytes after placeholder bytes are found
|
|
1092
|
+
// See: https://github.com/beatgammit/base64-js/issues/42
|
|
1093
|
+
let validLen = base64.indexOf("=");
|
|
1094
|
+
if (validLen === -1) {
|
|
1095
|
+
validLen = len;
|
|
1096
|
+
}
|
|
1097
|
+
const placeHoldersLen = validLen === len ? 0 : 4 - (validLen % 4);
|
|
1098
|
+
return [validLen, placeHoldersLen];
|
|
1099
|
+
}
|
|
1100
|
+
/**
|
|
1101
|
+
* Convert the triplet to base 64.
|
|
1102
|
+
* @param num The number to convert.
|
|
1103
|
+
* @returns The base64 encoding.
|
|
1104
|
+
* @internal
|
|
1105
|
+
*/
|
|
1106
|
+
static tripletToBase64(num) {
|
|
1107
|
+
return (Base64._LOOKUP[(num >> 18) & 0x3f] +
|
|
1108
|
+
Base64._LOOKUP[(num >> 12) & 0x3f] +
|
|
1109
|
+
Base64._LOOKUP[(num >> 6) & 0x3f] +
|
|
1110
|
+
Base64._LOOKUP[num & 0x3f]);
|
|
1111
|
+
}
|
|
1112
|
+
/**
|
|
1113
|
+
* Encode a chunk.
|
|
1114
|
+
* @param bytes The byte array.
|
|
1115
|
+
* @param start The start index in the buffer.
|
|
1116
|
+
* @param end The end index in the buffer.
|
|
1117
|
+
* @returns The encoded chunk.
|
|
1118
|
+
* @internal
|
|
1119
|
+
*/
|
|
1120
|
+
static encodeChunk(bytes, start, end) {
|
|
1121
|
+
let tmp;
|
|
1122
|
+
const output = [];
|
|
1123
|
+
for (let i = start; i < end; i += 3) {
|
|
1124
|
+
tmp = ((bytes[i] << 16) & 0xff0000) + ((bytes[i + 1] << 8) & 0xff00) + (bytes[i + 2] & 0xff);
|
|
1125
|
+
output.push(Base64.tripletToBase64(tmp));
|
|
1126
|
+
}
|
|
1127
|
+
return output.join("");
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
// Copyright 2024 IOTA Stiftung.
|
|
1132
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
1133
|
+
/**
|
|
1134
|
+
* Class to help with base64 URL Encoding/Decoding.
|
|
1135
|
+
* https://www.rfc-editor.org/rfc/rfc4648#section-5.
|
|
1136
|
+
*/
|
|
1137
|
+
class Base64Url {
|
|
1138
|
+
/**
|
|
1139
|
+
* Convert the base 64 string to a byte array.
|
|
1140
|
+
* @param base64Url The base64 url string to convert.
|
|
1141
|
+
* @returns The byte array.
|
|
1142
|
+
*/
|
|
1143
|
+
static decode(base64Url) {
|
|
1144
|
+
let base64 = base64Url;
|
|
1145
|
+
// Base 64 url can have padding removed, so add it back if it is missing.
|
|
1146
|
+
if (base64.length > 0 && !base64.endsWith("=")) {
|
|
1147
|
+
const placeHoldersLen = 4 - (base64.length % 4);
|
|
1148
|
+
if (placeHoldersLen > 0 && placeHoldersLen < 4) {
|
|
1149
|
+
base64 = base64.padEnd(base64.length + placeHoldersLen, "=");
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
base64 = base64.replace(/-/g, "+").replace(/_/g, "/");
|
|
1153
|
+
return Base64.decode(base64);
|
|
1154
|
+
}
|
|
1155
|
+
/**
|
|
1156
|
+
* Convert a byte array to base 64 url.
|
|
1157
|
+
* @param bytes The byte array to convert.
|
|
1158
|
+
* @returns The data as base64 url string.
|
|
1159
|
+
*/
|
|
1160
|
+
static encode(bytes) {
|
|
1161
|
+
const base64 = Base64.encode(bytes);
|
|
1162
|
+
// Base 64 url can have padding removed, so remove it.
|
|
1163
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
/**
|
|
1168
|
+
* Class to handle errors which are triggered by data already existing.
|
|
1169
|
+
*/
|
|
1170
|
+
class AlreadyExistsError extends BaseError {
|
|
1171
|
+
/**
|
|
1172
|
+
* Runtime name for the class.
|
|
1173
|
+
*/
|
|
1174
|
+
static CLASS_NAME = "AlreadyExistsError";
|
|
1175
|
+
/**
|
|
1176
|
+
* Create a new instance of AlreadyExistsError.
|
|
1177
|
+
* @param source The source of the error.
|
|
1178
|
+
* @param message The message as a code.
|
|
1179
|
+
* @param existingId The id for the item.
|
|
1180
|
+
* @param inner The inner error if we have wrapped another error.
|
|
1181
|
+
*/
|
|
1182
|
+
constructor(source, message, existingId, inner) {
|
|
1183
|
+
super(AlreadyExistsError.CLASS_NAME, source, message, { existingId }, inner);
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
/**
|
|
1188
|
+
* Class to handle errors which are triggered by conflicting data.
|
|
1189
|
+
*/
|
|
1190
|
+
class ConflictError extends BaseError {
|
|
1191
|
+
/**
|
|
1192
|
+
* Runtime name for the class.
|
|
1193
|
+
*/
|
|
1194
|
+
static CLASS_NAME = "ConflictError";
|
|
1195
|
+
/**
|
|
1196
|
+
* Create a new instance of ConflictError.
|
|
1197
|
+
* @param source The source of the error.
|
|
1198
|
+
* @param message The message as a code.
|
|
1199
|
+
* @param conflictId The id that has conflicts.
|
|
1200
|
+
* @param conflicts The conflicts that occurred.
|
|
1201
|
+
* @param inner The inner error if we have wrapped another error.
|
|
1202
|
+
*/
|
|
1203
|
+
constructor(source, message, conflictId, conflicts, inner) {
|
|
1204
|
+
super(ConflictError.CLASS_NAME, source, message, { conflictId, conflicts }, inner);
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
/**
|
|
1209
|
+
* Class to handle errors which are triggered by data guards.
|
|
1210
|
+
*/
|
|
1211
|
+
class GuardError extends BaseError {
|
|
1212
|
+
/**
|
|
1213
|
+
* Runtime name for the class.
|
|
1214
|
+
*/
|
|
1215
|
+
static CLASS_NAME = "GuardError";
|
|
1216
|
+
/**
|
|
1217
|
+
* Create a new instance of GuardError.
|
|
1218
|
+
* @param source The source of the error.
|
|
1219
|
+
* @param message The message as a code.
|
|
1220
|
+
* @param propertyName The property which triggered the guard error for the item.
|
|
1221
|
+
* @param propertyValue The property value which triggered the guard error for the item.
|
|
1222
|
+
* @param propertyOptions The property options which might be allowed.
|
|
1223
|
+
*/
|
|
1224
|
+
constructor(source, message, propertyName, propertyValue, propertyOptions) {
|
|
1225
|
+
super(GuardError.CLASS_NAME, source, message, {
|
|
1226
|
+
property: propertyName ?? "property",
|
|
1227
|
+
value: Is.undefined(propertyValue) ? "undefined" : propertyValue,
|
|
1228
|
+
options: propertyOptions
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
/**
|
|
1234
|
+
* Class to handle errors which are triggered by data not being found.
|
|
1235
|
+
*/
|
|
1236
|
+
class NotFoundError extends BaseError {
|
|
1237
|
+
/**
|
|
1238
|
+
* Runtime name for the class.
|
|
1239
|
+
*/
|
|
1240
|
+
static CLASS_NAME = "NotFoundError";
|
|
1241
|
+
/**
|
|
1242
|
+
* Create a new instance of NotFoundError.
|
|
1243
|
+
* @param source The source of the error.
|
|
1244
|
+
* @param message The message as a code.
|
|
1245
|
+
* @param notFoundId The id for the item.
|
|
1246
|
+
* @param inner The inner error if we have wrapped another error.
|
|
1247
|
+
*/
|
|
1248
|
+
constructor(source, message, notFoundId, inner) {
|
|
1249
|
+
super(NotFoundError.CLASS_NAME, source, message, { notFoundId }, inner);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
/**
|
|
1254
|
+
* Class to handle errors.
|
|
1255
|
+
*/
|
|
1256
|
+
class NotImplementedError extends BaseError {
|
|
1257
|
+
/**
|
|
1258
|
+
* Runtime name for the class.
|
|
1259
|
+
*/
|
|
1260
|
+
static CLASS_NAME = "NotImplementedError";
|
|
1261
|
+
/**
|
|
1262
|
+
* Create a new instance of NotImplementedError.
|
|
1263
|
+
* @param source The source of the error.
|
|
1264
|
+
* @param method The method for the error.
|
|
1265
|
+
*/
|
|
1266
|
+
constructor(source, method) {
|
|
1267
|
+
super(NotImplementedError.CLASS_NAME, source, "common.notImplementedMethod", {
|
|
1268
|
+
method
|
|
1269
|
+
});
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
/**
|
|
1274
|
+
* Class to handle errors when a feature is unsupported.
|
|
1275
|
+
*/
|
|
1276
|
+
class NotSupportedError extends BaseError {
|
|
1277
|
+
/**
|
|
1278
|
+
* Runtime name for the class.
|
|
1279
|
+
*/
|
|
1280
|
+
static CLASS_NAME = "NotSupportedError";
|
|
1281
|
+
/**
|
|
1282
|
+
* Create a new instance of NotSupportedError.
|
|
1283
|
+
* @param source The source of the error.
|
|
1284
|
+
* @param message The message as a code.
|
|
1285
|
+
* @param inner The inner error if we have wrapped another error.
|
|
1286
|
+
*/
|
|
1287
|
+
constructor(source, message, inner) {
|
|
1288
|
+
super(NotSupportedError.CLASS_NAME, source, message, undefined, inner);
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
/**
|
|
1293
|
+
* Class to handle errors which are triggered by access not being unauthorized.
|
|
1294
|
+
*/
|
|
1295
|
+
class UnauthorizedError extends BaseError {
|
|
1296
|
+
/**
|
|
1297
|
+
* Runtime name for the class.
|
|
1298
|
+
*/
|
|
1299
|
+
static CLASS_NAME = "UnauthorizedError";
|
|
1300
|
+
/**
|
|
1301
|
+
* Create a new instance of UnauthorizedError.
|
|
1302
|
+
* @param source The source of the error.
|
|
1303
|
+
* @param message The message as a code.
|
|
1304
|
+
* @param inner The inner error if we have wrapped another error.
|
|
1305
|
+
*/
|
|
1306
|
+
constructor(source, message, inner) {
|
|
1307
|
+
super(UnauthorizedError.CLASS_NAME, source, message, undefined, inner);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
/**
|
|
1312
|
+
* Class to handle errors when some data can not be processed.
|
|
1313
|
+
*/
|
|
1314
|
+
class UnprocessableError extends BaseError {
|
|
1315
|
+
/**
|
|
1316
|
+
* Runtime name for the class.
|
|
1317
|
+
*/
|
|
1318
|
+
static CLASS_NAME = "UnprocessableError";
|
|
1319
|
+
/**
|
|
1320
|
+
* Create a new instance of UnprocessableError.
|
|
1321
|
+
* @param source The source of the error.
|
|
1322
|
+
* @param message The message as a code.
|
|
1323
|
+
* @param properties Any additional information for the error.
|
|
1324
|
+
* @param inner The inner error if we have wrapped another error.
|
|
1325
|
+
*/
|
|
1326
|
+
constructor(source, message, properties, inner) {
|
|
1327
|
+
super(UnprocessableError.CLASS_NAME, source, message, properties, inner);
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
/**
|
|
1332
|
+
* Class to handle errors which are triggered by entity validation.
|
|
1333
|
+
*/
|
|
1334
|
+
class ValidationError extends BaseError {
|
|
1335
|
+
/**
|
|
1336
|
+
* Runtime name for the class.s
|
|
1337
|
+
*/
|
|
1338
|
+
static CLASS_NAME = "ValidationError";
|
|
1339
|
+
/**
|
|
1340
|
+
* Create a new instance of ValidationError.
|
|
1341
|
+
* @param source The source of the error.
|
|
1342
|
+
* @param validationObject The object that failed validation.
|
|
1343
|
+
* @param validationFailures The validation failures.
|
|
1344
|
+
*/
|
|
1345
|
+
constructor(source, validationObject, validationFailures) {
|
|
1346
|
+
super(ValidationError.CLASS_NAME, source, "common.validation", {
|
|
1347
|
+
validationObject,
|
|
1348
|
+
validationFailures
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
// Copyright 2024 IOTA Stiftung.
|
|
1354
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
1355
|
+
/**
|
|
1356
|
+
* Class to handle guard operations for parameters.
|
|
1357
|
+
*/
|
|
1358
|
+
class Guards {
|
|
1359
|
+
/**
|
|
1360
|
+
* Is the property defined.
|
|
1361
|
+
* @param source The source of the error.
|
|
1362
|
+
* @param property The name of the property.
|
|
1363
|
+
* @param value The value to test.
|
|
1364
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1365
|
+
*/
|
|
1366
|
+
static defined(source, property, value) {
|
|
1367
|
+
if (Is.undefined(value)) {
|
|
1368
|
+
throw new GuardError(source, "guard.undefined", property, value);
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
/**
|
|
1372
|
+
* Is the property a string.
|
|
1373
|
+
* @param source The source of the error.
|
|
1374
|
+
* @param property The name of the property.
|
|
1375
|
+
* @param value The value to test.
|
|
1376
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1377
|
+
*/
|
|
1378
|
+
static string(source, property, value) {
|
|
1379
|
+
if (!Is.string(value)) {
|
|
1380
|
+
throw new GuardError(source, "guard.string", property, value);
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
/**
|
|
1384
|
+
* Is the property a string with a value.
|
|
1385
|
+
* @param source The source of the error.
|
|
1386
|
+
* @param property The name of the property.
|
|
1387
|
+
* @param value The value to test.
|
|
1388
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1389
|
+
*/
|
|
1390
|
+
static stringValue(source, property, value) {
|
|
1391
|
+
if (!Is.string(value)) {
|
|
1392
|
+
throw new GuardError(source, "guard.string", property, value);
|
|
1393
|
+
}
|
|
1394
|
+
if (value.length === 0) {
|
|
1395
|
+
throw new GuardError(source, "guard.stringEmpty", property, value);
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
/**
|
|
1399
|
+
* Is the property a base64 string.
|
|
1400
|
+
* @param source The source of the error.
|
|
1401
|
+
* @param property The name of the property.
|
|
1402
|
+
* @param value The value to test.
|
|
1403
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1404
|
+
*/
|
|
1405
|
+
static stringBase64(source, property, value) {
|
|
1406
|
+
if (!Is.stringBase64(value)) {
|
|
1407
|
+
throw new GuardError(source, "guard.base64", property, value);
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
/**
|
|
1411
|
+
* Is the property a base64 url string.
|
|
1412
|
+
* @param source The source of the error.
|
|
1413
|
+
* @param property The name of the property.
|
|
1414
|
+
* @param value The value to test.
|
|
1415
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1416
|
+
*/
|
|
1417
|
+
static stringBase64Url(source, property, value) {
|
|
1418
|
+
if (!Is.stringBase64Url(value)) {
|
|
1419
|
+
throw new GuardError(source, "guard.base64Url", property, value);
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
/**
|
|
1423
|
+
* Is the property a string with a hex value.
|
|
1424
|
+
* @param source The source of the error.
|
|
1425
|
+
* @param property The name of the property.
|
|
1426
|
+
* @param value The value to test.
|
|
1427
|
+
* @param allowPrefix Allow the hex to have the 0x prefix.
|
|
1428
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1429
|
+
*/
|
|
1430
|
+
static stringHex(source, property, value, allowPrefix = false) {
|
|
1431
|
+
Guards.stringValue(source, property, value);
|
|
1432
|
+
if (!HexHelper.isHex(value, allowPrefix)) {
|
|
1433
|
+
throw new GuardError(source, "guard.stringHex", property, value);
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
/**
|
|
1437
|
+
* Is the property a string with a hex value with fixed length.
|
|
1438
|
+
* @param source The source of the error.
|
|
1439
|
+
* @param property The name of the property.
|
|
1440
|
+
* @param value The value to test.
|
|
1441
|
+
* @param length The length of the string to match.
|
|
1442
|
+
* @param allowPrefix Allow the hex to have the 0x prefix.
|
|
1443
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1444
|
+
*/
|
|
1445
|
+
static stringHexLength(source, property, value, length, allowPrefix = false) {
|
|
1446
|
+
Guards.stringHex(source, property, value, allowPrefix);
|
|
1447
|
+
if (HexHelper.stripPrefix(value).length !== length) {
|
|
1448
|
+
throw new GuardError(source, "guard.stringHexLength", property, value.length, length.toString());
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
/**
|
|
1452
|
+
* Is the property a number.
|
|
1453
|
+
* @param source The source of the error.
|
|
1454
|
+
* @param property The name of the property.
|
|
1455
|
+
* @param value The value to test.
|
|
1456
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1457
|
+
*/
|
|
1458
|
+
static number(source, property, value) {
|
|
1459
|
+
if (!Is.number(value)) {
|
|
1460
|
+
throw new GuardError(source, "guard.number", property, value);
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
/**
|
|
1464
|
+
* Is the property an integer.
|
|
1465
|
+
* @param source The source of the error.
|
|
1466
|
+
* @param property The name of the property.
|
|
1467
|
+
* @param value The value to test.
|
|
1468
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1469
|
+
*/
|
|
1470
|
+
static integer(source, property, value) {
|
|
1471
|
+
if (!Is.integer(value)) {
|
|
1472
|
+
throw new GuardError(source, "guard.integer", property, value);
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
/**
|
|
1476
|
+
* Is the property a bigint.
|
|
1477
|
+
* @param source The source of the error.
|
|
1478
|
+
* @param property The name of the property.
|
|
1479
|
+
* @param value The value to test.
|
|
1480
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1481
|
+
*/
|
|
1482
|
+
static bigint(source, property, value) {
|
|
1483
|
+
if (!Is.bigint(value)) {
|
|
1484
|
+
throw new GuardError(source, "guard.bigint", property, value);
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
/**
|
|
1488
|
+
* Is the property a boolean.
|
|
1489
|
+
* @param source The source of the error.
|
|
1490
|
+
* @param property The name of the property.
|
|
1491
|
+
* @param value The value to test.
|
|
1492
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1493
|
+
*/
|
|
1494
|
+
static boolean(source, property, value) {
|
|
1495
|
+
if (!Is.boolean(value)) {
|
|
1496
|
+
throw new GuardError(source, "guard.boolean", property, value);
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
/**
|
|
1500
|
+
* Is the property a date.
|
|
1501
|
+
* @param source The source of the error.
|
|
1502
|
+
* @param property The name of the property.
|
|
1503
|
+
* @param value The value to test.
|
|
1504
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1505
|
+
*/
|
|
1506
|
+
static date(source, property, value) {
|
|
1507
|
+
if (!Is.date(value)) {
|
|
1508
|
+
throw new GuardError(source, "guard.date", property, value);
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
/**
|
|
1512
|
+
* Is the property a timestamp in milliseconds.
|
|
1513
|
+
* @param source The source of the error.
|
|
1514
|
+
* @param property The name of the property.
|
|
1515
|
+
* @param value The value to test.
|
|
1516
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1517
|
+
*/
|
|
1518
|
+
static timestampMilliseconds(source, property, value) {
|
|
1519
|
+
if (!Is.timestampMilliseconds(value)) {
|
|
1520
|
+
throw new GuardError(source, "guard.timestampMilliseconds", property, value);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
/**
|
|
1524
|
+
* Is the property a timestamp in seconds.
|
|
1525
|
+
* @param source The source of the error.
|
|
1526
|
+
* @param property The name of the property.
|
|
1527
|
+
* @param value The value to test.
|
|
1528
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1529
|
+
*/
|
|
1530
|
+
static timestampSeconds(source, property, value) {
|
|
1531
|
+
if (!Is.timestampSeconds(value)) {
|
|
1532
|
+
throw new GuardError(source, "guard.timestampSeconds", property, value);
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
/**
|
|
1536
|
+
* Is the property an object.
|
|
1537
|
+
* @param source The source of the error.
|
|
1538
|
+
* @param property The name of the property.
|
|
1539
|
+
* @param value The value to test.
|
|
1540
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1541
|
+
*/
|
|
1542
|
+
static object(source, property, value) {
|
|
1543
|
+
if (Is.undefined(value)) {
|
|
1544
|
+
throw new GuardError(source, "guard.objectUndefined", property, value);
|
|
1545
|
+
}
|
|
1546
|
+
if (!Is.object(value)) {
|
|
1547
|
+
throw new GuardError(source, "guard.object", property, value);
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
/**
|
|
1551
|
+
* Is the property is an object with at least one property.
|
|
1552
|
+
* @param source The source of the error.
|
|
1553
|
+
* @param property The name of the property.
|
|
1554
|
+
* @param value The value to test.
|
|
1555
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1556
|
+
*/
|
|
1557
|
+
static objectValue(source, property, value) {
|
|
1558
|
+
if (Is.undefined(value)) {
|
|
1559
|
+
throw new GuardError(source, "guard.objectUndefined", property, value);
|
|
1560
|
+
}
|
|
1561
|
+
if (!Is.object(value)) {
|
|
1562
|
+
throw new GuardError(source, "guard.object", property, value);
|
|
1563
|
+
}
|
|
1564
|
+
if (Object.keys(value || {}).length === 0) {
|
|
1565
|
+
throw new GuardError(source, "guard.objectValue", property, value);
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
/**
|
|
1569
|
+
* Is the property is an array.
|
|
1570
|
+
* @param source The source of the error.
|
|
1571
|
+
* @param property The name of the property.
|
|
1572
|
+
* @param value The value to test.
|
|
1573
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1574
|
+
*/
|
|
1575
|
+
static array(source, property, value) {
|
|
1576
|
+
if (!Is.array(value)) {
|
|
1577
|
+
throw new GuardError(source, "guard.array", property, value);
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
/**
|
|
1581
|
+
* Is the property is an array with at least one item.
|
|
1582
|
+
* @param source The source of the error.
|
|
1583
|
+
* @param property The name of the property.
|
|
1584
|
+
* @param value The value to test.
|
|
1585
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1586
|
+
*/
|
|
1587
|
+
static arrayValue(source, property, value) {
|
|
1588
|
+
if (!Is.array(value)) {
|
|
1589
|
+
throw new GuardError(source, "guard.array", property, value);
|
|
1590
|
+
}
|
|
1591
|
+
if (value.length === 0) {
|
|
1592
|
+
throw new GuardError(source, "guard.arrayValue", property, value);
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
/**
|
|
1596
|
+
* Is the property one of a list of items.
|
|
1597
|
+
* @param source The source of the error.
|
|
1598
|
+
* @param property The name of the property.
|
|
1599
|
+
* @param value The value to test.
|
|
1600
|
+
* @param options The options the value must be one of.
|
|
1601
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1602
|
+
*/
|
|
1603
|
+
static arrayOneOf(source, property, value, options) {
|
|
1604
|
+
if (!Is.array(options)) {
|
|
1605
|
+
throw new GuardError(source, "guard.array", property, value);
|
|
1606
|
+
}
|
|
1607
|
+
if (!options.includes(value)) {
|
|
1608
|
+
throw new GuardError(source, "guard.arrayOneOf", property, value, options.join(", "));
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
/**
|
|
1612
|
+
* Is the property a Uint8Array.
|
|
1613
|
+
* @param source The source of the error.
|
|
1614
|
+
* @param property The name of the property.
|
|
1615
|
+
* @param value The value to test.
|
|
1616
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1617
|
+
*/
|
|
1618
|
+
static uint8Array(source, property, value) {
|
|
1619
|
+
if (!Is.uint8Array(value)) {
|
|
1620
|
+
throw new GuardError(source, "guard.uint8Array", property, value);
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
/**
|
|
1624
|
+
* Is the property a function.
|
|
1625
|
+
* @param source The source of the error.
|
|
1626
|
+
* @param property The name of the property.
|
|
1627
|
+
* @param value The value to test.
|
|
1628
|
+
* @returns True if the value is a function.
|
|
1629
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1630
|
+
*/
|
|
1631
|
+
static function(source, property, value) {
|
|
1632
|
+
if (!Is.function(value)) {
|
|
1633
|
+
throw new GuardError(source, "guard.function", property, value);
|
|
1634
|
+
}
|
|
1635
|
+
return true;
|
|
1636
|
+
}
|
|
1637
|
+
/**
|
|
1638
|
+
* Is the property a string formatted as an email address.
|
|
1639
|
+
* @param source The source of the error.
|
|
1640
|
+
* @param property The name of the property.
|
|
1641
|
+
* @param value The value to test.
|
|
1642
|
+
* @throws GuardError If the value does not match the assertion.
|
|
1643
|
+
*/
|
|
1644
|
+
static email(source, property, value) {
|
|
1645
|
+
if (!Is.email(value)) {
|
|
1646
|
+
throw new GuardError(source, "guard.email", property, value);
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
/**
|
|
1652
|
+
* Factory for creating implementation of generic types.
|
|
1653
|
+
*/
|
|
1654
|
+
class Factory {
|
|
1655
|
+
/**
|
|
1656
|
+
* Runtime name for the class.
|
|
1657
|
+
* @internal
|
|
1658
|
+
*/
|
|
1659
|
+
static _CLASS_NAME = "Factory";
|
|
1660
|
+
/**
|
|
1661
|
+
* Store all the created factories.
|
|
1662
|
+
* @internal
|
|
1663
|
+
*/
|
|
1664
|
+
static _factories = {};
|
|
1665
|
+
/**
|
|
1666
|
+
* Type name for the instances.
|
|
1667
|
+
* @internal
|
|
1668
|
+
*/
|
|
1669
|
+
_typeName;
|
|
1670
|
+
/**
|
|
1671
|
+
* Store the generators.
|
|
1672
|
+
* @internal
|
|
1673
|
+
*/
|
|
1674
|
+
_generators;
|
|
1675
|
+
/**
|
|
1676
|
+
* Store the created instances.
|
|
1677
|
+
* @internal
|
|
1678
|
+
*/
|
|
1679
|
+
_instances;
|
|
1680
|
+
/**
|
|
1681
|
+
* Counter for the ordering.
|
|
1682
|
+
* @internal
|
|
1683
|
+
*/
|
|
1684
|
+
_orderCounter;
|
|
1685
|
+
/**
|
|
1686
|
+
* Automatically created an instance when registered.
|
|
1687
|
+
* @internal
|
|
1688
|
+
*/
|
|
1689
|
+
_autoInstance;
|
|
1690
|
+
/**
|
|
1691
|
+
* Match the name of the instance.
|
|
1692
|
+
* @internal
|
|
1693
|
+
*/
|
|
1694
|
+
_matcher;
|
|
1695
|
+
/**
|
|
1696
|
+
* Create a new instance of Factory, private use createFactory.
|
|
1697
|
+
* @param typeName The type name for the instances.
|
|
1698
|
+
* @param autoInstance Automatically create an instance when registered.
|
|
1699
|
+
* @param matcher Match the name of the instance.
|
|
1700
|
+
* @internal
|
|
1701
|
+
*/
|
|
1702
|
+
constructor(typeName, autoInstance = false, matcher) {
|
|
1703
|
+
this._typeName = typeName;
|
|
1704
|
+
this._generators = {};
|
|
1705
|
+
this._instances = {};
|
|
1706
|
+
this._orderCounter = 0;
|
|
1707
|
+
this._autoInstance = autoInstance;
|
|
1708
|
+
this._matcher = matcher ?? this.defaultMatcher.bind(this);
|
|
1709
|
+
}
|
|
1710
|
+
/**
|
|
1711
|
+
* Create a new factory, which is shared throughout all library instances.
|
|
1712
|
+
* @param typeName The type name for the instances.
|
|
1713
|
+
* @param autoInstance Automatically create an instance when registered.
|
|
1714
|
+
* @param matcher Match the name of the instance.
|
|
1715
|
+
* @returns The factory instance.
|
|
1716
|
+
*/
|
|
1717
|
+
static createFactory(typeName, autoInstance = false, matcher) {
|
|
1718
|
+
if (Is.undefined(Factory._factories[typeName])) {
|
|
1719
|
+
Factory._factories[typeName] = new Factory(typeName, autoInstance, matcher);
|
|
1720
|
+
}
|
|
1721
|
+
return Factory._factories[typeName];
|
|
1722
|
+
}
|
|
1723
|
+
/**
|
|
1724
|
+
* Register a new generator.
|
|
1725
|
+
* @param name The name of the generator.
|
|
1726
|
+
* @param generator The function to create an instance.
|
|
1727
|
+
*/
|
|
1728
|
+
register(name, generator) {
|
|
1729
|
+
Guards.stringValue(Factory._CLASS_NAME, "name", name);
|
|
1730
|
+
Guards.function(Factory._CLASS_NAME, "generator", generator);
|
|
1731
|
+
this._generators[name] = {
|
|
1732
|
+
generator,
|
|
1733
|
+
order: this._orderCounter++
|
|
1734
|
+
};
|
|
1735
|
+
// Remove any existing instance
|
|
1736
|
+
this.removeInstance(name);
|
|
1737
|
+
if (this._autoInstance) {
|
|
1738
|
+
this._instances[name] = generator();
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
/**
|
|
1742
|
+
* Unregister a generator.
|
|
1743
|
+
* @param name The name of the generator to unregister.
|
|
1744
|
+
* @throws GuardError if the parameters are invalid.
|
|
1745
|
+
* @throws GeneralError if no generator exists.
|
|
1746
|
+
*/
|
|
1747
|
+
unregister(name) {
|
|
1748
|
+
Guards.stringValue(Factory._CLASS_NAME, "name", name);
|
|
1749
|
+
if (!this._generators[name]) {
|
|
1750
|
+
throw new GeneralError(Factory._CLASS_NAME, "noUnregister", {
|
|
1751
|
+
typeName: this._typeName,
|
|
1752
|
+
name
|
|
1753
|
+
});
|
|
1754
|
+
}
|
|
1755
|
+
delete this._generators[name];
|
|
1756
|
+
// Remove any existing instance
|
|
1757
|
+
this.removeInstance(name);
|
|
1758
|
+
}
|
|
1759
|
+
/**
|
|
1760
|
+
* Get a generator instance.
|
|
1761
|
+
* @param name The name of the instance to generate.
|
|
1762
|
+
* @returns An instance of the item.
|
|
1763
|
+
* @throws GuardError if the parameters are invalid.
|
|
1764
|
+
* @throws GeneralError if no item exists to get.
|
|
1765
|
+
*/
|
|
1766
|
+
get(name) {
|
|
1767
|
+
const instance = this.getIfExists(name);
|
|
1768
|
+
if (!instance) {
|
|
1769
|
+
throw new GeneralError(Factory._CLASS_NAME, "noGet", {
|
|
1770
|
+
typeName: this._typeName,
|
|
1771
|
+
name
|
|
1772
|
+
});
|
|
1773
|
+
}
|
|
1774
|
+
return instance;
|
|
1775
|
+
}
|
|
1776
|
+
/**
|
|
1777
|
+
* Get a generator instance with no exceptions.
|
|
1778
|
+
* @param name The name of the instance to generate.
|
|
1779
|
+
* @returns An instance of the item or undefined if it does not exist.
|
|
1780
|
+
*/
|
|
1781
|
+
getIfExists(name) {
|
|
1782
|
+
Guards.stringValue(Factory._CLASS_NAME, "name", name);
|
|
1783
|
+
const matchName = this._matcher(Object.keys(this._generators), name);
|
|
1784
|
+
if (Is.stringValue(matchName) && this._generators[matchName]) {
|
|
1785
|
+
if (!this._instances[matchName]) {
|
|
1786
|
+
this._instances[matchName] = this._generators[matchName].generator();
|
|
1787
|
+
}
|
|
1788
|
+
if (this._instances[matchName]) {
|
|
1789
|
+
return this._instances[matchName];
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
/**
|
|
1794
|
+
* Reset all the instances.
|
|
1795
|
+
*/
|
|
1796
|
+
reset() {
|
|
1797
|
+
for (const name in this._generators) {
|
|
1798
|
+
this.removeInstance(name);
|
|
1799
|
+
}
|
|
1800
|
+
this._instances = {};
|
|
1801
|
+
}
|
|
1802
|
+
/**
|
|
1803
|
+
* Get all the instances as a map.
|
|
1804
|
+
* @returns The instances as a map.
|
|
1805
|
+
*/
|
|
1806
|
+
instancesMap() {
|
|
1807
|
+
return this._instances;
|
|
1808
|
+
}
|
|
1809
|
+
/**
|
|
1810
|
+
* Get all the instances as a list in the order they were registered.
|
|
1811
|
+
* @returns The instances as a list in the order they were registered.
|
|
1812
|
+
*/
|
|
1813
|
+
instancesList() {
|
|
1814
|
+
const orderedInstances = [];
|
|
1815
|
+
for (const instanceName in this._instances) {
|
|
1816
|
+
orderedInstances.push({
|
|
1817
|
+
instance: this._instances[instanceName],
|
|
1818
|
+
order: this._generators[instanceName].order
|
|
1819
|
+
});
|
|
1820
|
+
}
|
|
1821
|
+
return orderedInstances.sort((a, b) => a.order - b.order).map(o => o.instance);
|
|
1822
|
+
}
|
|
1823
|
+
/**
|
|
1824
|
+
* Get all the generator names in the order they were registered.
|
|
1825
|
+
* @returns The ordered generator names.
|
|
1826
|
+
*/
|
|
1827
|
+
names() {
|
|
1828
|
+
const orderedNames = [];
|
|
1829
|
+
for (const generator in this._generators) {
|
|
1830
|
+
orderedNames.push({
|
|
1831
|
+
name: generator,
|
|
1832
|
+
order: this._generators[generator].order
|
|
1833
|
+
});
|
|
1834
|
+
}
|
|
1835
|
+
return orderedNames.sort((a, b) => a.order - b.order).map(o => o.name);
|
|
1836
|
+
}
|
|
1837
|
+
/**
|
|
1838
|
+
* Does the factory contain the name.
|
|
1839
|
+
* @param name The name of the instance to find.
|
|
1840
|
+
* @returns True if the factory has a matching name.
|
|
1841
|
+
*/
|
|
1842
|
+
hasName(name) {
|
|
1843
|
+
Guards.stringValue(Factory._CLASS_NAME, "name", name);
|
|
1844
|
+
return Is.stringValue(this._matcher(Object.keys(this._generators), name));
|
|
1845
|
+
}
|
|
1846
|
+
/**
|
|
1847
|
+
* Remove any instances of the given name.
|
|
1848
|
+
* @param name The name of the instances to remove.
|
|
1849
|
+
* @internal
|
|
1850
|
+
*/
|
|
1851
|
+
removeInstance(name) {
|
|
1852
|
+
delete this._instances[name];
|
|
1853
|
+
}
|
|
1854
|
+
/**
|
|
1855
|
+
* Match the requested name to the generator name.
|
|
1856
|
+
* @param names The list of names for all the generators.
|
|
1857
|
+
* @param name The name to match.
|
|
1858
|
+
* @returns The matched name or undefined if no match.
|
|
1859
|
+
* @internal
|
|
1860
|
+
*/
|
|
1861
|
+
defaultMatcher(names, name) {
|
|
1862
|
+
return this._generators[name] ? name : undefined;
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
// Copyright 2024 IOTA Stiftung.
|
|
1867
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
1868
|
+
/**
|
|
1869
|
+
* Factory for creating implementation of component types.
|
|
1870
|
+
*/
|
|
1871
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1872
|
+
const ComponentFactory = Factory.createFactory("component");
|
|
1873
|
+
|
|
1874
|
+
// Copyright 2024 IOTA Stiftung.
|
|
1875
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
1876
|
+
/**
|
|
1877
|
+
* Class to help with arrays.
|
|
1878
|
+
*/
|
|
1879
|
+
class ArrayHelper {
|
|
1880
|
+
/**
|
|
1881
|
+
* Do the two arrays match.
|
|
1882
|
+
* @param arr1 The first array.
|
|
1883
|
+
* @param arr2 The second array.
|
|
1884
|
+
* @returns True if both arrays are empty of have the same values.
|
|
1885
|
+
*/
|
|
1886
|
+
static matches(arr1, arr2) {
|
|
1887
|
+
if (Is.empty(arr1) && Is.empty(arr2)) {
|
|
1888
|
+
return true;
|
|
1889
|
+
}
|
|
1890
|
+
if (!((Is.array(arr1) && Is.array(arr2)) || (Is.typedArray(arr1) && Is.typedArray(arr2)))) {
|
|
1891
|
+
return false;
|
|
1892
|
+
}
|
|
1893
|
+
if (arr1.length !== arr2.length) {
|
|
1894
|
+
return false;
|
|
1895
|
+
}
|
|
1896
|
+
for (let i = 0; i < arr1.length; i++) {
|
|
1897
|
+
if (arr1[i] !== arr2[i]) {
|
|
1898
|
+
return false;
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
return true;
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
// Copyright 2024 IOTA Stiftung.
|
|
1906
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
1907
|
+
/**
|
|
1908
|
+
* Class to perform internationalization.
|
|
1909
|
+
*/
|
|
1910
|
+
class I18n {
|
|
1911
|
+
/**
|
|
1912
|
+
* The default translation.
|
|
1913
|
+
*/
|
|
1914
|
+
static DEFAULT_LOCALE = "en";
|
|
1915
|
+
/**
|
|
1916
|
+
* Dictionaries for lookups.
|
|
1917
|
+
* @internal
|
|
1918
|
+
*/
|
|
1919
|
+
static _localeDictionaries = {};
|
|
1920
|
+
/**
|
|
1921
|
+
* The current locale.
|
|
1922
|
+
* @internal
|
|
1923
|
+
*/
|
|
1924
|
+
static _currentLocale = I18n.DEFAULT_LOCALE;
|
|
1925
|
+
/**
|
|
1926
|
+
* Change handler for the locale being updated.
|
|
1927
|
+
* @internal
|
|
1928
|
+
*/
|
|
1929
|
+
static _localeChangedHandlers = {};
|
|
1930
|
+
/**
|
|
1931
|
+
* Change handler for the dictionaries being updated.
|
|
1932
|
+
* @internal
|
|
1933
|
+
*/
|
|
1934
|
+
static _dictionaryChangedHandlers = {};
|
|
1935
|
+
/**
|
|
1936
|
+
* Set the locale.
|
|
1937
|
+
* @param locale The new locale.
|
|
1938
|
+
*/
|
|
1939
|
+
static setLocale(locale) {
|
|
1940
|
+
I18n._currentLocale = locale;
|
|
1941
|
+
for (const callback in I18n._localeChangedHandlers) {
|
|
1942
|
+
I18n._localeChangedHandlers[callback](I18n._currentLocale);
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
/**
|
|
1946
|
+
* Get the locale.
|
|
1947
|
+
* @returns The current locale.
|
|
1948
|
+
*/
|
|
1949
|
+
static getLocale() {
|
|
1950
|
+
return I18n._currentLocale;
|
|
1951
|
+
}
|
|
1952
|
+
/**
|
|
1953
|
+
* Add a locale dictionary.
|
|
1954
|
+
* @param locale The locale.
|
|
1955
|
+
* @param dictionary The dictionary to add.
|
|
1956
|
+
*/
|
|
1957
|
+
static addDictionary(locale, dictionary) {
|
|
1958
|
+
const mergedKeys = {};
|
|
1959
|
+
I18n.flattenTranslationKeys(dictionary, "", mergedKeys);
|
|
1960
|
+
I18n._localeDictionaries[locale] = mergedKeys;
|
|
1961
|
+
for (const callback in I18n._dictionaryChangedHandlers) {
|
|
1962
|
+
I18n._dictionaryChangedHandlers[callback](I18n._currentLocale);
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
/**
|
|
1966
|
+
* Get a locale dictionary.
|
|
1967
|
+
* @param locale The locale.
|
|
1968
|
+
* @returns The dictionary of undefined if it does not exist.
|
|
1969
|
+
*/
|
|
1970
|
+
static getDictionary(locale) {
|
|
1971
|
+
return I18n._localeDictionaries[locale];
|
|
1972
|
+
}
|
|
1973
|
+
/**
|
|
1974
|
+
* Get all the locale dictionaries.
|
|
1975
|
+
* @returns The dictionaries.
|
|
1976
|
+
*/
|
|
1977
|
+
static getAllDictionaries() {
|
|
1978
|
+
return I18n._localeDictionaries;
|
|
1979
|
+
}
|
|
1980
|
+
/**
|
|
1981
|
+
* Add a locale changed handler.
|
|
1982
|
+
* @param id The id of the handler.
|
|
1983
|
+
* @param handler The handler to add.
|
|
1984
|
+
*/
|
|
1985
|
+
static addLocaleHandler(id, handler) {
|
|
1986
|
+
I18n._localeChangedHandlers[id] = handler;
|
|
1987
|
+
}
|
|
1988
|
+
/**
|
|
1989
|
+
* Remove a locale changed handler.
|
|
1990
|
+
* @param id The id of the handler.
|
|
1991
|
+
*/
|
|
1992
|
+
static removeLocaleHandler(id) {
|
|
1993
|
+
delete I18n._localeChangedHandlers[id];
|
|
1994
|
+
}
|
|
1995
|
+
/**
|
|
1996
|
+
* Add a dictionary changed handler.
|
|
1997
|
+
* @param id The id of the handler.
|
|
1998
|
+
* @param handler The handler to add.
|
|
1999
|
+
*/
|
|
2000
|
+
static addDictionaryHandler(id, handler) {
|
|
2001
|
+
I18n._dictionaryChangedHandlers[id] = handler;
|
|
2002
|
+
}
|
|
2003
|
+
/**
|
|
2004
|
+
* Remove a dictionary changed handler.
|
|
2005
|
+
* @param id The id of the handler.
|
|
2006
|
+
*/
|
|
2007
|
+
static removeDictionaryHandler(id) {
|
|
2008
|
+
delete I18n._dictionaryChangedHandlers[id];
|
|
2009
|
+
}
|
|
2010
|
+
/**
|
|
2011
|
+
* Format a message.
|
|
2012
|
+
* @param key The key of the message to format.
|
|
2013
|
+
* @param values The values to substitute into the message.
|
|
2014
|
+
* @param overrideLocale Override the locale.
|
|
2015
|
+
* @returns The formatted string.
|
|
2016
|
+
*/
|
|
2017
|
+
static formatMessage(key, values, overrideLocale) {
|
|
2018
|
+
let cl = overrideLocale ?? I18n._currentLocale;
|
|
2019
|
+
if (cl.startsWith("debug-")) {
|
|
2020
|
+
cl = I18n.DEFAULT_LOCALE;
|
|
2021
|
+
}
|
|
2022
|
+
if (!I18n._localeDictionaries[cl]) {
|
|
2023
|
+
return `!!Missing ${cl}`;
|
|
2024
|
+
}
|
|
2025
|
+
if (!I18n._localeDictionaries[cl][key]) {
|
|
2026
|
+
return `!!Missing ${cl}.${key}`;
|
|
2027
|
+
}
|
|
2028
|
+
if (I18n._currentLocale === "debug-k") {
|
|
2029
|
+
return key;
|
|
2030
|
+
}
|
|
2031
|
+
let ret = new intlMessageformat.IntlMessageFormat(I18n._localeDictionaries[cl][key], cl).format(values);
|
|
2032
|
+
if (I18n._currentLocale === "debug-x") {
|
|
2033
|
+
ret = ret.replace(/[a-z]/g, "x").replace(/[A-Z]/g, "x").replace(/\d/g, "n");
|
|
2034
|
+
}
|
|
2035
|
+
return ret;
|
|
2036
|
+
}
|
|
2037
|
+
/**
|
|
2038
|
+
* Check if the dictionaries have a message for the given key.
|
|
2039
|
+
* @param key The key to check for existence.
|
|
2040
|
+
* @returns True if the key exists.
|
|
2041
|
+
*/
|
|
2042
|
+
static hasMessage(key) {
|
|
2043
|
+
return Is.string(I18n._localeDictionaries[I18n._currentLocale]?.[key]);
|
|
2044
|
+
}
|
|
2045
|
+
/**
|
|
2046
|
+
* Flatten the translation property paths for faster lookup.
|
|
2047
|
+
* @param translation The translation to merge.
|
|
2048
|
+
* @param propertyPath The current root path.
|
|
2049
|
+
* @param mergedKeys The merged keys dictionary to populate.
|
|
2050
|
+
* @internal
|
|
2051
|
+
*/
|
|
2052
|
+
static flattenTranslationKeys(translation, propertyPath, mergedKeys) {
|
|
2053
|
+
for (const key in translation) {
|
|
2054
|
+
const val = translation[key];
|
|
2055
|
+
const mergedPath = propertyPath.length > 0 ? `${propertyPath}.${key}` : key;
|
|
2056
|
+
if (Is.string(val)) {
|
|
2057
|
+
mergedKeys[mergedPath] = val;
|
|
2058
|
+
}
|
|
2059
|
+
else if (Is.object(val)) {
|
|
2060
|
+
I18n.flattenTranslationKeys(val, mergedPath, mergedKeys);
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2067
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
2068
|
+
/**
|
|
2069
|
+
* Error helper functions.
|
|
2070
|
+
*/
|
|
2071
|
+
class ErrorHelper {
|
|
2072
|
+
/**
|
|
2073
|
+
* Format Errors and returns just their messages.
|
|
2074
|
+
* @param error The error to format.
|
|
2075
|
+
* @returns The error formatted including any inner errors.
|
|
2076
|
+
*/
|
|
2077
|
+
static formatErrors(error) {
|
|
2078
|
+
return ErrorHelper.localizeErrors(error).map(e => e.message);
|
|
2079
|
+
}
|
|
2080
|
+
/**
|
|
2081
|
+
* Localize the content of an error and any inner errors.
|
|
2082
|
+
* @param error The error to format.
|
|
2083
|
+
* @returns The localized version of the errors flattened.
|
|
2084
|
+
*/
|
|
2085
|
+
static localizeErrors(error) {
|
|
2086
|
+
const formattedErrors = [];
|
|
2087
|
+
if (Is.notEmpty(error)) {
|
|
2088
|
+
const errors = BaseError.flatten(error);
|
|
2089
|
+
for (const err of errors) {
|
|
2090
|
+
const errorNameKey = `errorNames.${StringHelper.camelCase(err.name)}`;
|
|
2091
|
+
const errorMessageKey = `error.${err.message}`;
|
|
2092
|
+
// If there is no error message then it is probably
|
|
2093
|
+
// from a 3rd party lib, so don't format it just display
|
|
2094
|
+
const hasErrorName = I18n.hasMessage(errorNameKey);
|
|
2095
|
+
const hasErrorMessage = I18n.hasMessage(errorMessageKey);
|
|
2096
|
+
const localizedError = {
|
|
2097
|
+
name: I18n.formatMessage(hasErrorName ? errorNameKey : "errorNames.error"),
|
|
2098
|
+
message: hasErrorMessage
|
|
2099
|
+
? I18n.formatMessage(errorMessageKey, err.properties)
|
|
2100
|
+
: err.message
|
|
2101
|
+
};
|
|
2102
|
+
if (Is.stringValue(err.source)) {
|
|
2103
|
+
localizedError.source = err.source;
|
|
2104
|
+
}
|
|
2105
|
+
if (Is.stringValue(err.stack)) {
|
|
2106
|
+
// Remove the first line from the stack traces as they
|
|
2107
|
+
// just have the error type and message duplicated
|
|
2108
|
+
const lines = err.stack.split("\n");
|
|
2109
|
+
lines.shift();
|
|
2110
|
+
localizedError.stack = lines.join("\n");
|
|
2111
|
+
}
|
|
2112
|
+
const additional = ErrorHelper.formatValidationErrors(err);
|
|
2113
|
+
if (Is.stringValue(additional)) {
|
|
2114
|
+
localizedError.additional = additional;
|
|
2115
|
+
}
|
|
2116
|
+
formattedErrors.push(localizedError);
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
return formattedErrors;
|
|
2120
|
+
}
|
|
2121
|
+
/**
|
|
2122
|
+
* Localize the content of an error and any inner errors.
|
|
2123
|
+
* @param error The error to format.
|
|
2124
|
+
* @returns The localized version of the errors flattened.
|
|
2125
|
+
*/
|
|
2126
|
+
static formatValidationErrors(error) {
|
|
2127
|
+
if (Is.object(error.properties) &&
|
|
2128
|
+
Object.keys(error.properties).length > 0 &&
|
|
2129
|
+
Is.object(error.properties) &&
|
|
2130
|
+
Is.arrayValue(error.properties.validationFailures)) {
|
|
2131
|
+
const validationErrors = [];
|
|
2132
|
+
for (const validationFailure of error.properties.validationFailures) {
|
|
2133
|
+
const errorI18n = `error.${validationFailure.reason}`;
|
|
2134
|
+
const errorMessage = I18n.hasMessage(errorI18n)
|
|
2135
|
+
? I18n.formatMessage(errorI18n, validationFailure.properties)
|
|
2136
|
+
: errorI18n;
|
|
2137
|
+
let v = `${validationFailure.property}: ${errorMessage}`;
|
|
2138
|
+
if (Is.object(validationFailure.properties) &&
|
|
2139
|
+
Is.notEmpty(validationFailure.properties.value)) {
|
|
2140
|
+
v += ` = ${JSON.stringify(validationFailure.properties.value)}`;
|
|
2141
|
+
}
|
|
2142
|
+
validationErrors.push(v);
|
|
2143
|
+
}
|
|
2144
|
+
return validationErrors.join("\n");
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2150
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
2151
|
+
/**
|
|
2152
|
+
* Coerce an object from one type to another.
|
|
2153
|
+
*/
|
|
2154
|
+
class Coerce {
|
|
2155
|
+
/**
|
|
2156
|
+
* Coerce the value to a string.
|
|
2157
|
+
* @param value The value to coerce.
|
|
2158
|
+
* @throws TypeError If the value can not be coerced.
|
|
2159
|
+
* @returns The value if it can be coerced.
|
|
2160
|
+
*/
|
|
2161
|
+
static string(value) {
|
|
2162
|
+
if (Is.undefined(value)) {
|
|
2163
|
+
return value;
|
|
2164
|
+
}
|
|
2165
|
+
if (Is.string(value)) {
|
|
2166
|
+
return value;
|
|
2167
|
+
}
|
|
2168
|
+
if (Is.number(value)) {
|
|
2169
|
+
return value.toString();
|
|
2170
|
+
}
|
|
2171
|
+
if (Is.boolean(value)) {
|
|
2172
|
+
return value ? "true" : "false";
|
|
2173
|
+
}
|
|
2174
|
+
if (Is.date(value)) {
|
|
2175
|
+
return value.toISOString();
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
/**
|
|
2179
|
+
* Coerce the value to a number.
|
|
2180
|
+
* @param value The value to coerce.
|
|
2181
|
+
* @throws TypeError If the value can not be coerced.
|
|
2182
|
+
* @returns The value if it can be coerced.
|
|
2183
|
+
*/
|
|
2184
|
+
static number(value) {
|
|
2185
|
+
if (Is.undefined(value)) {
|
|
2186
|
+
return value;
|
|
2187
|
+
}
|
|
2188
|
+
if (Is.number(value)) {
|
|
2189
|
+
return value;
|
|
2190
|
+
}
|
|
2191
|
+
if (Is.string(value)) {
|
|
2192
|
+
const parsed = Number.parseFloat(value);
|
|
2193
|
+
if (Is.number(parsed)) {
|
|
2194
|
+
return parsed;
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
if (Is.boolean(value)) {
|
|
2198
|
+
return value ? 1 : 0;
|
|
2199
|
+
}
|
|
2200
|
+
if (Is.date(value)) {
|
|
2201
|
+
return value.getTime();
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
/**
|
|
2205
|
+
* Coerce the value to a bigint.
|
|
2206
|
+
* @param value The value to coerce.
|
|
2207
|
+
* @throws TypeError If the value can not be coerced.
|
|
2208
|
+
* @returns The value if it can be coerced.
|
|
2209
|
+
*/
|
|
2210
|
+
static bigint(value) {
|
|
2211
|
+
if (Is.undefined(value)) {
|
|
2212
|
+
return value;
|
|
2213
|
+
}
|
|
2214
|
+
if (Is.bigint(value)) {
|
|
2215
|
+
return value;
|
|
2216
|
+
}
|
|
2217
|
+
if (Is.number(value)) {
|
|
2218
|
+
return BigInt(value);
|
|
2219
|
+
}
|
|
2220
|
+
if (Is.string(value)) {
|
|
2221
|
+
const parsed = Number.parseFloat(value);
|
|
2222
|
+
if (Is.integer(parsed)) {
|
|
2223
|
+
return BigInt(parsed);
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
if (Is.boolean(value)) {
|
|
2227
|
+
return value ? 1n : 0n;
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
/**
|
|
2231
|
+
* Coerce the value to a boolean.
|
|
2232
|
+
* @param value The value to coerce.
|
|
2233
|
+
* @throws TypeError If the value can not be coerced.
|
|
2234
|
+
* @returns The value if it can be coerced.
|
|
2235
|
+
*/
|
|
2236
|
+
static boolean(value) {
|
|
2237
|
+
if (Is.undefined(value)) {
|
|
2238
|
+
return value;
|
|
2239
|
+
}
|
|
2240
|
+
if (Is.boolean(value)) {
|
|
2241
|
+
return value;
|
|
2242
|
+
}
|
|
2243
|
+
if (Is.number(value)) {
|
|
2244
|
+
// eslint-disable-next-line no-unneeded-ternary
|
|
2245
|
+
return value ? true : false;
|
|
2246
|
+
}
|
|
2247
|
+
if (Is.string(value)) {
|
|
2248
|
+
if (/true/i.test(value)) {
|
|
2249
|
+
return true;
|
|
2250
|
+
}
|
|
2251
|
+
if (/false/i.test(value)) {
|
|
2252
|
+
return false;
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
/**
|
|
2257
|
+
* Coerce the value to a date.
|
|
2258
|
+
* @param value The value to coerce.
|
|
2259
|
+
* @throws TypeError If the value can not be coerced.
|
|
2260
|
+
* @returns The value if it can be coerced.
|
|
2261
|
+
*/
|
|
2262
|
+
static date(value) {
|
|
2263
|
+
if (Is.undefined(value)) {
|
|
2264
|
+
return value;
|
|
2265
|
+
}
|
|
2266
|
+
if (Is.date(value)) {
|
|
2267
|
+
return value;
|
|
2268
|
+
}
|
|
2269
|
+
if (Is.number(value)) {
|
|
2270
|
+
return new Date(value);
|
|
2271
|
+
}
|
|
2272
|
+
if (Is.string(value)) {
|
|
2273
|
+
const dt = new Date(value);
|
|
2274
|
+
if (!Number.isNaN(dt.getTime())) {
|
|
2275
|
+
const utc = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate());
|
|
2276
|
+
return new Date(utc);
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
/**
|
|
2281
|
+
* Coerce the value to a date/time.
|
|
2282
|
+
* @param value The value to coerce.
|
|
2283
|
+
* @throws TypeError If the value can not be coerced.
|
|
2284
|
+
* @returns The value if it can be coerced.
|
|
2285
|
+
*/
|
|
2286
|
+
static dateTime(value) {
|
|
2287
|
+
if (Is.undefined(value)) {
|
|
2288
|
+
return value;
|
|
2289
|
+
}
|
|
2290
|
+
if (Is.date(value)) {
|
|
2291
|
+
return value;
|
|
2292
|
+
}
|
|
2293
|
+
if (Is.number(value)) {
|
|
2294
|
+
return new Date(value);
|
|
2295
|
+
}
|
|
2296
|
+
if (Is.string(value)) {
|
|
2297
|
+
const dt = new Date(value);
|
|
2298
|
+
if (!Number.isNaN(dt.getTime())) {
|
|
2299
|
+
const utc = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate(), dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
|
|
2300
|
+
return new Date(utc);
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
/**
|
|
2305
|
+
* Coerce the value to a time.
|
|
2306
|
+
* @param value The value to coerce.
|
|
2307
|
+
* @throws TypeError If the value can not be coerced.
|
|
2308
|
+
* @returns The value if it can be coerced.
|
|
2309
|
+
*/
|
|
2310
|
+
static time(value) {
|
|
2311
|
+
if (Is.undefined(value)) {
|
|
2312
|
+
return value;
|
|
2313
|
+
}
|
|
2314
|
+
if (Is.date(value)) {
|
|
2315
|
+
return value;
|
|
2316
|
+
}
|
|
2317
|
+
if (Is.number(value)) {
|
|
2318
|
+
const dt = new Date(value);
|
|
2319
|
+
dt.setFullYear(1970, 0, 1);
|
|
2320
|
+
return dt;
|
|
2321
|
+
}
|
|
2322
|
+
if (Is.string(value)) {
|
|
2323
|
+
const dt = new Date(value);
|
|
2324
|
+
if (!Number.isNaN(dt.getTime())) {
|
|
2325
|
+
const utc = Date.UTC(1970, 0, 1, dt.getUTCHours(), dt.getUTCMinutes(), dt.getUTCSeconds(), dt.getUTCMilliseconds());
|
|
2326
|
+
return new Date(utc);
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
/**
|
|
2331
|
+
* Coerce the value to an object.
|
|
2332
|
+
* @param value The value to coerce.
|
|
2333
|
+
* @throws TypeError If the value can not be coerced.
|
|
2334
|
+
* @returns The value if it can be coerced.
|
|
2335
|
+
*/
|
|
2336
|
+
static object(value) {
|
|
2337
|
+
if (Is.undefined(value)) {
|
|
2338
|
+
return value;
|
|
2339
|
+
}
|
|
2340
|
+
if (Is.object(value)) {
|
|
2341
|
+
return value;
|
|
2342
|
+
}
|
|
2343
|
+
if (Is.stringValue(value)) {
|
|
2344
|
+
try {
|
|
2345
|
+
return JSON.parse(value);
|
|
2346
|
+
}
|
|
2347
|
+
catch { }
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2353
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
2354
|
+
/**
|
|
2355
|
+
* Class to help with filenames.
|
|
2356
|
+
*/
|
|
2357
|
+
class FilenameHelper {
|
|
2358
|
+
/**
|
|
2359
|
+
* Replaces any unsafe characters in the filename.
|
|
2360
|
+
* @param filename The filename to make safe.
|
|
2361
|
+
* @returns The safe filename.
|
|
2362
|
+
*/
|
|
2363
|
+
static safeFilename(filename) {
|
|
2364
|
+
let safe = Coerce.string(filename);
|
|
2365
|
+
if (Is.empty(safe)) {
|
|
2366
|
+
return "";
|
|
2367
|
+
}
|
|
2368
|
+
// Common non filename characters
|
|
2369
|
+
safe = safe.replace(/["*/:<>?\\|]/g, "_");
|
|
2370
|
+
// Windows non filename characters
|
|
2371
|
+
safe = safe.replace(/^(con|prn|aux|nul|com\d|lpt\d)$/i, "_");
|
|
2372
|
+
// Control characters
|
|
2373
|
+
safe = safe.replace(/[\u0000-\u001F\u0080-\u009F]/g, "_");
|
|
2374
|
+
// Relative paths
|
|
2375
|
+
safe = safe.replace(/^\.+/, "_");
|
|
2376
|
+
// Trailing periods
|
|
2377
|
+
safe = safe.replace(/\.+$/, "");
|
|
2378
|
+
return safe;
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2383
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
2384
|
+
/**
|
|
2385
|
+
* Helpers methods for JSON objects.
|
|
2386
|
+
*/
|
|
2387
|
+
class JsonHelper {
|
|
2388
|
+
/**
|
|
2389
|
+
* Serializes in canonical format.
|
|
2390
|
+
* Based on https://www.rfc-editor.org/rfc/rfc8785.
|
|
2391
|
+
* @param object The object to be serialized.
|
|
2392
|
+
* @returns The serialized object.
|
|
2393
|
+
*/
|
|
2394
|
+
static canonicalize(object) {
|
|
2395
|
+
const buffer = [];
|
|
2396
|
+
if (object === null ||
|
|
2397
|
+
typeof object !== "object" ||
|
|
2398
|
+
("toJSON" in object && object.toJSON instanceof Function)) {
|
|
2399
|
+
// Primitive data type
|
|
2400
|
+
buffer.push(JSON.stringify(object));
|
|
2401
|
+
}
|
|
2402
|
+
else if (Array.isArray(object)) {
|
|
2403
|
+
// Array maintain element order
|
|
2404
|
+
const parts = [];
|
|
2405
|
+
for (const element of object) {
|
|
2406
|
+
if (element === undefined) {
|
|
2407
|
+
parts.push("null");
|
|
2408
|
+
}
|
|
2409
|
+
else {
|
|
2410
|
+
parts.push(JsonHelper.canonicalize(element));
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
buffer.push(`[${parts.join(",")}]`);
|
|
2414
|
+
}
|
|
2415
|
+
else {
|
|
2416
|
+
// Object sort properties
|
|
2417
|
+
const props = [];
|
|
2418
|
+
const keys = Object.keys(object).sort();
|
|
2419
|
+
const o = object;
|
|
2420
|
+
for (const key of keys) {
|
|
2421
|
+
if (o[key] !== undefined) {
|
|
2422
|
+
props.push(`${JSON.stringify(key)}:${JsonHelper.canonicalize(o[key])}`);
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
buffer.push(`{${props.join(",")}}`);
|
|
2426
|
+
}
|
|
2427
|
+
return buffer.join("");
|
|
2428
|
+
}
|
|
2429
|
+
/**
|
|
2430
|
+
* Creates a RFC 6902 diff set.
|
|
2431
|
+
* Based on https://www.rfc-editor.org/rfc/rfc6902.
|
|
2432
|
+
* @param object1 The first object.
|
|
2433
|
+
* @param object2 The second object.
|
|
2434
|
+
* @returns The list of patches.
|
|
2435
|
+
*/
|
|
2436
|
+
static diff(object1, object2) {
|
|
2437
|
+
const operations = rfc6902.createPatch(object1, object2);
|
|
2438
|
+
return operations;
|
|
2439
|
+
}
|
|
2440
|
+
/**
|
|
2441
|
+
* Applies a RFC 6902 diff set to an object.
|
|
2442
|
+
* Based on https://www.rfc-editor.org/rfc/rfc6902.
|
|
2443
|
+
* @param object The object to patch.
|
|
2444
|
+
* @param patches The second object.
|
|
2445
|
+
* @returns The updated object.
|
|
2446
|
+
*/
|
|
2447
|
+
static patch(object, patches) {
|
|
2448
|
+
return rfc6902.applyPatch(object, patches);
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
|
|
2452
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2453
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
2454
|
+
/* eslint-disable no-bitwise */
|
|
2455
|
+
/**
|
|
2456
|
+
* Convert arrays to and from different formats.
|
|
2457
|
+
*/
|
|
2458
|
+
class Converter {
|
|
2459
|
+
/**
|
|
2460
|
+
* Lookup table for encoding.
|
|
2461
|
+
* @internal
|
|
2462
|
+
*/
|
|
2463
|
+
static _ENCODE_LOOKUP;
|
|
2464
|
+
/**
|
|
2465
|
+
* Lookup table for decoding.
|
|
2466
|
+
* @internal
|
|
2467
|
+
*/
|
|
2468
|
+
static _DECODE_LOOKUP;
|
|
2469
|
+
/**
|
|
2470
|
+
* Encode a raw array to UTF8 string.
|
|
2471
|
+
* @param array The bytes to encode.
|
|
2472
|
+
* @param startIndex The index to start in the bytes.
|
|
2473
|
+
* @param length The length of bytes to read.
|
|
2474
|
+
* @returns The array formatted as UTF8.
|
|
2475
|
+
*/
|
|
2476
|
+
static bytesToUtf8(array, startIndex, length) {
|
|
2477
|
+
const start = startIndex ?? 0;
|
|
2478
|
+
const len = length ?? array.length;
|
|
2479
|
+
let str = "";
|
|
2480
|
+
for (let i = start; i < start + len; i++) {
|
|
2481
|
+
const value = array[i];
|
|
2482
|
+
if (value < 0x80) {
|
|
2483
|
+
str += String.fromCharCode(value);
|
|
2484
|
+
}
|
|
2485
|
+
else if (value > 0xbf && value < 0xe0) {
|
|
2486
|
+
str += String.fromCharCode(((value & 0x1f) << 6) | (array[i + 1] & 0x3f));
|
|
2487
|
+
i += 1;
|
|
2488
|
+
}
|
|
2489
|
+
else if (value > 0xdf && value < 0xf0) {
|
|
2490
|
+
str += String.fromCharCode(((value & 0x0f) << 12) | ((array[i + 1] & 0x3f) << 6) | (array[i + 2] & 0x3f));
|
|
2491
|
+
i += 2;
|
|
2492
|
+
}
|
|
2493
|
+
else {
|
|
2494
|
+
// surrogate pair
|
|
2495
|
+
const charCode = (((value & 0x07) << 18) |
|
|
2496
|
+
((array[i + 1] & 0x3f) << 12) |
|
|
2497
|
+
((array[i + 2] & 0x3f) << 6) |
|
|
2498
|
+
(array[i + 3] & 0x3f)) -
|
|
2499
|
+
0x010000;
|
|
2500
|
+
str += String.fromCharCode((charCode >> 10) | 0xd800, (charCode & 0x03ff) | 0xdc00);
|
|
2501
|
+
i += 3;
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
return str;
|
|
2505
|
+
}
|
|
2506
|
+
/**
|
|
2507
|
+
* Convert a UTF8 string to raw array.
|
|
2508
|
+
* @param utf8 The text to decode.
|
|
2509
|
+
* @returns The array.
|
|
2510
|
+
*/
|
|
2511
|
+
static utf8ToBytes(utf8) {
|
|
2512
|
+
const bytes = [];
|
|
2513
|
+
for (let i = 0; i < utf8.length; i++) {
|
|
2514
|
+
let charCode = utf8.charCodeAt(i);
|
|
2515
|
+
if (charCode < 0x80) {
|
|
2516
|
+
bytes.push(charCode);
|
|
2517
|
+
}
|
|
2518
|
+
else if (charCode < 0x800) {
|
|
2519
|
+
bytes.push(0xc0 | (charCode >> 6), 0x80 | (charCode & 0x3f));
|
|
2520
|
+
}
|
|
2521
|
+
else if (charCode < 0xd800 || charCode >= 0xe000) {
|
|
2522
|
+
bytes.push(0xe0 | (charCode >> 12), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f));
|
|
2523
|
+
}
|
|
2524
|
+
else {
|
|
2525
|
+
// surrogate pair
|
|
2526
|
+
i++;
|
|
2527
|
+
// UTF-16 encodes 0x10000-0x10FFFF by
|
|
2528
|
+
// subtracting 0x10000 and splitting the
|
|
2529
|
+
// 20 bits of 0x0-0xFFFFF into two halves
|
|
2530
|
+
charCode = 0x10000 + (((charCode & 0x3ff) << 10) | (utf8.charCodeAt(i) & 0x3ff));
|
|
2531
|
+
bytes.push(0xf0 | (charCode >> 18), 0x80 | ((charCode >> 12) & 0x3f), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f));
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
2534
|
+
return Uint8Array.from(bytes);
|
|
2535
|
+
}
|
|
2536
|
+
/**
|
|
2537
|
+
* Encode a raw array to hex string.
|
|
2538
|
+
* @param array The bytes to encode.
|
|
2539
|
+
* @param includePrefix Include the 0x prefix on the returned hex.
|
|
2540
|
+
* @param startIndex The index to start in the bytes.
|
|
2541
|
+
* @param length The length of bytes to read.
|
|
2542
|
+
* @param reverse Reverse the combine direction.
|
|
2543
|
+
* @returns The array formatted as hex.
|
|
2544
|
+
*/
|
|
2545
|
+
static bytesToHex(array, includePrefix = false, startIndex, length, reverse) {
|
|
2546
|
+
let hex = "";
|
|
2547
|
+
this.buildHexLookups();
|
|
2548
|
+
if (Converter._ENCODE_LOOKUP) {
|
|
2549
|
+
const len = length ?? array.length;
|
|
2550
|
+
const start = startIndex ?? 0;
|
|
2551
|
+
if (reverse) {
|
|
2552
|
+
for (let i = 0; i < len; i++) {
|
|
2553
|
+
hex = Converter._ENCODE_LOOKUP[array[start + i]] + hex;
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
else {
|
|
2557
|
+
for (let i = 0; i < len; i++) {
|
|
2558
|
+
hex += Converter._ENCODE_LOOKUP[array[start + i]];
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
return includePrefix ? HexHelper.addPrefix(hex) : hex;
|
|
2563
|
+
}
|
|
2564
|
+
/**
|
|
2565
|
+
* Decode a hex string to raw array.
|
|
2566
|
+
* @param hex The hex to decode.
|
|
2567
|
+
* @param reverse Store the characters in reverse.
|
|
2568
|
+
* @returns The array.
|
|
2569
|
+
*/
|
|
2570
|
+
static hexToBytes(hex, reverse) {
|
|
2571
|
+
const strippedHex = HexHelper.stripPrefix(hex);
|
|
2572
|
+
const sizeof = strippedHex.length >> 1;
|
|
2573
|
+
const length = sizeof << 1;
|
|
2574
|
+
const array = new Uint8Array(sizeof);
|
|
2575
|
+
this.buildHexLookups();
|
|
2576
|
+
if (Converter._DECODE_LOOKUP) {
|
|
2577
|
+
let i = 0;
|
|
2578
|
+
let n = 0;
|
|
2579
|
+
while (i < length) {
|
|
2580
|
+
array[n++] =
|
|
2581
|
+
(Converter._DECODE_LOOKUP[strippedHex.charCodeAt(i++)] << 4) |
|
|
2582
|
+
Converter._DECODE_LOOKUP[strippedHex.charCodeAt(i++)];
|
|
2583
|
+
}
|
|
2584
|
+
if (reverse) {
|
|
2585
|
+
array.reverse();
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
return array;
|
|
2589
|
+
}
|
|
2590
|
+
/**
|
|
2591
|
+
* Convert the UTF8 to hex.
|
|
2592
|
+
* @param utf8 The text to convert.
|
|
2593
|
+
* @param includePrefix Include the 0x prefix on the returned hex.
|
|
2594
|
+
* @returns The hex version of the bytes.
|
|
2595
|
+
*/
|
|
2596
|
+
static utf8ToHex(utf8, includePrefix = false) {
|
|
2597
|
+
const hex = Converter.bytesToHex(Converter.utf8ToBytes(utf8));
|
|
2598
|
+
return includePrefix ? HexHelper.addPrefix(hex) : hex;
|
|
2599
|
+
}
|
|
2600
|
+
/**
|
|
2601
|
+
* Convert the hex text to text.
|
|
2602
|
+
* @param hex The hex to convert.
|
|
2603
|
+
* @returns The UTF8 version of the bytes.
|
|
2604
|
+
*/
|
|
2605
|
+
static hexToUtf8(hex) {
|
|
2606
|
+
return Converter.bytesToUtf8(Converter.hexToBytes(HexHelper.stripPrefix(hex)));
|
|
2607
|
+
}
|
|
2608
|
+
/**
|
|
2609
|
+
* Convert bytes to binary string.
|
|
2610
|
+
* @param bytes The bytes to convert.
|
|
2611
|
+
* @returns A binary string of the bytes.
|
|
2612
|
+
*/
|
|
2613
|
+
static bytesToBinary(bytes) {
|
|
2614
|
+
const b = [];
|
|
2615
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2616
|
+
b.push(bytes[i].toString(2).padStart(8, "0"));
|
|
2617
|
+
}
|
|
2618
|
+
return b.join("");
|
|
2619
|
+
}
|
|
2620
|
+
/**
|
|
2621
|
+
* Convert a binary string to bytes.
|
|
2622
|
+
* @param binary The binary string.
|
|
2623
|
+
* @returns The bytes.
|
|
2624
|
+
*/
|
|
2625
|
+
static binaryToBytes(binary) {
|
|
2626
|
+
const bytes = new Uint8Array(Math.ceil(binary.length / 8));
|
|
2627
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2628
|
+
bytes[i] = Number.parseInt(binary.slice(i * 8, (i + 1) * 8), 2);
|
|
2629
|
+
}
|
|
2630
|
+
return bytes;
|
|
2631
|
+
}
|
|
2632
|
+
/**
|
|
2633
|
+
* Convert bytes to base64 string.
|
|
2634
|
+
* @param bytes The bytes to convert.
|
|
2635
|
+
* @returns A base64 string of the bytes.
|
|
2636
|
+
*/
|
|
2637
|
+
static bytesToBase64(bytes) {
|
|
2638
|
+
return Base64.encode(bytes);
|
|
2639
|
+
}
|
|
2640
|
+
/**
|
|
2641
|
+
* Convert a base64 string to bytes.
|
|
2642
|
+
* @param base64 The base64 string.
|
|
2643
|
+
* @returns The bytes.
|
|
2644
|
+
*/
|
|
2645
|
+
static base64ToBytes(base64) {
|
|
2646
|
+
return Base64.decode(base64);
|
|
2647
|
+
}
|
|
2648
|
+
/**
|
|
2649
|
+
* Convert bytes to base64 url string.
|
|
2650
|
+
* @param bytes The bytes to convert.
|
|
2651
|
+
* @returns A base64 url string of the bytes.
|
|
2652
|
+
*/
|
|
2653
|
+
static bytesToBase64Url(bytes) {
|
|
2654
|
+
return Base64Url.encode(bytes);
|
|
2655
|
+
}
|
|
2656
|
+
/**
|
|
2657
|
+
* Convert a base64 url string to bytes.
|
|
2658
|
+
* @param base64Url The base64 url string.
|
|
2659
|
+
* @returns The bytes.
|
|
2660
|
+
*/
|
|
2661
|
+
static base64UrlToBytes(base64Url) {
|
|
2662
|
+
return Base64Url.decode(base64Url);
|
|
2663
|
+
}
|
|
2664
|
+
/**
|
|
2665
|
+
* Build the static lookup tables.
|
|
2666
|
+
* @internal
|
|
2667
|
+
*/
|
|
2668
|
+
static buildHexLookups() {
|
|
2669
|
+
if (!Converter._ENCODE_LOOKUP || !Converter._DECODE_LOOKUP) {
|
|
2670
|
+
const alphabet = "0123456789abcdef";
|
|
2671
|
+
Converter._ENCODE_LOOKUP = [];
|
|
2672
|
+
Converter._DECODE_LOOKUP = [];
|
|
2673
|
+
for (let i = 0; i < 256; i++) {
|
|
2674
|
+
Converter._ENCODE_LOOKUP[i] = alphabet[(i >> 4) & 0xf] + alphabet[i & 0xf];
|
|
2675
|
+
if (i < 16) {
|
|
2676
|
+
if (i < 10) {
|
|
2677
|
+
Converter._DECODE_LOOKUP[0x30 + i] = i;
|
|
2678
|
+
}
|
|
2679
|
+
else {
|
|
2680
|
+
Converter._DECODE_LOOKUP[0x61 - 10 + i] = i;
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2688
|
+
/**
|
|
2689
|
+
* Class to help with objects.
|
|
2690
|
+
*/
|
|
2691
|
+
class ObjectHelper {
|
|
2692
|
+
/**
|
|
2693
|
+
* Runtime name for the class.
|
|
2694
|
+
* @internal
|
|
2695
|
+
*/
|
|
2696
|
+
static _CLASS_NAME = "ObjectHelper";
|
|
2697
|
+
/**
|
|
2698
|
+
* Convert an object to bytes.
|
|
2699
|
+
* @param obj The object to convert.
|
|
2700
|
+
* @param format Format the JSON content.
|
|
2701
|
+
* @returns The object as bytes.
|
|
2702
|
+
*/
|
|
2703
|
+
static toBytes(obj, format = false) {
|
|
2704
|
+
if (obj === undefined) {
|
|
2705
|
+
return new Uint8Array();
|
|
2706
|
+
}
|
|
2707
|
+
const json = format ? JSON.stringify(obj, undefined, "\t") : JSON.stringify(obj);
|
|
2708
|
+
return Converter.utf8ToBytes(json);
|
|
2709
|
+
}
|
|
2710
|
+
/**
|
|
2711
|
+
* Convert a bytes to an object.
|
|
2712
|
+
* @param bytes The bytes to convert to an object.
|
|
2713
|
+
* @returns The object.
|
|
2714
|
+
* @throws GeneralError if there was an error parsing the JSON.
|
|
2715
|
+
*/
|
|
2716
|
+
static fromBytes(bytes) {
|
|
2717
|
+
if (Is.empty(bytes) || bytes.length === 0) {
|
|
2718
|
+
return undefined;
|
|
2719
|
+
}
|
|
2720
|
+
try {
|
|
2721
|
+
const utf8 = Converter.bytesToUtf8(bytes);
|
|
2722
|
+
return JSON.parse(utf8);
|
|
2723
|
+
}
|
|
2724
|
+
catch (err) {
|
|
2725
|
+
throw new GeneralError(ObjectHelper._CLASS_NAME, "failedBytesToJSON", undefined, err);
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
/**
|
|
2729
|
+
* Make a deep clone of an object.
|
|
2730
|
+
* @param obj The object to clone.
|
|
2731
|
+
* @returns The objects clone.
|
|
2732
|
+
*/
|
|
2733
|
+
static clone(obj) {
|
|
2734
|
+
if (Is.undefined(obj)) {
|
|
2735
|
+
return undefined;
|
|
2736
|
+
}
|
|
2737
|
+
return structuredClone(obj);
|
|
2738
|
+
}
|
|
2739
|
+
/**
|
|
2740
|
+
* Deep merge objects.
|
|
2741
|
+
* @param obj1 The first object to merge.
|
|
2742
|
+
* @param obj2 The second object to merge.
|
|
2743
|
+
* @returns The combined deep merge of the objects.
|
|
2744
|
+
*/
|
|
2745
|
+
static merge(obj1, obj2) {
|
|
2746
|
+
if (Is.empty(obj1)) {
|
|
2747
|
+
return ObjectHelper.clone(obj2);
|
|
2748
|
+
}
|
|
2749
|
+
if (Is.empty(obj2)) {
|
|
2750
|
+
return ObjectHelper.clone(obj1);
|
|
2751
|
+
}
|
|
2752
|
+
const obj1Clone = ObjectHelper.clone(obj1);
|
|
2753
|
+
if (Is.object(obj1Clone) && Is.object(obj2)) {
|
|
2754
|
+
const keys = Object.keys(obj2);
|
|
2755
|
+
for (const key of keys) {
|
|
2756
|
+
if (Is.object(obj1Clone[key]) && Is.object(obj2[key])) {
|
|
2757
|
+
ObjectHelper.propertySet(obj1Clone, key, ObjectHelper.merge(obj1Clone[key], obj2[key]));
|
|
2758
|
+
}
|
|
2759
|
+
else {
|
|
2760
|
+
ObjectHelper.propertySet(obj1Clone, key, obj2[key]);
|
|
2761
|
+
}
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2764
|
+
return obj1Clone;
|
|
2765
|
+
}
|
|
2766
|
+
/**
|
|
2767
|
+
* Does one object equal another.
|
|
2768
|
+
* @param obj1 The first object to compare.
|
|
2769
|
+
* @param obj2 The second object to compare.
|
|
2770
|
+
* @param strictPropertyOrder Should the properties be in the same order, defaults to true.
|
|
2771
|
+
* @returns True is the objects are equal.
|
|
2772
|
+
*/
|
|
2773
|
+
static equal(obj1, obj2, strictPropertyOrder) {
|
|
2774
|
+
if (strictPropertyOrder ?? true) {
|
|
2775
|
+
return JSON.stringify(obj1) === JSON.stringify(obj2);
|
|
2776
|
+
}
|
|
2777
|
+
return JsonHelper.canonicalize(obj1) === JsonHelper.canonicalize(obj2);
|
|
2778
|
+
}
|
|
2779
|
+
/**
|
|
2780
|
+
* Get the property of an unknown object.
|
|
2781
|
+
* @param obj The object to get the property from.
|
|
2782
|
+
* @param property The property to get, can be separated by dots for nested path.
|
|
2783
|
+
* @returns The property.
|
|
2784
|
+
*/
|
|
2785
|
+
static propertyGet(obj, property) {
|
|
2786
|
+
if (property.includes(".")) {
|
|
2787
|
+
const parts = property.split(".");
|
|
2788
|
+
let value = obj;
|
|
2789
|
+
for (const part of parts) {
|
|
2790
|
+
if (Is.object(value)) {
|
|
2791
|
+
value = value[part];
|
|
2792
|
+
}
|
|
2793
|
+
else {
|
|
2794
|
+
return undefined;
|
|
2795
|
+
}
|
|
2796
|
+
}
|
|
2797
|
+
return value;
|
|
2798
|
+
}
|
|
2799
|
+
return Is.object(obj) ? obj[property] : undefined;
|
|
2800
|
+
}
|
|
2801
|
+
/**
|
|
2802
|
+
* Set the property of an unknown object.
|
|
2803
|
+
* @param obj The object to set the property from.
|
|
2804
|
+
* @param property The property to set.
|
|
2805
|
+
* @param value The value to set.
|
|
2806
|
+
*/
|
|
2807
|
+
static propertySet(obj, property, value) {
|
|
2808
|
+
if (Is.object(obj)) {
|
|
2809
|
+
obj[property] = value;
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
/**
|
|
2813
|
+
* Delete the property of an unknown object.
|
|
2814
|
+
* @param obj The object to set the property from.
|
|
2815
|
+
* @param property The property to set
|
|
2816
|
+
*/
|
|
2817
|
+
static propertyDelete(obj, property) {
|
|
2818
|
+
if (Is.object(obj)) {
|
|
2819
|
+
delete obj[property];
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
/**
|
|
2823
|
+
* Pick a subset of properties from an object.
|
|
2824
|
+
* @param obj The object to pick the properties from.
|
|
2825
|
+
* @param keys The property keys to pick.
|
|
2826
|
+
* @returns The partial object.
|
|
2827
|
+
*/
|
|
2828
|
+
static pick(obj, keys) {
|
|
2829
|
+
if (Is.object(obj) && Is.arrayValue(keys)) {
|
|
2830
|
+
const result = {};
|
|
2831
|
+
for (const key of keys) {
|
|
2832
|
+
result[key] = obj[key];
|
|
2833
|
+
}
|
|
2834
|
+
return result;
|
|
2835
|
+
}
|
|
2836
|
+
return obj;
|
|
2837
|
+
}
|
|
2838
|
+
/**
|
|
2839
|
+
* Omit a subset of properties from an object.
|
|
2840
|
+
* @param obj The object to omit the properties from.
|
|
2841
|
+
* @param keys The property keys to omit.
|
|
2842
|
+
* @returns The partial object.
|
|
2843
|
+
*/
|
|
2844
|
+
static omit(obj, keys) {
|
|
2845
|
+
if (Is.object(obj) && Is.arrayValue(keys)) {
|
|
2846
|
+
const result = { ...obj };
|
|
2847
|
+
for (const key of keys) {
|
|
2848
|
+
delete result[key];
|
|
2849
|
+
}
|
|
2850
|
+
return result;
|
|
2851
|
+
}
|
|
2852
|
+
return obj;
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2856
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2857
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
2858
|
+
/**
|
|
2859
|
+
* Class to help with random generation.
|
|
2860
|
+
*/
|
|
2861
|
+
class RandomHelper {
|
|
2862
|
+
/**
|
|
2863
|
+
* Generate a new random array.
|
|
2864
|
+
* @param length The length of buffer to create.
|
|
2865
|
+
* @returns The random array.
|
|
2866
|
+
*/
|
|
2867
|
+
static generate(length) {
|
|
2868
|
+
const randomBytes = new Uint8Array(length);
|
|
2869
|
+
globalThis.crypto.getRandomValues(randomBytes);
|
|
2870
|
+
return randomBytes;
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
|
|
2874
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2875
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
2876
|
+
/**
|
|
2877
|
+
* Compression types.
|
|
2878
|
+
*/
|
|
2879
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
2880
|
+
const CompressionType = {
|
|
2881
|
+
/**
|
|
2882
|
+
* Gzip.
|
|
2883
|
+
*/
|
|
2884
|
+
Gzip: "gzip",
|
|
2885
|
+
/**
|
|
2886
|
+
* deflate.
|
|
2887
|
+
*/
|
|
2888
|
+
Deflate: "deflate"
|
|
2889
|
+
};
|
|
2890
|
+
|
|
2891
|
+
/**
|
|
2892
|
+
* A class to represent a bit string.
|
|
2893
|
+
*/
|
|
2894
|
+
class BitString {
|
|
2895
|
+
/**
|
|
2896
|
+
* Runtime name for the class.
|
|
2897
|
+
* @internal
|
|
2898
|
+
*/
|
|
2899
|
+
static _CLASS_NAME = "BitString";
|
|
2900
|
+
/**
|
|
2901
|
+
* The storage for the bits.
|
|
2902
|
+
* @internal
|
|
2903
|
+
*/
|
|
2904
|
+
_bits;
|
|
2905
|
+
/**
|
|
2906
|
+
* The number of bits stored in the buffer.
|
|
2907
|
+
* @internal
|
|
2908
|
+
*/
|
|
2909
|
+
_numberBits;
|
|
2910
|
+
/**
|
|
2911
|
+
* Create a new instance of BitString.
|
|
2912
|
+
* @param numberBits The length of the bit string.
|
|
2913
|
+
*/
|
|
2914
|
+
constructor(numberBits) {
|
|
2915
|
+
Guards.integer(BitString._CLASS_NAME, "numberBits", numberBits);
|
|
2916
|
+
this._numberBits = numberBits;
|
|
2917
|
+
this._bits = new Uint8Array(Math.ceil(numberBits / 8));
|
|
2918
|
+
}
|
|
2919
|
+
/**
|
|
2920
|
+
* Create a new instance of BitString from a bit array.
|
|
2921
|
+
* @param bits The bits to create the bit string from.
|
|
2922
|
+
* @param numberBits The number of bits in the bit string.
|
|
2923
|
+
* @returns The new instance of BitString.
|
|
2924
|
+
*/
|
|
2925
|
+
static fromBits(bits, numberBits) {
|
|
2926
|
+
Guards.uint8Array(BitString._CLASS_NAME, "bits", bits);
|
|
2927
|
+
Guards.integer(BitString._CLASS_NAME, "numberBits", numberBits);
|
|
2928
|
+
const bs = new BitString(numberBits);
|
|
2929
|
+
bs._bits.set(bits);
|
|
2930
|
+
return bs;
|
|
2931
|
+
}
|
|
2932
|
+
/**
|
|
2933
|
+
* Get the bit at the given index.
|
|
2934
|
+
* @param index The index to get the bit for.
|
|
2935
|
+
* @returns True if the bit at the index is set.
|
|
2936
|
+
* @throws GeneralError if the index is out of range.
|
|
2937
|
+
*/
|
|
2938
|
+
getBit(index) {
|
|
2939
|
+
Guards.integer(BitString._CLASS_NAME, "index", index);
|
|
2940
|
+
if (index < 0 || index >= this._numberBits) {
|
|
2941
|
+
throw new GeneralError(BitString._CLASS_NAME, "outOfRange", {
|
|
2942
|
+
index,
|
|
2943
|
+
numberBits: this._numberBits
|
|
2944
|
+
});
|
|
2945
|
+
}
|
|
2946
|
+
const byteIndex = Math.floor(index / 8);
|
|
2947
|
+
const bitIndex = index % 8;
|
|
2948
|
+
return (this._bits[byteIndex] & (1 << bitIndex)) !== 0;
|
|
2949
|
+
}
|
|
2950
|
+
/**
|
|
2951
|
+
* Set the bit at the given index.
|
|
2952
|
+
* @param index The index to set the bit for.
|
|
2953
|
+
* @param value The value to set the bit to.
|
|
2954
|
+
* @throws GeneralError if the index is out of range.
|
|
2955
|
+
*/
|
|
2956
|
+
setBit(index, value) {
|
|
2957
|
+
if (index < 0 || index >= this._numberBits) {
|
|
2958
|
+
throw new GeneralError(BitString._CLASS_NAME, "outOfRange", {
|
|
2959
|
+
index,
|
|
2960
|
+
numberBits: this._numberBits
|
|
2961
|
+
});
|
|
2962
|
+
}
|
|
2963
|
+
const byteIndex = Math.floor(index / 8);
|
|
2964
|
+
const bitIndex = index % 8;
|
|
2965
|
+
if (value) {
|
|
2966
|
+
this._bits[byteIndex] |= 1 << bitIndex;
|
|
2967
|
+
}
|
|
2968
|
+
else {
|
|
2969
|
+
this._bits[byteIndex] &= ~(1 << bitIndex);
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
/**
|
|
2973
|
+
* Get the bits of the bit string.
|
|
2974
|
+
* @returns The bits stored in a Uint8Array.
|
|
2975
|
+
*/
|
|
2976
|
+
getBits() {
|
|
2977
|
+
return this._bits;
|
|
2978
|
+
}
|
|
2979
|
+
/**
|
|
2980
|
+
* Get the length of the bit string.
|
|
2981
|
+
* @returns The length of the bit string.
|
|
2982
|
+
*/
|
|
2983
|
+
getLength() {
|
|
2984
|
+
return this._numberBits;
|
|
2985
|
+
}
|
|
2986
|
+
}
|
|
2987
|
+
|
|
2988
|
+
/**
|
|
2989
|
+
* Class to help with urls.
|
|
2990
|
+
*/
|
|
2991
|
+
class Url {
|
|
2992
|
+
/**
|
|
2993
|
+
* Runtime name for the class.
|
|
2994
|
+
* @internal
|
|
2995
|
+
*/
|
|
2996
|
+
static _CLASS_NAME = "Url";
|
|
2997
|
+
/**
|
|
2998
|
+
* The internal representation of the url.
|
|
2999
|
+
* @internal
|
|
3000
|
+
*/
|
|
3001
|
+
_urlParts;
|
|
3002
|
+
/**
|
|
3003
|
+
* Create a new instance of Url.
|
|
3004
|
+
* @param url The url string.
|
|
3005
|
+
*/
|
|
3006
|
+
constructor(url) {
|
|
3007
|
+
Guards.stringValue(Url._CLASS_NAME, "url", url);
|
|
3008
|
+
try {
|
|
3009
|
+
const u = new URL(url);
|
|
3010
|
+
this._urlParts = Url.fromURLToParts(u);
|
|
3011
|
+
}
|
|
3012
|
+
catch {
|
|
3013
|
+
throw new GuardError(Url._CLASS_NAME, "guard.url", "url", url);
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
/**
|
|
3017
|
+
* Try and parse a string into the url parts.
|
|
3018
|
+
* @param url The url to parse.
|
|
3019
|
+
* @returns The formatted url or undefined if the value is not a url.
|
|
3020
|
+
*/
|
|
3021
|
+
static tryParseExact(url) {
|
|
3022
|
+
if (!Is.stringValue(url)) {
|
|
3023
|
+
return;
|
|
3024
|
+
}
|
|
3025
|
+
try {
|
|
3026
|
+
// By constructing a new standard URL class this will
|
|
3027
|
+
// validate the format
|
|
3028
|
+
const u = new URL(url);
|
|
3029
|
+
return Url.fromParts(Url.fromURLToParts(u));
|
|
3030
|
+
}
|
|
3031
|
+
catch { }
|
|
3032
|
+
}
|
|
3033
|
+
/**
|
|
3034
|
+
* Parse a string into the url parts.
|
|
3035
|
+
* @param source The source of the error.
|
|
3036
|
+
* @param property The name of the property.
|
|
3037
|
+
* @param value The url to parse.
|
|
3038
|
+
* @throws GuardError If the value does not match the assertion.
|
|
3039
|
+
*/
|
|
3040
|
+
static guard(source, property, value) {
|
|
3041
|
+
Guards.stringValue(source, property, value);
|
|
3042
|
+
const result = Url.tryParseExact(value);
|
|
3043
|
+
if (!result) {
|
|
3044
|
+
throw new GuardError(source, "guard.url", property, value);
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
/**
|
|
3048
|
+
* Validate a string as a Url.
|
|
3049
|
+
* @param property Throw an exception if the url property is invalid.
|
|
3050
|
+
* @param value The url to parse.
|
|
3051
|
+
* @param failures The list of failures to add to.
|
|
3052
|
+
* @returns The formatted url.
|
|
3053
|
+
*/
|
|
3054
|
+
static validate(property, value, failures) {
|
|
3055
|
+
if (!Is.stringValue(value)) {
|
|
3056
|
+
failures.push({
|
|
3057
|
+
property,
|
|
3058
|
+
reason: "validation.notEmpty"
|
|
3059
|
+
});
|
|
3060
|
+
return false;
|
|
3061
|
+
}
|
|
3062
|
+
const result = Url.tryParseExact(value);
|
|
3063
|
+
if (Is.undefined(result)) {
|
|
3064
|
+
failures.push({
|
|
3065
|
+
property,
|
|
3066
|
+
reason: "validation.beUrl"
|
|
3067
|
+
});
|
|
3068
|
+
return false;
|
|
3069
|
+
}
|
|
3070
|
+
return true;
|
|
3071
|
+
}
|
|
3072
|
+
/**
|
|
3073
|
+
* Construct a url from a URL.
|
|
3074
|
+
* @param url The url to construct from.
|
|
3075
|
+
* @returns The formatted url.
|
|
3076
|
+
*/
|
|
3077
|
+
static fromURLToParts(url) {
|
|
3078
|
+
return {
|
|
3079
|
+
schema: url.protocol.replace(/:$/, ""),
|
|
3080
|
+
host: url.hostname,
|
|
3081
|
+
port: Coerce.number(url.port),
|
|
3082
|
+
path: url.pathname,
|
|
3083
|
+
params: Is.stringValue(url.search) ? url.search.replace(/^\?/, "") : undefined,
|
|
3084
|
+
hash: Is.stringValue(url.hash) ? url.hash.replace(/^#/, "") : undefined
|
|
3085
|
+
};
|
|
3086
|
+
}
|
|
3087
|
+
/**
|
|
3088
|
+
* Construct a url from valid parts.
|
|
3089
|
+
* @param urlParts The url to create the parts from.
|
|
3090
|
+
* @returns The formatted url.
|
|
3091
|
+
*/
|
|
3092
|
+
static fromParts(urlParts) {
|
|
3093
|
+
const u = new Url("http://dummy");
|
|
3094
|
+
u._urlParts = urlParts;
|
|
3095
|
+
return u;
|
|
3096
|
+
}
|
|
3097
|
+
/**
|
|
3098
|
+
* Get the parts of the url.
|
|
3099
|
+
* @returns The parts of the url.
|
|
3100
|
+
*/
|
|
3101
|
+
parts() {
|
|
3102
|
+
return this._urlParts;
|
|
3103
|
+
}
|
|
3104
|
+
/**
|
|
3105
|
+
* Convert the parts in to a full string.
|
|
3106
|
+
* @returns The formatted url.
|
|
3107
|
+
*/
|
|
3108
|
+
toString() {
|
|
3109
|
+
const parts = [this._urlParts.schema, "://", this._urlParts.host];
|
|
3110
|
+
if (Is.number(this._urlParts.port)) {
|
|
3111
|
+
parts.push(`:${this._urlParts.port}`);
|
|
3112
|
+
}
|
|
3113
|
+
if (Is.stringValue(this._urlParts.path)) {
|
|
3114
|
+
parts.push(this._urlParts.path);
|
|
3115
|
+
}
|
|
3116
|
+
if (Is.stringValue(this._urlParts.params)) {
|
|
3117
|
+
parts.push(`?${this._urlParts.params}`);
|
|
3118
|
+
}
|
|
3119
|
+
if (Is.stringValue(this._urlParts.hash)) {
|
|
3120
|
+
parts.push(`#${this._urlParts.hash}`);
|
|
3121
|
+
}
|
|
3122
|
+
return parts.join("");
|
|
3123
|
+
}
|
|
3124
|
+
}
|
|
3125
|
+
|
|
3126
|
+
/**
|
|
3127
|
+
* Class to help with urns.
|
|
3128
|
+
*/
|
|
3129
|
+
class Urn {
|
|
3130
|
+
/**
|
|
3131
|
+
* Runtime name for the class.
|
|
3132
|
+
* @internal
|
|
3133
|
+
*/
|
|
3134
|
+
static _CLASS_NAME = "Urn";
|
|
3135
|
+
/**
|
|
3136
|
+
* The specific part of the namespace.
|
|
3137
|
+
* @internal
|
|
3138
|
+
*/
|
|
3139
|
+
_urnParts;
|
|
3140
|
+
/**
|
|
3141
|
+
* Create a new instance of Urn.
|
|
3142
|
+
* @param namespaceIdentifier The identifier for the namespace.
|
|
3143
|
+
* @param namespaceSpecific The specific part of the namespace.
|
|
3144
|
+
*/
|
|
3145
|
+
constructor(namespaceIdentifier, namespaceSpecific) {
|
|
3146
|
+
Guards.stringValue(Urn._CLASS_NAME, "namespaceIdentifier", namespaceIdentifier);
|
|
3147
|
+
// Strip leading and trailing colons
|
|
3148
|
+
this._urnParts = [this.stripColons(namespaceIdentifier)];
|
|
3149
|
+
if (Is.array(namespaceSpecific)) {
|
|
3150
|
+
Guards.arrayValue(Urn._CLASS_NAME, "namespaceSpecific", namespaceSpecific);
|
|
3151
|
+
this._urnParts.push(...namespaceSpecific);
|
|
3152
|
+
}
|
|
3153
|
+
else {
|
|
3154
|
+
Guards.stringValue(Urn._CLASS_NAME, "namespaceSpecific", namespaceSpecific);
|
|
3155
|
+
this._urnParts.push(...this.stripColons(namespaceSpecific).split(":"));
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
3158
|
+
/**
|
|
3159
|
+
* Generate a random identifier with 32 byte id.
|
|
3160
|
+
* @param namespace The prefix for the urn.
|
|
3161
|
+
* @returns A new Id in URN format.
|
|
3162
|
+
*/
|
|
3163
|
+
static generateRandom(namespace) {
|
|
3164
|
+
return new Urn(namespace, Converter.bytesToHex(RandomHelper.generate(32)));
|
|
3165
|
+
}
|
|
3166
|
+
/**
|
|
3167
|
+
* Does the provided urn match the namespace.
|
|
3168
|
+
* @param urn The urn to check.
|
|
3169
|
+
* @param namespace The namespace to match.
|
|
3170
|
+
* @returns True if the namespace matches.
|
|
3171
|
+
*/
|
|
3172
|
+
static hasNamespace(urn, namespace) {
|
|
3173
|
+
if (!Is.stringValue(urn)) {
|
|
3174
|
+
return false;
|
|
3175
|
+
}
|
|
3176
|
+
if (urn.startsWith("urn:")) {
|
|
3177
|
+
urn = urn.slice(4);
|
|
3178
|
+
}
|
|
3179
|
+
if (urn.length === namespace.length) {
|
|
3180
|
+
return urn === namespace;
|
|
3181
|
+
}
|
|
3182
|
+
return urn.startsWith(`${namespace}:`);
|
|
3183
|
+
}
|
|
3184
|
+
/**
|
|
3185
|
+
* Try and parse a string into the urn parts.
|
|
3186
|
+
* @param urn The urn to parse.
|
|
3187
|
+
* @returns The formatted urn or undefined if the value is not a urn.
|
|
3188
|
+
*/
|
|
3189
|
+
static tryParseExact(urn) {
|
|
3190
|
+
if (!Is.stringValue(urn)) {
|
|
3191
|
+
return;
|
|
3192
|
+
}
|
|
3193
|
+
const parts = urn.split(":");
|
|
3194
|
+
if (parts[0] === "urn") {
|
|
3195
|
+
parts.shift();
|
|
3196
|
+
}
|
|
3197
|
+
if (parts.length < 2) {
|
|
3198
|
+
return;
|
|
3199
|
+
}
|
|
3200
|
+
if (!/[\da-z][\da-z-]{0,31}/.test(parts[0])) {
|
|
3201
|
+
return;
|
|
3202
|
+
}
|
|
3203
|
+
for (let i = 1; i < parts.length; i++) {
|
|
3204
|
+
if (!/[\d!#$%'()*+,./:;=?@_a-z-]+/.test(parts[i])) {
|
|
3205
|
+
return;
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
return new Urn(parts[0], parts.slice(1));
|
|
3209
|
+
}
|
|
3210
|
+
/**
|
|
3211
|
+
* Construct a urn from a string that has already been validated.
|
|
3212
|
+
* @param urn The urn to parse.
|
|
3213
|
+
* @returns The formatted urn.
|
|
3214
|
+
*/
|
|
3215
|
+
static fromValidString(urn) {
|
|
3216
|
+
const parts = urn.split(":");
|
|
3217
|
+
if (parts[0] === "urn") {
|
|
3218
|
+
parts.shift();
|
|
3219
|
+
}
|
|
3220
|
+
return new Urn(parts[0], parts.slice(1));
|
|
3221
|
+
}
|
|
3222
|
+
/**
|
|
3223
|
+
* Add a urn: prefix if there isn't one already.
|
|
3224
|
+
* @param urn The urn string to add a prefix to.
|
|
3225
|
+
* @returns The urn with a prefix.
|
|
3226
|
+
*/
|
|
3227
|
+
static addPrefix(urn) {
|
|
3228
|
+
if (Is.stringValue(urn)) {
|
|
3229
|
+
if (urn.startsWith("urn:")) {
|
|
3230
|
+
return urn;
|
|
3231
|
+
}
|
|
3232
|
+
return `urn:${urn}`;
|
|
3233
|
+
}
|
|
3234
|
+
}
|
|
3235
|
+
/**
|
|
3236
|
+
* Parse a string into the urn parts.
|
|
3237
|
+
* @param source The source of the error.
|
|
3238
|
+
* @param property The name of the property.
|
|
3239
|
+
* @param value The urn to parse.
|
|
3240
|
+
* @throws GuardError If the value does not match the assertion.
|
|
3241
|
+
*/
|
|
3242
|
+
static guard(source, property, value) {
|
|
3243
|
+
Guards.stringValue(source, property, value);
|
|
3244
|
+
const result = Urn.tryParseExact(value);
|
|
3245
|
+
if (!result) {
|
|
3246
|
+
throw new GuardError(source, "guard.urn", property, value);
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
/**
|
|
3250
|
+
* Validate a string as a Urn.
|
|
3251
|
+
* @param property Throw an exception if the urn property is invalid.
|
|
3252
|
+
* @param value The urn to parse.
|
|
3253
|
+
* @param failures The list of failures to add to.
|
|
3254
|
+
* @returns The formatted urn.
|
|
3255
|
+
*/
|
|
3256
|
+
static validate(property, value, failures) {
|
|
3257
|
+
if (!Is.stringValue(value)) {
|
|
3258
|
+
failures.push({
|
|
3259
|
+
property,
|
|
3260
|
+
reason: "validation.notEmpty"
|
|
3261
|
+
});
|
|
3262
|
+
return false;
|
|
3263
|
+
}
|
|
3264
|
+
const result = Urn.tryParseExact(value);
|
|
3265
|
+
if (Is.undefined(result)) {
|
|
3266
|
+
failures.push({
|
|
3267
|
+
property,
|
|
3268
|
+
reason: "validation.beUrn"
|
|
3269
|
+
});
|
|
3270
|
+
return false;
|
|
3271
|
+
}
|
|
3272
|
+
return true;
|
|
3273
|
+
}
|
|
3274
|
+
/**
|
|
3275
|
+
* Get the parts.
|
|
3276
|
+
* @param startIndex The index to start from, defaults to 0.
|
|
3277
|
+
* @returns The parts.
|
|
3278
|
+
*/
|
|
3279
|
+
parts(startIndex = 0) {
|
|
3280
|
+
return this._urnParts.slice(startIndex);
|
|
3281
|
+
}
|
|
3282
|
+
/**
|
|
3283
|
+
* Get the namespace identifier.
|
|
3284
|
+
* @returns The namespace identifier.
|
|
3285
|
+
*/
|
|
3286
|
+
namespaceIdentifier() {
|
|
3287
|
+
return this._urnParts[0];
|
|
3288
|
+
}
|
|
3289
|
+
/**
|
|
3290
|
+
* Get the namespace method, the first component after the identifier.
|
|
3291
|
+
* @returns The namespace method.
|
|
3292
|
+
*/
|
|
3293
|
+
namespaceMethod() {
|
|
3294
|
+
return this._urnParts.length > 1 ? this._urnParts[1] : "";
|
|
3295
|
+
}
|
|
3296
|
+
/**
|
|
3297
|
+
* Get the namespace specific parts.
|
|
3298
|
+
* @param startIndex The index to start from, defaults to 0.
|
|
3299
|
+
* @returns The namespace specific parts.
|
|
3300
|
+
*/
|
|
3301
|
+
namespaceSpecificParts(startIndex = 0) {
|
|
3302
|
+
return this._urnParts.length > 1 ? this._urnParts.slice(startIndex + 1) : [];
|
|
3303
|
+
}
|
|
3304
|
+
/**
|
|
3305
|
+
* Get the namespace specific.
|
|
3306
|
+
* @param startIndex The index to start from, defaults to 0.
|
|
3307
|
+
* @returns The namespace specific.
|
|
3308
|
+
*/
|
|
3309
|
+
namespaceSpecific(startIndex = 0) {
|
|
3310
|
+
return this._urnParts.length > 1 ? this._urnParts.slice(startIndex + 1).join(":") : "";
|
|
3311
|
+
}
|
|
3312
|
+
/**
|
|
3313
|
+
* Convert the parts in to a full string.
|
|
3314
|
+
* @param omitPrefix Omit the urn: prefix from the string.
|
|
3315
|
+
* @returns The formatted urn.
|
|
3316
|
+
*/
|
|
3317
|
+
toString(omitPrefix = true) {
|
|
3318
|
+
return omitPrefix ? this._urnParts.join(":") : `urn:${this._urnParts.join(":")}`;
|
|
3319
|
+
}
|
|
3320
|
+
/**
|
|
3321
|
+
* Strip the leading and trailing colons from a string.
|
|
3322
|
+
* @param val The value to strip.
|
|
3323
|
+
* @returns The stripped string.
|
|
3324
|
+
* @internal
|
|
3325
|
+
*/
|
|
3326
|
+
stripColons(val) {
|
|
3327
|
+
return val.replace(/^:?(.*?):?$/, "$1");
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
|
|
3331
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3332
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3333
|
+
/**
|
|
3334
|
+
* Cache the results from asynchronous requests.
|
|
3335
|
+
*/
|
|
3336
|
+
class AsyncCache {
|
|
3337
|
+
/**
|
|
3338
|
+
* Cache for the fetch requests.
|
|
3339
|
+
* @internal
|
|
3340
|
+
*/
|
|
3341
|
+
static _cache = {};
|
|
3342
|
+
/**
|
|
3343
|
+
* Execute an async request and cache the result.
|
|
3344
|
+
* @param key The key for the entry in the cache.
|
|
3345
|
+
* @param ttlMs The TTL of the entry in the cache.
|
|
3346
|
+
* @param requestMethod The method to call if not cached.
|
|
3347
|
+
* @returns The response.
|
|
3348
|
+
*/
|
|
3349
|
+
static exec(key, ttlMs, requestMethod) {
|
|
3350
|
+
const cacheEnabled = Is.integer(ttlMs) && ttlMs >= 0;
|
|
3351
|
+
if (cacheEnabled) {
|
|
3352
|
+
AsyncCache.cleanupExpired();
|
|
3353
|
+
if (!this._cache[key]) {
|
|
3354
|
+
this._cache[key] = {
|
|
3355
|
+
response: requestMethod(),
|
|
3356
|
+
expires: ttlMs === 0 ? 0 : Date.now() + ttlMs
|
|
3357
|
+
};
|
|
3358
|
+
}
|
|
3359
|
+
return this._cache[key].response;
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
3362
|
+
/**
|
|
3363
|
+
* Get an entry from the cache.
|
|
3364
|
+
* @param key The key to get from the cache.
|
|
3365
|
+
* @returns The item from the cache if it exists.
|
|
3366
|
+
*/
|
|
3367
|
+
static async get(key) {
|
|
3368
|
+
return AsyncCache._cache[key]?.response;
|
|
3369
|
+
}
|
|
3370
|
+
/**
|
|
3371
|
+
* Remove an entry from the cache.
|
|
3372
|
+
* @param key The key to remove from the cache.
|
|
3373
|
+
*/
|
|
3374
|
+
static remove(key) {
|
|
3375
|
+
delete AsyncCache._cache[key];
|
|
3376
|
+
}
|
|
3377
|
+
/**
|
|
3378
|
+
* Clear the cache.
|
|
3379
|
+
* @param prefix Optional prefix to clear only entries with that prefix.
|
|
3380
|
+
*/
|
|
3381
|
+
static clearCache(prefix) {
|
|
3382
|
+
if (Is.stringValue(prefix)) {
|
|
3383
|
+
for (const entry in this._cache) {
|
|
3384
|
+
if (entry.startsWith(prefix)) {
|
|
3385
|
+
delete this._cache[entry];
|
|
3386
|
+
}
|
|
3387
|
+
}
|
|
3388
|
+
}
|
|
3389
|
+
else {
|
|
3390
|
+
AsyncCache._cache = {};
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
/**
|
|
3394
|
+
* Perform a cleanup of the expired entries in the cache.
|
|
3395
|
+
*/
|
|
3396
|
+
static cleanupExpired() {
|
|
3397
|
+
for (const entry in this._cache) {
|
|
3398
|
+
if (AsyncCache._cache[entry].expires > 0 && AsyncCache._cache[entry].expires < Date.now()) {
|
|
3399
|
+
delete this._cache[entry];
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
}
|
|
3404
|
+
|
|
3405
|
+
/**
|
|
3406
|
+
* A class to handle compression.
|
|
3407
|
+
*/
|
|
3408
|
+
class Compression {
|
|
3409
|
+
/**
|
|
3410
|
+
* Runtime name for the class.
|
|
3411
|
+
* @internal
|
|
3412
|
+
*/
|
|
3413
|
+
static _CLASS_NAME = "Compression";
|
|
3414
|
+
/**
|
|
3415
|
+
* Compress bytes using GZIP.
|
|
3416
|
+
* @param bytes The bytes to compress.
|
|
3417
|
+
* @param type The type of compression to use.
|
|
3418
|
+
* @returns The compressed bytes.
|
|
3419
|
+
*/
|
|
3420
|
+
static async compress(bytes, type) {
|
|
3421
|
+
Guards.uint8Array(Compression._CLASS_NAME, "bytes", bytes);
|
|
3422
|
+
Guards.arrayOneOf(Compression._CLASS_NAME, "type", type, Object.values(CompressionType));
|
|
3423
|
+
const blob = new Blob([bytes]);
|
|
3424
|
+
const ds = new CompressionStream(type);
|
|
3425
|
+
const compressedStream = blob.stream().pipeThrough(ds);
|
|
3426
|
+
const compressedBlob = await new Response(compressedStream).blob();
|
|
3427
|
+
const ab = await compressedBlob.arrayBuffer();
|
|
3428
|
+
const compressedBytes = new Uint8Array(ab);
|
|
3429
|
+
// GZIP header contains a byte which specifies the OS the
|
|
3430
|
+
// compression was performed on. We set this to 3 (Unix) to ensure
|
|
3431
|
+
// that we produce consistent results.
|
|
3432
|
+
if (type === "gzip" && compressedBytes.length >= 10) {
|
|
3433
|
+
compressedBytes[9] = 3;
|
|
3434
|
+
}
|
|
3435
|
+
return compressedBytes;
|
|
3436
|
+
}
|
|
3437
|
+
/**
|
|
3438
|
+
* Decompress a gzipped compressed byte array.
|
|
3439
|
+
* @param compressedBytes The compressed bytes.
|
|
3440
|
+
* @param type The type of compression to use.
|
|
3441
|
+
* @returns The decompressed bytes.
|
|
3442
|
+
*/
|
|
3443
|
+
static async decompress(compressedBytes, type) {
|
|
3444
|
+
Guards.uint8Array(Compression._CLASS_NAME, "compressedBytes", compressedBytes);
|
|
3445
|
+
Guards.arrayOneOf(Compression._CLASS_NAME, "type", type, Object.values(CompressionType));
|
|
3446
|
+
const blob = new Blob([compressedBytes]);
|
|
3447
|
+
const ds = new DecompressionStream(type);
|
|
3448
|
+
const decompressedStream = blob.stream().pipeThrough(ds);
|
|
3449
|
+
const decompressedBlob = await new Response(decompressedStream).blob();
|
|
3450
|
+
const ab = await decompressedBlob.arrayBuffer();
|
|
3451
|
+
return new Uint8Array(ab);
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
3454
|
+
|
|
3455
|
+
// Copyright 2024 IOTA Stiftung.
|
|
3456
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3457
|
+
/**
|
|
3458
|
+
* Class to handle validation operations.
|
|
3459
|
+
*/
|
|
3460
|
+
class Validation {
|
|
3461
|
+
/**
|
|
3462
|
+
* Is the property null or undefined.
|
|
3463
|
+
* @param property The name of the property.
|
|
3464
|
+
* @param value The value to test.
|
|
3465
|
+
* @param failures The list of failures to add to.
|
|
3466
|
+
* @param fieldNameResource Optional i18n resource of the field name to display in the message.
|
|
3467
|
+
* @returns True if the value is a empty.
|
|
3468
|
+
*/
|
|
3469
|
+
static empty(property, value, failures, fieldNameResource) {
|
|
3470
|
+
const is = Is.empty(value);
|
|
3471
|
+
if (!is) {
|
|
3472
|
+
failures.push({
|
|
3473
|
+
property,
|
|
3474
|
+
reason: "validation.beEmpty",
|
|
3475
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
3476
|
+
properties: { value }
|
|
3477
|
+
});
|
|
3478
|
+
}
|
|
3479
|
+
return is;
|
|
3480
|
+
}
|
|
3481
|
+
/**
|
|
3482
|
+
* Is the property is not null or undefined.
|
|
3483
|
+
* @param property The name of the property.
|
|
3484
|
+
* @param value The value to test.
|
|
3485
|
+
* @param failures The list of failures to add to.
|
|
3486
|
+
* @param fieldNameResource Optional i18n resource of the field name to display in the message.
|
|
3487
|
+
* @returns True if the value is a not empty.
|
|
3488
|
+
*/
|
|
3489
|
+
static notEmpty(property, value, failures, fieldNameResource) {
|
|
3490
|
+
const is = Is.notEmpty(value);
|
|
3491
|
+
if (!is) {
|
|
3492
|
+
failures.push({
|
|
3493
|
+
property,
|
|
3494
|
+
reason: "validation.beNotEmpty",
|
|
3495
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
3496
|
+
});
|
|
3497
|
+
}
|
|
3498
|
+
return is;
|
|
3499
|
+
}
|
|
3500
|
+
/**
|
|
3501
|
+
* Is the property a string.
|
|
3502
|
+
* @param property The name of the property.
|
|
3503
|
+
* @param value The value to test.
|
|
3504
|
+
* @param failures The list of failures to add to.
|
|
3505
|
+
* @param fieldNameResource Optional i18n resource of the field name to display in the message.
|
|
3506
|
+
* @param options Additional options for the validation.
|
|
3507
|
+
* @param options.minLength The minimum length of the string.
|
|
3508
|
+
* @param options.maxLength The maximum length of the string.
|
|
3509
|
+
* @returns True if the value is a valid string.
|
|
3510
|
+
*/
|
|
3511
|
+
static string(property, value, failures, fieldNameResource, options) {
|
|
3512
|
+
const is = Is.string(value);
|
|
3513
|
+
if (!is) {
|
|
3514
|
+
failures.push({
|
|
3515
|
+
property,
|
|
3516
|
+
reason: "validation.beText",
|
|
3517
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
3518
|
+
});
|
|
3519
|
+
}
|
|
3520
|
+
else {
|
|
3521
|
+
const minLength = options?.minLength;
|
|
3522
|
+
const maxLength = options?.maxLength;
|
|
3523
|
+
const minLimitDefined = Is.integer(minLength);
|
|
3524
|
+
const maxLimitDefined = Is.integer(maxLength);
|
|
3525
|
+
const belowMin = minLimitDefined && value.length < minLength;
|
|
3526
|
+
const aboveMax = maxLimitDefined && value.length > maxLength;
|
|
3527
|
+
if (minLimitDefined && maxLimitDefined && (belowMin || aboveMax)) {
|
|
3528
|
+
failures.push({
|
|
3529
|
+
property,
|
|
3530
|
+
reason: "validation.beTextMinMax",
|
|
3531
|
+
properties: {
|
|
3532
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
3533
|
+
value,
|
|
3534
|
+
minLength,
|
|
3535
|
+
maxLength
|
|
3536
|
+
}
|
|
3537
|
+
});
|
|
3538
|
+
}
|
|
3539
|
+
else if (minLimitDefined && belowMin) {
|
|
3540
|
+
failures.push({
|
|
3541
|
+
property,
|
|
3542
|
+
reason: "validation.beTextMin",
|
|
3543
|
+
properties: {
|
|
3544
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
3545
|
+
value,
|
|
3546
|
+
minLength
|
|
3547
|
+
}
|
|
3548
|
+
});
|
|
3549
|
+
}
|
|
3550
|
+
else if (maxLimitDefined && aboveMax) {
|
|
3551
|
+
failures.push({
|
|
3552
|
+
property,
|
|
3553
|
+
reason: "validation.beTextMax",
|
|
3554
|
+
properties: {
|
|
3555
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
3556
|
+
value,
|
|
3557
|
+
maxLength
|
|
3558
|
+
}
|
|
3559
|
+
});
|
|
3560
|
+
}
|
|
3561
|
+
}
|
|
3562
|
+
return failures.length === 0;
|
|
3563
|
+
}
|
|
3564
|
+
/**
|
|
3565
|
+
* Is the property a string with a value.
|
|
3566
|
+
* @param property The name of the property.
|
|
3567
|
+
* @param value The value to test.
|
|
3568
|
+
* @param failures The list of failures to add to.
|
|
3569
|
+
* @param fieldNameResource Optional i18n resource of the field name to display in the message.
|
|
3570
|
+
* @param options Additional options for the validation.
|
|
3571
|
+
* @param options.minLength The minimum length of the string.
|
|
3572
|
+
* @param options.maxLength The maximum length of the string.
|
|
3573
|
+
* @returns True if the value is a valid string.
|
|
3574
|
+
*/
|
|
3575
|
+
static stringValue(property, value, failures, fieldNameResource, options) {
|
|
3576
|
+
const is = Is.stringValue(value);
|
|
3577
|
+
if (!is) {
|
|
3578
|
+
failures.push({
|
|
3579
|
+
property,
|
|
3580
|
+
reason: "validation.beTextValue",
|
|
3581
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
3582
|
+
});
|
|
3583
|
+
}
|
|
3584
|
+
else {
|
|
3585
|
+
const minLength = options?.minLength;
|
|
3586
|
+
const maxLength = options?.maxLength;
|
|
3587
|
+
const minLimitDefined = Is.integer(minLength);
|
|
3588
|
+
const maxLimitDefined = Is.integer(maxLength);
|
|
3589
|
+
const belowMin = minLimitDefined && value.length < minLength;
|
|
3590
|
+
const aboveMax = maxLimitDefined && value.length > maxLength;
|
|
3591
|
+
if (minLimitDefined && maxLimitDefined && (belowMin || aboveMax)) {
|
|
3592
|
+
failures.push({
|
|
3593
|
+
property,
|
|
3594
|
+
reason: "validation.beTextMinMax",
|
|
3595
|
+
properties: {
|
|
3596
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
3597
|
+
value,
|
|
3598
|
+
minLength,
|
|
3599
|
+
maxLength
|
|
3600
|
+
}
|
|
3601
|
+
});
|
|
3602
|
+
}
|
|
3603
|
+
else if (minLimitDefined && belowMin) {
|
|
3604
|
+
failures.push({
|
|
3605
|
+
property,
|
|
3606
|
+
reason: "validation.beTextMin",
|
|
3607
|
+
properties: {
|
|
3608
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
3609
|
+
value,
|
|
3610
|
+
minLength
|
|
3611
|
+
}
|
|
3612
|
+
});
|
|
3613
|
+
}
|
|
3614
|
+
else if (maxLimitDefined && aboveMax) {
|
|
3615
|
+
failures.push({
|
|
3616
|
+
property,
|
|
3617
|
+
reason: "validation.beTextMax",
|
|
3618
|
+
properties: {
|
|
3619
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
3620
|
+
value,
|
|
3621
|
+
maxLength
|
|
3622
|
+
}
|
|
3623
|
+
});
|
|
3624
|
+
}
|
|
3625
|
+
}
|
|
3626
|
+
return failures.length === 0;
|
|
3627
|
+
}
|
|
3628
|
+
/**
|
|
3629
|
+
* Is the property a number.
|
|
3630
|
+
* @param property The name of the property.
|
|
3631
|
+
* @param value The value to test.
|
|
3632
|
+
* @param failures The list of failures to add to.
|
|
3633
|
+
* @param fieldNameResource Optional i18n resource of the field name to display in the message.
|
|
3634
|
+
* @param options Additional options for the validation.
|
|
3635
|
+
* @param options.minValue The minimum value of the number.
|
|
3636
|
+
* @param options.maxValue The maximum value of the number.
|
|
3637
|
+
* @returns True if the value is a valid number.
|
|
3638
|
+
*/
|
|
3639
|
+
static number(property, value, failures, fieldNameResource, options) {
|
|
3640
|
+
const is = Is.number(value);
|
|
3641
|
+
if (!is) {
|
|
3642
|
+
failures.push({
|
|
3643
|
+
property,
|
|
3644
|
+
reason: "validation.beNumber",
|
|
3645
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
3646
|
+
});
|
|
3647
|
+
}
|
|
3648
|
+
else {
|
|
3649
|
+
const minValue = options?.minValue;
|
|
3650
|
+
const maxValue = options?.maxValue;
|
|
3651
|
+
const minLimitDefined = Is.number(minValue);
|
|
3652
|
+
const maxLimitDefined = Is.number(maxValue);
|
|
3653
|
+
const belowMin = minLimitDefined && value < minValue;
|
|
3654
|
+
const aboveMax = maxLimitDefined && value > maxValue;
|
|
3655
|
+
if (minLimitDefined && maxLimitDefined && (belowMin || aboveMax)) {
|
|
3656
|
+
failures.push({
|
|
3657
|
+
property,
|
|
3658
|
+
reason: "validation.beNumberMinMax",
|
|
3659
|
+
properties: {
|
|
3660
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
3661
|
+
value,
|
|
3662
|
+
minValue,
|
|
3663
|
+
maxValue
|
|
3664
|
+
}
|
|
3665
|
+
});
|
|
3666
|
+
}
|
|
3667
|
+
else if (minLimitDefined && belowMin) {
|
|
3668
|
+
failures.push({
|
|
3669
|
+
property,
|
|
3670
|
+
reason: "validation.beNumberMin",
|
|
3671
|
+
properties: {
|
|
3672
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
3673
|
+
value,
|
|
3674
|
+
minValue
|
|
3675
|
+
}
|
|
3676
|
+
});
|
|
3677
|
+
}
|
|
3678
|
+
else if (maxLimitDefined && aboveMax) {
|
|
3679
|
+
failures.push({
|
|
3680
|
+
property,
|
|
3681
|
+
reason: "validation.beNumberMax",
|
|
3682
|
+
properties: {
|
|
3683
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
3684
|
+
value,
|
|
3685
|
+
maxValue
|
|
3686
|
+
}
|
|
3687
|
+
});
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3690
|
+
return failures.length === 0;
|
|
3691
|
+
}
|
|
3692
|
+
/**
|
|
3693
|
+
* Is the property an integer.
|
|
3694
|
+
* @param property The name of the property.
|
|
3695
|
+
* @param value The value to test.
|
|
3696
|
+
* @param failures The list of failures to add to.
|
|
3697
|
+
* @param fieldNameResource Optional i18n resource of the field name to display in the message.
|
|
3698
|
+
* @param options Additional options for the validation.
|
|
3699
|
+
* @param options.minValue The minimum value of the integer.
|
|
3700
|
+
* @param options.maxValue The maximum value of the integer.
|
|
3701
|
+
* @returns True if the value is a valid integer.
|
|
3702
|
+
*/
|
|
3703
|
+
static integer(property, value, failures, fieldNameResource, options) {
|
|
3704
|
+
const is = Is.integer(value);
|
|
3705
|
+
if (!is) {
|
|
3706
|
+
failures.push({
|
|
3707
|
+
property,
|
|
3708
|
+
reason: "validation.beWholeNumber",
|
|
3709
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
3710
|
+
});
|
|
3711
|
+
}
|
|
3712
|
+
else {
|
|
3713
|
+
const minValue = options?.minValue;
|
|
3714
|
+
const maxValue = options?.maxValue;
|
|
3715
|
+
const minLimitDefined = Is.integer(minValue);
|
|
3716
|
+
const maxLimitDefined = Is.integer(maxValue);
|
|
3717
|
+
const belowMin = minLimitDefined && value < minValue;
|
|
3718
|
+
const aboveMax = maxLimitDefined && value > maxValue;
|
|
3719
|
+
if (minLimitDefined && maxLimitDefined && (belowMin || aboveMax)) {
|
|
3720
|
+
failures.push({
|
|
3721
|
+
property,
|
|
3722
|
+
reason: "validation.beWholeNumberMinMax",
|
|
3723
|
+
properties: {
|
|
3724
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
3725
|
+
value,
|
|
3726
|
+
minValue,
|
|
3727
|
+
maxValue
|
|
3728
|
+
}
|
|
3729
|
+
});
|
|
3730
|
+
}
|
|
3731
|
+
else if (minLimitDefined && belowMin) {
|
|
3732
|
+
failures.push({
|
|
3733
|
+
property,
|
|
3734
|
+
reason: "validation.beWholeNumberMin",
|
|
3735
|
+
properties: {
|
|
3736
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
3737
|
+
value,
|
|
3738
|
+
minValue
|
|
3739
|
+
}
|
|
3740
|
+
});
|
|
3741
|
+
}
|
|
3742
|
+
else if (maxLimitDefined && aboveMax) {
|
|
3743
|
+
failures.push({
|
|
3744
|
+
property,
|
|
3745
|
+
reason: "validation.beWholeNumberMax",
|
|
3746
|
+
properties: {
|
|
3747
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
3748
|
+
value,
|
|
3749
|
+
maxValue
|
|
3750
|
+
}
|
|
3751
|
+
});
|
|
3752
|
+
}
|
|
3753
|
+
}
|
|
3754
|
+
return failures.length === 0;
|
|
3755
|
+
}
|
|
3756
|
+
/**
|
|
3757
|
+
* Is the property a bigint.
|
|
3758
|
+
* @param property The name of the property.
|
|
3759
|
+
* @param value The value to test.
|
|
3760
|
+
* @param failures The list of failures to add to.
|
|
3761
|
+
* @param fieldNameResource Optional i18n resource of the field name to display in the message.
|
|
3762
|
+
* @param options Additional options for the validation.
|
|
3763
|
+
* @param options.minValue The minimum value of the bigint.
|
|
3764
|
+
* @param options.maxValue The maximum value of the bigint.
|
|
3765
|
+
* @returns True if the value is a valid bigint.
|
|
3766
|
+
*/
|
|
3767
|
+
static bigint(property, value, failures, fieldNameResource, options) {
|
|
3768
|
+
const is = Is.bigint(value);
|
|
3769
|
+
if (!is) {
|
|
3770
|
+
failures.push({
|
|
3771
|
+
property,
|
|
3772
|
+
reason: "validation.beBigInteger",
|
|
3773
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
3774
|
+
});
|
|
3775
|
+
}
|
|
3776
|
+
else {
|
|
3777
|
+
const minValue = options?.minValue;
|
|
3778
|
+
const maxValue = options?.maxValue;
|
|
3779
|
+
const minLimitDefined = Is.bigint(minValue);
|
|
3780
|
+
const maxLimitDefined = Is.bigint(maxValue);
|
|
3781
|
+
const belowMin = minLimitDefined && value < minValue;
|
|
3782
|
+
const aboveMax = maxLimitDefined && value > maxValue;
|
|
3783
|
+
if (minLimitDefined && maxLimitDefined && (belowMin || aboveMax)) {
|
|
3784
|
+
failures.push({
|
|
3785
|
+
property,
|
|
3786
|
+
reason: "validation.beBigIntegerMinMax",
|
|
3787
|
+
properties: {
|
|
3788
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
3789
|
+
value,
|
|
3790
|
+
minValue,
|
|
3791
|
+
maxValue
|
|
3792
|
+
}
|
|
3793
|
+
});
|
|
3794
|
+
}
|
|
3795
|
+
else if (minLimitDefined && belowMin) {
|
|
3796
|
+
failures.push({
|
|
3797
|
+
property,
|
|
3798
|
+
reason: "validation.beBigIntegerMin",
|
|
3799
|
+
properties: {
|
|
3800
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
3801
|
+
value,
|
|
3802
|
+
minValue
|
|
3803
|
+
}
|
|
3804
|
+
});
|
|
3805
|
+
}
|
|
3806
|
+
else if (maxLimitDefined && aboveMax) {
|
|
3807
|
+
failures.push({
|
|
3808
|
+
property,
|
|
3809
|
+
reason: "validation.beBigIntegerMax",
|
|
3810
|
+
properties: {
|
|
3811
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
3812
|
+
value,
|
|
3813
|
+
maxValue
|
|
3814
|
+
}
|
|
3815
|
+
});
|
|
3816
|
+
}
|
|
3817
|
+
}
|
|
3818
|
+
return failures.length === 0;
|
|
3819
|
+
}
|
|
3820
|
+
/**
|
|
3821
|
+
* Is the property a boolean.
|
|
3822
|
+
* @param property The name of the property.
|
|
3823
|
+
* @param value The value to test.
|
|
3824
|
+
* @param failures The list of failures to add to.
|
|
3825
|
+
* @param fieldNameResource Optional i18n resource of the field name to display in the message.
|
|
3826
|
+
* @returns True if the value is a boolean.
|
|
3827
|
+
*/
|
|
3828
|
+
static boolean(property, value, failures, fieldNameResource) {
|
|
3829
|
+
const is = Is.boolean(value);
|
|
3830
|
+
if (!is) {
|
|
3831
|
+
failures.push({
|
|
3832
|
+
property,
|
|
3833
|
+
reason: "validation.beBoolean",
|
|
3834
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
3835
|
+
});
|
|
3836
|
+
}
|
|
3837
|
+
return is;
|
|
3838
|
+
}
|
|
3839
|
+
/**
|
|
3840
|
+
* Is the property a date.
|
|
3841
|
+
* @param property The name of the property.
|
|
3842
|
+
* @param value The value to test.
|
|
3843
|
+
* @param failures The list of failures to add to.
|
|
3844
|
+
* @param fieldNameResource Optional i18n resource of the field name to display in the message.
|
|
3845
|
+
* @returns True if the value is a date.
|
|
3846
|
+
*/
|
|
3847
|
+
static date(property, value, failures, fieldNameResource) {
|
|
3848
|
+
if (Is.dateEmpty(value)) {
|
|
3849
|
+
failures.push({
|
|
3850
|
+
property,
|
|
3851
|
+
reason: "validation.beDate",
|
|
3852
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
3853
|
+
});
|
|
3854
|
+
return false;
|
|
3855
|
+
}
|
|
3856
|
+
const is = Is.date(value);
|
|
3857
|
+
if (!is) {
|
|
3858
|
+
failures.push({
|
|
3859
|
+
property,
|
|
3860
|
+
reason: "validation.beDate",
|
|
3861
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
3862
|
+
});
|
|
3863
|
+
}
|
|
3864
|
+
return is;
|
|
3865
|
+
}
|
|
3866
|
+
/**
|
|
3867
|
+
* Is the property a date in ISO 8601 format.
|
|
3868
|
+
* @param property The name of the property.
|
|
3869
|
+
* @param value The value to test.
|
|
3870
|
+
* @param failures The list of failures to add to.
|
|
3871
|
+
* @param fieldNameResource Optional i18n resource of the field name to display in the message.
|
|
3872
|
+
* @returns True if the value is a date.
|
|
3873
|
+
*/
|
|
3874
|
+
static dateString(property, value, failures, fieldNameResource) {
|
|
3875
|
+
if (!Is.stringValue(value)) {
|
|
3876
|
+
failures.push({
|
|
3877
|
+
property,
|
|
3878
|
+
reason: "validation.beDate",
|
|
3879
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
3880
|
+
});
|
|
3881
|
+
return false;
|
|
3882
|
+
}
|
|
3883
|
+
const is = Is.dateString(value);
|
|
3884
|
+
if (!is) {
|
|
3885
|
+
failures.push({
|
|
3886
|
+
property,
|
|
3887
|
+
reason: "validation.beDate",
|
|
3888
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
3889
|
+
});
|
|
3890
|
+
}
|
|
3891
|
+
return is;
|
|
3892
|
+
}
|
|
3893
|
+
/**
|
|
3894
|
+
* Is the property a date/time in ISO 8601 format.
|
|
3895
|
+
* @param property The name of the property.
|
|
3896
|
+
* @param value The value to test.
|
|
3897
|
+
* @param failures The list of failures to add to.
|
|
3898
|
+
* @param fieldNameResource Optional i18n resource of the field name to display in the message.
|
|
3899
|
+
* @returns True if the value is a date/time.
|
|
3900
|
+
*/
|
|
3901
|
+
static dateTimeString(property, value, failures, fieldNameResource) {
|
|
3902
|
+
if (!Is.stringValue(value)) {
|
|
3903
|
+
failures.push({
|
|
3904
|
+
property,
|
|
3905
|
+
reason: "validation.beDateTime",
|
|
3906
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
3907
|
+
});
|
|
3908
|
+
return false;
|
|
3909
|
+
}
|
|
3910
|
+
const is = Is.dateTimeString(value);
|
|
3911
|
+
if (!is) {
|
|
3912
|
+
failures.push({
|
|
3913
|
+
property,
|
|
3914
|
+
reason: "validation.beDateTime",
|
|
3915
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
3916
|
+
});
|
|
3917
|
+
}
|
|
3918
|
+
return is;
|
|
3919
|
+
}
|
|
3920
|
+
/**
|
|
3921
|
+
* Is the property a time in ISO 8601 format.
|
|
3922
|
+
* @param property The name of the property.
|
|
3923
|
+
* @param value The value to test.
|
|
3924
|
+
* @param failures The list of failures to add to.
|
|
3925
|
+
* @param fieldNameResource Optional i18n resource of the field name to display in the message.
|
|
3926
|
+
* @returns True if the value is a time.
|
|
3927
|
+
*/
|
|
3928
|
+
static timeString(property, value, failures, fieldNameResource) {
|
|
3929
|
+
if (!Is.stringValue(value)) {
|
|
3930
|
+
failures.push({
|
|
3931
|
+
property,
|
|
3932
|
+
reason: "validation.beTime",
|
|
3933
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
3934
|
+
});
|
|
3935
|
+
return false;
|
|
3936
|
+
}
|
|
3937
|
+
const is = Is.timeString(value);
|
|
3938
|
+
if (!is) {
|
|
3939
|
+
failures.push({
|
|
3940
|
+
property,
|
|
3941
|
+
reason: "validation.beTime",
|
|
3942
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
3943
|
+
});
|
|
3944
|
+
}
|
|
3945
|
+
return is;
|
|
3946
|
+
}
|
|
3947
|
+
/**
|
|
3948
|
+
* Is the property a timestamp in milliseconds.
|
|
3949
|
+
* @param property The name of the property.
|
|
3950
|
+
* @param value The value to test.
|
|
3951
|
+
* @param failures The list of failures to add to.
|
|
3952
|
+
* @param fieldNameResource Optional i18n resource of the field name to display in the message.
|
|
3953
|
+
* @returns True if the value is a timestamp in milliseconds.
|
|
3954
|
+
*/
|
|
3955
|
+
static timestampMilliseconds(property, value, failures, fieldNameResource) {
|
|
3956
|
+
if (!Is.integer(value)) {
|
|
3957
|
+
failures.push({
|
|
3958
|
+
property,
|
|
3959
|
+
reason: "validation.beTimestampMilliseconds",
|
|
3960
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
3961
|
+
});
|
|
3962
|
+
return false;
|
|
3963
|
+
}
|
|
3964
|
+
const is = Is.timestampMilliseconds(value);
|
|
3965
|
+
if (!is) {
|
|
3966
|
+
failures.push({
|
|
3967
|
+
property,
|
|
3968
|
+
reason: "validation.beTimestampMilliseconds",
|
|
3969
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
3970
|
+
});
|
|
3971
|
+
}
|
|
3972
|
+
return is;
|
|
3973
|
+
}
|
|
3974
|
+
/**
|
|
3975
|
+
* Is the property a timestamp in seconds.
|
|
3976
|
+
* @param property The name of the property.
|
|
3977
|
+
* @param value The value to test.
|
|
3978
|
+
* @param failures The list of failures to add to.
|
|
3979
|
+
* @param fieldNameResource Optional i18n resource of the field name to display in the message.
|
|
3980
|
+
* @returns True if the value is a timestamp in seconds.
|
|
3981
|
+
*/
|
|
3982
|
+
static timestampSeconds(property, value, failures, fieldNameResource) {
|
|
3983
|
+
if (!Is.integer(value)) {
|
|
3984
|
+
failures.push({
|
|
3985
|
+
property,
|
|
3986
|
+
reason: "validation.beTimestampSeconds",
|
|
3987
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
3988
|
+
});
|
|
3989
|
+
return false;
|
|
3990
|
+
}
|
|
3991
|
+
const is = Is.timestampSeconds(value);
|
|
3992
|
+
if (!is) {
|
|
3993
|
+
failures.push({
|
|
3994
|
+
property,
|
|
3995
|
+
reason: "validation.beTimestampSeconds",
|
|
3996
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
3997
|
+
});
|
|
3998
|
+
}
|
|
3999
|
+
return is;
|
|
4000
|
+
}
|
|
4001
|
+
/**
|
|
4002
|
+
* Is the property an object.
|
|
4003
|
+
* @param property The name of the property.
|
|
4004
|
+
* @param value The value to test.
|
|
4005
|
+
* @param failures The list of failures to add to.
|
|
4006
|
+
* @param fieldNameResource Optional i18n resource of the field name to display in the message.
|
|
4007
|
+
* @returns True if the value is a object.
|
|
4008
|
+
*/
|
|
4009
|
+
static object(property, value, failures, fieldNameResource) {
|
|
4010
|
+
const is = Is.object(value);
|
|
4011
|
+
if (!is) {
|
|
4012
|
+
failures.push({
|
|
4013
|
+
property,
|
|
4014
|
+
reason: "validation.beObject",
|
|
4015
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
4016
|
+
});
|
|
4017
|
+
}
|
|
4018
|
+
return is;
|
|
4019
|
+
}
|
|
4020
|
+
/**
|
|
4021
|
+
* Is the property an array.
|
|
4022
|
+
* @param property The name of the property.
|
|
4023
|
+
* @param value The value to test.
|
|
4024
|
+
* @param failures The list of failures to add to.
|
|
4025
|
+
* @param fieldNameResource Optional i18n resource of the field name to display in the message.
|
|
4026
|
+
* @returns True if the value is an array.
|
|
4027
|
+
*/
|
|
4028
|
+
static array(property, value, failures, fieldNameResource) {
|
|
4029
|
+
const is = Is.array(value);
|
|
4030
|
+
if (!is) {
|
|
4031
|
+
failures.push({
|
|
4032
|
+
property,
|
|
4033
|
+
reason: "validation.beArray",
|
|
4034
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
4035
|
+
});
|
|
4036
|
+
}
|
|
4037
|
+
return is;
|
|
4038
|
+
}
|
|
4039
|
+
/**
|
|
4040
|
+
* Is the property an array with at least one item.
|
|
4041
|
+
* @param property The name of the property.
|
|
4042
|
+
* @param value The value to test.
|
|
4043
|
+
* @param failures The list of failures to add to.
|
|
4044
|
+
* @param fieldNameResource Optional i18n resource of the field name to display in the message.
|
|
4045
|
+
* @returns True if the value is an array with at least one element.
|
|
4046
|
+
*/
|
|
4047
|
+
static arrayValue(property, value, failures, fieldNameResource) {
|
|
4048
|
+
const is = Is.array(value) && value.length > 0;
|
|
4049
|
+
if (!is) {
|
|
4050
|
+
failures.push({
|
|
4051
|
+
property,
|
|
4052
|
+
reason: "validation.beArrayValue",
|
|
4053
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
4054
|
+
});
|
|
4055
|
+
}
|
|
4056
|
+
return is;
|
|
4057
|
+
}
|
|
4058
|
+
/**
|
|
4059
|
+
* Is the property one of a list of items.
|
|
4060
|
+
* @param property The name of the property.
|
|
4061
|
+
* @param value The value to test.
|
|
4062
|
+
* @param options The options the value must be one of.
|
|
4063
|
+
* @param failures The list of failures to add to.
|
|
4064
|
+
* @param fieldNameResource Optional i18n resource of the field name to display in the message.
|
|
4065
|
+
* @returns True if the value is one of the items in the options.
|
|
4066
|
+
*/
|
|
4067
|
+
static arrayOneOf(property, value, options, failures, fieldNameResource) {
|
|
4068
|
+
if (Is.empty(value)) {
|
|
4069
|
+
failures.push({
|
|
4070
|
+
property,
|
|
4071
|
+
reason: "validation.beIncluded",
|
|
4072
|
+
properties: {
|
|
4073
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
4074
|
+
value,
|
|
4075
|
+
options
|
|
4076
|
+
}
|
|
4077
|
+
});
|
|
4078
|
+
return false;
|
|
4079
|
+
}
|
|
4080
|
+
const is = Is.arrayOneOf(value, options);
|
|
4081
|
+
if (!is) {
|
|
4082
|
+
failures.push({
|
|
4083
|
+
property,
|
|
4084
|
+
reason: "validation.beIncluded",
|
|
4085
|
+
properties: {
|
|
4086
|
+
fieldName: fieldNameResource ?? "validation.defaultFieldName",
|
|
4087
|
+
value,
|
|
4088
|
+
options
|
|
4089
|
+
}
|
|
4090
|
+
});
|
|
4091
|
+
}
|
|
4092
|
+
return is;
|
|
4093
|
+
}
|
|
4094
|
+
/**
|
|
4095
|
+
* Is the property a Uint8Array.
|
|
4096
|
+
* @param property The name of the property.
|
|
4097
|
+
* @param value The value to test.
|
|
4098
|
+
* @param failures The list of failures to add to.
|
|
4099
|
+
* @param fieldNameResource Optional i18n resource of the field name to display in the message.
|
|
4100
|
+
* @returns True if the value is a Uint8Array.
|
|
4101
|
+
*/
|
|
4102
|
+
static uint8Array(property, value, failures, fieldNameResource) {
|
|
4103
|
+
const is = Is.uint8Array(value);
|
|
4104
|
+
if (!is) {
|
|
4105
|
+
failures.push({
|
|
4106
|
+
property,
|
|
4107
|
+
reason: "validation.beByteArray",
|
|
4108
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
4109
|
+
});
|
|
4110
|
+
}
|
|
4111
|
+
return is;
|
|
4112
|
+
}
|
|
4113
|
+
/**
|
|
4114
|
+
* Is the property valid JSON.
|
|
4115
|
+
* @param property The name of the property.
|
|
4116
|
+
* @param value The value to test.
|
|
4117
|
+
* @param failures The list of failures to add to.
|
|
4118
|
+
* @param fieldNameResource Optional i18n resource of the field name to display in the message.
|
|
4119
|
+
* @returns True if the value is valid JSON.
|
|
4120
|
+
*/
|
|
4121
|
+
static json(property, value, failures, fieldNameResource) {
|
|
4122
|
+
const is = Is.json(value);
|
|
4123
|
+
if (!is) {
|
|
4124
|
+
failures.push({
|
|
4125
|
+
property,
|
|
4126
|
+
reason: "validation.beJSON",
|
|
4127
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
4128
|
+
});
|
|
4129
|
+
}
|
|
4130
|
+
return is;
|
|
4131
|
+
}
|
|
4132
|
+
/**
|
|
4133
|
+
* Is the property a string in e-mail format.
|
|
4134
|
+
* @param property The name of the property.
|
|
4135
|
+
* @param value The value to test.
|
|
4136
|
+
* @param failures The list of failures to add to.
|
|
4137
|
+
* @param fieldNameResource Optional i18n resource of the field name to display in the message.
|
|
4138
|
+
* @returns True if the value is a valid looking e-mail.
|
|
4139
|
+
*/
|
|
4140
|
+
static email(property, value, failures, fieldNameResource) {
|
|
4141
|
+
const is = Is.email(value);
|
|
4142
|
+
if (!is) {
|
|
4143
|
+
failures.push({
|
|
4144
|
+
property,
|
|
4145
|
+
reason: "validation.beEmail",
|
|
4146
|
+
properties: { fieldName: fieldNameResource ?? "validation.defaultFieldName", value }
|
|
4147
|
+
});
|
|
4148
|
+
}
|
|
4149
|
+
return is;
|
|
4150
|
+
}
|
|
4151
|
+
/**
|
|
4152
|
+
* Throw the validation failures as a ValidationError.
|
|
4153
|
+
* @param source The source of the error.
|
|
4154
|
+
* @param objectName The object that was being validated.
|
|
4155
|
+
* @param failures The validation failures.
|
|
4156
|
+
* @throws ValidationError From the converted failures.
|
|
4157
|
+
*/
|
|
4158
|
+
static asValidationError(source, objectName, failures) {
|
|
4159
|
+
if (Is.arrayValue(failures)) {
|
|
4160
|
+
throw new ValidationError(source, objectName, failures);
|
|
4161
|
+
}
|
|
4162
|
+
}
|
|
4163
|
+
/**
|
|
4164
|
+
* Map a list of failures to their properties in a map.
|
|
4165
|
+
* @param failures The validation failures to combine into the map for the properties.
|
|
4166
|
+
* @param propertyMap The map to add the failures to.
|
|
4167
|
+
* @param clearMap Should the map be cleared before adding the failures.
|
|
4168
|
+
*/
|
|
4169
|
+
static toPropertyMap(failures, propertyMap, clearMap = true) {
|
|
4170
|
+
if (clearMap) {
|
|
4171
|
+
for (const prop in propertyMap) {
|
|
4172
|
+
delete propertyMap[prop];
|
|
4173
|
+
}
|
|
4174
|
+
}
|
|
4175
|
+
for (const validationFailure of failures) {
|
|
4176
|
+
if (Is.array(propertyMap[validationFailure.property])) {
|
|
4177
|
+
propertyMap[validationFailure.property].push(validationFailure);
|
|
4178
|
+
}
|
|
4179
|
+
else {
|
|
4180
|
+
propertyMap[validationFailure.property] = [validationFailure];
|
|
4181
|
+
}
|
|
4182
|
+
}
|
|
4183
|
+
}
|
|
4184
|
+
}
|
|
4185
|
+
|
|
4186
|
+
exports.AlreadyExistsError = AlreadyExistsError;
|
|
4187
|
+
exports.ArrayHelper = ArrayHelper;
|
|
4188
|
+
exports.AsyncCache = AsyncCache;
|
|
4189
|
+
exports.Base32 = Base32;
|
|
4190
|
+
exports.Base64 = Base64;
|
|
4191
|
+
exports.Base64Url = Base64Url;
|
|
4192
|
+
exports.BaseError = BaseError;
|
|
4193
|
+
exports.BitString = BitString;
|
|
4194
|
+
exports.Coerce = Coerce;
|
|
4195
|
+
exports.ComponentFactory = ComponentFactory;
|
|
4196
|
+
exports.Compression = Compression;
|
|
4197
|
+
exports.CompressionType = CompressionType;
|
|
4198
|
+
exports.ConflictError = ConflictError;
|
|
4199
|
+
exports.Converter = Converter;
|
|
4200
|
+
exports.ErrorHelper = ErrorHelper;
|
|
4201
|
+
exports.Factory = Factory;
|
|
4202
|
+
exports.FilenameHelper = FilenameHelper;
|
|
4203
|
+
exports.GeneralError = GeneralError;
|
|
4204
|
+
exports.GuardError = GuardError;
|
|
4205
|
+
exports.Guards = Guards;
|
|
4206
|
+
exports.HexHelper = HexHelper;
|
|
4207
|
+
exports.I18n = I18n;
|
|
4208
|
+
exports.Is = Is;
|
|
4209
|
+
exports.JsonHelper = JsonHelper;
|
|
4210
|
+
exports.NotFoundError = NotFoundError;
|
|
4211
|
+
exports.NotImplementedError = NotImplementedError;
|
|
4212
|
+
exports.NotSupportedError = NotSupportedError;
|
|
4213
|
+
exports.ObjectHelper = ObjectHelper;
|
|
4214
|
+
exports.RandomHelper = RandomHelper;
|
|
4215
|
+
exports.StringHelper = StringHelper;
|
|
4216
|
+
exports.UnauthorizedError = UnauthorizedError;
|
|
4217
|
+
exports.UnprocessableError = UnprocessableError;
|
|
4218
|
+
exports.Url = Url;
|
|
4219
|
+
exports.Urn = Urn;
|
|
4220
|
+
exports.Validation = Validation;
|
|
4221
|
+
exports.ValidationError = ValidationError;
|