@aligent/microservice-util-lib 1.2.2 → 1.3.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/package.json +1 -1
- package/src/openapi-fetch-middlewares/retry.d.ts +2 -1
- package/src/openapi-fetch-middlewares/retry.js +21 -14
- package/src/openapi-fetch-middlewares/utils/http-response-error.d.ts +42 -0
- package/src/openapi-fetch-middlewares/utils/http-response-error.js +65 -0
- package/src/remap/remap.d.ts +6 -2
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Middleware } from 'openapi-fetch';
|
|
2
2
|
import type { RetryConfig, RetryContext, RetryDelayFn } from './types/retry';
|
|
3
|
+
import { HttpResponseError, isHttpResponseError } from './utils/http-response-error';
|
|
3
4
|
/**
|
|
4
5
|
* This middleware implements retry logic with support for:
|
|
5
6
|
* - Configurable number of retry attempts
|
|
@@ -50,5 +51,5 @@ import type { RetryConfig, RetryContext, RetryDelayFn } from './types/retry';
|
|
|
50
51
|
* });
|
|
51
52
|
*/
|
|
52
53
|
declare function retryMiddleware(config?: RetryConfig): Middleware;
|
|
53
|
-
export { retryMiddleware };
|
|
54
|
+
export { HttpResponseError, isHttpResponseError, retryMiddleware };
|
|
54
55
|
export type { RetryConfig, RetryContext, RetryDelayFn };
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isHttpResponseError = exports.HttpResponseError = void 0;
|
|
3
4
|
exports.retryMiddleware = retryMiddleware;
|
|
5
|
+
const http_response_error_1 = require("./utils/http-response-error");
|
|
6
|
+
Object.defineProperty(exports, "HttpResponseError", { enumerable: true, get: function () { return http_response_error_1.HttpResponseError; } });
|
|
7
|
+
Object.defineProperty(exports, "isHttpResponseError", { enumerable: true, get: function () { return http_response_error_1.isHttpResponseError; } });
|
|
4
8
|
const is_network_error_1 = require("./utils/is-network-error");
|
|
5
9
|
const IDEMPOTENT_HTTP_METHODS = ['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE'];
|
|
6
10
|
/**
|
|
@@ -85,14 +89,15 @@ function shouldRetryOnStatus(status, retryOn) {
|
|
|
85
89
|
return retryOn.includes(status);
|
|
86
90
|
}
|
|
87
91
|
/**
|
|
88
|
-
* Checks the response status and
|
|
92
|
+
* Checks the response status and throws HttpResponseError if it's not "ok".
|
|
89
93
|
*
|
|
90
94
|
* @param {Response} response - The HTTP response object.
|
|
91
|
-
* @
|
|
95
|
+
* @param {Request} request - The HTTP request object.
|
|
96
|
+
* @throws {HttpResponseError} When the response is not an "ok" response.
|
|
92
97
|
*/
|
|
93
|
-
function throwErrorIfNotOkResponse(response) {
|
|
98
|
+
function throwErrorIfNotOkResponse(response, request) {
|
|
94
99
|
if (!response.ok) {
|
|
95
|
-
throw new
|
|
100
|
+
throw new http_response_error_1.HttpResponseError(response, request);
|
|
96
101
|
}
|
|
97
102
|
}
|
|
98
103
|
/**
|
|
@@ -158,17 +163,17 @@ function retryMiddleware(config) {
|
|
|
158
163
|
const context = { attempt: 1, request, response, error: null };
|
|
159
164
|
// If retryOn is specified, only use that list
|
|
160
165
|
if (config?.retryOn && config.retryOn.length > 0) {
|
|
161
|
-
if (!shouldRetryOnStatus(
|
|
162
|
-
throwErrorIfNotOkResponse(
|
|
163
|
-
return
|
|
166
|
+
if (!shouldRetryOnStatus(response.status, config.retryOn)) {
|
|
167
|
+
throwErrorIfNotOkResponse(response, request);
|
|
168
|
+
return response;
|
|
164
169
|
}
|
|
165
170
|
return await performRetries(normalisedConfig, context);
|
|
166
171
|
}
|
|
167
172
|
// Otherwise, check if we should retry based on retry condition
|
|
168
173
|
const shouldRetry = await normalisedConfig.retryCondition(context, normalisedConfig.idempotentOnly);
|
|
169
174
|
if (!shouldRetry) {
|
|
170
|
-
throwErrorIfNotOkResponse(
|
|
171
|
-
return
|
|
175
|
+
throwErrorIfNotOkResponse(response, request);
|
|
176
|
+
return response;
|
|
172
177
|
}
|
|
173
178
|
return await performRetries(normalisedConfig, context);
|
|
174
179
|
},
|
|
@@ -199,8 +204,10 @@ async function performRetries(config, context) {
|
|
|
199
204
|
await config.onRetry({ ...context, attempt, response });
|
|
200
205
|
}
|
|
201
206
|
try {
|
|
202
|
-
const
|
|
203
|
-
|
|
207
|
+
const request = config.shouldResetTimeout
|
|
208
|
+
? new Request(context.request)
|
|
209
|
+
: new Request(context.request, { signal: context.request.signal });
|
|
210
|
+
response = await config.fetch(request);
|
|
204
211
|
context = { ...context, attempt: attempt + 1, response, error: null };
|
|
205
212
|
attempt++;
|
|
206
213
|
}
|
|
@@ -214,18 +221,18 @@ async function performRetries(config, context) {
|
|
|
214
221
|
continue;
|
|
215
222
|
}
|
|
216
223
|
if (config.retryOn && !shouldRetryOnStatus(response?.status, config.retryOn)) {
|
|
217
|
-
throwErrorIfNotOkResponse(response);
|
|
224
|
+
throwErrorIfNotOkResponse(response, context.request);
|
|
218
225
|
return response;
|
|
219
226
|
}
|
|
220
227
|
const shouldRetry = await config.retryCondition(context, config.idempotentOnly);
|
|
221
228
|
if (!shouldRetry) {
|
|
222
|
-
throwErrorIfNotOkResponse(response);
|
|
229
|
+
throwErrorIfNotOkResponse(response, context.request);
|
|
223
230
|
return response;
|
|
224
231
|
}
|
|
225
232
|
} while (attempt <= maxRetries);
|
|
226
233
|
if (!response) {
|
|
227
234
|
throw context.error;
|
|
228
235
|
}
|
|
229
|
-
throwErrorIfNotOkResponse(response);
|
|
236
|
+
throwErrorIfNotOkResponse(response, context.request);
|
|
230
237
|
return response;
|
|
231
238
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom error class for HTTP response errors, similar to Axios Error.
|
|
3
|
+
* Provides detailed information about the failed request and response.
|
|
4
|
+
*/
|
|
5
|
+
export declare class HttpResponseError extends Error {
|
|
6
|
+
readonly name = "HttpResponseError";
|
|
7
|
+
readonly status: number;
|
|
8
|
+
readonly statusText: string;
|
|
9
|
+
readonly response: Response;
|
|
10
|
+
readonly request: Request;
|
|
11
|
+
readonly isHttpResponseError = true;
|
|
12
|
+
constructor(response: Response, request: Request);
|
|
13
|
+
/**
|
|
14
|
+
* Returns a JSON representation of the error for logging/debugging.
|
|
15
|
+
*/
|
|
16
|
+
toJson(): {
|
|
17
|
+
name: string;
|
|
18
|
+
message: string;
|
|
19
|
+
status: number;
|
|
20
|
+
statusText: string;
|
|
21
|
+
method: string;
|
|
22
|
+
url: string;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Type guard to check if an error is an HttpResponseError.
|
|
27
|
+
* Useful for narrowing error types in catch blocks.
|
|
28
|
+
*
|
|
29
|
+
* @param {unknown} error - The error to check.
|
|
30
|
+
* @returns {boolean} True if the error is an HttpResponseError.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* try {
|
|
34
|
+
* await fetchData();
|
|
35
|
+
* } catch (error) {
|
|
36
|
+
* if (isHttpResponseError(error)) {
|
|
37
|
+
* console.log(`Request failed with status ${error.status}`);
|
|
38
|
+
* console.log(`URL: ${error.request.url}`);
|
|
39
|
+
* }
|
|
40
|
+
* }
|
|
41
|
+
*/
|
|
42
|
+
export declare function isHttpResponseError(error: unknown): error is HttpResponseError;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HttpResponseError = void 0;
|
|
4
|
+
exports.isHttpResponseError = isHttpResponseError;
|
|
5
|
+
/**
|
|
6
|
+
* Custom error class for HTTP response errors, similar to Axios Error.
|
|
7
|
+
* Provides detailed information about the failed request and response.
|
|
8
|
+
*/
|
|
9
|
+
class HttpResponseError extends Error {
|
|
10
|
+
name = 'HttpResponseError';
|
|
11
|
+
status;
|
|
12
|
+
statusText;
|
|
13
|
+
response;
|
|
14
|
+
request;
|
|
15
|
+
isHttpResponseError = true;
|
|
16
|
+
constructor(response, request) {
|
|
17
|
+
super(`${response.status}: ${response.statusText}`);
|
|
18
|
+
this.status = response.status;
|
|
19
|
+
this.statusText = response.statusText;
|
|
20
|
+
this.response = response;
|
|
21
|
+
this.request = request;
|
|
22
|
+
// Maintains proper stack trace for where error was thrown (V8 engines)
|
|
23
|
+
if (Error.captureStackTrace) {
|
|
24
|
+
Error.captureStackTrace(this, HttpResponseError);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Returns a JSON representation of the error for logging/debugging.
|
|
29
|
+
*/
|
|
30
|
+
toJson() {
|
|
31
|
+
return {
|
|
32
|
+
name: this.name,
|
|
33
|
+
message: this.message,
|
|
34
|
+
status: this.status,
|
|
35
|
+
statusText: this.statusText,
|
|
36
|
+
method: this.request.method,
|
|
37
|
+
url: this.request.url,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.HttpResponseError = HttpResponseError;
|
|
42
|
+
/**
|
|
43
|
+
* Type guard to check if an error is an HttpResponseError.
|
|
44
|
+
* Useful for narrowing error types in catch blocks.
|
|
45
|
+
*
|
|
46
|
+
* @param {unknown} error - The error to check.
|
|
47
|
+
* @returns {boolean} True if the error is an HttpResponseError.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* try {
|
|
51
|
+
* await fetchData();
|
|
52
|
+
* } catch (error) {
|
|
53
|
+
* if (isHttpResponseError(error)) {
|
|
54
|
+
* console.log(`Request failed with status ${error.status}`);
|
|
55
|
+
* console.log(`URL: ${error.request.url}`);
|
|
56
|
+
* }
|
|
57
|
+
* }
|
|
58
|
+
*/
|
|
59
|
+
function isHttpResponseError(error) {
|
|
60
|
+
return (error instanceof HttpResponseError ||
|
|
61
|
+
(typeof error === 'object' &&
|
|
62
|
+
error !== null &&
|
|
63
|
+
'isHttpResponseError' in error &&
|
|
64
|
+
error.isHttpResponseError === true));
|
|
65
|
+
}
|
package/src/remap/remap.d.ts
CHANGED
|
@@ -33,7 +33,11 @@ type GetKeyType<Key extends string, O extends {
|
|
|
33
33
|
* Given an ObjectMap, return a new ObjectMap with the first index of each
|
|
34
34
|
* tuple replaced with the value of the second index
|
|
35
35
|
*/
|
|
36
|
-
type OverrideIndex<M extends ObjectMap[number], V extends string> =
|
|
36
|
+
type OverrideIndex<M extends ObjectMap[number], V extends string> = M extends readonly [
|
|
37
|
+
unknown,
|
|
38
|
+
unknown,
|
|
39
|
+
infer T
|
|
40
|
+
] ? readonly [M[0], V, T] : readonly [M[0], V];
|
|
37
41
|
/**
|
|
38
42
|
* Given a key and a base object, return the type of the property referencable
|
|
39
43
|
* by the key on the base object
|
|
@@ -48,7 +52,7 @@ type OverrideIndex<M extends ObjectMap[number], V extends string> = readonly [M[
|
|
|
48
52
|
*/
|
|
49
53
|
type ConstructTypeFromPropertiesInternal<M extends ObjectMap[number], O extends {
|
|
50
54
|
[key: string]: any;
|
|
51
|
-
}> = M[1] extends `${infer P}.${infer Rest}` ? {
|
|
55
|
+
}> = M[1] extends `${infer P extends string}.${infer Rest extends string}` ? {
|
|
52
56
|
[key in P]: ConstructTypeFromPropertiesInternal<OverrideIndex<M, Rest>, O>;
|
|
53
57
|
} : {
|
|
54
58
|
[key in M[1]]: GetKeyType<M[0], O, M>;
|