create-request 1.3.0 → 1.4.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/README.md +59 -17
- package/dist/library/BaseRequest.d.ts +225 -22
- package/dist/library/index.cjs +320 -62
- package/dist/library/index.cjs.map +1 -1
- package/dist/library/index.d.ts +1 -1
- package/dist/library/index.esm.js +320 -62
- package/dist/library/index.esm.js.map +1 -1
- package/dist/library/index.esm.min.js +1 -1
- package/dist/library/index.esm.min.js.map +1 -1
- package/dist/library/index.min.cjs +1 -1
- package/dist/library/index.min.cjs.map +1 -1
- package/dist/library/types.d.ts +26 -2
- package/package.json +3 -2
package/dist/library/index.cjs
CHANGED
|
@@ -96,6 +96,12 @@ var CacheMode;
|
|
|
96
96
|
})(CacheMode || (CacheMode = {}));
|
|
97
97
|
|
|
98
98
|
class RequestError extends Error {
|
|
99
|
+
status;
|
|
100
|
+
response;
|
|
101
|
+
url;
|
|
102
|
+
method;
|
|
103
|
+
isTimeout;
|
|
104
|
+
isAborted;
|
|
99
105
|
constructor(message, url, method, options = {}) {
|
|
100
106
|
super(message);
|
|
101
107
|
this.name = "RequestError";
|
|
@@ -194,6 +200,11 @@ class RequestError extends Error {
|
|
|
194
200
|
* Wrapper for HTTP responses with methods to transform the response data
|
|
195
201
|
*/
|
|
196
202
|
class ResponseWrapper {
|
|
203
|
+
response;
|
|
204
|
+
url;
|
|
205
|
+
method;
|
|
206
|
+
// GraphQL-specific options
|
|
207
|
+
graphQLOptions;
|
|
197
208
|
constructor(response, url, method, graphQLOptions) {
|
|
198
209
|
this.response = response;
|
|
199
210
|
this.url = url;
|
|
@@ -226,8 +237,7 @@ class ResponseWrapper {
|
|
|
226
237
|
* @throws RequestError if GraphQL response contains errors and throwOnError is enabled
|
|
227
238
|
*/
|
|
228
239
|
checkGraphQLErrors(data) {
|
|
229
|
-
|
|
230
|
-
if (!((_a = this.graphQLOptions) === null || _a === void 0 ? void 0 : _a.throwOnError) || typeof data !== "object" || data === null)
|
|
240
|
+
if (!this.graphQLOptions?.throwOnError || typeof data !== "object" || data === null)
|
|
231
241
|
return;
|
|
232
242
|
const responseData = data;
|
|
233
243
|
if (!Array.isArray(responseData.errors) || responseData.errors.length === 0)
|
|
@@ -488,7 +498,7 @@ class CsrfUtils {
|
|
|
488
498
|
return null;
|
|
489
499
|
}
|
|
490
500
|
const meta = document.querySelector(`meta[name="${metaName}"]`);
|
|
491
|
-
return
|
|
501
|
+
return meta?.getAttribute("content") || null;
|
|
492
502
|
}
|
|
493
503
|
/**
|
|
494
504
|
* Extracts CSRF token from a cookie
|
|
@@ -545,20 +555,20 @@ class CsrfUtils {
|
|
|
545
555
|
* Global configuration for create-request
|
|
546
556
|
*/
|
|
547
557
|
class Config {
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
}
|
|
558
|
+
static instance;
|
|
559
|
+
// CSRF configuration
|
|
560
|
+
csrfHeaderName = "X-CSRF-Token";
|
|
561
|
+
xsrfCookieName = "XSRF-TOKEN";
|
|
562
|
+
xsrfHeaderName = "X-XSRF-TOKEN";
|
|
563
|
+
csrfToken = null;
|
|
564
|
+
enableAutoXsrf = true;
|
|
565
|
+
enableAntiCsrf = true; // X-Requested-With header
|
|
566
|
+
// Interceptor configuration
|
|
567
|
+
requestInterceptors = [];
|
|
568
|
+
responseInterceptors = [];
|
|
569
|
+
errorInterceptors = [];
|
|
570
|
+
nextInterceptorId = 1;
|
|
571
|
+
constructor() { }
|
|
562
572
|
/**
|
|
563
573
|
* Get the singleton instance of the Config class
|
|
564
574
|
*
|
|
@@ -841,16 +851,18 @@ class Config {
|
|
|
841
851
|
* Provides the core request building and execution capabilities.
|
|
842
852
|
*/
|
|
843
853
|
class BaseRequest {
|
|
854
|
+
url;
|
|
855
|
+
requestOptions = {
|
|
856
|
+
headers: {},
|
|
857
|
+
};
|
|
858
|
+
abortController;
|
|
859
|
+
queryParams = new URLSearchParams();
|
|
860
|
+
autoApplyCsrfProtection = true;
|
|
861
|
+
// Per-request interceptors
|
|
862
|
+
requestInterceptors = [];
|
|
863
|
+
responseInterceptors = [];
|
|
864
|
+
errorInterceptors = [];
|
|
844
865
|
constructor(url) {
|
|
845
|
-
this.requestOptions = {
|
|
846
|
-
headers: {},
|
|
847
|
-
};
|
|
848
|
-
this.queryParams = new URLSearchParams();
|
|
849
|
-
this.autoApplyCsrfProtection = true;
|
|
850
|
-
// Per-request interceptors
|
|
851
|
-
this.requestInterceptors = [];
|
|
852
|
-
this.responseInterceptors = [];
|
|
853
|
-
this.errorInterceptors = [];
|
|
854
866
|
this.url = url;
|
|
855
867
|
}
|
|
856
868
|
/**
|
|
@@ -881,7 +893,7 @@ class BaseRequest {
|
|
|
881
893
|
return Object.assign(callable, fluent);
|
|
882
894
|
}
|
|
883
895
|
validateUrl(url) {
|
|
884
|
-
if (!
|
|
896
|
+
if (!url?.trim())
|
|
885
897
|
throw new RequestError("URL cannot be empty", url, this.method);
|
|
886
898
|
if (url.includes("\0") || url.includes("\r") || url.includes("\n")) {
|
|
887
899
|
throw new RequestError("Invalid URL (control chars)", url, this.method);
|
|
@@ -891,7 +903,7 @@ class BaseRequest {
|
|
|
891
903
|
try {
|
|
892
904
|
new URL(trimmed);
|
|
893
905
|
}
|
|
894
|
-
catch
|
|
906
|
+
catch {
|
|
895
907
|
throw new RequestError(`Invalid URL: ${trimmed}`, trimmed, this.method);
|
|
896
908
|
}
|
|
897
909
|
}
|
|
@@ -959,17 +971,60 @@ class BaseRequest {
|
|
|
959
971
|
/**
|
|
960
972
|
* Configure automatic retry behavior for failed requests
|
|
961
973
|
*
|
|
962
|
-
* @param retries - Number of retry attempts before failing
|
|
974
|
+
* @param retries - Number of retry attempts before failing, or a configuration object
|
|
963
975
|
* @returns The request instance for chaining
|
|
964
|
-
* @throws RequestError if retries is not a non-negative integer
|
|
976
|
+
* @throws RequestError if retries is not a non-negative integer or invalid config
|
|
965
977
|
*
|
|
966
978
|
* @example
|
|
979
|
+
* // Simple number (backward compatible)
|
|
967
980
|
* request.withRetries(3); // Retry up to 3 times
|
|
981
|
+
*
|
|
982
|
+
* @example
|
|
983
|
+
* // With fixed delay
|
|
984
|
+
* request.withRetries({ attempts: 3, delay: 1000 }); // Retry 3 times with 1 second delay
|
|
985
|
+
*
|
|
986
|
+
* @example
|
|
987
|
+
* // With exponential backoff function
|
|
988
|
+
* request.withRetries({
|
|
989
|
+
* attempts: 3,
|
|
990
|
+
* delay: ({ attempt }) => Math.min(1000 * Math.pow(2, attempt - 1), 10000)
|
|
991
|
+
* });
|
|
992
|
+
*
|
|
993
|
+
* @example
|
|
994
|
+
* // With delay function based on error
|
|
995
|
+
* request.withRetries({
|
|
996
|
+
* attempts: 3,
|
|
997
|
+
* delay: ({ attempt, error }) => {
|
|
998
|
+
* if (error.status === 429) return 5000; // Rate limited, wait longer
|
|
999
|
+
* return attempt * 1000; // Exponential backoff
|
|
1000
|
+
* }
|
|
1001
|
+
* });
|
|
968
1002
|
*/
|
|
969
1003
|
withRetries(retries) {
|
|
970
|
-
if (
|
|
971
|
-
|
|
972
|
-
|
|
1004
|
+
if (typeof retries === "number") {
|
|
1005
|
+
if (!Number.isInteger(retries) || retries < 0) {
|
|
1006
|
+
throw new RequestError(`Invalid retries: ${retries}`, this.url, this.method);
|
|
1007
|
+
}
|
|
1008
|
+
this.requestOptions.retries = retries;
|
|
1009
|
+
}
|
|
1010
|
+
else {
|
|
1011
|
+
// Validate RetryConfig
|
|
1012
|
+
if (!Number.isInteger(retries.attempts) || retries.attempts < 0) {
|
|
1013
|
+
throw new RequestError(`Invalid attempts: ${retries.attempts}`, this.url, this.method);
|
|
1014
|
+
}
|
|
1015
|
+
// Validate delay if provided
|
|
1016
|
+
if (retries.delay !== undefined) {
|
|
1017
|
+
if (typeof retries.delay === "number") {
|
|
1018
|
+
if (!Number.isFinite(retries.delay) || retries.delay < 0) {
|
|
1019
|
+
throw new RequestError(`Invalid delay: ${retries.delay}`, this.url, this.method);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
else if (typeof retries.delay !== "function") {
|
|
1023
|
+
throw new RequestError(`Invalid delay: ${typeof retries.delay}`, this.url, this.method);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
this.requestOptions.retries = retries;
|
|
1027
|
+
}
|
|
973
1028
|
return this;
|
|
974
1029
|
}
|
|
975
1030
|
/**
|
|
@@ -990,10 +1045,26 @@ class BaseRequest {
|
|
|
990
1045
|
return this;
|
|
991
1046
|
}
|
|
992
1047
|
/**
|
|
993
|
-
* Sets credentials policy
|
|
1048
|
+
* Sets the credentials policy for the request, controlling whether cookies and authentication
|
|
1049
|
+
* headers are sent with cross-origin requests.
|
|
1050
|
+
*
|
|
1051
|
+
* @param credentialsPolicy - The credentials policy to use:
|
|
1052
|
+
* - `"include"` or `CredentialsPolicy.INCLUDE`: Always send credentials (cookies, authorization headers) with the request, even for cross-origin requests.
|
|
1053
|
+
* - `"omit"` or `CredentialsPolicy.OMIT`: Never send credentials, even for same-origin requests.
|
|
1054
|
+
* - `"same-origin"` or `CredentialsPolicy.SAME_ORIGIN`: Only send credentials for same-origin requests (default behavior in most browsers).
|
|
1055
|
+
*
|
|
1056
|
+
* @returns The request instance for chaining
|
|
1057
|
+
*
|
|
994
1058
|
* @example
|
|
1059
|
+
* // Using string values
|
|
995
1060
|
* request.withCredentials("include")
|
|
1061
|
+
*
|
|
1062
|
+
* @example
|
|
1063
|
+
* // Using enum values
|
|
996
1064
|
* request.withCredentials(CredentialsPolicy.INCLUDE)
|
|
1065
|
+
*
|
|
1066
|
+
* @example
|
|
1067
|
+
* // Using fluent API
|
|
997
1068
|
* request.withCredentials.INCLUDE()
|
|
998
1069
|
*/
|
|
999
1070
|
get withCredentials() {
|
|
@@ -1004,28 +1075,83 @@ class BaseRequest {
|
|
|
1004
1075
|
});
|
|
1005
1076
|
}
|
|
1006
1077
|
/**
|
|
1007
|
-
* Allows providing an external AbortController to cancel the request
|
|
1008
|
-
*
|
|
1009
|
-
*
|
|
1078
|
+
* Allows providing an external AbortController to cancel the request.
|
|
1079
|
+
* This is useful when you need to cancel a request from outside the request chain,
|
|
1080
|
+
*
|
|
1081
|
+
* @param controller - The AbortController to use for this request. When `controller.abort()` is called,
|
|
1082
|
+
* the request will be cancelled and throw an abort error.
|
|
1083
|
+
*
|
|
1084
|
+
* @returns The request instance for chaining
|
|
1085
|
+
*
|
|
1086
|
+
* @example
|
|
1087
|
+
* const controller = new AbortController();
|
|
1088
|
+
* const request = createRequest('/api/data')
|
|
1089
|
+
* .withAbortController(controller)
|
|
1090
|
+
* .getJson();
|
|
1091
|
+
*
|
|
1092
|
+
* // Later, cancel the request
|
|
1093
|
+
* controller.abort();
|
|
1094
|
+
*
|
|
1095
|
+
* @example
|
|
1096
|
+
* // Share abort controller across multiple requests
|
|
1097
|
+
* const controller = new AbortController();
|
|
1098
|
+
* request1.withAbortController(controller).getJson();
|
|
1099
|
+
* request2.withAbortController(controller).getJson();
|
|
1100
|
+
* // Aborting will cancel both requests
|
|
1101
|
+
* controller.abort();
|
|
1010
1102
|
*/
|
|
1011
1103
|
withAbortController(controller) {
|
|
1012
1104
|
this.abortController = controller;
|
|
1013
1105
|
return this;
|
|
1014
1106
|
}
|
|
1015
1107
|
/**
|
|
1016
|
-
* Sets the referrer for the request
|
|
1017
|
-
*
|
|
1018
|
-
*
|
|
1108
|
+
* Sets the referrer URL for the request. The referrer is the URL of the page that initiated the request.
|
|
1109
|
+
* This can be used to override the default referrer that the browser would normally send.
|
|
1110
|
+
*
|
|
1111
|
+
* @param referrer - The referrer URL to send with the request. Can be:
|
|
1112
|
+
* - A full URL (e.g., "https://example.com/page")
|
|
1113
|
+
* - An empty string to omit the referrer
|
|
1114
|
+
* - A relative URL (will be resolved relative to the current page)
|
|
1115
|
+
*
|
|
1116
|
+
* @returns The request instance for chaining
|
|
1117
|
+
*
|
|
1118
|
+
* @example
|
|
1119
|
+
* request.withReferrer("https://example.com/previous-page")
|
|
1120
|
+
*
|
|
1121
|
+
* @example
|
|
1122
|
+
* // Omit referrer
|
|
1123
|
+
* request.withReferrer("")
|
|
1019
1124
|
*/
|
|
1020
1125
|
withReferrer(referrer) {
|
|
1021
1126
|
this.requestOptions.referrer = referrer;
|
|
1022
1127
|
return this;
|
|
1023
1128
|
}
|
|
1024
1129
|
/**
|
|
1025
|
-
* Sets referrer policy
|
|
1130
|
+
* Sets the referrer policy for the request, controlling how much referrer information
|
|
1131
|
+
* is sent with the request. This helps balance privacy and functionality.
|
|
1132
|
+
*
|
|
1133
|
+
* @param policy - The referrer policy to use:
|
|
1134
|
+
* - `"no-referrer"` or `ReferrerPolicy.NO_REFERRER`: Never send the referrer header.
|
|
1135
|
+
* - `"no-referrer-when-downgrade"` or `ReferrerPolicy.NO_REFERRER_WHEN_DOWNGRADE`: Send full referrer for same-origin or HTTPS→HTTPS, omit for HTTPS→HTTP (default in most browsers).
|
|
1136
|
+
* - `"origin"` or `ReferrerPolicy.ORIGIN`: Only send the origin (scheme, host, port), not the full URL.
|
|
1137
|
+
* - `"origin-when-cross-origin"` or `ReferrerPolicy.ORIGIN_WHEN_CROSS_ORIGIN`: Send full referrer for same-origin, only origin for cross-origin.
|
|
1138
|
+
* - `"same-origin"` or `ReferrerPolicy.SAME_ORIGIN`: Send full referrer for same-origin requests only, omit for cross-origin.
|
|
1139
|
+
* - `"strict-origin"` or `ReferrerPolicy.STRICT_ORIGIN`: Send origin for HTTPS→HTTPS or HTTP→HTTP, omit for HTTPS→HTTP.
|
|
1140
|
+
* - `"strict-origin-when-cross-origin"` or `ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN`: Send full referrer for same-origin, origin for cross-origin HTTPS→HTTPS, omit for HTTPS→HTTP.
|
|
1141
|
+
* - `"unsafe-url"` or `ReferrerPolicy.UNSAFE_URL`: Always send the full referrer URL (may leak sensitive information).
|
|
1142
|
+
*
|
|
1143
|
+
* @returns The request instance for chaining
|
|
1144
|
+
*
|
|
1026
1145
|
* @example
|
|
1146
|
+
* // Using string values
|
|
1027
1147
|
* request.withReferrerPolicy("no-referrer")
|
|
1148
|
+
*
|
|
1149
|
+
* @example
|
|
1150
|
+
* // Using enum values
|
|
1028
1151
|
* request.withReferrerPolicy(ReferrerPolicy.NO_REFERRER)
|
|
1152
|
+
*
|
|
1153
|
+
* @example
|
|
1154
|
+
* // Using fluent API
|
|
1029
1155
|
* request.withReferrerPolicy.NO_REFERRER()
|
|
1030
1156
|
*/
|
|
1031
1157
|
get withReferrerPolicy() {
|
|
@@ -1041,11 +1167,30 @@ class BaseRequest {
|
|
|
1041
1167
|
});
|
|
1042
1168
|
}
|
|
1043
1169
|
/**
|
|
1044
|
-
* Sets
|
|
1170
|
+
* Sets how the request handles HTTP redirects (3xx status codes).
|
|
1171
|
+
*
|
|
1172
|
+
* @param redirect - The redirect handling mode:
|
|
1173
|
+
* - `"follow"` or `RedirectMode.FOLLOW`: Automatically follow redirects. The fetch will transparently follow redirects and return the final response (default behavior).
|
|
1174
|
+
* - `"error"` or `RedirectMode.ERROR`: Treat redirects as errors. If a redirect occurs, the request will fail with an error.
|
|
1175
|
+
* - `"manual"` or `RedirectMode.MANUAL`: Return the redirect response without following it. The response will have a `type` of "opaqueredirect" and you can manually handle the redirect.
|
|
1176
|
+
*
|
|
1177
|
+
* @returns The request instance for chaining
|
|
1178
|
+
*
|
|
1045
1179
|
* @example
|
|
1180
|
+
* // Using string values
|
|
1046
1181
|
* request.withRedirect("follow")
|
|
1182
|
+
*
|
|
1183
|
+
* @example
|
|
1184
|
+
* // Using enum values
|
|
1047
1185
|
* request.withRedirect(RedirectMode.FOLLOW)
|
|
1186
|
+
*
|
|
1187
|
+
* @example
|
|
1188
|
+
* // Using fluent API
|
|
1048
1189
|
* request.withRedirect.FOLLOW()
|
|
1190
|
+
*
|
|
1191
|
+
* @example
|
|
1192
|
+
* // Fail on redirects
|
|
1193
|
+
* request.withRedirect.ERROR()
|
|
1049
1194
|
*/
|
|
1050
1195
|
get withRedirect() {
|
|
1051
1196
|
return this.createFluentSetter("redirect", {
|
|
@@ -1055,20 +1200,50 @@ class BaseRequest {
|
|
|
1055
1200
|
});
|
|
1056
1201
|
}
|
|
1057
1202
|
/**
|
|
1058
|
-
* Sets the keepalive flag for the request
|
|
1059
|
-
*
|
|
1060
|
-
*
|
|
1203
|
+
* Sets the keepalive flag for the request. When enabled, the request can continue
|
|
1204
|
+
* even after the page that initiated it is closed. This is useful for analytics,
|
|
1205
|
+
* logging, or other background requests that should complete even if the user navigates away.
|
|
1206
|
+
*
|
|
1207
|
+
* @param keepalive - Whether to allow the request to outlive the page:
|
|
1208
|
+
* - `true`: The request will continue even if the page is closed or navigated away.
|
|
1209
|
+
* - `false`: The request will be cancelled if the page is closed (default).
|
|
1210
|
+
*
|
|
1211
|
+
* @returns The request instance for chaining
|
|
1212
|
+
*
|
|
1213
|
+
* @example
|
|
1214
|
+
* // Send analytics event that should complete even if user navigates away
|
|
1215
|
+
* request.withKeepAlive(true)
|
|
1061
1216
|
*/
|
|
1062
1217
|
withKeepAlive(keepalive) {
|
|
1063
1218
|
this.requestOptions.keepalive = keepalive;
|
|
1064
1219
|
return this;
|
|
1065
1220
|
}
|
|
1066
1221
|
/**
|
|
1067
|
-
* Sets
|
|
1222
|
+
* Sets the priority hint for the request, indicating to the browser how important
|
|
1223
|
+
* this request is relative to other requests. This helps the browser optimize resource loading.
|
|
1224
|
+
*
|
|
1225
|
+
* @param priority - The request priority:
|
|
1226
|
+
* - `"high"` or `RequestPriority.HIGH`: High priority - the browser should prioritize this request.
|
|
1227
|
+
* - `"low"` or `RequestPriority.LOW`: Low priority - the browser can defer this request if needed.
|
|
1228
|
+
* - `"auto"` or `RequestPriority.AUTO`: Automatic priority based on the request type (default).
|
|
1229
|
+
*
|
|
1230
|
+
* @returns The request instance for chaining
|
|
1231
|
+
*
|
|
1068
1232
|
* @example
|
|
1233
|
+
* // Using string values
|
|
1069
1234
|
* request.withPriority("high")
|
|
1235
|
+
*
|
|
1236
|
+
* @example
|
|
1237
|
+
* // Using enum values
|
|
1070
1238
|
* request.withPriority(RequestPriority.HIGH)
|
|
1239
|
+
*
|
|
1240
|
+
* @example
|
|
1241
|
+
* // Using fluent API
|
|
1071
1242
|
* request.withPriority.HIGH()
|
|
1243
|
+
*
|
|
1244
|
+
* @example
|
|
1245
|
+
* // Low priority for non-critical requests
|
|
1246
|
+
* request.withPriority.LOW()
|
|
1072
1247
|
*/
|
|
1073
1248
|
get withPriority() {
|
|
1074
1249
|
return this.createFluentSetter("priority", {
|
|
@@ -1078,22 +1253,62 @@ class BaseRequest {
|
|
|
1078
1253
|
});
|
|
1079
1254
|
}
|
|
1080
1255
|
/**
|
|
1081
|
-
* Sets the integrity hash for
|
|
1082
|
-
*
|
|
1083
|
-
*
|
|
1256
|
+
* Sets the integrity hash for Subresource Integrity (SRI) verification.
|
|
1257
|
+
* This allows the browser to verify that the fetched resource hasn't been tampered with
|
|
1258
|
+
* by comparing its hash against the provided value. If the hashes don't match, the request fails.
|
|
1259
|
+
*
|
|
1260
|
+
* @param integrity - The integrity hash string in the format `"algorithm-hash"`:
|
|
1261
|
+
* - Example: `"sha256-abcdef1234567890..."` (SHA-256 hash)
|
|
1262
|
+
* - Example: `"sha384-abcdef1234567890..."` (SHA-384 hash)
|
|
1263
|
+
* - Example: `"sha512-abcdef1234567890..."` (SHA-512 hash)
|
|
1264
|
+
* - Multiple hashes can be separated by spaces: `"sha256-... sha384-..."`
|
|
1265
|
+
*
|
|
1266
|
+
* @returns The request instance for chaining
|
|
1267
|
+
*
|
|
1084
1268
|
* @example
|
|
1085
1269
|
* request.withIntegrity("sha256-abcdef1234567890...")
|
|
1270
|
+
*
|
|
1271
|
+
* @example
|
|
1272
|
+
* // Multiple algorithms for better compatibility
|
|
1273
|
+
* request.withIntegrity("sha256-... sha384-...")
|
|
1086
1274
|
*/
|
|
1087
1275
|
withIntegrity(integrity) {
|
|
1088
1276
|
this.requestOptions.integrity = integrity;
|
|
1089
1277
|
return this;
|
|
1090
1278
|
}
|
|
1091
1279
|
/**
|
|
1092
|
-
* Sets cache mode
|
|
1280
|
+
* Sets the cache mode for the request, controlling how the browser's HTTP cache
|
|
1281
|
+
* is used for this request.
|
|
1282
|
+
*
|
|
1283
|
+
* @param cache - The cache mode:
|
|
1284
|
+
* - `"default"` or `CacheMode.DEFAULT`: Use the browser's default cache behavior. The browser will check the cache and use it if valid, otherwise fetch from network.
|
|
1285
|
+
* - `"no-store"` or `CacheMode.NO_STORE`: Never use the cache and don't store the response in cache. Always fetch from network.
|
|
1286
|
+
* - `"reload"` or `CacheMode.RELOAD`: Bypass the cache but store the response. Always fetch from network, ignoring cached responses.
|
|
1287
|
+
* - `"no-cache"` or `CacheMode.NO_CACHE`: Check the cache but revalidate with the server. Use cached response only if server confirms it's still valid.
|
|
1288
|
+
* - `"force-cache"` or `CacheMode.FORCE_CACHE`: Use the cache if available, even if stale. Only fetch from network if not in cache.
|
|
1289
|
+
* - `"only-if-cached"` or `CacheMode.ONLY_IF_CACHED`: Only use the cache. If not in cache, return an error. Never fetch from network.
|
|
1290
|
+
*
|
|
1291
|
+
* @returns The request instance for chaining
|
|
1292
|
+
*
|
|
1093
1293
|
* @example
|
|
1294
|
+
* // Using string values
|
|
1094
1295
|
* request.withCache("no-cache")
|
|
1296
|
+
*
|
|
1297
|
+
* @example
|
|
1298
|
+
* // Using enum values
|
|
1095
1299
|
* request.withCache(CacheMode.NO_CACHE)
|
|
1300
|
+
*
|
|
1301
|
+
* @example
|
|
1302
|
+
* // Using fluent API
|
|
1096
1303
|
* request.withCache.NO_CACHE()
|
|
1304
|
+
*
|
|
1305
|
+
* @example
|
|
1306
|
+
* // Always fetch fresh data
|
|
1307
|
+
* request.withCache.RELOAD()
|
|
1308
|
+
*
|
|
1309
|
+
* @example
|
|
1310
|
+
* // Use cache only, fail if not cached
|
|
1311
|
+
* request.withCache.ONLY_IF_CACHED()
|
|
1097
1312
|
*/
|
|
1098
1313
|
get withCache() {
|
|
1099
1314
|
const cacheOptions = {
|
|
@@ -1142,11 +1357,32 @@ class BaseRequest {
|
|
|
1142
1357
|
return this;
|
|
1143
1358
|
}
|
|
1144
1359
|
/**
|
|
1145
|
-
* Sets request mode
|
|
1360
|
+
* Sets the request mode, which determines the CORS (Cross-Origin Resource Sharing) behavior
|
|
1361
|
+
* for the request. This controls how the browser handles cross-origin requests.
|
|
1362
|
+
*
|
|
1363
|
+
* @param mode - The request mode:
|
|
1364
|
+
* - `"cors"` or `RequestMode.CORS`: Enable CORS. The browser will send CORS headers and enforce CORS rules. This is the default for most cross-origin requests.
|
|
1365
|
+
* - `"no-cors"` or `RequestMode.NO_CORS`: Disable CORS. The request is sent as a "simple" request without CORS headers. The response will be opaque (you can't read it).
|
|
1366
|
+
* - `"same-origin"` or `RequestMode.SAME_ORIGIN`: Only allow same-origin requests. Cross-origin requests will fail.
|
|
1367
|
+
* - `"navigate"` or `RequestMode.NAVIGATE`: Used for navigation requests (typically only used by the browser itself).
|
|
1368
|
+
*
|
|
1369
|
+
* @returns The request instance for chaining
|
|
1370
|
+
*
|
|
1146
1371
|
* @example
|
|
1372
|
+
* // Using string values
|
|
1147
1373
|
* request.withMode("cors")
|
|
1374
|
+
*
|
|
1375
|
+
* @example
|
|
1376
|
+
* // Using enum values
|
|
1148
1377
|
* request.withMode(RequestMode.CORS)
|
|
1378
|
+
*
|
|
1379
|
+
* @example
|
|
1380
|
+
* // Using fluent API
|
|
1149
1381
|
* request.withMode.CORS()
|
|
1382
|
+
*
|
|
1383
|
+
* @example
|
|
1384
|
+
* // Restrict to same-origin only
|
|
1385
|
+
* request.withMode.SAME_ORIGIN()
|
|
1150
1386
|
*/
|
|
1151
1387
|
get withMode() {
|
|
1152
1388
|
return this.createFluentSetter("mode", {
|
|
@@ -1541,7 +1777,8 @@ class BaseRequest {
|
|
|
1541
1777
|
* @throws RequestError if the request fails after all retries
|
|
1542
1778
|
*/
|
|
1543
1779
|
async executeWithRetries(url, fetchOptions) {
|
|
1544
|
-
const
|
|
1780
|
+
const retriesConfig = this.requestOptions.retries;
|
|
1781
|
+
const maxRetries = typeof retriesConfig === "number" ? retriesConfig : retriesConfig?.attempts || 0;
|
|
1545
1782
|
const method = typeof fetchOptions.method === "string" ? fetchOptions.method : "GET";
|
|
1546
1783
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
1547
1784
|
try {
|
|
@@ -1551,9 +1788,22 @@ class BaseRequest {
|
|
|
1551
1788
|
const requestError = error instanceof RequestError ? error : RequestError.networkError(url, method, error instanceof Error ? error : new Error(String(error)));
|
|
1552
1789
|
if (attempt >= maxRetries)
|
|
1553
1790
|
throw requestError;
|
|
1791
|
+
// Call onRetry callback if provided
|
|
1554
1792
|
if (this.requestOptions.onRetry) {
|
|
1555
1793
|
await this.requestOptions.onRetry({ attempt: attempt + 1, error: requestError });
|
|
1556
1794
|
}
|
|
1795
|
+
// Apply delay if configured
|
|
1796
|
+
if (typeof retriesConfig === "object" && retriesConfig.delay !== undefined) {
|
|
1797
|
+
const delay = typeof retriesConfig.delay === "function" ? retriesConfig.delay({ attempt: attempt + 1, error: requestError }) : retriesConfig.delay;
|
|
1798
|
+
// Validate delay result
|
|
1799
|
+
if (typeof delay !== "number" || !Number.isFinite(delay) || delay < 0) {
|
|
1800
|
+
throw new RequestError(`Invalid retry delay: ${delay}`, url, method);
|
|
1801
|
+
}
|
|
1802
|
+
// Wait for the delay
|
|
1803
|
+
if (delay > 0) {
|
|
1804
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1557
1807
|
}
|
|
1558
1808
|
}
|
|
1559
1809
|
// This should never happen but is needed for type safety
|
|
@@ -1670,7 +1920,7 @@ class BaseRequest {
|
|
|
1670
1920
|
// No timeout - just use external controller
|
|
1671
1921
|
if (!timeoutMs) {
|
|
1672
1922
|
return {
|
|
1673
|
-
signal: externalController
|
|
1923
|
+
signal: externalController?.signal,
|
|
1674
1924
|
cleanup: () => { },
|
|
1675
1925
|
wasTimeout: () => false,
|
|
1676
1926
|
};
|
|
@@ -1853,9 +2103,11 @@ class BaseRequest {
|
|
|
1853
2103
|
* Base class for requests that can have a body (POST, PUT, PATCH)
|
|
1854
2104
|
*/
|
|
1855
2105
|
class BodyRequest extends BaseRequest {
|
|
2106
|
+
body;
|
|
2107
|
+
bodyType;
|
|
2108
|
+
graphQLOptions = undefined;
|
|
1856
2109
|
constructor(url) {
|
|
1857
2110
|
super(url);
|
|
1858
|
-
this.graphQLOptions = undefined;
|
|
1859
2111
|
}
|
|
1860
2112
|
/**
|
|
1861
2113
|
* Sets the body of the request
|
|
@@ -1870,7 +2122,13 @@ class BodyRequest extends BaseRequest {
|
|
|
1870
2122
|
}
|
|
1871
2123
|
else if (body !== null &&
|
|
1872
2124
|
typeof body === "object" &&
|
|
1873
|
-
!(body instanceof FormData ||
|
|
2125
|
+
!(body instanceof FormData ||
|
|
2126
|
+
body instanceof Blob ||
|
|
2127
|
+
body instanceof File ||
|
|
2128
|
+
body instanceof ArrayBuffer ||
|
|
2129
|
+
ArrayBuffer.isView(body) || // Handles TypedArray and DataView
|
|
2130
|
+
body instanceof URLSearchParams ||
|
|
2131
|
+
body instanceof ReadableStream)) {
|
|
1874
2132
|
this.bodyType = BodyType.JSON;
|
|
1875
2133
|
this.setContentTypeIfNeeded("application/json");
|
|
1876
2134
|
// Validate JSON is stringifiable early
|
|
@@ -2000,9 +2258,9 @@ class BodyRequest extends BaseRequest {
|
|
|
2000
2258
|
* const data = await request.getData();
|
|
2001
2259
|
*/
|
|
2002
2260
|
class GetRequest extends BaseRequest {
|
|
2261
|
+
method = exports.HttpMethod.GET;
|
|
2003
2262
|
constructor(url) {
|
|
2004
2263
|
super(url);
|
|
2005
|
-
this.method = exports.HttpMethod.GET;
|
|
2006
2264
|
}
|
|
2007
2265
|
}
|
|
2008
2266
|
/**
|
|
@@ -2014,9 +2272,9 @@ class GetRequest extends BaseRequest {
|
|
|
2014
2272
|
* const response = await request.getResponse();
|
|
2015
2273
|
*/
|
|
2016
2274
|
class HeadRequest extends BaseRequest {
|
|
2275
|
+
method = exports.HttpMethod.HEAD;
|
|
2017
2276
|
constructor(url) {
|
|
2018
2277
|
super(url);
|
|
2019
|
-
this.method = exports.HttpMethod.HEAD;
|
|
2020
2278
|
}
|
|
2021
2279
|
}
|
|
2022
2280
|
/**
|
|
@@ -2028,9 +2286,9 @@ class HeadRequest extends BaseRequest {
|
|
|
2028
2286
|
* const response = await request.getResponse();
|
|
2029
2287
|
*/
|
|
2030
2288
|
class OptionsRequest extends BaseRequest {
|
|
2289
|
+
method = exports.HttpMethod.OPTIONS;
|
|
2031
2290
|
constructor(url) {
|
|
2032
2291
|
super(url);
|
|
2033
|
-
this.method = exports.HttpMethod.OPTIONS;
|
|
2034
2292
|
}
|
|
2035
2293
|
}
|
|
2036
2294
|
/**
|
|
@@ -2042,9 +2300,9 @@ class OptionsRequest extends BaseRequest {
|
|
|
2042
2300
|
* await request.getData();
|
|
2043
2301
|
*/
|
|
2044
2302
|
class DeleteRequest extends BaseRequest {
|
|
2303
|
+
method = exports.HttpMethod.DELETE;
|
|
2045
2304
|
constructor(url) {
|
|
2046
2305
|
super(url);
|
|
2047
|
-
this.method = exports.HttpMethod.DELETE;
|
|
2048
2306
|
}
|
|
2049
2307
|
}
|
|
2050
2308
|
/**
|
|
@@ -2057,9 +2315,9 @@ class DeleteRequest extends BaseRequest {
|
|
|
2057
2315
|
* const data = await request.getData();
|
|
2058
2316
|
*/
|
|
2059
2317
|
class PostRequest extends BodyRequest {
|
|
2318
|
+
method = exports.HttpMethod.POST;
|
|
2060
2319
|
constructor(url) {
|
|
2061
2320
|
super(url);
|
|
2062
|
-
this.method = exports.HttpMethod.POST;
|
|
2063
2321
|
}
|
|
2064
2322
|
}
|
|
2065
2323
|
/**
|
|
@@ -2072,9 +2330,9 @@ class PostRequest extends BodyRequest {
|
|
|
2072
2330
|
* const data = await request.getData();
|
|
2073
2331
|
*/
|
|
2074
2332
|
class PutRequest extends BodyRequest {
|
|
2333
|
+
method = exports.HttpMethod.PUT;
|
|
2075
2334
|
constructor(url) {
|
|
2076
2335
|
super(url);
|
|
2077
|
-
this.method = exports.HttpMethod.PUT;
|
|
2078
2336
|
}
|
|
2079
2337
|
}
|
|
2080
2338
|
/**
|
|
@@ -2087,9 +2345,9 @@ class PutRequest extends BodyRequest {
|
|
|
2087
2345
|
* const data = await request.getData();
|
|
2088
2346
|
*/
|
|
2089
2347
|
class PatchRequest extends BodyRequest {
|
|
2348
|
+
method = exports.HttpMethod.PATCH;
|
|
2090
2349
|
constructor(url) {
|
|
2091
2350
|
super(url);
|
|
2092
|
-
this.method = exports.HttpMethod.PATCH;
|
|
2093
2351
|
}
|
|
2094
2352
|
}
|
|
2095
2353
|
|