@byndyusoft-ui/http-client 0.1.0-alpha.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.
Files changed (91) hide show
  1. package/README.md +479 -0
  2. package/dist/cjs/adapters/FetchAdapter.cjs +218 -0
  3. package/dist/cjs/adapters/XhrAdapter.cjs +357 -0
  4. package/dist/cjs/asserts/assertNonBlankString.cjs +11 -0
  5. package/dist/cjs/asserts/assertValidAdapter.cjs +15 -0
  6. package/dist/cjs/asserts/assertValidBaseUrl.cjs +10 -0
  7. package/dist/cjs/asserts/assertValidBody.cjs +17 -0
  8. package/dist/cjs/asserts/assertValidFetchAdapterOptions.cjs +70 -0
  9. package/dist/cjs/asserts/assertValidHeader.cjs +18 -0
  10. package/dist/cjs/asserts/assertValidHeaders.cjs +17 -0
  11. package/dist/cjs/asserts/assertValidHttpClientOptions.cjs +60 -0
  12. package/dist/cjs/asserts/assertValidMethod.cjs +13 -0
  13. package/dist/cjs/asserts/assertValidParam.cjs +23 -0
  14. package/dist/cjs/asserts/assertValidParams.cjs +17 -0
  15. package/dist/cjs/asserts/assertValidRequestConfig.cjs +55 -0
  16. package/dist/cjs/asserts/assertValidResponseType.cjs +13 -0
  17. package/dist/cjs/asserts/assertValidSignal.cjs +16 -0
  18. package/dist/cjs/asserts/assertValidTimeout.cjs +12 -0
  19. package/dist/cjs/asserts/assertValidUrl.cjs +10 -0
  20. package/dist/cjs/asserts/assertValidValidateStatus.cjs +13 -0
  21. package/dist/cjs/asserts/assertValidWithCredentials.cjs +12 -0
  22. package/dist/cjs/asserts/assertValidXhrAdapterOptions.cjs +39 -0
  23. package/dist/cjs/asserts/headerValueLineBreakPattern.cjs +5 -0
  24. package/dist/cjs/asserts/isRecord.cjs +11 -0
  25. package/dist/cjs/constants/httpMethods.cjs +13 -0
  26. package/dist/cjs/constants/httpResponseTypes.cjs +11 -0
  27. package/dist/cjs/constants/httpStatusCodes.cjs +68 -0
  28. package/dist/cjs/constants/requestBuilderErrorCodes.cjs +26 -0
  29. package/dist/cjs/core/HttpClient.cjs +141 -0
  30. package/dist/cjs/core/HttpRequestBuilder.cjs +129 -0
  31. package/dist/cjs/errors/AbortError.cjs +12 -0
  32. package/dist/cjs/errors/HttpClientError.cjs +18 -0
  33. package/dist/cjs/errors/HttpResponseError.cjs +26 -0
  34. package/dist/cjs/errors/NetworkError.cjs +12 -0
  35. package/dist/cjs/errors/ParseError.cjs +20 -0
  36. package/dist/cjs/errors/RequestBuilderError.cjs +18 -0
  37. package/dist/cjs/errors/RequestPreparationError.cjs +13 -0
  38. package/dist/cjs/errors/TimeoutError.cjs +18 -0
  39. package/dist/cjs/index.cjs +45 -0
  40. package/dist/cjs/utilities/buildUrl.cjs +94 -0
  41. package/dist/cjs/utilities/getErrorMessage.cjs +8 -0
  42. package/dist/cjs/utilities/isStatusAccepted.cjs +8 -0
  43. package/dist/cjs/utilities/mergeHeaders.cjs +30 -0
  44. package/dist/cjs/utilities/mergeParams.cjs +37 -0
  45. package/dist/cjs/utilities/prepareRequestBody.cjs +43 -0
  46. package/dist/esm/adapters/FetchAdapter.js +216 -0
  47. package/dist/esm/adapters/XhrAdapter.js +355 -0
  48. package/dist/esm/asserts/assertNonBlankString.js +9 -0
  49. package/dist/esm/asserts/assertValidAdapter.js +13 -0
  50. package/dist/esm/asserts/assertValidBaseUrl.js +8 -0
  51. package/dist/esm/asserts/assertValidBody.js +15 -0
  52. package/dist/esm/asserts/assertValidFetchAdapterOptions.js +68 -0
  53. package/dist/esm/asserts/assertValidHeader.js +16 -0
  54. package/dist/esm/asserts/assertValidHeaders.js +15 -0
  55. package/dist/esm/asserts/assertValidHttpClientOptions.js +58 -0
  56. package/dist/esm/asserts/assertValidMethod.js +11 -0
  57. package/dist/esm/asserts/assertValidParam.js +21 -0
  58. package/dist/esm/asserts/assertValidParams.js +15 -0
  59. package/dist/esm/asserts/assertValidRequestConfig.js +53 -0
  60. package/dist/esm/asserts/assertValidResponseType.js +11 -0
  61. package/dist/esm/asserts/assertValidSignal.js +14 -0
  62. package/dist/esm/asserts/assertValidTimeout.js +10 -0
  63. package/dist/esm/asserts/assertValidUrl.js +8 -0
  64. package/dist/esm/asserts/assertValidValidateStatus.js +11 -0
  65. package/dist/esm/asserts/assertValidWithCredentials.js +10 -0
  66. package/dist/esm/asserts/assertValidXhrAdapterOptions.js +37 -0
  67. package/dist/esm/asserts/headerValueLineBreakPattern.js +3 -0
  68. package/dist/esm/asserts/isRecord.js +9 -0
  69. package/dist/esm/constants/httpMethods.js +11 -0
  70. package/dist/esm/constants/httpResponseTypes.js +9 -0
  71. package/dist/esm/constants/httpStatusCodes.js +66 -0
  72. package/dist/esm/constants/requestBuilderErrorCodes.js +24 -0
  73. package/dist/esm/core/HttpClient.js +139 -0
  74. package/dist/esm/core/HttpRequestBuilder.js +127 -0
  75. package/dist/esm/errors/AbortError.js +9 -0
  76. package/dist/esm/errors/HttpClientError.js +15 -0
  77. package/dist/esm/errors/HttpResponseError.js +23 -0
  78. package/dist/esm/errors/NetworkError.js +9 -0
  79. package/dist/esm/errors/ParseError.js +17 -0
  80. package/dist/esm/errors/RequestBuilderError.js +15 -0
  81. package/dist/esm/errors/RequestPreparationError.js +10 -0
  82. package/dist/esm/errors/TimeoutError.js +15 -0
  83. package/dist/esm/index.js +16 -0
  84. package/dist/esm/utilities/buildUrl.js +92 -0
  85. package/dist/esm/utilities/getErrorMessage.js +6 -0
  86. package/dist/esm/utilities/isStatusAccepted.js +6 -0
  87. package/dist/esm/utilities/mergeHeaders.js +27 -0
  88. package/dist/esm/utilities/mergeParams.js +35 -0
  89. package/dist/esm/utilities/prepareRequestBody.js +41 -0
  90. package/dist/index.d.ts +354 -0
  91. package/package.json +53 -0
