@aligent/microservice-util-lib 1.2.1 → 1.3.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aligent/microservice-util-lib",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "description": "A set of utility functions for Aligent Microservices",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -64,27 +64,18 @@ function combineUrlAndPathParams(url, pathParams) {
64
64
  *
65
65
  * @param {string} baseURL - The base URL.
66
66
  * @param {string} url - The URL to process.
67
- * @returns {{ baseUri: string, searchParams: URLSearchParams | null }} The processed URL and its search parameters.
67
+ * @returns The processed URL.
68
68
  */
69
- function handleOAuthUrl(baseURL, url) {
69
+ function getOAuthUrl(baseURL, url) {
70
70
  const oauthUrl = new URL(!baseURL || isAbsoluteURL(url) ? url : combineURLs(baseURL, url));
71
- let searchParams = null;
72
- // Query parameters are hashed as part of params rather than as part of the URL
73
- if (oauthUrl.search) {
74
- searchParams = new URLSearchParams(oauthUrl.search);
75
- oauthUrl.search = '';
76
- }
77
- // Do not include hash in signature
78
- oauthUrl.hash = '';
71
+ oauthUrl.search = ''; // Query parameters are hashed as part of params rather than as part of the URL
72
+ oauthUrl.hash = ''; // Do not include hash in signature
79
73
  // Remove port if it is the default for that protocol
80
74
  if ((oauthUrl.protocol === 'https:' && oauthUrl.port === '443') ||
81
75
  (oauthUrl.protocol === 'http:' && oauthUrl.port === '80')) {
82
76
  oauthUrl.port = '';
83
77
  }
84
- return {
85
- baseUri: oauthUrl.toString(),
86
- searchParams,
87
- };
78
+ return oauthUrl.toString();
88
79
  }
89
80
  /**
90
81
  * Adds a parameter to the list of parameters to sign.
@@ -174,10 +165,6 @@ async function generateOauthParams(request, options, params, config) {
174
165
  if (params.query) {
175
166
  addParamsToSign(paramsToSign, params.query);
176
167
  }
177
- const { baseUri, searchParams } = handleOAuthUrl(options.baseUrl, url);
178
- if (searchParams) {
179
- addParamsToSign(paramsToSign, searchParams);
180
- }
181
168
  const body = await request.text();
182
169
  // If user submit a form, then include form parameters in the
183
170
  // signature as parameters rather than the body hash
@@ -195,7 +182,8 @@ async function generateOauthParams(request, options, params, config) {
195
182
  addParamToSign(paramsToSign, 'oauth_body_hash', bodyHash);
196
183
  }
197
184
  }
198
- oauthParams.oauth_signature = (0, oauth_sign_1.sign)(algorithm, method, baseUri, paramsToSign, consumerSecret, tokenSecret);
185
+ const oauthUrl = getOAuthUrl(options.baseUrl, url);
186
+ oauthParams.oauth_signature = (0, oauth_sign_1.sign)(algorithm, method, oauthUrl, paramsToSign, consumerSecret, tokenSecret);
199
187
  // realm should not be included in the signature calculation
200
188
  // but is optional in the OAuth 1.0 Authorization header
201
189
  // so we need to add it after signing the request
@@ -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 throw error if it's not "ok".
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
- * @throws {Error} When the response is not an "ok" response.
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 Error(`${response.status}: ${response.statusText}`);
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(context.response.status, config.retryOn)) {
162
- throwErrorIfNotOkResponse(context.response);
163
- return context.response;
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(context.response);
171
- return context.response;
175
+ throwErrorIfNotOkResponse(response, request);
176
+ return response;
172
177
  }
173
178
  return await performRetries(normalisedConfig, context);
174
179
  },
@@ -214,18 +219,18 @@ async function performRetries(config, context) {
214
219
  continue;
215
220
  }
216
221
  if (config.retryOn && !shouldRetryOnStatus(response?.status, config.retryOn)) {
217
- throwErrorIfNotOkResponse(response);
222
+ throwErrorIfNotOkResponse(response, context.request);
218
223
  return response;
219
224
  }
220
225
  const shouldRetry = await config.retryCondition(context, config.idempotentOnly);
221
226
  if (!shouldRetry) {
222
- throwErrorIfNotOkResponse(response);
227
+ throwErrorIfNotOkResponse(response, context.request);
223
228
  return response;
224
229
  }
225
230
  } while (attempt <= maxRetries);
226
231
  if (!response) {
227
232
  throw context.error;
228
233
  }
229
- throwErrorIfNotOkResponse(response);
234
+ throwErrorIfNotOkResponse(response, context.request);
230
235
  return response;
231
236
  }
@@ -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
+ }