@nu-art/http-client 0.401.9 → 0.500.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.
@@ -1,8 +1,8 @@
1
1
  import { Logger } from '@nu-art/ts-common';
2
2
  import { HttpRequest } from './HttpRequest.js';
3
- import { AxiosRequestConfig as Axios_RequestConfig } from 'axios';
3
+ import { AxiosRequestConfig as Axios_RequestConfig, AxiosResponse as Axios_Response } from 'axios';
4
4
  import { HttpException } from '../exceptions/HttpException.js';
5
- import { ApiDef, TypedApi } from '../types/api-types.js';
5
+ import { ApiDef, GeneralApi } from '../types/api-types.js';
6
6
  /**
7
7
  * HTTP client configuration.
8
8
  */
@@ -20,7 +20,7 @@ export type HttpConfig = {
20
20
  *
21
21
  * Extends Logger for built-in logging capabilities.
22
22
  */
23
- export declare class HttpClient_Class extends Logger {
23
+ export declare class HttpClient extends Logger {
24
24
  protected origin?: string;
25
25
  protected timeout: number;
26
26
  protected compress: boolean;
@@ -28,6 +28,8 @@ export declare class HttpClient_Class extends Logger {
28
28
  protected defaultOnComplete?: (response: unknown, input: unknown, request: HttpRequest<any>) => Promise<any>;
29
29
  protected defaultOnError?: (errorResponse: HttpException) => Promise<any>;
30
30
  private requestOption;
31
+ static default: HttpClient;
32
+ static setDefault(config: HttpConfig): void;
31
33
  /**
32
34
  * Creates a new HTTP client instance.
33
35
  *
@@ -45,7 +47,14 @@ export declare class HttpClient_Class extends Logger {
45
47
  */
46
48
  setConfig(config: HttpConfig): void;
47
49
  getOrigin(): string | undefined;
50
+ getTimeout(): number;
51
+ getRequestOption(): Axios_RequestConfig;
48
52
  shouldCompress(): boolean;
53
+ /**
54
+ * Sends the request (single boundary to the real world).
55
+ * Override in tests to assert on options and return a mock response.
56
+ */
57
+ sendRequest(options: Axios_RequestConfig): Promise<Axios_Response>;
49
58
  setDefaultOnComplete: (defaultOnComplete: (response: unknown, input: unknown, request: HttpRequest<any>) => Promise<any>) => void;
50
59
  setDefaultOnError: (defaultOnError: (errorResponse: HttpException) => Promise<any>) => void;
51
60
  /**
@@ -74,19 +83,13 @@ export declare class HttpClient_Class extends Logger {
74
83
  };
75
84
  setRequestOption(requestOption: Axios_RequestConfig): this;
76
85
  /**
77
- * Creates a new typed HTTP request with default configuration applied.
78
- *
79
- * The request is pre-configured with:
80
- * - Method, timeout, and request options from client defaults
81
- * - Default headers (evaluated at creation time)
82
- * - Default error and completion callbacks (if set)
83
- * - URL (either fullUrl or composed from baseUrl/origin + path)
86
+ * Creates a new typed HTTP request bound to this client.
87
+ * The request reads config (origin, timeout, headers) from this client and calls sendRequest on it when executing.
84
88
  *
85
89
  * @template API - Typed API definition
86
- * @param apiDef - API definition with method, path, and optional URL configuration
90
+ * @param apiDef - API definition with method, path, and optional timeout
87
91
  * @param data - Optional request data (used as request key identifier)
88
- * @returns Configured HttpRequest instance ready for further customization
92
+ * @returns HttpRequest instance ready for setUrlParams/setBodyAsJson and execute()
89
93
  */
90
- createRequest<API extends TypedApi<any, any, any, any>>(apiDef: ApiDef<API>, data?: string): HttpRequest<API>;
94
+ createRequest<API extends GeneralApi>(apiDef: ApiDef<API>, data?: string): HttpRequest<API>;
91
95
  }
92
- export declare const HttpClient: HttpClient_Class;
@@ -6,6 +6,8 @@
6
6
  // noinspection TypeScriptPreferShortImport
7
7
  import { BadImplementationException, Logger } from '@nu-art/ts-common';
8
8
  import { HttpRequest } from './HttpRequest.js';
9
+ // Axios v1+ import style
10
+ import axios from 'axios';
9
11
  /**
10
12
  * HTTP client for creating and configuring typed HTTP requests.
11
13
  *
@@ -15,7 +17,7 @@ import { HttpRequest } from './HttpRequest.js';
15
17
  *
16
18
  * Extends Logger for built-in logging capabilities.
17
19
  */
18
- export class HttpClient_Class extends Logger {
20
+ export class HttpClient extends Logger {
19
21
  origin;
20
22
  timeout = 10000;
21
23
  compress = true;
@@ -23,6 +25,10 @@ export class HttpClient_Class extends Logger {
23
25
  defaultOnComplete;
24
26
  defaultOnError;
25
27
  requestOption = {};
28
+ static default;
29
+ static setDefault(config) {
30
+ this.default = new HttpClient(config);
31
+ }
26
32
  /**
27
33
  * Creates a new HTTP client instance.
28
34
  *
@@ -58,9 +64,22 @@ export class HttpClient_Class extends Logger {
58
64
  getOrigin() {
59
65
  return this.origin;
60
66
  }
67
+ getTimeout() {
68
+ return this.timeout;
69
+ }
70
+ getRequestOption() {
71
+ return this.requestOption;
72
+ }
61
73
  shouldCompress() {
62
74
  return this.compress;
63
75
  }
76
+ /**
77
+ * Sends the request (single boundary to the real world).
78
+ * Override in tests to assert on options and return a mock response.
79
+ */
80
+ async sendRequest(options) {
81
+ return axios.request(options);
82
+ }
64
83
  setDefaultOnComplete = (defaultOnComplete) => {
65
84
  this.defaultOnComplete = defaultOnComplete;
66
85
  };
@@ -124,29 +143,16 @@ export class HttpClient_Class extends Logger {
124
143
  return this;
125
144
  }
126
145
  /**
127
- * Creates a new typed HTTP request with default configuration applied.
128
- *
129
- * The request is pre-configured with:
130
- * - Method, timeout, and request options from client defaults
131
- * - Default headers (evaluated at creation time)
132
- * - Default error and completion callbacks (if set)
133
- * - URL (either fullUrl or composed from baseUrl/origin + path)
146
+ * Creates a new typed HTTP request bound to this client.
147
+ * The request reads config (origin, timeout, headers) from this client and calls sendRequest on it when executing.
134
148
  *
135
149
  * @template API - Typed API definition
136
- * @param apiDef - API definition with method, path, and optional URL configuration
150
+ * @param apiDef - API definition with method, path, and optional timeout
137
151
  * @param data - Optional request data (used as request key identifier)
138
- * @returns Configured HttpRequest instance ready for further customization
152
+ * @returns HttpRequest instance ready for setUrlParams/setBodyAsJson and execute()
139
153
  */
140
154
  createRequest(apiDef, data) {
141
- const request = new HttpRequest(apiDef.path, data, this.shouldCompress())
142
- .setMethod(apiDef.method)
143
- .setTimeout(this.timeout)
144
- .setRequestOption(this.requestOption)
145
- .addHeaders(this.getDefaultHeaders());
146
- if (apiDef.fullUrl)
147
- request.setUrl(apiDef.fullUrl);
148
- else
149
- request.setOrigin(apiDef.baseUrl ?? this.origin).setRelativeUrl(apiDef.path);
155
+ const request = new HttpRequest(apiDef, this, data);
150
156
  if (this.defaultOnError)
151
157
  request.setOnError(this.defaultOnError);
152
158
  if (this.defaultOnComplete)
@@ -154,4 +160,3 @@ export class HttpClient_Class extends Logger {
154
160
  return request;
155
161
  }
156
162
  }
157
- export const HttpClient = new HttpClient_Class();
@@ -3,7 +3,8 @@ import { ApiError_GeneralErrorMessage, ApiErrorResponse, ResponseError } from '@
3
3
  import { AxiosRequestConfig as Axios_RequestConfig, AxiosResponse as Axios_Response } from 'axios';
4
4
  import { HttpException } from '../exceptions/HttpException.js';
5
5
  import { TS_Progress } from '../types/error-types.js';
6
- import { HttpMethod, TypedApi } from '../types/api-types.js';
6
+ import type { HttpClient } from './HttpClient.js';
7
+ import { ApiDef, HttpMethod, GeneralApi } from '../types/api-types.js';
7
8
  /**
8
9
  * Typed HTTP request with fluent builder API and comprehensive logging.
9
10
  *
@@ -20,7 +21,7 @@ import { HttpMethod, TypedApi } from '../types/api-types.js';
20
21
  *
21
22
  * @template API - Typed API definition specifying method, response, body, params, and error types
22
23
  */
23
- export declare class HttpRequest<API extends TypedApi<any, any, any, any>> extends Logger {
24
+ export declare class HttpRequest<API extends GeneralApi> extends Logger {
24
25
  key: string;
25
26
  requestData: any;
26
27
  protected origin?: string;
@@ -45,17 +46,16 @@ export declare class HttpRequest<API extends TypedApi<any, any, any, any>> exten
45
46
  private cancelController;
46
47
  protected status?: number;
47
48
  private requestOption;
49
+ private readonly client;
48
50
  /**
49
- * Creates a new HTTP request instance.
51
+ * Creates a new HTTP request bound to a client.
52
+ * Config (origin, timeout, headers) is taken from the client; execute() calls client.sendRequest().
50
53
  *
51
- * Automatically extends timeout to 5 minutes in debug mode for development.
52
- * Initializes AbortController for request cancellation support.
53
- *
54
- * @param requestKey - Identifier for this request (used in logging)
55
- * @param requestData - Optional data associated with the request
56
- * @param shouldCompress - Whether to enable compression (default: false)
54
+ * @param apiDef - API definition (method, path, optional timeout)
55
+ * @param client - HttpClient that provides config and performs the send
56
+ * @param requestData - Optional data (used as request key identifier)
57
57
  */
58
- constructor(requestKey: string, requestData?: any, shouldCompress?: boolean);
58
+ constructor(apiDef: ApiDef<API>, client: HttpClient, requestData?: any);
59
59
  resolveTypedException(exception: HttpException<any> | unknown): API['E'] | undefined;
60
60
  getRequestData(): any;
61
61
  setOrigin(origin?: string): this;
@@ -7,7 +7,7 @@
7
7
  import { _keys, asArray, BadImplementationException, exists, isErrorOfType, Logger, MimeType_json } from '@nu-art/ts-common';
8
8
  import { composeUrl } from '../utils/utils.js';
9
9
  // Axios v1+ import style
10
- import axios, { CanceledError } from 'axios';
10
+ import { CanceledError } from 'axios';
11
11
  import { HttpException } from '../exceptions/HttpException.js';
12
12
  import { HttpMethod } from '../types/api-types.js';
13
13
  /**
@@ -47,25 +47,30 @@ export class HttpRequest extends Logger {
47
47
  cancelController;
48
48
  status;
49
49
  requestOption = {};
50
+ client;
50
51
  /**
51
- * Creates a new HTTP request instance.
52
+ * Creates a new HTTP request bound to a client.
53
+ * Config (origin, timeout, headers) is taken from the client; execute() calls client.sendRequest().
52
54
  *
53
- * Automatically extends timeout to 5 minutes in debug mode for development.
54
- * Initializes AbortController for request cancellation support.
55
- *
56
- * @param requestKey - Identifier for this request (used in logging)
57
- * @param requestData - Optional data associated with the request
58
- * @param shouldCompress - Whether to enable compression (default: false)
55
+ * @param apiDef - API definition (method, path, optional timeout)
56
+ * @param client - HttpClient that provides config and performs the send
57
+ * @param requestData - Optional data (used as request key identifier)
59
58
  */
60
- constructor(requestKey, requestData, shouldCompress) {
61
- const label = `http request: ${requestKey}${requestData ? ` ${requestData}` : ''}`;
59
+ constructor(apiDef, client, requestData) {
60
+ const label = `http request: ${apiDef.path}${requestData ? ` ${requestData}` : ''}`;
62
61
  super(label);
63
- this.key = requestKey;
62
+ this.client = client;
63
+ this.key = apiDef.path;
64
64
  this.requestData = requestData;
65
65
  this.label = label;
66
- this.compress = shouldCompress === undefined ? false : shouldCompress;
66
+ this.method = apiDef.method;
67
+ this.compress = client.shouldCompress();
68
+ this.timeout = apiDef.timeout ?? client.getTimeout();
69
+ this.setRequestOption(client.getRequestOption());
70
+ this.addHeaders(client.getDefaultHeaders());
71
+ this.setOrigin(client.getOrigin()).setRelativeUrl(apiDef.path);
67
72
  this.cancelController = new AbortController();
68
- this.logVerbose('HttpRequest created', { key: requestKey, requestData, compress: this.compress, timeout: this.timeout });
73
+ this.logVerbose('HttpRequest created', { key: this.key, requestData, compress: this.compress, timeout: this.timeout });
69
74
  }
70
75
  resolveTypedException(exception) {
71
76
  if (isErrorOfType(exception, HttpException))
@@ -310,7 +315,7 @@ export class HttpRequest extends Logger {
310
315
  this.logDebug('Request options', options);
311
316
  this.logInfo(`Calling: ${this.method} - ${fullUrl}`);
312
317
  try {
313
- this.response = await axios.request(options);
318
+ this.response = await this.client.sendRequest(options);
314
319
  this.status = this.response?.status ?? 200;
315
320
  this.logVerbose('Response received', { status: this.status, headers: this.response.headers });
316
321
  }
@@ -0,0 +1,25 @@
1
+ import { ApiDef, GeneralApi } from '../types/api-types.js';
2
+ import type { ApiCallback, ApiCallContext } from './types.js';
3
+ import { ResolvableContent } from '@nu-art/ts-common';
4
+ import { HttpClient } from '../core/HttpClient.js';
5
+ /**
6
+ * Module callback factory - receives module instance and context.
7
+ */
8
+ export type ModuleCallback<Module, API extends GeneralApi> = (module: Module, ctx: ApiCallContext<API>) => void | Promise<void>;
9
+ /**
10
+ * Configuration options for ApiCaller decorator.
11
+ */
12
+ export type ApiCallerOptions<Module, API extends GeneralApi> = {
13
+ onComplete?: ModuleCallback<Module, API>;
14
+ httpClient?: ResolvableContent<HttpClient, [Module]>;
15
+ };
16
+ /** True when ApiDef uses query params (GET/DELETE); false when it uses body (POST/PUT/PATCH). */
17
+ export declare function isQueryMethod(method: string): boolean;
18
+ /**
19
+ * TC39 Stage 3 decorator for API calls. Infers body vs query from apiDef.method:
20
+ * GET/DELETE → params, setUrlParams; POST/PUT/PATCH → body, setBodyAsJson.
21
+ *
22
+ * @param _apiDef - API definition or ResolvableContent (value or getter with instance as first arg)
23
+ * @param options - Optional: onComplete, httpClient (default shared HttpClient)
24
+ */
25
+ export declare function ApiCaller<API extends GeneralApi, Module = any>(_apiDef: ResolvableContent<ApiDef<API>, [Module]>, options?: ApiCallerOptions<Module, API>): <This extends Module>(originalMethod: (this: This, payload: API["B"] | API["P"], userCallback?: ApiCallback<API>) => unknown, context: ClassMethodDecoratorContext<This>) => (this: This, payload: API["B"] | API["P"], userCallback?: ApiCallback<API>) => Promise<API["R"]>;
@@ -0,0 +1,59 @@
1
+ /*
2
+ * @nu-art/http-client - Type-safe HTTP client for Thunderstorm
3
+ * Copyright (C) 2024 Adam van der Kruk aka TacB0sS
4
+ * Licensed under the Apache License, Version 2.0
5
+ */
6
+ import { HttpMethod } from '../types/api-types.js';
7
+ import { resolveContent } from '@nu-art/ts-common';
8
+ import { HttpClient } from '../core/HttpClient.js';
9
+ /** True when ApiDef uses query params (GET/DELETE); false when it uses body (POST/PUT/PATCH). */
10
+ export function isQueryMethod(method) {
11
+ return method === HttpMethod.GET || method === HttpMethod.DELETE;
12
+ }
13
+ /**
14
+ * TC39 Stage 3 decorator for API calls. Infers body vs query from apiDef.method:
15
+ * GET/DELETE → params, setUrlParams; POST/PUT/PATCH → body, setBodyAsJson.
16
+ *
17
+ * @param _apiDef - API definition or ResolvableContent (value or getter with instance as first arg)
18
+ * @param options - Optional: onComplete, httpClient (default shared HttpClient)
19
+ */
20
+ export function ApiCaller(_apiDef, options) {
21
+ return function (originalMethod, context) {
22
+ return async function (payload, userCallback) {
23
+ await originalMethod.call(this, payload, userCallback);
24
+ const apiDef = resolveContent(_apiDef, this);
25
+ const method = apiDef.method;
26
+ const useQuery = isQueryMethod(method);
27
+ const startTime = Date.now();
28
+ const client = resolveContent(options?.httpClient, this) ?? HttpClient.default;
29
+ const request = client.createRequest(apiDef);
30
+ if (useQuery)
31
+ request.setUrlParams(payload);
32
+ else
33
+ request.setBodyAsJson(payload);
34
+ const response = await request.execute();
35
+ const axiosResponse = request.getRawResponse();
36
+ const rawResponse = {
37
+ data: axiosResponse.data,
38
+ status: axiosResponse.status,
39
+ statusText: axiosResponse.statusText,
40
+ headers: axiosResponse.headers,
41
+ config: axiosResponse.config,
42
+ };
43
+ const ctx = {
44
+ response,
45
+ statusCode: rawResponse.status,
46
+ headers: rawResponse.headers,
47
+ apiDef,
48
+ duration: Date.now() - startTime,
49
+ rawResponse,
50
+ ...(useQuery ? { params: payload } : { body: payload }),
51
+ };
52
+ if (options?.onComplete)
53
+ await options.onComplete(this, ctx);
54
+ if (userCallback)
55
+ await userCallback(ctx);
56
+ return response;
57
+ };
58
+ };
59
+ }
@@ -0,0 +1,29 @@
1
+ import type { ApiDef, GeneralApi } from '../types/api-types.js';
2
+ /**
3
+ * Raw HTTP response structure.
4
+ * Compatible with Axios response structure.
5
+ */
6
+ export type RawHttpResponse<R> = {
7
+ data: R;
8
+ status: number;
9
+ statusText: string;
10
+ headers: Record<string, string | string[] | undefined>;
11
+ config: unknown;
12
+ };
13
+ /**
14
+ * Full context object passed to callbacks after API execution.
15
+ */
16
+ export type ApiCallContext<API extends GeneralApi> = {
17
+ response: API['R'];
18
+ statusCode: number;
19
+ headers: Record<string, string | string[] | undefined>;
20
+ body?: API['B'];
21
+ params?: API['P'];
22
+ apiDef: ApiDef<API>;
23
+ duration: number;
24
+ rawResponse: RawHttpResponse<API['R']>;
25
+ };
26
+ /**
27
+ * User-provided callback after API response.
28
+ */
29
+ export type ApiCallback<API extends GeneralApi> = (ctx: ApiCallContext<API>) => void | Promise<void>;
@@ -0,0 +1,6 @@
1
+ /*
2
+ * @nu-art/http-client - Type-safe HTTP client for Thunderstorm
3
+ * Copyright (C) 2024 Adam van der Kruk aka TacB0sS
4
+ * Licensed under the Apache License, Version 2.0
5
+ */
6
+ export {};
@@ -1,79 +1,15 @@
1
1
  import { CustomException } from '@nu-art/ts-common';
2
2
  import type { HttpRequest } from '../core/HttpRequest.js';
3
- import type { ApiError_GeneralErrorMessage, ApiErrorResponse, ResponseError } from '../types/error-types.js';
3
+ import type { ApiErrorResponse, ResponseError } from '../types/error-types.js';
4
4
  /**
5
5
  * HTTP exception containing error details and the original request.
6
6
  *
7
- * Provides complete context for error handling including the request that failed,
8
- * allowing error handlers to retry, inspect request details, or perform recovery operations.
9
- *
10
7
  * @template E - Response error type
11
8
  */
12
9
  export declare class HttpException<E extends ResponseError = ResponseError> extends CustomException {
13
- /** HTTP status code */
14
10
  responseCode: number;
15
- /** Error response from server (if available) */
16
11
  errorResponse?: ApiErrorResponse<E>;
17
- /** The HTTP request that failed - provides access to method, headers, URL, body, params, etc. */
18
12
  request: HttpRequest<any>;
19
13
  constructor(responseCode: number, request: HttpRequest<any>, errorResponse?: ApiErrorResponse<E>);
20
14
  }
21
- /**
22
- * Exception class for API errors with HTTP response codes and structured error bodies.
23
- *
24
- * Used for errors that need to be returned to API clients with:
25
- * - HTTP status code
26
- * - Structured error response body
27
- * - Debug message for server-side logging
28
- *
29
- * The constructor accepts `causeOrMessage` as either a string (message) or Error (cause),
30
- * allowing flexible error construction. If both `causeOrMessage` (as Error) and `cause`
31
- * are provided, `causeOrMessage` takes precedence.
32
- *
33
- * @template Err - Type of the error body (must extend ResponseError)
34
- *
35
- * @category Exceptions
36
- *
37
- * @example
38
- * ```typescript
39
- * // With message string
40
- * throw new ApiException(404, 'Resource not found');
41
- *
42
- * // With cause Error
43
- * throw new ApiException(500, originalError);
44
- *
45
- * // With both message and cause
46
- * throw new ApiException(400, 'Invalid input', validationError);
47
- * ```
48
- */
49
- export declare class ApiException<Err extends ResponseError = ApiError_GeneralErrorMessage> extends CustomException {
50
- /** Structured error response body for API clients */
51
- readonly responseBody: ApiErrorResponse<Err>;
52
- /** HTTP status code for the error response */
53
- readonly responseCode: number;
54
- /**
55
- * Sets the error body and returns this instance for method chaining.
56
- *
57
- * @param errorBody - Error body object to include in the response
58
- * @returns This instance for chaining
59
- */
60
- readonly setErrorBody: (errorBody: Err) => this;
61
- /**
62
- * Creates a new ApiException.
63
- *
64
- * @param responseCode - HTTP status code (e.g., 404, 500)
65
- * @param causeOrMessage - Either a message string or an Error object (as cause)
66
- * @param cause - Optional additional cause Error (only used if causeOrMessage is a string)
67
- */
68
- constructor(responseCode: number, causeOrMessage?: string | Error, cause?: Error);
69
- /**
70
- * Extracts message string from causeOrMessage if it's a string.
71
- */
72
- private static getMessage;
73
- /**
74
- * Extracts cause Error from parameters.
75
- * If causeOrMessage is an Error, it's used as the cause.
76
- * Otherwise, the cause parameter is used.
77
- */
78
- private static getCause;
79
- }
15
+ export { ApiException } from '@nu-art/api-types';
@@ -3,21 +3,15 @@
3
3
  * Copyright (C) 2024 Adam van der Kruk aka TacB0sS
4
4
  * Licensed under the Apache License, Version 2.0
5
5
  */
6
- import { _logger_logException, CustomException } from '@nu-art/ts-common';
6
+ import { CustomException } from '@nu-art/ts-common';
7
7
  /**
8
8
  * HTTP exception containing error details and the original request.
9
9
  *
10
- * Provides complete context for error handling including the request that failed,
11
- * allowing error handlers to retry, inspect request details, or perform recovery operations.
12
- *
13
10
  * @template E - Response error type
14
11
  */
15
12
  export class HttpException extends CustomException {
16
- /** HTTP status code */
17
13
  responseCode;
18
- /** Error response from server (if available) */
19
14
  errorResponse;
20
- /** The HTTP request that failed - provides access to method, headers, URL, body, params, etc. */
21
15
  request;
22
16
  constructor(responseCode, request, errorResponse) {
23
17
  const url = request.getUrl();
@@ -27,73 +21,4 @@ export class HttpException extends CustomException {
27
21
  this.request = request;
28
22
  }
29
23
  }
30
- /**
31
- * Exception class for API errors with HTTP response codes and structured error bodies.
32
- *
33
- * Used for errors that need to be returned to API clients with:
34
- * - HTTP status code
35
- * - Structured error response body
36
- * - Debug message for server-side logging
37
- *
38
- * The constructor accepts `causeOrMessage` as either a string (message) or Error (cause),
39
- * allowing flexible error construction. If both `causeOrMessage` (as Error) and `cause`
40
- * are provided, `causeOrMessage` takes precedence.
41
- *
42
- * @template Err - Type of the error body (must extend ResponseError)
43
- *
44
- * @category Exceptions
45
- *
46
- * @example
47
- * ```typescript
48
- * // With message string
49
- * throw new ApiException(404, 'Resource not found');
50
- *
51
- * // With cause Error
52
- * throw new ApiException(500, originalError);
53
- *
54
- * // With both message and cause
55
- * throw new ApiException(400, 'Invalid input', validationError);
56
- * ```
57
- */
58
- export class ApiException extends CustomException {
59
- /** Structured error response body for API clients */
60
- responseBody = {};
61
- /** HTTP status code for the error response */
62
- responseCode;
63
- /**
64
- * Sets the error body and returns this instance for method chaining.
65
- *
66
- * @param errorBody - Error body object to include in the response
67
- * @returns This instance for chaining
68
- */
69
- setErrorBody = (errorBody) => {
70
- this.responseBody.error = errorBody;
71
- return this;
72
- };
73
- /**
74
- * Creates a new ApiException.
75
- *
76
- * @param responseCode - HTTP status code (e.g., 404, 500)
77
- * @param causeOrMessage - Either a message string or an Error object (as cause)
78
- * @param cause - Optional additional cause Error (only used if causeOrMessage is a string)
79
- */
80
- constructor(responseCode, causeOrMessage, cause) {
81
- super(ApiException, `${responseCode}${ApiException.getMessage(causeOrMessage)}`, ApiException.getCause(causeOrMessage, cause));
82
- this.responseCode = responseCode;
83
- this.responseBody.debugMessage = _logger_logException(this);
84
- }
85
- /**
86
- * Extracts message string from causeOrMessage if it's a string.
87
- */
88
- static getMessage(causeOrMessage) {
89
- return typeof causeOrMessage === 'string' ? `-${JSON.stringify(causeOrMessage)}` : '';
90
- }
91
- /**
92
- * Extracts cause Error from parameters.
93
- * If causeOrMessage is an Error, it's used as the cause.
94
- * Otherwise, the cause parameter is used.
95
- */
96
- static getCause(causeOrMessage, cause) {
97
- return typeof causeOrMessage != 'string' ? causeOrMessage : cause;
98
- }
99
- }
24
+ export { ApiException } from '@nu-art/api-types';
package/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  export * from './core/HttpClient.js';
2
2
  export * from './core/HttpRequest.js';
3
+ export * from './decorator/ApiCaller.js';
3
4
  export * from './types/api-types.js';
5
+ export * from './decorator/types.js';
4
6
  export * from './types/error-types.js';
5
7
  export * from './types/types.js';
6
8
  export * from './exceptions/HttpException.js';
package/index.js CHANGED
@@ -6,8 +6,10 @@
6
6
  // Core classes
7
7
  export * from './core/HttpClient.js';
8
8
  export * from './core/HttpRequest.js';
9
+ export * from './decorator/ApiCaller.js';
9
10
  // Types
10
11
  export * from './types/api-types.js';
12
+ export * from './decorator/types.js';
11
13
  export * from './types/error-types.js';
12
14
  export * from './types/types.js';
13
15
  // Exceptions
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nu-art/http-client",
3
- "version": "0.401.9",
3
+ "version": "0.500.0",
4
4
  "description": "Type-safe HTTP client with fluent API, comprehensive logging, and request/response type safety",
5
5
  "keywords": [
6
6
  "TacB0sS",
@@ -40,14 +40,16 @@
40
40
  }
41
41
  ],
42
42
  "publishConfig": {
43
- "directory": "dist"
43
+ "directory": "dist",
44
+ "linkDirectory": true
44
45
  },
45
46
  "dependencies": {
46
- "@nu-art/ts-common": "0.401.9",
47
+ "@nu-art/api-types": "{{THUNDERSTORM_VERSION}}",
48
+ "@nu-art/ts-common": "0.500.0",
47
49
  "axios": "^1.13.1"
48
50
  },
49
51
  "devDependencies": {
50
- "@nu-art/testalot": "0.401.9"
52
+ "@nu-art/testalot": "0.500.0"
51
53
  },
52
54
  "unitConfig": {
53
55
  "type": "typescript-lib"
@@ -1,142 +1 @@
1
- import type { HttpRequest } from '../core/HttpRequest.js';
2
- import type { ResponseError } from './error-types.js';
3
- import type { QueryParams } from './types.js';
4
- /**
5
- * HTTP method enumeration.
6
- *
7
- * Standard HTTP methods supported by the client.
8
- */
9
- export declare enum HttpMethod {
10
- ALL = "all",
11
- POST = "post",
12
- GET = "get",
13
- PATCH = "patch",
14
- DELETE = "delete",
15
- PUT = "put",
16
- OPTIONS = "options",
17
- HEAD = "head"
18
- }
19
- /** HTTP methods that use query parameters (no request body) */
20
- export type HttpMethod_Query = 'get' | 'delete';
21
- /** HTTP methods that use request body */
22
- export type HttpMethod_Body = 'post' | 'put' | 'patch';
23
- /** HTTP methods with no parameters or body */
24
- export type HttpMethod_Empty = 'options' | 'head';
25
- /**
26
- * Type-safe API definition with full type information for requests and responses.
27
- *
28
- * Provides compile-time type safety for HTTP API calls, ensuring request bodies,
29
- * query parameters, and responses match their declared types.
30
- *
31
- * @template M - HTTP method (string literal)
32
- * @template R - Response type
33
- * @template B - Body type (internal representation)
34
- * @template P - Query parameters type
35
- * @template IB - Input body type (what callers provide, defaults to B)
36
- * @template IP - Input params type (what callers provide, defaults to P)
37
- * @template E - Error response type
38
- *
39
- * @example
40
- * ```typescript
41
- * type MyApi = TypedApi<'post', {id: string}, {name: string}, {filter: string}, {name: string}, {filter: string}>;
42
- * // Method: POST
43
- * // Response: {id: string}
44
- * // Body: {name: string}
45
- * // Params: {filter: string}
46
- * ```
47
- */
48
- export type TypedApi<M extends string, R, B, P extends QueryParams | undefined, IB = B, IP = P, E extends ResponseError = ResponseError> = {
49
- Method: M;
50
- Response: R;
51
- Body: B;
52
- Params: P;
53
- InternalParams: IP;
54
- InternalBody: IB;
55
- Error: E;
56
- M: M;
57
- R: R;
58
- B: B;
59
- P: P;
60
- IP: IP;
61
- IB: IB;
62
- E: E;
63
- };
64
- /**
65
- * Convenience type for APIs that use request bodies (POST, PUT, PATCH).
66
- *
67
- * @template R - Response type
68
- * @template B - Body type
69
- * @template IB - Input body type (defaults to B)
70
- * @template E - Error type
71
- * @template M - HTTP method (defaults to POST)
72
- * @template P - Query params type (defaults to never)
73
- */
74
- export type BodyApi<R, B, IB = B, E extends ResponseError = ResponseError, M extends HttpMethod_Body = HttpMethod.POST, P extends QueryParams = never> = TypedApi<M, R, B, P, IB, P, E>;
75
- /**
76
- * Convenience type for APIs that use query parameters (GET, DELETE).
77
- *
78
- * @template R - Response type
79
- * @template P - Query params type
80
- * @template E - Error type
81
- * @template IP - Input params type (defaults to P)
82
- * @template M - HTTP method (defaults to GET)
83
- * @template B - Body type (defaults to never)
84
- */
85
- export type QueryApi<R, P extends QueryParams | undefined = QueryParams, E extends ResponseError = ResponseError, IP = P, M extends HttpMethod_Query = HttpMethod.GET, B = never> = TypedApi<M, R, B, P, B, IP, E>;
86
- /**
87
- * Convenience type for APIs with no parameters or body (OPTIONS, HEAD).
88
- *
89
- * @template R - Response type
90
- * @template M - HTTP method (must be OPTIONS or HEAD)
91
- * @template E - Error type
92
- * @template P - Query params type (defaults to never)
93
- * @template B - Body type (defaults to never)
94
- */
95
- export type EmptyApi<R, M extends HttpMethod_Empty, E extends ResponseError = ResponseError, P extends QueryParams = never, B = never> = TypedApi<M, R, B, P, B, P, E>;
96
- /**
97
- * API definition for creating requests.
98
- *
99
- * Specifies the HTTP method, URL configuration, and optional timeout.
100
- * Either `fullUrl` (absolute) or `baseUrl` + `path` (relative) must be provided.
101
- *
102
- * @template API - Typed API definition
103
- */
104
- export type ApiDef<API extends TypedApi<any, any, any, any, any>> = {
105
- method: API['Method'];
106
- fullUrl?: string;
107
- baseUrl?: string;
108
- path: string;
109
- timeout?: number;
110
- errors?: API['Error']['type'];
111
- };
112
- /**
113
- * Recursive API structure type for organizing APIs into namespaces.
114
- *
115
- * Allows nesting APIs in a hierarchical structure, where each level can contain
116
- * either a TypedApi or another ApiStruct (sub-namespace).
117
- */
118
- export type ApiStruct = {
119
- [k: string]: (TypedApi<any, any, any, any, any> | ApiStruct);
120
- };
121
- export type ApiDefResolver<API_Struct extends ApiStruct> = API_Struct extends TypedApi<any, any, any, any, any> ? ApiDef<API_Struct> : API_Struct extends ApiStruct ? ApiDefRouter<API_Struct> : never;
122
- export type ApiDefRouter<API_Struct extends ApiStruct> = {
123
- [P in keyof API_Struct]: ApiDefResolver<API_Struct[P]>;
124
- };
125
- export type ApiDefCaller<API_Struct extends ApiStruct> = API_Struct extends TypedApi<any, any, any, any, any> ? ApiCaller<API_Struct> : API_Struct extends ApiStruct ? ApiCallerRouter<API_Struct> : never;
126
- export type ApiCallerRouter<API_Struct extends ApiStruct> = {
127
- [P in keyof API_Struct]: ApiDefCaller<API_Struct[P]>;
128
- };
129
- export type ApiCaller_Query<API extends QueryApi<any, any, any, any, HttpMethod_Query>> = API['IP'] extends undefined ? () => HttpRequest<API> : (query: API['IP']) => HttpRequest<API>;
130
- export type ApiCaller_Body<API extends BodyApi<any, any, any, any, HttpMethod_Body>> = API['IB'] extends undefined ? () => HttpRequest<API> : (query: API['IB']) => HttpRequest<API>;
131
- export type ApiCaller_Any<API extends TypedApi<any, any, any, any, any>> = (body: API['IB'], query: API['IP']) => HttpRequest<API>;
132
- /**
133
- * Resolves the appropriate caller function type based on API method.
134
- *
135
- * Automatically selects the correct function signature:
136
- * - QueryApi → function(query?) → HttpRequest
137
- * - BodyApi → function(body?) → HttpRequest
138
- * - Other → function(body, query) → HttpRequest
139
- *
140
- * @template API - Typed API definition
141
- */
142
- export type ApiCaller<API> = API extends QueryApi<any, any, any, any, HttpMethod_Query> ? ApiCaller_Query<API> : API extends BodyApi<any, any, any, any, HttpMethod_Body> ? ApiCaller_Body<API> : API extends TypedApi<any, any, any, any, any> ? ApiCaller_Any<API> : never;
1
+ export { GeneralApi, HttpMethod, HttpMethod_Query, HttpMethod_Body, HttpMethod_Empty, TypedApi, BodyApi, QueryApi, EmptyApi, ApiDef, ApiDefCaller, ApiStruct, ApiDefResolver } from '@nu-art/api-types';
@@ -3,19 +3,4 @@
3
3
  * Copyright (C) 2024 Adam van der Kruk aka TacB0sS
4
4
  * Licensed under the Apache License, Version 2.0
5
5
  */
6
- /**
7
- * HTTP method enumeration.
8
- *
9
- * Standard HTTP methods supported by the client.
10
- */
11
- export var HttpMethod;
12
- (function (HttpMethod) {
13
- HttpMethod["ALL"] = "all";
14
- HttpMethod["POST"] = "post";
15
- HttpMethod["GET"] = "get";
16
- HttpMethod["PATCH"] = "patch";
17
- HttpMethod["DELETE"] = "delete";
18
- HttpMethod["PUT"] = "put";
19
- HttpMethod["OPTIONS"] = "options";
20
- HttpMethod["HEAD"] = "head";
21
- })(HttpMethod || (HttpMethod = {}));
6
+ export { HttpMethod } from '@nu-art/api-types';
@@ -1,42 +1,6 @@
1
- /**
2
- * Generic response error type for API error handling.
3
- *
4
- * @template K - Error type identifier (string literal)
5
- * @template Data - Error data payload
6
- */
7
- export type ResponseError<K extends string = string, Data = any> = {
8
- /** Error type identifier */
9
- type: K;
10
- /** Error data payload */
11
- data: Data;
12
- };
13
- /**
14
- * Standard error message response type.
15
- *
16
- * Used for general error messages in API responses.
17
- */
18
- export type ApiError_GeneralErrorMessage = ResponseError<'error-message', {
19
- message: string;
20
- }>;
21
- /**
22
- * API error response structure.
23
- *
24
- * Contains an optional debug message (for developers) and an optional
25
- * structured error object (for clients).
26
- *
27
- * @template E - Response error type
28
- */
29
- export type ApiErrorResponse<E extends ResponseError> = {
30
- /** Optional debug message (typically includes stack traces, internal details) */
31
- debugMessage?: string;
32
- /** Optional structured error object */
33
- error?: E;
34
- };
1
+ export type { ResponseError, ApiError_GeneralErrorMessage, ApiErrorResponse } from '@nu-art/api-types';
35
2
  /**
36
3
  * Progress event type for upload/download tracking.
37
- *
38
- * Mirrors the ProgressEvent interface for compatibility with XMLHttpRequest
39
- * and fetch progress events.
40
4
  */
41
5
  export type TS_Progress = {
42
6
  readonly lengthComputable: boolean;
package/types/types.d.ts CHANGED
@@ -1,26 +1,7 @@
1
+ export type { QueryParams, UrlQueryParams } from '@nu-art/api-types';
1
2
  /**
2
3
  * Type for route/query parameters with dynamic value support.
3
- *
4
- * Supports static values (string, number, boolean) or functions that return values.
5
- * Functions are evaluated when composing the query string, allowing dynamic
6
- * parameter generation (e.g., timestamps, IDs).
7
4
  */
8
5
  export type RouteParams = {
9
6
  [key: string]: string | number | boolean | undefined | (() => string | number);
10
7
  };
11
- /**
12
- * Static query parameters type (no functions).
13
- *
14
- * Used for API type definitions where parameters are known at compile time.
15
- */
16
- export type QueryParams = {
17
- [key: string]: string | number | boolean | undefined;
18
- };
19
- /**
20
- * URL query parameters with string values only.
21
- *
22
- * Used for URL composition where all values must be strings.
23
- */
24
- export type UrlQueryParams = {
25
- [key: string]: string | undefined;
26
- };