@twin.org/web 0.0.2-next.9 → 0.0.3-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/errors/fetchError.js +27 -0
- package/dist/es/errors/fetchError.js.map +1 -0
- package/dist/es/index.js +20 -0
- package/dist/es/index.js.map +1 -0
- package/dist/es/models/IFetchOptions.js +2 -0
- package/dist/es/models/IFetchOptions.js.map +1 -0
- package/dist/es/models/IHttpHeaders.js +4 -0
- package/dist/es/models/IHttpHeaders.js.map +1 -0
- package/dist/es/models/IJwk.js +2 -0
- package/dist/es/models/IJwk.js.map +1 -0
- package/dist/es/models/IJwtHeader.js +2 -0
- package/dist/es/models/IJwtHeader.js.map +1 -0
- package/dist/es/models/IJwtPayload.js +2 -0
- package/dist/es/models/IJwtPayload.js.map +1 -0
- package/dist/es/models/headerTypes.js +41 -0
- package/dist/es/models/headerTypes.js.map +1 -0
- package/dist/es/models/httpMethod.js +18 -0
- package/dist/es/models/httpMethod.js.map +1 -0
- package/dist/es/models/httpStatusCode.js +257 -0
- package/dist/es/models/httpStatusCode.js.map +1 -0
- package/dist/es/models/jwkCryptoKey.js +4 -0
- package/dist/es/models/jwkCryptoKey.js.map +1 -0
- package/dist/es/models/mimeTypes.js +101 -0
- package/dist/es/models/mimeTypes.js.map +1 -0
- package/dist/es/utils/fetchHelper.js +271 -0
- package/dist/es/utils/fetchHelper.js.map +1 -0
- package/dist/es/utils/headerHelper.js +34 -0
- package/dist/es/utils/headerHelper.js.map +1 -0
- package/dist/es/utils/jwk.js +99 -0
- package/dist/es/utils/jwk.js.map +1 -0
- package/dist/es/utils/jws.js +58 -0
- package/dist/es/utils/jws.js.map +1 -0
- package/dist/es/utils/jwt.js +243 -0
- package/dist/es/utils/jwt.js.map +1 -0
- package/dist/es/utils/mimeTypeHelper.js +144 -0
- package/dist/es/utils/mimeTypeHelper.js.map +1 -0
- package/dist/types/errors/fetchError.d.ts +1 -1
- package/dist/types/index.d.ts +17 -16
- package/dist/types/models/IFetchOptions.d.ts +1 -1
- package/dist/types/models/IJwk.d.ts +1 -2
- package/dist/types/models/IJwtHeader.d.ts +1 -2
- package/dist/types/models/IJwtPayload.d.ts +1 -2
- package/dist/types/utils/fetchHelper.d.ts +6 -2
- package/dist/types/utils/headerHelper.d.ts +17 -0
- package/dist/types/utils/jwk.d.ts +10 -4
- package/dist/types/utils/jws.d.ts +5 -1
- package/dist/types/utils/jwt.d.ts +7 -3
- package/docs/changelog.md +312 -0
- package/docs/reference/classes/FetchHelper.md +10 -2
- package/docs/reference/classes/HeaderHelper.md +57 -0
- package/docs/reference/classes/Jwk.md +27 -7
- package/docs/reference/classes/Jws.md +8 -0
- package/docs/reference/classes/Jwt.md +30 -22
- package/docs/reference/classes/MimeTypeHelper.md +5 -5
- package/docs/reference/index.md +4 -3
- package/docs/reference/type-aliases/IJwk.md +5 -0
- package/docs/reference/type-aliases/IJwtHeader.md +5 -0
- package/docs/reference/type-aliases/IJwtPayload.md +5 -0
- package/locales/.validate-ignore +1 -0
- package/locales/en.json +3 -2
- package/package.json +25 -12
- package/dist/cjs/index.cjs +0 -1269
- package/dist/esm/index.mjs +0 -1258
- package/docs/reference/interfaces/IJwk.md +0 -7
- package/docs/reference/interfaces/IJwtHeader.md +0 -13
- package/docs/reference/interfaces/IJwtPayload.md +0 -13
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
import { AsyncCache, BaseError, Guards, Is, ObjectHelper } from "@twin.org/core";
|
|
4
|
+
import { FetchError } from "../errors/fetchError.js";
|
|
5
|
+
import { HeaderTypes } from "../models/headerTypes.js";
|
|
6
|
+
import { HttpMethod } from "../models/httpMethod.js";
|
|
7
|
+
import { HttpStatusCode } from "../models/httpStatusCode.js";
|
|
8
|
+
import { MimeTypes } from "../models/mimeTypes.js";
|
|
9
|
+
/**
|
|
10
|
+
* Class to helper with fetch operations.
|
|
11
|
+
*/
|
|
12
|
+
export class FetchHelper {
|
|
13
|
+
/**
|
|
14
|
+
* Runtime name for the class.
|
|
15
|
+
*/
|
|
16
|
+
static CLASS_NAME = "FetchHelper";
|
|
17
|
+
/**
|
|
18
|
+
* Prefix to use for cache entries.
|
|
19
|
+
* @internal
|
|
20
|
+
*/
|
|
21
|
+
static _CACHE_PREFIX = "fetch_";
|
|
22
|
+
/**
|
|
23
|
+
* Perform a fetch request.
|
|
24
|
+
* @param source The source for the request.
|
|
25
|
+
* @param url The url for the request.
|
|
26
|
+
* @param method The http method.
|
|
27
|
+
* @param body Request to send to the endpoint.
|
|
28
|
+
* @param options Options for sending the requests.
|
|
29
|
+
* @returns The response.
|
|
30
|
+
*/
|
|
31
|
+
static async fetch(source, url, method, body, options) {
|
|
32
|
+
Guards.string(FetchHelper.CLASS_NAME, "source", source);
|
|
33
|
+
Guards.string(FetchHelper.CLASS_NAME, "url", url);
|
|
34
|
+
Guards.arrayOneOf(FetchHelper.CLASS_NAME, "method", method, Object.values(HttpMethod));
|
|
35
|
+
if (!Is.undefined(body) && !Is.uint8Array(body)) {
|
|
36
|
+
Guards.string(FetchHelper.CLASS_NAME, "body", body);
|
|
37
|
+
}
|
|
38
|
+
if (!Is.undefined(options)) {
|
|
39
|
+
Guards.object(FetchHelper.CLASS_NAME, "options", options);
|
|
40
|
+
if (!Is.undefined(options.headers)) {
|
|
41
|
+
Guards.object(FetchHelper.CLASS_NAME, "options.headers", options.headers);
|
|
42
|
+
}
|
|
43
|
+
if (!Is.undefined(options.timeoutMs)) {
|
|
44
|
+
Guards.integer(FetchHelper.CLASS_NAME, "options.timeoutMs", options.timeoutMs);
|
|
45
|
+
}
|
|
46
|
+
if (!Is.undefined(options.includeCredentials)) {
|
|
47
|
+
Guards.boolean(FetchHelper.CLASS_NAME, "options.includeCredentials", options.includeCredentials);
|
|
48
|
+
}
|
|
49
|
+
if (!Is.undefined(options.retryCount)) {
|
|
50
|
+
Guards.integer(FetchHelper.CLASS_NAME, "options.retryCount", options.retryCount);
|
|
51
|
+
}
|
|
52
|
+
if (!Is.undefined(options.retryDelayMs)) {
|
|
53
|
+
Guards.integer(FetchHelper.CLASS_NAME, "options.retryDelayMs", options.retryDelayMs);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
let controller;
|
|
57
|
+
let timerId;
|
|
58
|
+
const retryCount = options?.retryCount ?? 1;
|
|
59
|
+
const baseDelayMilliseconds = options?.retryDelayMs ?? 3000;
|
|
60
|
+
let lastError;
|
|
61
|
+
let attempt;
|
|
62
|
+
for (attempt = 0; attempt < retryCount; attempt++) {
|
|
63
|
+
if (attempt > 0) {
|
|
64
|
+
const exponentialBackoffDelay = baseDelayMilliseconds * Math.pow(2, attempt - 1);
|
|
65
|
+
await new Promise(resolve => globalThis.setTimeout(resolve, exponentialBackoffDelay));
|
|
66
|
+
}
|
|
67
|
+
if (options?.timeoutMs !== undefined) {
|
|
68
|
+
controller = new AbortController();
|
|
69
|
+
timerId = globalThis.setTimeout(() => {
|
|
70
|
+
if (controller) {
|
|
71
|
+
controller.abort();
|
|
72
|
+
}
|
|
73
|
+
}, options?.timeoutMs);
|
|
74
|
+
}
|
|
75
|
+
let finalBody;
|
|
76
|
+
if (method === HttpMethod.POST || method === HttpMethod.PUT) {
|
|
77
|
+
if (Is.string(body)) {
|
|
78
|
+
finalBody = body;
|
|
79
|
+
}
|
|
80
|
+
else if (Is.uint8Array(body)) {
|
|
81
|
+
finalBody = new Uint8Array(body);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
const requestOptions = {
|
|
86
|
+
method,
|
|
87
|
+
headers: options?.headers,
|
|
88
|
+
body: finalBody,
|
|
89
|
+
signal: controller ? controller.signal : undefined
|
|
90
|
+
};
|
|
91
|
+
if (Is.boolean(options?.includeCredentials)) {
|
|
92
|
+
requestOptions.credentials = "include";
|
|
93
|
+
}
|
|
94
|
+
const response = await fetch(url, requestOptions);
|
|
95
|
+
if (!response.ok && retryCount > 1) {
|
|
96
|
+
lastError = new FetchError(source, `${"fetchHelper"}.general`, response.status ?? HttpStatusCode.internalServerError, {
|
|
97
|
+
url,
|
|
98
|
+
statusText: response.statusText
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
return response;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
const isErr = Is.object(err);
|
|
107
|
+
if (isErr && Is.stringValue(err.message) && err.message.includes("Failed to fetch")) {
|
|
108
|
+
lastError = new FetchError(source, `${"fetchHelper"}.connectivity`, HttpStatusCode.serviceUnavailable, {
|
|
109
|
+
url
|
|
110
|
+
}, err);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
const isAbort = isErr && err.name === "AbortError";
|
|
114
|
+
const props = { url };
|
|
115
|
+
let httpStatus = HttpStatusCode.internalServerError;
|
|
116
|
+
if (isAbort) {
|
|
117
|
+
httpStatus = HttpStatusCode.requestTimeout;
|
|
118
|
+
}
|
|
119
|
+
else if (isErr && "httpStatus" in err) {
|
|
120
|
+
httpStatus = err.httpStatus;
|
|
121
|
+
}
|
|
122
|
+
if (isErr && "statusText" in err) {
|
|
123
|
+
props.statusText = err.statusText;
|
|
124
|
+
}
|
|
125
|
+
if (isAbort) {
|
|
126
|
+
lastError = new FetchError(source, `${"fetchHelper"}.timeout`, httpStatus, props, err);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
lastError = new FetchError(source, `${"fetchHelper"}.general`, httpStatus, props, err);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
if (timerId) {
|
|
135
|
+
globalThis.clearTimeout(timerId);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (retryCount > 1 && attempt === retryCount) {
|
|
140
|
+
throw new FetchError(source, `${"fetchHelper"}.retryLimitExceeded`, HttpStatusCode.internalServerError, { url }, lastError);
|
|
141
|
+
}
|
|
142
|
+
throw lastError;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Perform a request in json format.
|
|
146
|
+
* @param source The source for the request.
|
|
147
|
+
* @param url The url for the request.
|
|
148
|
+
* @param method The http method.
|
|
149
|
+
* @param requestData Request to send to the endpoint.
|
|
150
|
+
* @param options Options for sending the requests.
|
|
151
|
+
* @returns The response.
|
|
152
|
+
*/
|
|
153
|
+
static async fetchJson(source, url, method, requestData, options) {
|
|
154
|
+
if (Is.integer(options?.cacheTtlMs) && options.cacheTtlMs >= 0) {
|
|
155
|
+
// The cache option is set, so call the same method again but without
|
|
156
|
+
// the cache option to get the result and cache it.
|
|
157
|
+
const cacheResponse = AsyncCache.exec(`${FetchHelper._CACHE_PREFIX}${url}`, options.cacheTtlMs, async () => FetchHelper.fetchJson(source, url, method, requestData, ObjectHelper.omit(options, ["cacheTtlMs"])));
|
|
158
|
+
// If the return value is a promise return it, otherwise continue
|
|
159
|
+
// with the regular processing.
|
|
160
|
+
if (Is.promise(cacheResponse)) {
|
|
161
|
+
return cacheResponse;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
options ??= {};
|
|
165
|
+
options.headers ??= {};
|
|
166
|
+
if (Is.undefined(options.headers[HeaderTypes.ContentType]) &&
|
|
167
|
+
(method === HttpMethod.POST || method === HttpMethod.PUT || method === HttpMethod.PATCH)) {
|
|
168
|
+
options.headers[HeaderTypes.ContentType] = MimeTypes.Json;
|
|
169
|
+
}
|
|
170
|
+
const response = await FetchHelper.fetch(source, url, method, requestData ? JSON.stringify(requestData) : undefined, options);
|
|
171
|
+
if (response.ok) {
|
|
172
|
+
if (response.status === HttpStatusCode.noContent) {
|
|
173
|
+
return {};
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
return (await response.json());
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
throw new FetchError(source, `${"fetchHelper"}.decodingJSON`, HttpStatusCode.badRequest, { url }, err);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const errorResponseData = await response.json();
|
|
183
|
+
const errorResponse = BaseError.fromError(errorResponseData);
|
|
184
|
+
const isErrorEmpty = BaseError.isEmpty(errorResponse);
|
|
185
|
+
throw new FetchError(source, `${"fetchHelper"}.failureStatusText`, response.status, {
|
|
186
|
+
statusText: response.statusText,
|
|
187
|
+
url,
|
|
188
|
+
data: isErrorEmpty ? errorResponseData : undefined
|
|
189
|
+
}, isErrorEmpty ? undefined : errorResponse);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Perform a request for binary data.
|
|
193
|
+
* @param source The source for the request.
|
|
194
|
+
* @param url The url for the request.
|
|
195
|
+
* @param method The http method.
|
|
196
|
+
* @param requestData Request to send to the endpoint.
|
|
197
|
+
* @param options Options for sending the requests.
|
|
198
|
+
* @returns The response.
|
|
199
|
+
*/
|
|
200
|
+
static async fetchBinary(source, url, method, requestData, options) {
|
|
201
|
+
if (Is.integer(options?.cacheTtlMs) && options.cacheTtlMs >= 0) {
|
|
202
|
+
// The cache option is set, so call the same method again but without
|
|
203
|
+
// the cache option to get the result and cache it.
|
|
204
|
+
const cacheResponse = AsyncCache.exec(`${FetchHelper._CACHE_PREFIX}${url}`, options.cacheTtlMs * 1000, async () => FetchHelper.fetchBinary(source, url, method, requestData, ObjectHelper.omit(options, ["cacheTtlMs"])));
|
|
205
|
+
// If the return value is a promise return it, otherwise continue
|
|
206
|
+
// with the regular processing.
|
|
207
|
+
if (Is.promise(cacheResponse)) {
|
|
208
|
+
return cacheResponse;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
options ??= {};
|
|
212
|
+
options.headers ??= {};
|
|
213
|
+
if (Is.undefined(options.headers[HeaderTypes.ContentType])) {
|
|
214
|
+
options.headers[HeaderTypes.ContentType] = MimeTypes.OctetStream;
|
|
215
|
+
}
|
|
216
|
+
const response = await FetchHelper.fetch(source, url, method, requestData, options);
|
|
217
|
+
if (response.ok) {
|
|
218
|
+
if (method === HttpMethod.GET) {
|
|
219
|
+
if (response.status === HttpStatusCode.noContent) {
|
|
220
|
+
return new Uint8Array();
|
|
221
|
+
}
|
|
222
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
223
|
+
}
|
|
224
|
+
try {
|
|
225
|
+
return (await response.json());
|
|
226
|
+
}
|
|
227
|
+
catch (err) {
|
|
228
|
+
throw new FetchError(source, `${"fetchHelper"}.decodingJSON`, HttpStatusCode.badRequest, { url }, err);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const errorResponseData = await response.json();
|
|
232
|
+
const errorResponse = BaseError.fromError(errorResponseData);
|
|
233
|
+
const isErrorEmpty = BaseError.isEmpty(errorResponse);
|
|
234
|
+
throw new FetchError(source, `${"fetchHelper"}.failureStatusText`, response.status, {
|
|
235
|
+
statusText: response.statusText,
|
|
236
|
+
url,
|
|
237
|
+
data: isErrorEmpty ? errorResponseData : undefined
|
|
238
|
+
}, isErrorEmpty ? undefined : errorResponse);
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Clears the cache.
|
|
242
|
+
*/
|
|
243
|
+
static clearCache() {
|
|
244
|
+
AsyncCache.clearCache(FetchHelper._CACHE_PREFIX);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Get a cache entry.
|
|
248
|
+
* @param url The url for the request.
|
|
249
|
+
* @returns The cache entry if it exists.
|
|
250
|
+
*/
|
|
251
|
+
static async getCacheEntry(url) {
|
|
252
|
+
return AsyncCache.get(`${FetchHelper._CACHE_PREFIX}${url}`);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Set a cache entry.
|
|
256
|
+
* @param url The url for the request.
|
|
257
|
+
* @param value The value to cache.
|
|
258
|
+
* @returns The cache entry if it exists.
|
|
259
|
+
*/
|
|
260
|
+
static async setCacheEntry(url, value) {
|
|
261
|
+
await AsyncCache.set(`${FetchHelper._CACHE_PREFIX}${url}`, value);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Remove a cache entry.
|
|
265
|
+
* @param url The url for the request.
|
|
266
|
+
*/
|
|
267
|
+
static removeCacheEntry(url) {
|
|
268
|
+
AsyncCache.remove(`${FetchHelper._CACHE_PREFIX}${url}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
//# sourceMappingURL=fetchHelper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetchHelper.js","sourceRoot":"","sources":["../../../src/utils/fetchHelper.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,YAAY,EAAe,MAAM,gBAAgB,CAAC;AAE9F,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAG7D,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEnD;;GAEG;AACH,MAAM,OAAO,WAAW;IACvB;;OAEG;IACI,MAAM,CAAU,UAAU,iBAAiC;IAElE;;;OAGG;IACK,MAAM,CAAU,aAAa,GAAW,QAAQ,CAAC;IAEzD;;;;;;;;OAQG;IACI,MAAM,CAAC,KAAK,CAAC,KAAK,CACxB,MAAc,EACd,GAAW,EACX,MAAkB,EAClB,IAA0B,EAC1B,OAAgD;QAEhD,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,YAAkB,MAAM,CAAC,CAAC;QAC9D,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,SAAe,GAAG,CAAC,CAAC;QACxD,MAAM,CAAC,UAAU,CAChB,WAAW,CAAC,UAAU,YAEtB,MAAM,EACN,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CACzB,CAAC;QACF,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACjD,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,UAAgB,IAAI,CAAC,CAAC;QAC3D,CAAC;QACD,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,MAAM,CAAC,MAAM,CAAgB,WAAW,CAAC,UAAU,aAAmB,OAAO,CAAC,CAAC;YAC/E,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;gBACpC,MAAM,CAAC,MAAM,CACZ,WAAW,CAAC,UAAU,qBAEtB,OAAO,CAAC,OAAO,CACf,CAAC;YACH,CAAC;YACD,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;gBACtC,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,UAAU,uBAA6B,OAAO,CAAC,SAAS,CAAC,CAAC;YACtF,CAAC;YACD,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,kBAAkB,CAAC,EAAE,CAAC;gBAC/C,MAAM,CAAC,OAAO,CACb,WAAW,CAAC,UAAU,gCAEtB,OAAO,CAAC,kBAAkB,CAC1B,CAAC;YACH,CAAC;YACD,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;gBACvC,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,UAAU,wBAA8B,OAAO,CAAC,UAAU,CAAC,CAAC;YACxF,CAAC;YACD,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;gBACzC,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,UAAU,0BAAgC,OAAO,CAAC,YAAY,CAAC,CAAC;YAC5F,CAAC;QACF,CAAC;QAED,IAAI,UAAuC,CAAC;QAC5C,IAAI,OAA4C,CAAC;QACjD,MAAM,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,CAAC,CAAC;QAC5C,MAAM,qBAAqB,GAAG,OAAO,EAAE,YAAY,IAAI,IAAI,CAAC;QAE5D,IAAI,SAA6B,CAAC;QAClC,IAAI,OAAO,CAAC;QACZ,KAAK,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;YACnD,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;gBACjB,MAAM,uBAAuB,GAAG,qBAAqB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC;gBACjF,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,OAAO,EAAE,uBAAuB,CAAC,CAAC,CAAC;YACvF,CAAC;YAED,IAAI,OAAO,EAAE,SAAS,KAAK,SAAS,EAAE,CAAC;gBACtC,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;gBACnC,OAAO,GAAG,UAAU,CAAC,UAAU,CAAC,GAAG,EAAE;oBACpC,IAAI,UAAU,EAAE,CAAC;wBAChB,UAAU,CAAC,KAAK,EAAE,CAAC;oBACpB,CAAC;gBACF,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;YACxB,CAAC;YAED,IAAI,SAAS,CAAC;YACd,IAAI,MAAM,KAAK,UAAU,CAAC,IAAI,IAAI,MAAM,KAAK,UAAU,CAAC,GAAG,EAAE,CAAC;gBAC7D,IAAI,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;oBACrB,SAAS,GAAG,IAAI,CAAC;gBAClB,CAAC;qBAAM,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;oBAChC,SAAS,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;gBAClC,CAAC;YACF,CAAC;YAED,IAAI,CAAC;gBACJ,MAAM,cAAc,GAAgB;oBACnC,MAAM;oBACN,OAAO,EAAE,OAAO,EAAE,OAAsB;oBACxC,IAAI,EAAE,SAAS;oBACf,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;iBAClD,CAAC;gBACF,IAAI,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,kBAAkB,CAAC,EAAE,CAAC;oBAC7C,cAAc,CAAC,WAAW,GAAG,SAAS,CAAC;gBACxC,CAAC;gBAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;gBAElD,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;oBACpC,SAAS,GAAG,IAAI,UAAU,CACzB,MAAM,EACN,GAAG,aAA8B,UAAU,EAC1C,QAAQ,CAAC,MAAyB,IAAI,cAAc,CAAC,mBAAmB,EACzE;wBACC,GAAG;wBACH,UAAU,EAAE,QAAQ,CAAC,UAAU;qBAC/B,CACD,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACP,OAAO,QAAQ,CAAC;gBACjB,CAAC;YACF,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,MAAM,KAAK,GAAG,EAAE,CAAC,MAAM,CAAS,GAAG,CAAC,CAAC;gBACrC,IAAI,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;oBACrF,SAAS,GAAG,IAAI,UAAU,CACzB,MAAM,EACN,GAAG,aAA8B,eAAe,EAChD,cAAc,CAAC,kBAAkB,EACjC;wBACC,GAAG;qBACH,EACD,GAAG,CACH,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACP,MAAM,OAAO,GAAG,KAAK,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,CAAC;oBACnD,MAAM,KAAK,GAA8B,EAAE,GAAG,EAAE,CAAC;oBACjD,IAAI,UAAU,GAAmB,cAAc,CAAC,mBAAmB,CAAC;oBACpE,IAAI,OAAO,EAAE,CAAC;wBACb,UAAU,GAAG,cAAc,CAAC,cAAc,CAAC;oBAC5C,CAAC;yBAAM,IAAI,KAAK,IAAI,YAAY,IAAI,GAAG,EAAE,CAAC;wBACzC,UAAU,GAAG,GAAG,CAAC,UAA4B,CAAC;oBAC/C,CAAC;oBACD,IAAI,KAAK,IAAI,YAAY,IAAI,GAAG,EAAE,CAAC;wBAClC,KAAK,CAAC,UAAU,GAAG,GAAG,CAAC,UAAU,CAAC;oBACnC,CAAC;oBAED,IAAI,OAAO,EAAE,CAAC;wBACb,SAAS,GAAG,IAAI,UAAU,CACzB,MAAM,EACN,GAAG,aAA8B,UAAU,EAC3C,UAAU,EACV,KAAK,EACL,GAAG,CACH,CAAC;oBACH,CAAC;yBAAM,CAAC;wBACP,SAAS,GAAG,IAAI,UAAU,CACzB,MAAM,EACN,GAAG,aAA8B,UAAU,EAC3C,UAAU,EACV,KAAK,EACL,GAAG,CACH,CAAC;oBACH,CAAC;gBACF,CAAC;YACF,CAAC;oBAAS,CAAC;gBACV,IAAI,OAAO,EAAE,CAAC;oBACb,UAAU,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;gBAClC,CAAC;YACF,CAAC;QACF,CAAC;QAED,IAAI,UAAU,GAAG,CAAC,IAAI,OAAO,KAAK,UAAU,EAAE,CAAC;YAC9C,MAAM,IAAI,UAAU,CACnB,MAAM,EACN,GAAG,aAA8B,qBAAqB,EACtD,cAAc,CAAC,mBAAmB,EAClC,EAAE,GAAG,EAAE,EACP,SAAS,CACT,CAAC;QACH,CAAC;QAED,MAAM,SAAkB,CAAC;IAC1B,CAAC;IAED;;;;;;;;OAQG;IACI,MAAM,CAAC,KAAK,CAAC,SAAS,CAC5B,MAAc,EACd,GAAW,EACX,MAAkB,EAClB,WAAe,EACf,OAAuB;QAEvB,IAAI,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,UAAU,CAAC,IAAI,OAAO,CAAC,UAAU,IAAI,CAAC,EAAE,CAAC;YAChE,qEAAqE;YACrE,mDAAmD;YACnD,MAAM,aAAa,GAAG,UAAU,CAAC,IAAI,CACpC,GAAG,WAAW,CAAC,aAAa,GAAG,GAAG,EAAE,EACpC,OAAO,CAAC,UAAU,EAClB,KAAK,IAAI,EAAE,CACV,WAAW,CAAC,SAAS,CACpB,MAAM,EACN,GAAG,EACH,MAAM,EACN,WAAW,EACX,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,YAAY,CAAC,CAAC,CAC1C,CACF,CAAC;YAEF,iEAAiE;YACjE,+BAA+B;YAC/B,IAAI,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;gBAC/B,OAAO,aAAa,CAAC;YACtB,CAAC;QACF,CAAC;QAED,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,OAAO,KAAK,EAAE,CAAC;QAEvB,IACC,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;YACtD,CAAC,MAAM,KAAK,UAAU,CAAC,IAAI,IAAI,MAAM,KAAK,UAAU,CAAC,GAAG,IAAI,MAAM,KAAK,UAAU,CAAC,KAAK,CAAC,EACvF,CAAC;YACF,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC;QAC3D,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,KAAK,CACvC,MAAM,EACN,GAAG,EACH,MAAM,EACN,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,EACrD,OAAO,CACP,CAAC;QAEF,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,IAAI,QAAQ,CAAC,MAAM,KAAK,cAAc,CAAC,SAAS,EAAE,CAAC;gBAClD,OAAO,EAAO,CAAC;YAChB,CAAC;YACD,IAAI,CAAC;gBACJ,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAM,CAAC;YACrC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,MAAM,IAAI,UAAU,CACnB,MAAM,EACN,GAAG,aAA8B,eAAe,EAChD,cAAc,CAAC,UAAU,EACzB,EAAE,GAAG,EAAE,EACP,GAAG,CACH,CAAC;YACH,CAAC;QACF,CAAC;QAED,MAAM,iBAAiB,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAChD,MAAM,aAAa,GAAG,SAAS,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAC7D,MAAM,YAAY,GAAG,SAAS,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAEtD,MAAM,IAAI,UAAU,CACnB,MAAM,EACN,GAAG,aAA8B,oBAAoB,EACrD,QAAQ,CAAC,MAAwB,EACjC;YACC,UAAU,EAAE,QAAQ,CAAC,UAAU;YAC/B,GAAG;YACH,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS;SAClD,EACD,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,CACxC,CAAC;IACH,CAAC;IAED;;;;;;;;OAQG;IACI,MAAM,CAAC,KAAK,CAAC,WAAW,CAC9B,MAAc,EACd,GAAW,EACX,MAA2C,EAC3C,WAAwB,EACxB,OAAuB;QAEvB,IAAI,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,UAAU,CAAC,IAAI,OAAO,CAAC,UAAU,IAAI,CAAC,EAAE,CAAC;YAChE,qEAAqE;YACrE,mDAAmD;YACnD,MAAM,aAAa,GAAG,UAAU,CAAC,IAAI,CACpC,GAAG,WAAW,CAAC,aAAa,GAAG,GAAG,EAAE,EACpC,OAAO,CAAC,UAAU,GAAG,IAAI,EACzB,KAAK,IAAI,EAAE,CACV,WAAW,CAAC,WAAW,CACtB,MAAM,EACN,GAAG,EACH,MAAM,EACN,WAAW,EACX,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,YAAY,CAAC,CAAC,CAC1C,CACF,CAAC;YAEF,iEAAiE;YACjE,+BAA+B;YAC/B,IAAI,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;gBAC/B,OAAO,aAAa,CAAC;YACtB,CAAC;QACF,CAAC;QAED,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,OAAO,KAAK,EAAE,CAAC;QAEvB,IAAI,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;YAC5D,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,GAAG,SAAS,CAAC,WAAW,CAAC;QAClE,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;QAEpF,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,IAAI,MAAM,KAAK,UAAU,CAAC,GAAG,EAAE,CAAC;gBAC/B,IAAI,QAAQ,CAAC,MAAM,KAAK,cAAc,CAAC,SAAS,EAAE,CAAC;oBAClD,OAAO,IAAI,UAAU,EAAE,CAAC;gBACzB,CAAC;gBACD,OAAO,IAAI,UAAU,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;YACrD,CAAC;YACD,IAAI,CAAC;gBACJ,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAM,CAAC;YACrC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,MAAM,IAAI,UAAU,CACnB,MAAM,EACN,GAAG,aAA8B,eAAe,EAChD,cAAc,CAAC,UAAU,EACzB,EAAE,GAAG,EAAE,EACP,GAAG,CACH,CAAC;YACH,CAAC;QACF,CAAC;QAED,MAAM,iBAAiB,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAChD,MAAM,aAAa,GAAG,SAAS,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAC7D,MAAM,YAAY,GAAG,SAAS,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAEtD,MAAM,IAAI,UAAU,CACnB,MAAM,EACN,GAAG,aAA8B,oBAAoB,EACrD,QAAQ,CAAC,MAAwB,EACjC;YACC,UAAU,EAAE,QAAQ,CAAC,UAAU;YAC/B,GAAG;YACH,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS;SAClD,EACD,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,CACxC,CAAC;IACH,CAAC;IAED;;OAEG;IACI,MAAM,CAAC,UAAU;QACvB,UAAU,CAAC,UAAU,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;IAClD,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,KAAK,CAAC,aAAa,CAAI,GAAW;QAC/C,OAAO,UAAU,CAAC,GAAG,CAAI,GAAG,WAAW,CAAC,aAAa,GAAG,GAAG,EAAE,CAAC,CAAC;IAChE,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,KAAK,CAAC,aAAa,CAAI,GAAW,EAAE,KAAQ;QACzD,MAAM,UAAU,CAAC,GAAG,CAAI,GAAG,WAAW,CAAC,aAAa,GAAG,GAAG,EAAE,EAAE,KAAK,CAAC,CAAC;IACtE,CAAC;IAED;;;OAGG;IACI,MAAM,CAAC,gBAAgB,CAAC,GAAW;QACzC,UAAU,CAAC,MAAM,CAAC,GAAG,WAAW,CAAC,aAAa,GAAG,GAAG,EAAE,CAAC,CAAC;IACzD,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { AsyncCache, BaseError, Guards, Is, ObjectHelper, type IError } from \"@twin.org/core\";\nimport { nameof, nameofCamelCase } from \"@twin.org/nameof\";\nimport { FetchError } from \"../errors/fetchError.js\";\nimport { HeaderTypes } from \"../models/headerTypes.js\";\nimport { HttpMethod } from \"../models/httpMethod.js\";\nimport { HttpStatusCode } from \"../models/httpStatusCode.js\";\nimport type { IFetchOptions } from \"../models/IFetchOptions.js\";\nimport type { IHttpHeaders } from \"../models/IHttpHeaders.js\";\nimport { MimeTypes } from \"../models/mimeTypes.js\";\n\n/**\n * Class to helper with fetch operations.\n */\nexport class FetchHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<FetchHelper>();\n\n\t/**\n\t * Prefix to use for cache entries.\n\t * @internal\n\t */\n\tprivate static readonly _CACHE_PREFIX: string = \"fetch_\";\n\n\t/**\n\t * Perform a fetch request.\n\t * @param source The source for the request.\n\t * @param url The url for the request.\n\t * @param method The http method.\n\t * @param body Request to send to the endpoint.\n\t * @param options Options for sending the requests.\n\t * @returns The response.\n\t */\n\tpublic static async fetch(\n\t\tsource: string,\n\t\turl: string,\n\t\tmethod: HttpMethod,\n\t\tbody?: string | Uint8Array,\n\t\toptions?: Omit<IFetchOptions, \"cacheTtlSeconds\">\n\t): Promise<Response> {\n\t\tGuards.string(FetchHelper.CLASS_NAME, nameof(source), source);\n\t\tGuards.string(FetchHelper.CLASS_NAME, nameof(url), url);\n\t\tGuards.arrayOneOf<HttpMethod>(\n\t\t\tFetchHelper.CLASS_NAME,\n\t\t\tnameof(method),\n\t\t\tmethod,\n\t\t\tObject.values(HttpMethod)\n\t\t);\n\t\tif (!Is.undefined(body) && !Is.uint8Array(body)) {\n\t\t\tGuards.string(FetchHelper.CLASS_NAME, nameof(body), body);\n\t\t}\n\t\tif (!Is.undefined(options)) {\n\t\t\tGuards.object<IFetchOptions>(FetchHelper.CLASS_NAME, nameof(options), options);\n\t\t\tif (!Is.undefined(options.headers)) {\n\t\t\t\tGuards.object<IHttpHeaders>(\n\t\t\t\t\tFetchHelper.CLASS_NAME,\n\t\t\t\t\tnameof(options.headers),\n\t\t\t\t\toptions.headers\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!Is.undefined(options.timeoutMs)) {\n\t\t\t\tGuards.integer(FetchHelper.CLASS_NAME, nameof(options.timeoutMs), options.timeoutMs);\n\t\t\t}\n\t\t\tif (!Is.undefined(options.includeCredentials)) {\n\t\t\t\tGuards.boolean(\n\t\t\t\t\tFetchHelper.CLASS_NAME,\n\t\t\t\t\tnameof(options.includeCredentials),\n\t\t\t\t\toptions.includeCredentials\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!Is.undefined(options.retryCount)) {\n\t\t\t\tGuards.integer(FetchHelper.CLASS_NAME, nameof(options.retryCount), options.retryCount);\n\t\t\t}\n\t\t\tif (!Is.undefined(options.retryDelayMs)) {\n\t\t\t\tGuards.integer(FetchHelper.CLASS_NAME, nameof(options.retryDelayMs), options.retryDelayMs);\n\t\t\t}\n\t\t}\n\n\t\tlet controller: AbortController | undefined;\n\t\tlet timerId: number | NodeJS.Timeout | undefined;\n\t\tconst retryCount = options?.retryCount ?? 1;\n\t\tconst baseDelayMilliseconds = options?.retryDelayMs ?? 3000;\n\n\t\tlet lastError: IError | undefined;\n\t\tlet attempt;\n\t\tfor (attempt = 0; attempt < retryCount; attempt++) {\n\t\t\tif (attempt > 0) {\n\t\t\t\tconst exponentialBackoffDelay = baseDelayMilliseconds * Math.pow(2, attempt - 1);\n\t\t\t\tawait new Promise(resolve => globalThis.setTimeout(resolve, exponentialBackoffDelay));\n\t\t\t}\n\n\t\t\tif (options?.timeoutMs !== undefined) {\n\t\t\t\tcontroller = new AbortController();\n\t\t\t\ttimerId = globalThis.setTimeout(() => {\n\t\t\t\t\tif (controller) {\n\t\t\t\t\t\tcontroller.abort();\n\t\t\t\t\t}\n\t\t\t\t}, options?.timeoutMs);\n\t\t\t}\n\n\t\t\tlet finalBody;\n\t\t\tif (method === HttpMethod.POST || method === HttpMethod.PUT) {\n\t\t\t\tif (Is.string(body)) {\n\t\t\t\t\tfinalBody = body;\n\t\t\t\t} else if (Is.uint8Array(body)) {\n\t\t\t\t\tfinalBody = new Uint8Array(body);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst requestOptions: RequestInit = {\n\t\t\t\t\tmethod,\n\t\t\t\t\theaders: options?.headers as HeadersInit,\n\t\t\t\t\tbody: finalBody,\n\t\t\t\t\tsignal: controller ? controller.signal : undefined\n\t\t\t\t};\n\t\t\t\tif (Is.boolean(options?.includeCredentials)) {\n\t\t\t\t\trequestOptions.credentials = \"include\";\n\t\t\t\t}\n\n\t\t\t\tconst response = await fetch(url, requestOptions);\n\n\t\t\t\tif (!response.ok && retryCount > 1) {\n\t\t\t\t\tlastError = new FetchError(\n\t\t\t\t\t\tsource,\n\t\t\t\t\t\t`${nameofCamelCase<FetchHelper>()}.general`,\n\t\t\t\t\t\t(response.status as HttpStatusCode) ?? HttpStatusCode.internalServerError,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\turl,\n\t\t\t\t\t\t\tstatusText: response.statusText\n\t\t\t\t\t\t}\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\treturn response;\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tconst isErr = Is.object<IError>(err);\n\t\t\t\tif (isErr && Is.stringValue(err.message) && err.message.includes(\"Failed to fetch\")) {\n\t\t\t\t\tlastError = new FetchError(\n\t\t\t\t\t\tsource,\n\t\t\t\t\t\t`${nameofCamelCase<FetchHelper>()}.connectivity`,\n\t\t\t\t\t\tHttpStatusCode.serviceUnavailable,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\turl\n\t\t\t\t\t\t},\n\t\t\t\t\t\terr\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tconst isAbort = isErr && err.name === \"AbortError\";\n\t\t\t\t\tconst props: { [id: string]: unknown } = { url };\n\t\t\t\t\tlet httpStatus: HttpStatusCode = HttpStatusCode.internalServerError;\n\t\t\t\t\tif (isAbort) {\n\t\t\t\t\t\thttpStatus = HttpStatusCode.requestTimeout;\n\t\t\t\t\t} else if (isErr && \"httpStatus\" in err) {\n\t\t\t\t\t\thttpStatus = err.httpStatus as HttpStatusCode;\n\t\t\t\t\t}\n\t\t\t\t\tif (isErr && \"statusText\" in err) {\n\t\t\t\t\t\tprops.statusText = err.statusText;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (isAbort) {\n\t\t\t\t\t\tlastError = new FetchError(\n\t\t\t\t\t\t\tsource,\n\t\t\t\t\t\t\t`${nameofCamelCase<FetchHelper>()}.timeout`,\n\t\t\t\t\t\t\thttpStatus,\n\t\t\t\t\t\t\tprops,\n\t\t\t\t\t\t\terr\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlastError = new FetchError(\n\t\t\t\t\t\t\tsource,\n\t\t\t\t\t\t\t`${nameofCamelCase<FetchHelper>()}.general`,\n\t\t\t\t\t\t\thttpStatus,\n\t\t\t\t\t\t\tprops,\n\t\t\t\t\t\t\terr\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tif (timerId) {\n\t\t\t\t\tglobalThis.clearTimeout(timerId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (retryCount > 1 && attempt === retryCount) {\n\t\t\tthrow new FetchError(\n\t\t\t\tsource,\n\t\t\t\t`${nameofCamelCase<FetchHelper>()}.retryLimitExceeded`,\n\t\t\t\tHttpStatusCode.internalServerError,\n\t\t\t\t{ url },\n\t\t\t\tlastError\n\t\t\t);\n\t\t}\n\n\t\tthrow lastError as Error;\n\t}\n\n\t/**\n\t * Perform a request in json format.\n\t * @param source The source for the request.\n\t * @param url The url for the request.\n\t * @param method The http method.\n\t * @param requestData Request to send to the endpoint.\n\t * @param options Options for sending the requests.\n\t * @returns The response.\n\t */\n\tpublic static async fetchJson<T, U>(\n\t\tsource: string,\n\t\turl: string,\n\t\tmethod: HttpMethod,\n\t\trequestData?: T,\n\t\toptions?: IFetchOptions\n\t): Promise<U> {\n\t\tif (Is.integer(options?.cacheTtlMs) && options.cacheTtlMs >= 0) {\n\t\t\t// The cache option is set, so call the same method again but without\n\t\t\t// the cache option to get the result and cache it.\n\t\t\tconst cacheResponse = AsyncCache.exec(\n\t\t\t\t`${FetchHelper._CACHE_PREFIX}${url}`,\n\t\t\t\toptions.cacheTtlMs,\n\t\t\t\tasync () =>\n\t\t\t\t\tFetchHelper.fetchJson<T, U>(\n\t\t\t\t\t\tsource,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\tmethod,\n\t\t\t\t\t\trequestData,\n\t\t\t\t\t\tObjectHelper.omit(options, [\"cacheTtlMs\"])\n\t\t\t\t\t)\n\t\t\t);\n\n\t\t\t// If the return value is a promise return it, otherwise continue\n\t\t\t// with the regular processing.\n\t\t\tif (Is.promise(cacheResponse)) {\n\t\t\t\treturn cacheResponse;\n\t\t\t}\n\t\t}\n\n\t\toptions ??= {};\n\t\toptions.headers ??= {};\n\n\t\tif (\n\t\t\tIs.undefined(options.headers[HeaderTypes.ContentType]) &&\n\t\t\t(method === HttpMethod.POST || method === HttpMethod.PUT || method === HttpMethod.PATCH)\n\t\t) {\n\t\t\toptions.headers[HeaderTypes.ContentType] = MimeTypes.Json;\n\t\t}\n\n\t\tconst response = await FetchHelper.fetch(\n\t\t\tsource,\n\t\t\turl,\n\t\t\tmethod,\n\t\t\trequestData ? JSON.stringify(requestData) : undefined,\n\t\t\toptions\n\t\t);\n\n\t\tif (response.ok) {\n\t\t\tif (response.status === HttpStatusCode.noContent) {\n\t\t\t\treturn {} as U;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn (await response.json()) as U;\n\t\t\t} catch (err) {\n\t\t\t\tthrow new FetchError(\n\t\t\t\t\tsource,\n\t\t\t\t\t`${nameofCamelCase<FetchHelper>()}.decodingJSON`,\n\t\t\t\t\tHttpStatusCode.badRequest,\n\t\t\t\t\t{ url },\n\t\t\t\t\terr\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst errorResponseData = await response.json();\n\t\tconst errorResponse = BaseError.fromError(errorResponseData);\n\t\tconst isErrorEmpty = BaseError.isEmpty(errorResponse);\n\n\t\tthrow new FetchError(\n\t\t\tsource,\n\t\t\t`${nameofCamelCase<FetchHelper>()}.failureStatusText`,\n\t\t\tresponse.status as HttpStatusCode,\n\t\t\t{\n\t\t\t\tstatusText: response.statusText,\n\t\t\t\turl,\n\t\t\t\tdata: isErrorEmpty ? errorResponseData : undefined\n\t\t\t},\n\t\t\tisErrorEmpty ? undefined : errorResponse\n\t\t);\n\t}\n\n\t/**\n\t * Perform a request for binary data.\n\t * @param source The source for the request.\n\t * @param url The url for the request.\n\t * @param method The http method.\n\t * @param requestData Request to send to the endpoint.\n\t * @param options Options for sending the requests.\n\t * @returns The response.\n\t */\n\tpublic static async fetchBinary<T>(\n\t\tsource: string,\n\t\turl: string,\n\t\tmethod: Extract<HttpMethod, \"GET\" | \"POST\">,\n\t\trequestData?: Uint8Array,\n\t\toptions?: IFetchOptions\n\t): Promise<Uint8Array | T> {\n\t\tif (Is.integer(options?.cacheTtlMs) && options.cacheTtlMs >= 0) {\n\t\t\t// The cache option is set, so call the same method again but without\n\t\t\t// the cache option to get the result and cache it.\n\t\t\tconst cacheResponse = AsyncCache.exec(\n\t\t\t\t`${FetchHelper._CACHE_PREFIX}${url}`,\n\t\t\t\toptions.cacheTtlMs * 1000,\n\t\t\t\tasync () =>\n\t\t\t\t\tFetchHelper.fetchBinary<T>(\n\t\t\t\t\t\tsource,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\tmethod,\n\t\t\t\t\t\trequestData,\n\t\t\t\t\t\tObjectHelper.omit(options, [\"cacheTtlMs\"])\n\t\t\t\t\t)\n\t\t\t);\n\n\t\t\t// If the return value is a promise return it, otherwise continue\n\t\t\t// with the regular processing.\n\t\t\tif (Is.promise(cacheResponse)) {\n\t\t\t\treturn cacheResponse;\n\t\t\t}\n\t\t}\n\n\t\toptions ??= {};\n\t\toptions.headers ??= {};\n\n\t\tif (Is.undefined(options.headers[HeaderTypes.ContentType])) {\n\t\t\toptions.headers[HeaderTypes.ContentType] = MimeTypes.OctetStream;\n\t\t}\n\n\t\tconst response = await FetchHelper.fetch(source, url, method, requestData, options);\n\n\t\tif (response.ok) {\n\t\t\tif (method === HttpMethod.GET) {\n\t\t\t\tif (response.status === HttpStatusCode.noContent) {\n\t\t\t\t\treturn new Uint8Array();\n\t\t\t\t}\n\t\t\t\treturn new Uint8Array(await response.arrayBuffer());\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn (await response.json()) as T;\n\t\t\t} catch (err) {\n\t\t\t\tthrow new FetchError(\n\t\t\t\t\tsource,\n\t\t\t\t\t`${nameofCamelCase<FetchHelper>()}.decodingJSON`,\n\t\t\t\t\tHttpStatusCode.badRequest,\n\t\t\t\t\t{ url },\n\t\t\t\t\terr\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst errorResponseData = await response.json();\n\t\tconst errorResponse = BaseError.fromError(errorResponseData);\n\t\tconst isErrorEmpty = BaseError.isEmpty(errorResponse);\n\n\t\tthrow new FetchError(\n\t\t\tsource,\n\t\t\t`${nameofCamelCase<FetchHelper>()}.failureStatusText`,\n\t\t\tresponse.status as HttpStatusCode,\n\t\t\t{\n\t\t\t\tstatusText: response.statusText,\n\t\t\t\turl,\n\t\t\t\tdata: isErrorEmpty ? errorResponseData : undefined\n\t\t\t},\n\t\t\tisErrorEmpty ? undefined : errorResponse\n\t\t);\n\t}\n\n\t/**\n\t * Clears the cache.\n\t */\n\tpublic static clearCache(): void {\n\t\tAsyncCache.clearCache(FetchHelper._CACHE_PREFIX);\n\t}\n\n\t/**\n\t * Get a cache entry.\n\t * @param url The url for the request.\n\t * @returns The cache entry if it exists.\n\t */\n\tpublic static async getCacheEntry<T>(url: string): Promise<T | undefined> {\n\t\treturn AsyncCache.get<T>(`${FetchHelper._CACHE_PREFIX}${url}`);\n\t}\n\n\t/**\n\t * Set a cache entry.\n\t * @param url The url for the request.\n\t * @param value The value to cache.\n\t * @returns The cache entry if it exists.\n\t */\n\tpublic static async setCacheEntry<T>(url: string, value: T): Promise<void> {\n\t\tawait AsyncCache.set<T>(`${FetchHelper._CACHE_PREFIX}${url}`, value);\n\t}\n\n\t/**\n\t * Remove a cache entry.\n\t * @param url The url for the request.\n\t */\n\tpublic static removeCacheEntry(url: string): void {\n\t\tAsyncCache.remove(`${FetchHelper._CACHE_PREFIX}${url}`);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
import { Is } from "@twin.org/core";
|
|
4
|
+
/**
|
|
5
|
+
* Class to helper with header operations.
|
|
6
|
+
*/
|
|
7
|
+
export class HeaderHelper {
|
|
8
|
+
/**
|
|
9
|
+
* Create a bearer token header.
|
|
10
|
+
* @param token The token to create the header for.
|
|
11
|
+
* @returns The bearer token header.
|
|
12
|
+
*/
|
|
13
|
+
static createBearer(token) {
|
|
14
|
+
if (Is.stringValue(token)) {
|
|
15
|
+
if (token.startsWith("Bearer ")) {
|
|
16
|
+
return token;
|
|
17
|
+
}
|
|
18
|
+
return `Bearer ${token.trim()}`;
|
|
19
|
+
}
|
|
20
|
+
return "";
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Extract the bearer token from a header.
|
|
24
|
+
* @param header The header value to extract the token from.
|
|
25
|
+
* @returns The extracted token if it exists.
|
|
26
|
+
*/
|
|
27
|
+
static extractBearer(header) {
|
|
28
|
+
if (Is.stringValue(header) && header.startsWith("Bearer ")) {
|
|
29
|
+
return header.slice(7, header.length).trim();
|
|
30
|
+
}
|
|
31
|
+
return "";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=headerHelper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headerHelper.js","sourceRoot":"","sources":["../../../src/utils/headerHelper.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAEpC;;GAEG;AACH,MAAM,OAAO,YAAY;IACxB;;;;OAIG;IACI,MAAM,CAAC,YAAY,CAAC,KAAc;QACxC,IAAI,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,IAAI,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBACjC,OAAO,KAAK,CAAC;YACd,CAAC;YACD,OAAO,UAAU,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QACjC,CAAC;QACD,OAAO,EAAE,CAAC;IACX,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,aAAa,CAAC,MAAe;QAC1C,IAAI,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5D,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9C,CAAC;QACD,OAAO,EAAE,CAAC;IACX,CAAC;CACD","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Is } from \"@twin.org/core\";\n\n/**\n * Class to helper with header operations.\n */\nexport class HeaderHelper {\n\t/**\n\t * Create a bearer token header.\n\t * @param token The token to create the header for.\n\t * @returns The bearer token header.\n\t */\n\tpublic static createBearer(token: unknown): string {\n\t\tif (Is.stringValue(token)) {\n\t\t\tif (token.startsWith(\"Bearer \")) {\n\t\t\t\treturn token;\n\t\t\t}\n\t\t\treturn `Bearer ${token.trim()}`;\n\t\t}\n\t\treturn \"\";\n\t}\n\n\t/**\n\t * Extract the bearer token from a header.\n\t * @param header The header value to extract the token from.\n\t * @returns The extracted token if it exists.\n\t */\n\tpublic static extractBearer(header: unknown): string {\n\t\tif (Is.stringValue(header) && header.startsWith(\"Bearer \")) {\n\t\t\treturn header.slice(7, header.length).trim();\n\t\t}\n\t\treturn \"\";\n\t}\n}\n"]}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
import { Converter, GeneralError, Guards, Is, JsonHelper, ObjectHelper } from "@twin.org/core";
|
|
4
|
+
import { Ed25519, Sha256 } from "@twin.org/crypto";
|
|
5
|
+
import { importJWK } from "jose";
|
|
6
|
+
/**
|
|
7
|
+
* Class to handle JSON Web Keys.
|
|
8
|
+
*/
|
|
9
|
+
export class Jwk {
|
|
10
|
+
/**
|
|
11
|
+
* Runtime name for the class.
|
|
12
|
+
*/
|
|
13
|
+
static CLASS_NAME = "Jwk";
|
|
14
|
+
/**
|
|
15
|
+
* Convert the JWK to a crypto key.
|
|
16
|
+
* @param jwk The JWK to convert.
|
|
17
|
+
* @param alg The alg to be used, defaults to jwk.alg.
|
|
18
|
+
* @returns The crypto key.
|
|
19
|
+
*/
|
|
20
|
+
static async toCryptoKey(jwk, alg) {
|
|
21
|
+
Guards.object(Jwk.CLASS_NAME, "jwk", jwk);
|
|
22
|
+
try {
|
|
23
|
+
const imported = await importJWK(jwk, alg);
|
|
24
|
+
return imported;
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
throw new GeneralError(Jwk.CLASS_NAME, "jwkImportFailed", undefined, err);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Convert the Ed25519 private key to a crypto key.
|
|
32
|
+
* @param privateKey The private key to use.
|
|
33
|
+
* @param overrideUse Optional override for the use property, defaults to "sig".
|
|
34
|
+
* @returns The crypto key.
|
|
35
|
+
*/
|
|
36
|
+
static async fromEd25519Private(privateKey, overrideUse) {
|
|
37
|
+
Guards.uint8Array(Jwk.CLASS_NAME, "privateKey", privateKey);
|
|
38
|
+
const publicKey = Ed25519.publicKeyFromPrivateKey(privateKey);
|
|
39
|
+
const jwk = {
|
|
40
|
+
kty: "OKP",
|
|
41
|
+
use: overrideUse ?? "sig",
|
|
42
|
+
alg: "EdDSA",
|
|
43
|
+
crv: "Ed25519",
|
|
44
|
+
x: Converter.bytesToBase64Url(publicKey),
|
|
45
|
+
d: Converter.bytesToBase64Url(privateKey)
|
|
46
|
+
};
|
|
47
|
+
return jwk;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Convert the Ed25519 public key to a crypto key.
|
|
51
|
+
* @param publicKey The private key to use.
|
|
52
|
+
* @param overrideUse Optional override for the use property, defaults to "sig".
|
|
53
|
+
* @returns The crypto key.
|
|
54
|
+
*/
|
|
55
|
+
static async fromEd25519Public(publicKey, overrideUse) {
|
|
56
|
+
Guards.uint8Array(Jwk.CLASS_NAME, "publicKey", publicKey);
|
|
57
|
+
const jwk = {
|
|
58
|
+
kty: "OKP",
|
|
59
|
+
use: overrideUse ?? "sig",
|
|
60
|
+
alg: "EdDSA",
|
|
61
|
+
crv: "Ed25519",
|
|
62
|
+
x: Converter.bytesToBase64Url(publicKey)
|
|
63
|
+
};
|
|
64
|
+
return jwk;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Convert the JWK to raw keys.
|
|
68
|
+
* @param jwk The JWK to convert to raw.
|
|
69
|
+
* @returns The crypto key.
|
|
70
|
+
*/
|
|
71
|
+
static async toRaw(jwk) {
|
|
72
|
+
Guards.object(Jwk.CLASS_NAME, "jwk", jwk);
|
|
73
|
+
let publicKey;
|
|
74
|
+
let privateKey;
|
|
75
|
+
if (Is.stringBase64Url(jwk.x)) {
|
|
76
|
+
publicKey = Converter.base64UrlToBytes(jwk.x);
|
|
77
|
+
}
|
|
78
|
+
if (Is.stringBase64Url(jwk.d)) {
|
|
79
|
+
privateKey = Converter.base64UrlToBytes(jwk.d);
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
publicKey,
|
|
83
|
+
privateKey
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Generate a KID for the JWK.
|
|
88
|
+
* @param jwk The JWK to generate a KID for.
|
|
89
|
+
* @returns The KID.
|
|
90
|
+
*/
|
|
91
|
+
static async generateKid(jwk) {
|
|
92
|
+
Guards.object(Jwk.CLASS_NAME, "jwk", jwk);
|
|
93
|
+
const kidProps = ObjectHelper.pick(jwk, ["crv", "kty", "x"]);
|
|
94
|
+
const canonicalJson = JsonHelper.canonicalize(kidProps);
|
|
95
|
+
const hash = Sha256.sum256(Converter.utf8ToBytes(canonicalJson));
|
|
96
|
+
return Converter.bytesToBase64Url(hash);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=jwk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwk.js","sourceRoot":"","sources":["../../../src/utils/jwk.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Converter, GeneralError, Guards, Is, JsonHelper, ObjectHelper } from \"@twin.org/core\";\nimport { Ed25519, Sha256 } from \"@twin.org/crypto\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { importJWK } from \"jose\";\nimport type { IJwk } from \"../models/IJwk.js\";\nimport type { JwkCryptoKey } from \"../models/jwkCryptoKey.js\";\n\n/**\n * Class to handle JSON Web Keys.\n */\nexport class Jwk {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<Jwk>();\n\n\t/**\n\t * Convert the JWK to a crypto key.\n\t * @param jwk The JWK to convert.\n\t * @param alg The alg to be used, defaults to jwk.alg.\n\t * @returns The crypto key.\n\t */\n\tpublic static async toCryptoKey(jwk: IJwk, alg?: string): Promise<JwkCryptoKey> {\n\t\tGuards.object<IJwk>(Jwk.CLASS_NAME, nameof(jwk), jwk);\n\t\ttry {\n\t\t\tconst imported = await importJWK(jwk, alg);\n\t\t\treturn imported;\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(Jwk.CLASS_NAME, \"jwkImportFailed\", undefined, err);\n\t\t}\n\t}\n\n\t/**\n\t * Convert the Ed25519 private key to a crypto key.\n\t * @param privateKey The private key to use.\n\t * @param overrideUse Optional override for the use property, defaults to \"sig\".\n\t * @returns The crypto key.\n\t */\n\tpublic static async fromEd25519Private(\n\t\tprivateKey: Uint8Array,\n\t\toverrideUse?: \"enc\" | \"sig\" | string\n\t): Promise<IJwk> {\n\t\tGuards.uint8Array(Jwk.CLASS_NAME, nameof(privateKey), privateKey);\n\n\t\tconst publicKey = Ed25519.publicKeyFromPrivateKey(privateKey);\n\n\t\tconst jwk: IJwk = {\n\t\t\tkty: \"OKP\",\n\t\t\tuse: overrideUse ?? \"sig\",\n\t\t\talg: \"EdDSA\",\n\t\t\tcrv: \"Ed25519\",\n\t\t\tx: Converter.bytesToBase64Url(publicKey),\n\t\t\td: Converter.bytesToBase64Url(privateKey)\n\t\t};\n\n\t\treturn jwk;\n\t}\n\n\t/**\n\t * Convert the Ed25519 public key to a crypto key.\n\t * @param publicKey The private key to use.\n\t * @param overrideUse Optional override for the use property, defaults to \"sig\".\n\t * @returns The crypto key.\n\t */\n\tpublic static async fromEd25519Public(\n\t\tpublicKey: Uint8Array,\n\t\toverrideUse?: \"enc\" | \"sig\" | string\n\t): Promise<IJwk> {\n\t\tGuards.uint8Array(Jwk.CLASS_NAME, nameof(publicKey), publicKey);\n\n\t\tconst jwk: IJwk = {\n\t\t\tkty: \"OKP\",\n\t\t\tuse: overrideUse ?? \"sig\",\n\t\t\talg: \"EdDSA\",\n\t\t\tcrv: \"Ed25519\",\n\t\t\tx: Converter.bytesToBase64Url(publicKey)\n\t\t};\n\n\t\treturn jwk;\n\t}\n\n\t/**\n\t * Convert the JWK to raw keys.\n\t * @param jwk The JWK to convert to raw.\n\t * @returns The crypto key.\n\t */\n\tpublic static async toRaw(jwk: IJwk): Promise<{\n\t\tpublicKey?: Uint8Array;\n\t\tprivateKey?: Uint8Array;\n\t}> {\n\t\tGuards.object<IJwk>(Jwk.CLASS_NAME, nameof(jwk), jwk);\n\n\t\tlet publicKey: Uint8Array | undefined;\n\t\tlet privateKey: Uint8Array | undefined;\n\n\t\tif (Is.stringBase64Url(jwk.x)) {\n\t\t\tpublicKey = Converter.base64UrlToBytes(jwk.x);\n\t\t}\n\t\tif (Is.stringBase64Url(jwk.d)) {\n\t\t\tprivateKey = Converter.base64UrlToBytes(jwk.d);\n\t\t}\n\n\t\treturn {\n\t\t\tpublicKey,\n\t\t\tprivateKey\n\t\t};\n\t}\n\n\t/**\n\t * Generate a KID for the JWK.\n\t * @param jwk The JWK to generate a KID for.\n\t * @returns The KID.\n\t */\n\tpublic static async generateKid(jwk: IJwk): Promise<string> {\n\t\tGuards.object<IJwk>(Jwk.CLASS_NAME, nameof(jwk), jwk);\n\n\t\tconst kidProps = ObjectHelper.pick(jwk, [\"crv\", \"kty\", \"x\"]);\n\n\t\tconst canonicalJson = JsonHelper.canonicalize(kidProps);\n\n\t\tconst hash = Sha256.sum256(Converter.utf8ToBytes(canonicalJson));\n\n\t\treturn Converter.bytesToBase64Url(hash);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
import { GeneralError, Guards, Is } from "@twin.org/core";
|
|
4
|
+
import { CompactSign, flattenedVerify } from "jose";
|
|
5
|
+
/**
|
|
6
|
+
* Class to handle JSON Web Signatures.
|
|
7
|
+
*/
|
|
8
|
+
export class Jws {
|
|
9
|
+
/**
|
|
10
|
+
* Runtime name for the class.
|
|
11
|
+
*/
|
|
12
|
+
static CLASS_NAME = "Jws";
|
|
13
|
+
/**
|
|
14
|
+
* Create a signature.
|
|
15
|
+
* @param privateKey The private key to use.
|
|
16
|
+
* @param hash The hash to sign.
|
|
17
|
+
* @param algOverride An optional algorithm override.
|
|
18
|
+
* @returns The signature.
|
|
19
|
+
*/
|
|
20
|
+
static async create(privateKey, hash, algOverride) {
|
|
21
|
+
Guards.defined(Jws.CLASS_NAME, "privateKey", privateKey);
|
|
22
|
+
Guards.uint8Array(Jws.CLASS_NAME, "hash", hash);
|
|
23
|
+
try {
|
|
24
|
+
const jws = await new CompactSign(hash)
|
|
25
|
+
.setProtectedHeader({
|
|
26
|
+
alg: algOverride ?? (Is.uint8Array(privateKey) ? "EdDSA" : privateKey.algorithm.name),
|
|
27
|
+
b64: false,
|
|
28
|
+
crit: ["b64"]
|
|
29
|
+
})
|
|
30
|
+
.sign(privateKey);
|
|
31
|
+
return jws;
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
throw new GeneralError(Jws.CLASS_NAME, "createFailed", undefined, err);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Verify a signature.
|
|
39
|
+
* @param jws The signature to verify.
|
|
40
|
+
* @param publicKey The public key to verify the signature with.
|
|
41
|
+
* @param hash The hash to verify.
|
|
42
|
+
* @returns True if the signature was verified.
|
|
43
|
+
*/
|
|
44
|
+
static async verify(jws, publicKey, hash) {
|
|
45
|
+
Guards.stringValue(Jws.CLASS_NAME, "jws", jws);
|
|
46
|
+
Guards.defined(Jws.CLASS_NAME, "publicKey", publicKey);
|
|
47
|
+
Guards.uint8Array(Jws.CLASS_NAME, "hash", hash);
|
|
48
|
+
try {
|
|
49
|
+
const jwsParts = jws.split(".");
|
|
50
|
+
await flattenedVerify({ protected: jwsParts[0], payload: hash, signature: jwsParts[2] }, publicKey);
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
throw new GeneralError(Jws.CLASS_NAME, "verifyFailed", undefined, err);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=jws.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jws.js","sourceRoot":"","sources":["../../../src/utils/jws.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAE1D,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AAGpD;;GAEG;AACH,MAAM,OAAO,GAAG;IACf;;OAEG;IACI,MAAM,CAAU,UAAU,SAAyB;IAE1D;;;;;;OAMG;IACI,MAAM,CAAC,KAAK,CAAC,MAAM,CACzB,UAAwB,EACxB,IAAgB,EAChB,WAAoB;QAEpB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,gBAAsB,UAAU,CAAC,CAAC;QAC/D,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,UAAU,UAAgB,IAAI,CAAC,CAAC;QAEtD,IAAI,CAAC;YACJ,MAAM,GAAG,GAAG,MAAM,IAAI,WAAW,CAAC,IAAI,CAAC;iBACrC,kBAAkB,CAAC;gBACnB,GAAG,EAAE,WAAW,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC;gBACrF,GAAG,EAAE,KAAK;gBACV,IAAI,EAAE,CAAC,KAAK,CAAC;aACb,CAAC;iBACD,IAAI,CAAC,UAAU,CAAC,CAAC;YAEnB,OAAO,GAAG,CAAC;QACZ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,MAAM,IAAI,YAAY,CAAC,GAAG,CAAC,UAAU,EAAE,cAAc,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;QACxE,CAAC;IACF,CAAC;IAED;;;;;;OAMG;IACI,MAAM,CAAC,KAAK,CAAC,MAAM,CACzB,GAAW,EACX,SAAuB,EACvB,IAAgB;QAEhB,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,SAAe,GAAG,CAAC,CAAC;QACrD,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,eAAqB,SAAS,CAAC,CAAC;QAC7D,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,UAAU,UAAgB,IAAI,CAAC,CAAC;QAEtD,IAAI,CAAC;YACJ,MAAM,QAAQ,GAAa,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAE1C,MAAM,eAAe,CACpB,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,EACjE,SAAS,CACT,CAAC;YAEF,OAAO,IAAI,CAAC;QACb,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,MAAM,IAAI,YAAY,CAAC,GAAG,CAAC,UAAU,EAAE,cAAc,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;QACxE,CAAC;IACF,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { GeneralError, Guards, Is } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { CompactSign, flattenedVerify } from \"jose\";\nimport type { JwkCryptoKey } from \"../models/jwkCryptoKey.js\";\n\n/**\n * Class to handle JSON Web Signatures.\n */\nexport class Jws {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<Jws>();\n\n\t/**\n\t * Create a signature.\n\t * @param privateKey The private key to use.\n\t * @param hash The hash to sign.\n\t * @param algOverride An optional algorithm override.\n\t * @returns The signature.\n\t */\n\tpublic static async create(\n\t\tprivateKey: JwkCryptoKey,\n\t\thash: Uint8Array,\n\t\talgOverride?: string\n\t): Promise<string> {\n\t\tGuards.defined(Jws.CLASS_NAME, nameof(privateKey), privateKey);\n\t\tGuards.uint8Array(Jws.CLASS_NAME, nameof(hash), hash);\n\n\t\ttry {\n\t\t\tconst jws = await new CompactSign(hash)\n\t\t\t\t.setProtectedHeader({\n\t\t\t\t\talg: algOverride ?? (Is.uint8Array(privateKey) ? \"EdDSA\" : privateKey.algorithm.name),\n\t\t\t\t\tb64: false,\n\t\t\t\t\tcrit: [\"b64\"]\n\t\t\t\t})\n\t\t\t\t.sign(privateKey);\n\n\t\t\treturn jws;\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(Jws.CLASS_NAME, \"createFailed\", undefined, err);\n\t\t}\n\t}\n\n\t/**\n\t * Verify a signature.\n\t * @param jws The signature to verify.\n\t * @param publicKey The public key to verify the signature with.\n\t * @param hash The hash to verify.\n\t * @returns True if the signature was verified.\n\t */\n\tpublic static async verify(\n\t\tjws: string,\n\t\tpublicKey: JwkCryptoKey,\n\t\thash: Uint8Array\n\t): Promise<boolean> {\n\t\tGuards.stringValue(Jws.CLASS_NAME, nameof(jws), jws);\n\t\tGuards.defined(Jws.CLASS_NAME, nameof(publicKey), publicKey);\n\t\tGuards.uint8Array(Jws.CLASS_NAME, nameof(hash), hash);\n\n\t\ttry {\n\t\t\tconst jwsParts: string[] = jws.split(\".\");\n\n\t\t\tawait flattenedVerify(\n\t\t\t\t{ protected: jwsParts[0], payload: hash, signature: jwsParts[2] },\n\t\t\t\tpublicKey\n\t\t\t);\n\n\t\t\treturn true;\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(Jws.CLASS_NAME, \"verifyFailed\", undefined, err);\n\t\t}\n\t}\n}\n"]}
|