@@ -0,0 +1,68 @@
1
+ import { REQUEST_BUILDER_ERROR_CODES } from '../constants/requestBuilderErrorCodes.js';
2
+ import { RequestBuilderError } from '../errors/RequestBuilderError.js';
3
+ import { isRecord } from './isRecord.js';
4
+
5
+ const FETCH_CACHE_VALUES = ['default', 'no-store', 'reload', 'no-cache', 'force-cache', 'only-if-cached'];
6
+ const FETCH_CREDENTIALS_VALUES = ['omit', 'same-origin', 'include'];
7
+ const FETCH_MODE_VALUES = ['cors', 'no-cors', 'same-origin'];
8
+ const FETCH_REDIRECT_VALUES = ['error', 'follow', 'manual'];
9
+ const FETCH_REFERRER_POLICY_VALUES = [
10
+ '',
11
+ 'no-referrer',
12
+ 'no-referrer-when-downgrade',
13
+ 'origin',
14
+ 'origin-when-cross-origin',
15
+ 'same-origin',
16
+ 'strict-origin',
17
+ 'strict-origin-when-cross-origin',
18
+ 'unsafe-url'
19
+ ];
20
+ function throwInvalidOptions(message) {
21
+ throw new RequestBuilderError(message, REQUEST_BUILDER_ERROR_CODES.INVALID_FETCH_ADAPTER_OPTIONS);
22
+ }
23
+ function assertValidEnum(value, values, name) {
24
+ if (typeof value !== 'string' || !values.includes(value)) {
25
+ throwInvalidOptions(`${name} must be a valid Fetch option`);
26
+ }
27
+ }
28
+ function assertValidString(value, name) {
29
+ if (typeof value !== 'string') {
30
+ throwInvalidOptions(`${name} must be a string`);
31
+ }
32
+ }
33
+ /** Validates options passed to the Fetch adapter constructor. */
34
+ function assertValidFetchAdapterOptions(options) {
35
+ if (!isRecord(options)) {
36
+ throwInvalidOptions('Fetch adapter options must be an object');
37
+ }
38
+ const { cache, credentials, integrity, keepalive, mode, redirect, referrer, referrerPolicy } = options;
39
+ if (cache !== undefined) {
40
+ assertValidEnum(cache, FETCH_CACHE_VALUES, 'cache');
41
+ }
42
+ if (credentials !== undefined) {
43
+ assertValidEnum(credentials, FETCH_CREDENTIALS_VALUES, 'credentials');
44
+ }
45
+ if (integrity !== undefined) {
46
+ assertValidString(integrity, 'integrity');
47
+ }
48
+ if (keepalive !== undefined && typeof keepalive !== 'boolean') {
49
+ throwInvalidOptions('keepalive must be a boolean');
50
+ }
51
+ if (mode !== undefined) {
52
+ assertValidEnum(mode, FETCH_MODE_VALUES, 'mode');
53
+ }
54
+ if (cache === 'only-if-cached' && mode !== 'same-origin') {
55
+ throwInvalidOptions('cache "only-if-cached" requires mode "same-origin"');
56
+ }
57
+ if (redirect !== undefined) {
58
+ assertValidEnum(redirect, FETCH_REDIRECT_VALUES, 'redirect');
59
+ }
60
+ if (referrer !== undefined) {
61
+ assertValidString(referrer, 'referrer');
62
+ }
63
+ if (referrerPolicy !== undefined) {
64
+ assertValidEnum(referrerPolicy, FETCH_REFERRER_POLICY_VALUES, 'referrerPolicy');
65
+ }
66
+ }
67
+
68
+ export { assertValidFetchAdapterOptions };
@@ -0,0 +1,16 @@
1
+ import { REQUEST_BUILDER_ERROR_CODES } from '../constants/requestBuilderErrorCodes.js';
2
+ import { RequestBuilderError } from '../errors/RequestBuilderError.js';
3
+ import { assertNonBlankString } from './assertNonBlankString.js';
4
+ import { HEADER_VALUE_LINE_BREAK_PATTERN } from './headerValueLineBreakPattern.js';
5
+
6
+ function assertValidHeader(key, value) {
7
+ assertNonBlankString(key, 'Header key must be a non-empty string', REQUEST_BUILDER_ERROR_CODES.INVALID_HEADER);
8
+ if (!/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/.test(key)) {
9
+ throw new RequestBuilderError('Header key contains invalid characters', REQUEST_BUILDER_ERROR_CODES.INVALID_HEADER);
10
+ }
11
+ if (typeof value !== 'string' || HEADER_VALUE_LINE_BREAK_PATTERN.test(value)) {
12
+ throw new RequestBuilderError('Header value must be a string without line breaks', REQUEST_BUILDER_ERROR_CODES.INVALID_HEADER);
13
+ }
14
+ }
15
+
16
+ export { assertValidHeader };
@@ -0,0 +1,15 @@
1
+ import { REQUEST_BUILDER_ERROR_CODES } from '../constants/requestBuilderErrorCodes.js';
2
+ import { RequestBuilderError } from '../errors/RequestBuilderError.js';
3
+ import { assertValidHeader } from './assertValidHeader.js';
4
+ import { isRecord } from './isRecord.js';
5
+
6
+ function assertValidHeaders(headers) {
7
+ if (!isRecord(headers)) {
8
+ throw new RequestBuilderError('Headers must be an object', REQUEST_BUILDER_ERROR_CODES.INVALID_HEADERS);
9
+ }
10
+ for (const [key, value] of Object.entries(headers)) {
11
+ assertValidHeader(key, value);
12
+ }
13
+ }
14
+
15
+ export { assertValidHeaders };
@@ -0,0 +1,58 @@
1
+ import { REQUEST_BUILDER_ERROR_CODES } from '../constants/requestBuilderErrorCodes.js';
2
+ import { RequestBuilderError } from '../errors/RequestBuilderError.js';
3
+ import { assertValidAdapter } from './assertValidAdapter.js';
4
+ import { assertValidBaseUrl } from './assertValidBaseUrl.js';
5
+ import { assertValidHeaders } from './assertValidHeaders.js';
6
+ import { assertValidParams } from './assertValidParams.js';
7
+ import { assertValidTimeout } from './assertValidTimeout.js';
8
+ import { assertValidWithCredentials } from './assertValidWithCredentials.js';
9
+ import { assertValidValidateStatus } from './assertValidValidateStatus.js';
10
+ import { isRecord } from './isRecord.js';
11
+
12
+ function assertValidHook(hook, name) {
13
+ if (typeof hook !== 'function') {
14
+ throw new RequestBuilderError(`${name} must be a function`, REQUEST_BUILDER_ERROR_CODES.INVALID_HOOK);
15
+ }
16
+ }
17
+ /** Validates options passed to the HTTP client constructor. */
18
+ function assertValidHttpClientOptions(options) {
19
+ if (!isRecord(options)) {
20
+ throw new RequestBuilderError('Client options must be an object', REQUEST_BUILDER_ERROR_CODES.INVALID_CONFIG);
21
+ }
22
+ const { adapter, baseUrl, headers, params, timeout, validateStatus, withCredentials, onRequest, onRequestError, onResponse, onResponseError } = options;
23
+ if (adapter !== undefined) {
24
+ assertValidAdapter(adapter);
25
+ }
26
+ if (baseUrl !== undefined) {
27
+ assertValidBaseUrl(baseUrl);
28
+ }
29
+ if (headers !== undefined) {
30
+ assertValidHeaders(headers);
31
+ }
32
+ if (params !== undefined) {
33
+ assertValidParams(params);
34
+ }
35
+ if (timeout !== undefined) {
36
+ assertValidTimeout(timeout);
37
+ }
38
+ if (validateStatus !== undefined) {
39
+ assertValidValidateStatus(validateStatus);
40
+ }
41
+ if (withCredentials !== undefined) {
42
+ assertValidWithCredentials(withCredentials);
43
+ }
44
+ if (onRequest !== undefined) {
45
+ assertValidHook(onRequest, 'onRequest');
46
+ }
47
+ if (onRequestError !== undefined) {
48
+ assertValidHook(onRequestError, 'onRequestError');
49
+ }
50
+ if (onResponse !== undefined) {
51
+ assertValidHook(onResponse, 'onResponse');
52
+ }
53
+ if (onResponseError !== undefined) {
54
+ assertValidHook(onResponseError, 'onResponseError');
55
+ }
56
+ }
57
+
58
+ export { assertValidHttpClientOptions };
@@ -0,0 +1,11 @@
1
+ import { HTTP_METHODS } from '../constants/httpMethods.js';
2
+ import { REQUEST_BUILDER_ERROR_CODES } from '../constants/requestBuilderErrorCodes.js';
3
+ import { RequestBuilderError } from '../errors/RequestBuilderError.js';
4
+
5
+ function assertValidMethod(method) {
6
+ if (!Object.values(HTTP_METHODS).includes(method)) {
7
+ throw new RequestBuilderError('Method must be a valid HTTP method', REQUEST_BUILDER_ERROR_CODES.INVALID_METHOD);
8
+ }
9
+ }
10
+
11
+ export { assertValidMethod };
@@ -0,0 +1,21 @@
1
+ import { REQUEST_BUILDER_ERROR_CODES } from '../constants/requestBuilderErrorCodes.js';
2
+ import { RequestBuilderError } from '../errors/RequestBuilderError.js';
3
+ import { assertNonBlankString } from './assertNonBlankString.js';
4
+
5
+ function isValidParamPrimitive(value) {
6
+ return (typeof value === 'string' || typeof value === 'boolean' || (typeof value === 'number' && Number.isFinite(value)));
7
+ }
8
+ function isValidParamValue(value) {
9
+ if (value === null || value === undefined || isValidParamPrimitive(value)) {
10
+ return true;
11
+ }
12
+ return (Array.isArray(value) && value.every(item => item === null || item === undefined || isValidParamPrimitive(item)));
13
+ }
14
+ function assertValidParam(key, value) {
15
+ assertNonBlankString(key, 'Param key must be a non-empty string', REQUEST_BUILDER_ERROR_CODES.INVALID_PARAM);
16
+ if (!isValidParamValue(value)) {
17
+ throw new RequestBuilderError('Param value must be a string, finite number, boolean, null, undefined, or an array of these values', REQUEST_BUILDER_ERROR_CODES.INVALID_PARAM);
18
+ }
19
+ }
20
+
21
+ export { assertValidParam };
@@ -0,0 +1,15 @@
1
+ import { REQUEST_BUILDER_ERROR_CODES } from '../constants/requestBuilderErrorCodes.js';
2
+ import { RequestBuilderError } from '../errors/RequestBuilderError.js';
3
+ import { assertValidParam } from './assertValidParam.js';
4
+ import { isRecord } from './isRecord.js';
5
+
6
+ function assertValidParams(params) {
7
+ if (!isRecord(params)) {
8
+ throw new RequestBuilderError('Params must be an object', REQUEST_BUILDER_ERROR_CODES.INVALID_PARAMS);
9
+ }
10
+ for (const [key, value] of Object.entries(params)) {
11
+ assertValidParam(key, value);
12
+ }
13
+ }
14
+
15
+ export { assertValidParams };
@@ -0,0 +1,53 @@
1
+ import { REQUEST_BUILDER_ERROR_CODES } from '../constants/requestBuilderErrorCodes.js';
2
+ import { RequestBuilderError } from '../errors/RequestBuilderError.js';
3
+ import { assertValidBody } from './assertValidBody.js';
4
+ import { assertValidBaseUrl } from './assertValidBaseUrl.js';
5
+ import { assertValidHeaders } from './assertValidHeaders.js';
6
+ import { assertValidMethod } from './assertValidMethod.js';
7
+ import { assertValidParams } from './assertValidParams.js';
8
+ import { assertValidResponseType } from './assertValidResponseType.js';
9
+ import { assertValidSignal } from './assertValidSignal.js';
10
+ import { assertValidTimeout } from './assertValidTimeout.js';
11
+ import { assertValidUrl } from './assertValidUrl.js';
12
+ import { assertValidWithCredentials } from './assertValidWithCredentials.js';
13
+ import { assertValidValidateStatus } from './assertValidValidateStatus.js';
14
+ import { isRecord } from './isRecord.js';
15
+
16
+ /** Validates a complete request config, including values returned by request hooks. */
17
+ function assertValidRequestConfig(config) {
18
+ if (!isRecord(config)) {
19
+ throw new RequestBuilderError('Request config must be an object', REQUEST_BUILDER_ERROR_CODES.INVALID_CONFIG);
20
+ }
21
+ const { method, url, baseUrl, headers, params, data, signal, timeout, validateStatus, withCredentials, responseType } = config;
22
+ assertValidMethod(method);
23
+ assertValidUrl(url);
24
+ if (baseUrl !== undefined) {
25
+ assertValidBaseUrl(baseUrl);
26
+ }
27
+ if (headers !== undefined) {
28
+ assertValidHeaders(headers);
29
+ }
30
+ if (params !== undefined) {
31
+ assertValidParams(params);
32
+ }
33
+ if (signal !== undefined) {
34
+ assertValidSignal(signal);
35
+ }
36
+ if (timeout !== undefined) {
37
+ assertValidTimeout(timeout);
38
+ }
39
+ if (validateStatus !== undefined) {
40
+ assertValidValidateStatus(validateStatus);
41
+ }
42
+ if (withCredentials !== undefined) {
43
+ assertValidWithCredentials(withCredentials);
44
+ }
45
+ if (responseType !== undefined) {
46
+ assertValidResponseType(responseType);
47
+ }
48
+ if (Object.prototype.hasOwnProperty.call(config, 'data')) {
49
+ assertValidBody(method, data);
50
+ }
51
+ }
52
+
53
+ export { assertValidRequestConfig };
@@ -0,0 +1,11 @@
1
+ import { HTTP_RESPONSE_TYPES } from '../constants/httpResponseTypes.js';
2
+ import { REQUEST_BUILDER_ERROR_CODES } from '../constants/requestBuilderErrorCodes.js';
3
+ import { RequestBuilderError } from '../errors/RequestBuilderError.js';
4
+
5
+ function assertValidResponseType(responseType) {
6
+ if (!Object.values(HTTP_RESPONSE_TYPES).includes(responseType)) {
7
+ throw new RequestBuilderError('Response type must be a valid HTTP response type', REQUEST_BUILDER_ERROR_CODES.INVALID_RESPONSE_TYPE);
8
+ }
9
+ }
10
+
11
+ export { assertValidResponseType };
@@ -0,0 +1,14 @@
1
+ import { REQUEST_BUILDER_ERROR_CODES } from '../constants/requestBuilderErrorCodes.js';
2
+ import { RequestBuilderError } from '../errors/RequestBuilderError.js';
3
+
4
+ function assertValidSignal(signal) {
5
+ if (signal === null ||
6
+ typeof signal !== 'object' ||
7
+ typeof signal.aborted !== 'boolean' ||
8
+ typeof signal.addEventListener !== 'function' ||
9
+ typeof signal.removeEventListener !== 'function') {
10
+ throw new RequestBuilderError('Signal must be an AbortSignal', REQUEST_BUILDER_ERROR_CODES.INVALID_SIGNAL);
11
+ }
12
+ }
13
+
14
+ export { assertValidSignal };
@@ -0,0 +1,10 @@
1
+ import { REQUEST_BUILDER_ERROR_CODES } from '../constants/requestBuilderErrorCodes.js';
2
+ import { RequestBuilderError } from '../errors/RequestBuilderError.js';
3
+
4
+ function assertValidTimeout(timeout) {
5
+ if (typeof timeout !== 'number' || !Number.isFinite(timeout) || timeout < 0) {
6
+ throw new RequestBuilderError('Timeout must be a finite non-negative number', REQUEST_BUILDER_ERROR_CODES.INVALID_TIMEOUT);
7
+ }
8
+ }
9
+
10
+ export { assertValidTimeout };
@@ -0,0 +1,8 @@
1
+ import { REQUEST_BUILDER_ERROR_CODES } from '../constants/requestBuilderErrorCodes.js';
2
+ import { assertNonBlankString } from './assertNonBlankString.js';
3
+
4
+ function assertValidUrl(url) {
5
+ assertNonBlankString(url, 'URL must be a non-empty string', REQUEST_BUILDER_ERROR_CODES.INVALID_URL);
6
+ }
7
+
8
+ export { assertValidUrl };
@@ -0,0 +1,11 @@
1
+ import { REQUEST_BUILDER_ERROR_CODES } from '../constants/requestBuilderErrorCodes.js';
2
+ import { RequestBuilderError } from '../errors/RequestBuilderError.js';
3
+
4
+ /** Validates a custom HTTP response status predicate. */
5
+ function assertValidValidateStatus(value) {
6
+ if (typeof value !== 'function') {
7
+ throw new RequestBuilderError('validateStatus must be a function', REQUEST_BUILDER_ERROR_CODES.INVALID_VALIDATE_STATUS);
8
+ }
9
+ }
10
+
11
+ export { assertValidValidateStatus };
@@ -0,0 +1,10 @@
1
+ import { REQUEST_BUILDER_ERROR_CODES } from '../constants/requestBuilderErrorCodes.js';
2
+ import { RequestBuilderError } from '../errors/RequestBuilderError.js';
3
+
4
+ function assertValidWithCredentials(withCredentials) {
5
+ if (typeof withCredentials !== 'boolean') {
6
+ throw new RequestBuilderError('withCredentials must be a boolean', REQUEST_BUILDER_ERROR_CODES.INVALID_WITH_CREDENTIALS);
7
+ }
8
+ }
9
+
10
+ export { assertValidWithCredentials };
@@ -0,0 +1,37 @@
1
+ import { REQUEST_BUILDER_ERROR_CODES } from '../constants/requestBuilderErrorCodes.js';
2
+ import { RequestBuilderError } from '../errors/RequestBuilderError.js';
3
+ import { assertValidResponseType } from './assertValidResponseType.js';
4
+ import { assertValidTimeout } from './assertValidTimeout.js';
5
+ import { assertValidWithCredentials } from './assertValidWithCredentials.js';
6
+ import { isRecord } from './isRecord.js';
7
+
8
+ function throwInvalidOptions(message) {
9
+ throw new RequestBuilderError(message, REQUEST_BUILDER_ERROR_CODES.INVALID_XHR_ADAPTER_OPTIONS);
10
+ }
11
+ /** Validates options passed to the XMLHttpRequest adapter constructor. */
12
+ function assertValidXhrAdapterOptions(options) {
13
+ if (!isRecord(options)) {
14
+ throwInvalidOptions('XHR adapter options must be an object');
15
+ }
16
+ const { mimeType, responseType, timeout, withCredentials, onDownloadProgress, onUploadProgress } = options;
17
+ if (mimeType !== undefined && (typeof mimeType !== 'string' || mimeType.trim().length === 0)) {
18
+ throwInvalidOptions('mimeType must be a non-empty string');
19
+ }
20
+ if (responseType !== undefined) {
21
+ assertValidResponseType(responseType);
22
+ }
23
+ if (timeout !== undefined) {
24
+ assertValidTimeout(timeout);
25
+ }
26
+ if (withCredentials !== undefined) {
27
+ assertValidWithCredentials(withCredentials);
28
+ }
29
+ if (onDownloadProgress !== undefined && typeof onDownloadProgress !== 'function') {
30
+ throwInvalidOptions('onDownloadProgress must be a function');
31
+ }
32
+ if (onUploadProgress !== undefined && typeof onUploadProgress !== 'function') {
33
+ throwInvalidOptions('onUploadProgress must be a function');
34
+ }
35
+ }
36
+
37
+ export { assertValidXhrAdapterOptions };
@@ -0,0 +1,3 @@
1
+ const HEADER_VALUE_LINE_BREAK_PATTERN = /[\r\n]/;
2
+
3
+ export { HEADER_VALUE_LINE_BREAK_PATTERN };
@@ -0,0 +1,9 @@
1
+ function isRecord(value) {
2
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
3
+ return false;
4
+ }
5
+ const prototype = Object.getPrototypeOf(value);
6
+ return prototype === Object.prototype || prototype === null;
7
+ }
8
+
9
+ export { isRecord };
@@ -0,0 +1,11 @@
1
+ const HTTP_METHODS = {
2
+ GET: 'GET',
3
+ HEAD: 'HEAD',
4
+ POST: 'POST',
5
+ PUT: 'PUT',
6
+ DELETE: 'DELETE',
7
+ OPTIONS: 'OPTIONS',
8
+ PATCH: 'PATCH'
9
+ };
10
+
11
+ export { HTTP_METHODS };
@@ -0,0 +1,9 @@
1
+ const HTTP_RESPONSE_TYPES = {
2
+ ARRAY_BUFFER: 'arrayBuffer',
3
+ BLOB: 'blob',
4
+ JSON: 'json',
5
+ STREAM: 'stream',
6
+ TEXT: 'text'
7
+ };
8
+
9
+ export { HTTP_RESPONSE_TYPES };
@@ -0,0 +1,66 @@
1
+ const HTTP_STATUS_CODES = {
2
+ CONTINUE: 100,
3
+ SWITCHING_PROTOCOLS: 101,
4
+ PROCESSING: 102,
5
+ EARLY_HINTS: 103,
6
+ OK: 200,
7
+ CREATED: 201,
8
+ ACCEPTED: 202,
9
+ NON_AUTHORITATIVE_INFORMATION: 203,
10
+ NO_CONTENT: 204,
11
+ RESET_CONTENT: 205,
12
+ PARTIAL_CONTENT: 206,
13
+ MULTI_STATUS: 207,
14
+ ALREADY_REPORTED: 208,
15
+ IM_USED: 226,
16
+ MULTIPLE_CHOICES: 300,
17
+ MOVED_PERMANENTLY: 301,
18
+ FOUND: 302,
19
+ SEE_OTHER: 303,
20
+ NOT_MODIFIED: 304,
21
+ USE_PROXY: 305,
22
+ TEMPORARY_REDIRECT: 307,
23
+ PERMANENT_REDIRECT: 308,
24
+ BAD_REQUEST: 400,
25
+ UNAUTHORIZED: 401,
26
+ PAYMENT_REQUIRED: 402,
27
+ FORBIDDEN: 403,
28
+ NOT_FOUND: 404,
29
+ METHOD_NOT_ALLOWED: 405,
30
+ NOT_ACCEPTABLE: 406,
31
+ PROXY_AUTHENTICATION_REQUIRED: 407,
32
+ REQUEST_TIMEOUT: 408,
33
+ CONFLICT: 409,
34
+ GONE: 410,
35
+ LENGTH_REQUIRED: 411,
36
+ PRECONDITION_FAILED: 412,
37
+ PAYLOAD_TOO_LARGE: 413,
38
+ URI_TOO_LONG: 414,
39
+ UNSUPPORTED_MEDIA_TYPE: 415,
40
+ RANGE_NOT_SATISFIABLE: 416,
41
+ EXPECTATION_FAILED: 417,
42
+ IM_A_TEAPOT: 418,
43
+ MISDIRECTED_REQUEST: 421,
44
+ UNPROCESSABLE_ENTITY: 422,
45
+ LOCKED: 423,
46
+ FAILED_DEPENDENCY: 424,
47
+ TOO_EARLY: 425,
48
+ UPGRADE_REQUIRED: 426,
49
+ PRECONDITION_REQUIRED: 428,
50
+ TOO_MANY_REQUESTS: 429,
51
+ REQUEST_HEADER_FIELDS_TOO_LARGE: 431,
52
+ UNAVAILABLE_FOR_LEGAL_REASONS: 451,
53
+ INTERNAL_SERVER_ERROR: 500,
54
+ NOT_IMPLEMENTED: 501,
55
+ BAD_GATEWAY: 502,
56
+ SERVICE_UNAVAILABLE: 503,
57
+ GATEWAY_TIMEOUT: 504,
58
+ HTTP_VERSION_NOT_SUPPORTED: 505,
59
+ VARIANT_ALSO_NEGOTIATES: 506,
60
+ INSUFFICIENT_STORAGE: 507,
61
+ LOOP_DETECTED: 508,
62
+ NOT_EXTENDED: 510,
63
+ NETWORK_AUTHENTICATION_REQUIRED: 511
64
+ };
65
+
66
+ export { HTTP_STATUS_CODES };
@@ -0,0 +1,24 @@
1
+ const REQUEST_BUILDER_ERROR_CODES = {
2
+ INVALID_BASE_URL: 'INVALID_BASE_URL',
3
+ INVALID_BEARER_TOKEN: 'INVALID_BEARER_TOKEN',
4
+ INVALID_ADAPTER: 'INVALID_ADAPTER',
5
+ INVALID_BODY: 'INVALID_BODY',
6
+ INVALID_CONFIG: 'INVALID_CONFIG',
7
+ INVALID_EXECUTOR: 'INVALID_EXECUTOR',
8
+ INVALID_FETCH_ADAPTER_OPTIONS: 'INVALID_FETCH_ADAPTER_OPTIONS',
9
+ INVALID_HEADER: 'INVALID_HEADER',
10
+ INVALID_HEADERS: 'INVALID_HEADERS',
11
+ INVALID_HOOK: 'INVALID_HOOK',
12
+ INVALID_METHOD: 'INVALID_METHOD',
13
+ INVALID_PARAM: 'INVALID_PARAM',
14
+ INVALID_PARAMS: 'INVALID_PARAMS',
15
+ INVALID_RESPONSE_TYPE: 'INVALID_RESPONSE_TYPE',
16
+ INVALID_SIGNAL: 'INVALID_SIGNAL',
17
+ INVALID_TIMEOUT: 'INVALID_TIMEOUT',
18
+ INVALID_URL: 'INVALID_URL',
19
+ INVALID_VALIDATE_STATUS: 'INVALID_VALIDATE_STATUS',
20
+ INVALID_WITH_CREDENTIALS: 'INVALID_WITH_CREDENTIALS',
21
+ INVALID_XHR_ADAPTER_OPTIONS: 'INVALID_XHR_ADAPTER_OPTIONS'
22
+ };
23
+
24
+ export { REQUEST_BUILDER_ERROR_CODES };
@@ -0,0 +1,139 @@
1
+ import { HTTP_METHODS } from '../constants/httpMethods.js';
2
+ import { assertValidAdapter } from '../asserts/assertValidAdapter.js';
3
+ import { assertValidHttpClientOptions } from '../asserts/assertValidHttpClientOptions.js';
4
+ import { assertValidRequestConfig } from '../asserts/assertValidRequestConfig.js';
5
+ import { FetchAdapter } from '../adapters/FetchAdapter.js';
6
+ import { mergeHeaders } from '../utilities/mergeHeaders.js';
7
+ import { mergeParams } from '../utilities/mergeParams.js';
8
+ import { HttpRequestBuilder } from './HttpRequestBuilder.js';
9
+
10
+ class HttpClient {
11
+ adapter;
12
+ defaultConfig;
13
+ onRequestHook;
14
+ onRequestErrorHook;
15
+ onResponseHook;
16
+ onResponseErrorHook;
17
+ constructor(options) {
18
+ assertValidHttpClientOptions(options);
19
+ this.adapter = options.adapter ?? new FetchAdapter();
20
+ this.onRequestHook = options.onRequest;
21
+ this.onRequestErrorHook = options.onRequestError;
22
+ this.onResponseHook = options.onResponse;
23
+ this.onResponseErrorHook = options.onResponseError;
24
+ this.defaultConfig = {
25
+ baseUrl: options.baseUrl,
26
+ headers: mergeHeaders(options.headers),
27
+ params: mergeParams(options.params),
28
+ timeout: options.timeout,
29
+ validateStatus: options.validateStatus,
30
+ withCredentials: options.withCredentials
31
+ };
32
+ }
33
+ /** Creates an independent client with the current defaults and hooks, using the provided adapter. */
34
+ withAdapter(adapter) {
35
+ assertValidAdapter(adapter);
36
+ return new HttpClient({
37
+ ...this.defaultConfig,
38
+ adapter,
39
+ onRequest: this.onRequestHook,
40
+ onRequestError: this.onRequestErrorHook,
41
+ onResponse: this.onResponseHook,
42
+ onResponseError: this.onResponseErrorHook
43
+ });
44
+ }
45
+ /** Sets the onRequest hook, replacing the hook configured through constructor options. Returns this client for chaining. */
46
+ onRequest(hook) {
47
+ this.onRequestHook = hook;
48
+ return this;
49
+ }
50
+ /** Sets the onRequestError hook, replacing the hook configured through constructor options. Returns this client for chaining. */
51
+ onRequestError(hook) {
52
+ this.onRequestErrorHook = hook;
53
+ return this;
54
+ }
55
+ /** Sets the onResponse hook, replacing the hook configured through constructor options. Returns this client for chaining. */
56
+ onResponse(hook) {
57
+ this.onResponseHook = hook;
58
+ return this;
59
+ }
60
+ /** Sets the onResponseError hook, replacing the hook configured through constructor options. Returns this client for chaining. */
61
+ onResponseError(hook) {
62
+ this.onResponseErrorHook = hook;
63
+ return this;
64
+ }
65
+ request(method, url) {
66
+ const executor = (config) => this.executeRequest(config);
67
+ return new HttpRequestBuilder(executor, method, url);
68
+ }
69
+ get(url) {
70
+ return this.request(HTTP_METHODS.GET, url);
71
+ }
72
+ head(url) {
73
+ return this.request(HTTP_METHODS.HEAD, url);
74
+ }
75
+ post(url) {
76
+ return this.request(HTTP_METHODS.POST, url);
77
+ }
78
+ put(url) {
79
+ return this.request(HTTP_METHODS.PUT, url);
80
+ }
81
+ delete(url) {
82
+ return this.request(HTTP_METHODS.DELETE, url);
83
+ }
84
+ options(url) {
85
+ return this.request(HTTP_METHODS.OPTIONS, url);
86
+ }
87
+ patch(url) {
88
+ return this.request(HTTP_METHODS.PATCH, url);
89
+ }
90
+ async executeRequest(config) {
91
+ const { onRequestHook: onRequest, onRequestErrorHook: onRequestError, onResponseHook: onResponse, onResponseErrorHook: onResponseError } = this;
92
+ let mergedConfig = {
93
+ ...config,
94
+ baseUrl: config.baseUrl ?? this.defaultConfig.baseUrl,
95
+ timeout: config.timeout ?? this.defaultConfig.timeout,
96
+ validateStatus: config.validateStatus ?? this.defaultConfig.validateStatus,
97
+ withCredentials: config.withCredentials ?? this.defaultConfig.withCredentials,
98
+ headers: mergeHeaders(this.defaultConfig.headers, config.headers),
99
+ params: mergeParams(this.defaultConfig.params, config.params)
100
+ };
101
+ if (onRequest !== undefined) {
102
+ try {
103
+ const requestConfig = await onRequest(mergedConfig);
104
+ assertValidRequestConfig(requestConfig);
105
+ mergedConfig = requestConfig;
106
+ }
107
+ catch (error) {
108
+ if (onRequestError === undefined) {
109
+ throw error;
110
+ }
111
+ const recoveredConfig = await onRequestError(error);
112
+ if (recoveredConfig === undefined) {
113
+ throw error;
114
+ }
115
+ assertValidRequestConfig(recoveredConfig);
116
+ mergedConfig = recoveredConfig;
117
+ }
118
+ }
119
+ try {
120
+ let response = await this.adapter.request(mergedConfig);
121
+ if (onResponse !== undefined) {
122
+ response = await onResponse(response);
123
+ }
124
+ return response;
125
+ }
126
+ catch (error) {
127
+ if (onResponseError === undefined) {
128
+ throw error;
129
+ }
130
+ const recoveredResponse = await onResponseError(error);
131
+ if (recoveredResponse === undefined) {
132
+ throw error;
133
+ }
134
+ return recoveredResponse;
135
+ }
136
+ }
137
+ }
138
+
139
+ export { HttpClient };