create-request 1.2.3 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/library/BaseRequest.d.ts +199 -18
- package/dist/library/ResponseWrapper.d.ts +18 -13
- package/dist/library/index.cjs +299 -134
- package/dist/library/index.cjs.map +1 -1
- package/dist/library/index.esm.js +299 -134
- 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 +1 -1
- 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";
|
|
@@ -192,11 +198,14 @@ class RequestError extends Error {
|
|
|
192
198
|
|
|
193
199
|
/**
|
|
194
200
|
* Wrapper for HTTP responses with methods to transform the response data
|
|
195
|
-
* Provides caching and conversion between different response formats.
|
|
196
201
|
*/
|
|
197
202
|
class ResponseWrapper {
|
|
203
|
+
response;
|
|
204
|
+
url;
|
|
205
|
+
method;
|
|
206
|
+
// GraphQL-specific options
|
|
207
|
+
graphQLOptions;
|
|
198
208
|
constructor(response, url, method, graphQLOptions) {
|
|
199
|
-
this.bodyUsed = false;
|
|
200
209
|
this.response = response;
|
|
201
210
|
this.url = url;
|
|
202
211
|
this.method = method;
|
|
@@ -228,8 +237,7 @@ class ResponseWrapper {
|
|
|
228
237
|
* @throws RequestError if GraphQL response contains errors and throwOnError is enabled
|
|
229
238
|
*/
|
|
230
239
|
checkGraphQLErrors(data) {
|
|
231
|
-
|
|
232
|
-
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)
|
|
233
241
|
return;
|
|
234
242
|
const responseData = data;
|
|
235
243
|
if (!Array.isArray(responseData.errors) || responseData.errors.length === 0)
|
|
@@ -244,7 +252,7 @@ class ResponseWrapper {
|
|
|
244
252
|
}
|
|
245
253
|
/**
|
|
246
254
|
* Parse the response body as JSON
|
|
247
|
-
*
|
|
255
|
+
* Note: This consumes the response body and can only be called once.
|
|
248
256
|
* If GraphQL options are set with throwOnError=true, will check for GraphQL errors and throw.
|
|
249
257
|
*
|
|
250
258
|
* @returns The parsed JSON data
|
|
@@ -265,40 +273,14 @@ class ResponseWrapper {
|
|
|
265
273
|
* }
|
|
266
274
|
*/
|
|
267
275
|
async getJson() {
|
|
268
|
-
if (this.
|
|
269
|
-
this.checkGraphQLErrors(this.jsonCache);
|
|
270
|
-
return this.jsonCache;
|
|
271
|
-
}
|
|
272
|
-
if (this.bodyUsed) {
|
|
273
|
-
// If we already have text, try to parse it as JSON
|
|
274
|
-
if (this.textCache !== undefined) {
|
|
275
|
-
try {
|
|
276
|
-
const parsed = JSON.parse(this.textCache);
|
|
277
|
-
this.jsonCache = parsed;
|
|
278
|
-
this.checkGraphQLErrors(parsed);
|
|
279
|
-
return parsed;
|
|
280
|
-
}
|
|
281
|
-
catch (error) {
|
|
282
|
-
// Re-throw RequestErrors from checkGraphQLErrors without wrapping
|
|
283
|
-
if (error instanceof RequestError) {
|
|
284
|
-
throw error;
|
|
285
|
-
}
|
|
286
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
287
|
-
throw new RequestError(`Invalid JSON: ${errorMessage}`, this.url || "", this.method || "", {
|
|
288
|
-
status: this.response.status,
|
|
289
|
-
response: this.response,
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
}
|
|
276
|
+
if (this.response.bodyUsed) {
|
|
293
277
|
throw new RequestError("Body already consumed", this.url || "", this.method || "", {
|
|
294
278
|
status: this.response.status,
|
|
295
279
|
response: this.response,
|
|
296
280
|
});
|
|
297
281
|
}
|
|
298
|
-
this.bodyUsed = true;
|
|
299
282
|
try {
|
|
300
283
|
const parsed = await this.response.json();
|
|
301
|
-
this.jsonCache = parsed;
|
|
302
284
|
this.checkGraphQLErrors(parsed);
|
|
303
285
|
return parsed;
|
|
304
286
|
}
|
|
@@ -316,48 +298,23 @@ class ResponseWrapper {
|
|
|
316
298
|
}
|
|
317
299
|
/**
|
|
318
300
|
* Get the response body as text
|
|
319
|
-
*
|
|
320
|
-
* if the body has already been consumed in those formats.
|
|
301
|
+
* Note: This consumes the response body and can only be called once.
|
|
321
302
|
*
|
|
322
303
|
* @returns The response text
|
|
323
|
-
* @throws {RequestError} When reading fails or the response has already been consumed
|
|
304
|
+
* @throws {RequestError} When reading fails or the response has already been consumed
|
|
324
305
|
*
|
|
325
306
|
* @example
|
|
326
307
|
* const text = await response.getText();
|
|
327
308
|
*/
|
|
328
309
|
async getText() {
|
|
329
|
-
if (this.
|
|
330
|
-
|
|
331
|
-
}
|
|
332
|
-
if (this.bodyUsed) {
|
|
333
|
-
// If we already have JSON, convert it to text
|
|
334
|
-
if (this.jsonCache !== undefined) {
|
|
335
|
-
this.textCache = JSON.stringify(this.jsonCache);
|
|
336
|
-
return this.textCache;
|
|
337
|
-
}
|
|
338
|
-
// If we have blob, convert to text
|
|
339
|
-
if (this.blobCache !== undefined) {
|
|
340
|
-
try {
|
|
341
|
-
this.textCache = await this.blobCache.text();
|
|
342
|
-
return this.textCache;
|
|
343
|
-
}
|
|
344
|
-
catch (e) {
|
|
345
|
-
const errorMessage = e instanceof Error ? e.message : String(e);
|
|
346
|
-
throw new RequestError(`Cannot convert to text: ${errorMessage}`, this.url || "", this.method || "", {
|
|
347
|
-
status: this.response.status,
|
|
348
|
-
response: this.response,
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
throw new RequestError("Cannot convert to text", this.url || "", this.method || "", {
|
|
310
|
+
if (this.response.bodyUsed) {
|
|
311
|
+
throw new RequestError("Body already consumed", this.url || "", this.method || "", {
|
|
353
312
|
status: this.response.status,
|
|
354
313
|
response: this.response,
|
|
355
314
|
});
|
|
356
315
|
}
|
|
357
|
-
this.bodyUsed = true;
|
|
358
316
|
try {
|
|
359
|
-
|
|
360
|
-
return this.textCache;
|
|
317
|
+
return await this.response.text();
|
|
361
318
|
}
|
|
362
319
|
catch (e) {
|
|
363
320
|
const errorMessage = e instanceof Error ? e.message : String(e);
|
|
@@ -369,35 +326,53 @@ class ResponseWrapper {
|
|
|
369
326
|
}
|
|
370
327
|
/**
|
|
371
328
|
* Get the response body as a Blob
|
|
372
|
-
*
|
|
373
|
-
* if the body has already been consumed as text.
|
|
329
|
+
* Note: This consumes the response body and can only be called once.
|
|
374
330
|
*
|
|
375
331
|
* @returns The response as a Blob
|
|
376
|
-
* @throws {RequestError} When reading fails or the response has already been consumed
|
|
332
|
+
* @throws {RequestError} When reading fails or the response has already been consumed
|
|
377
333
|
*
|
|
378
334
|
* @example
|
|
379
335
|
* const blob = await response.getBlob();
|
|
380
336
|
* const url = URL.createObjectURL(blob);
|
|
381
337
|
*/
|
|
382
338
|
async getBlob() {
|
|
383
|
-
if (this.
|
|
384
|
-
|
|
339
|
+
if (this.response.bodyUsed) {
|
|
340
|
+
throw new RequestError("Body already consumed", this.url || "", this.method || "", {
|
|
341
|
+
status: this.response.status,
|
|
342
|
+
response: this.response,
|
|
343
|
+
});
|
|
385
344
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
}
|
|
392
|
-
|
|
345
|
+
try {
|
|
346
|
+
return await this.response.blob();
|
|
347
|
+
}
|
|
348
|
+
catch (e) {
|
|
349
|
+
const errorMessage = e instanceof Error ? e.message : String(e);
|
|
350
|
+
throw new RequestError(`Read failed: ${errorMessage}`, this.url || "", this.method || "", {
|
|
351
|
+
status: this.response.status,
|
|
352
|
+
response: this.response,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Get the response body as an ArrayBuffer
|
|
358
|
+
* Note: This consumes the response body and can only be called once.
|
|
359
|
+
*
|
|
360
|
+
* @returns The response as an ArrayBuffer
|
|
361
|
+
* @throws {RequestError} When reading fails or the response has already been consumed
|
|
362
|
+
*
|
|
363
|
+
* @example
|
|
364
|
+
* const buffer = await response.getArrayBuffer();
|
|
365
|
+
* const uint8Array = new Uint8Array(buffer);
|
|
366
|
+
*/
|
|
367
|
+
async getArrayBuffer() {
|
|
368
|
+
if (this.response.bodyUsed) {
|
|
369
|
+
throw new RequestError("Body already consumed", this.url || "", this.method || "", {
|
|
393
370
|
status: this.response.status,
|
|
394
371
|
response: this.response,
|
|
395
372
|
});
|
|
396
373
|
}
|
|
397
|
-
this.bodyUsed = true;
|
|
398
374
|
try {
|
|
399
|
-
|
|
400
|
-
return this.blobCache;
|
|
375
|
+
return await this.response.arrayBuffer();
|
|
401
376
|
}
|
|
402
377
|
catch (e) {
|
|
403
378
|
const errorMessage = e instanceof Error ? e.message : String(e);
|
|
@@ -409,7 +384,7 @@ class ResponseWrapper {
|
|
|
409
384
|
}
|
|
410
385
|
/**
|
|
411
386
|
* Get the raw response body as a ReadableStream
|
|
412
|
-
* This
|
|
387
|
+
* Note: This consumes the response body and should only be called once.
|
|
413
388
|
*
|
|
414
389
|
* @returns The response body as a ReadableStream or null
|
|
415
390
|
* @throws {RequestError} When the response body has already been consumed
|
|
@@ -422,13 +397,12 @@ class ResponseWrapper {
|
|
|
422
397
|
* }
|
|
423
398
|
*/
|
|
424
399
|
getBody() {
|
|
425
|
-
if (this.bodyUsed) {
|
|
400
|
+
if (this.response.bodyUsed) {
|
|
426
401
|
throw new RequestError("Body already consumed", this.url || "", this.method || "", {
|
|
427
402
|
status: this.response.status,
|
|
428
403
|
response: this.response,
|
|
429
404
|
});
|
|
430
405
|
}
|
|
431
|
-
this.bodyUsed = true;
|
|
432
406
|
return this.response.body;
|
|
433
407
|
}
|
|
434
408
|
/**
|
|
@@ -524,7 +498,7 @@ class CsrfUtils {
|
|
|
524
498
|
return null;
|
|
525
499
|
}
|
|
526
500
|
const meta = document.querySelector(`meta[name="${metaName}"]`);
|
|
527
|
-
return
|
|
501
|
+
return meta?.getAttribute("content") || null;
|
|
528
502
|
}
|
|
529
503
|
/**
|
|
530
504
|
* Extracts CSRF token from a cookie
|
|
@@ -581,20 +555,20 @@ class CsrfUtils {
|
|
|
581
555
|
* Global configuration for create-request
|
|
582
556
|
*/
|
|
583
557
|
class Config {
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
}
|
|
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() { }
|
|
598
572
|
/**
|
|
599
573
|
* Get the singleton instance of the Config class
|
|
600
574
|
*
|
|
@@ -877,16 +851,18 @@ class Config {
|
|
|
877
851
|
* Provides the core request building and execution capabilities.
|
|
878
852
|
*/
|
|
879
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 = [];
|
|
880
865
|
constructor(url) {
|
|
881
|
-
this.requestOptions = {
|
|
882
|
-
headers: {},
|
|
883
|
-
};
|
|
884
|
-
this.queryParams = new URLSearchParams();
|
|
885
|
-
this.autoApplyCsrfProtection = true;
|
|
886
|
-
// Per-request interceptors
|
|
887
|
-
this.requestInterceptors = [];
|
|
888
|
-
this.responseInterceptors = [];
|
|
889
|
-
this.errorInterceptors = [];
|
|
890
866
|
this.url = url;
|
|
891
867
|
}
|
|
892
868
|
/**
|
|
@@ -917,7 +893,7 @@ class BaseRequest {
|
|
|
917
893
|
return Object.assign(callable, fluent);
|
|
918
894
|
}
|
|
919
895
|
validateUrl(url) {
|
|
920
|
-
if (!
|
|
896
|
+
if (!url?.trim())
|
|
921
897
|
throw new RequestError("URL cannot be empty", url, this.method);
|
|
922
898
|
if (url.includes("\0") || url.includes("\r") || url.includes("\n")) {
|
|
923
899
|
throw new RequestError("Invalid URL (control chars)", url, this.method);
|
|
@@ -927,7 +903,7 @@ class BaseRequest {
|
|
|
927
903
|
try {
|
|
928
904
|
new URL(trimmed);
|
|
929
905
|
}
|
|
930
|
-
catch
|
|
906
|
+
catch {
|
|
931
907
|
throw new RequestError(`Invalid URL: ${trimmed}`, trimmed, this.method);
|
|
932
908
|
}
|
|
933
909
|
}
|
|
@@ -1026,10 +1002,26 @@ class BaseRequest {
|
|
|
1026
1002
|
return this;
|
|
1027
1003
|
}
|
|
1028
1004
|
/**
|
|
1029
|
-
* Sets credentials policy
|
|
1005
|
+
* Sets the credentials policy for the request, controlling whether cookies and authentication
|
|
1006
|
+
* headers are sent with cross-origin requests.
|
|
1007
|
+
*
|
|
1008
|
+
* @param credentialsPolicy - The credentials policy to use:
|
|
1009
|
+
* - `"include"` or `CredentialsPolicy.INCLUDE`: Always send credentials (cookies, authorization headers) with the request, even for cross-origin requests.
|
|
1010
|
+
* - `"omit"` or `CredentialsPolicy.OMIT`: Never send credentials, even for same-origin requests.
|
|
1011
|
+
* - `"same-origin"` or `CredentialsPolicy.SAME_ORIGIN`: Only send credentials for same-origin requests (default behavior in most browsers).
|
|
1012
|
+
*
|
|
1013
|
+
* @returns The request instance for chaining
|
|
1014
|
+
*
|
|
1030
1015
|
* @example
|
|
1016
|
+
* // Using string values
|
|
1031
1017
|
* request.withCredentials("include")
|
|
1018
|
+
*
|
|
1019
|
+
* @example
|
|
1020
|
+
* // Using enum values
|
|
1032
1021
|
* request.withCredentials(CredentialsPolicy.INCLUDE)
|
|
1022
|
+
*
|
|
1023
|
+
* @example
|
|
1024
|
+
* // Using fluent API
|
|
1033
1025
|
* request.withCredentials.INCLUDE()
|
|
1034
1026
|
*/
|
|
1035
1027
|
get withCredentials() {
|
|
@@ -1040,28 +1032,83 @@ class BaseRequest {
|
|
|
1040
1032
|
});
|
|
1041
1033
|
}
|
|
1042
1034
|
/**
|
|
1043
|
-
* Allows providing an external AbortController to cancel the request
|
|
1044
|
-
*
|
|
1045
|
-
*
|
|
1035
|
+
* Allows providing an external AbortController to cancel the request.
|
|
1036
|
+
* This is useful when you need to cancel a request from outside the request chain,
|
|
1037
|
+
*
|
|
1038
|
+
* @param controller - The AbortController to use for this request. When `controller.abort()` is called,
|
|
1039
|
+
* the request will be cancelled and throw an abort error.
|
|
1040
|
+
*
|
|
1041
|
+
* @returns The request instance for chaining
|
|
1042
|
+
*
|
|
1043
|
+
* @example
|
|
1044
|
+
* const controller = new AbortController();
|
|
1045
|
+
* const request = createRequest('/api/data')
|
|
1046
|
+
* .withAbortController(controller)
|
|
1047
|
+
* .getJson();
|
|
1048
|
+
*
|
|
1049
|
+
* // Later, cancel the request
|
|
1050
|
+
* controller.abort();
|
|
1051
|
+
*
|
|
1052
|
+
* @example
|
|
1053
|
+
* // Share abort controller across multiple requests
|
|
1054
|
+
* const controller = new AbortController();
|
|
1055
|
+
* request1.withAbortController(controller).getJson();
|
|
1056
|
+
* request2.withAbortController(controller).getJson();
|
|
1057
|
+
* // Aborting will cancel both requests
|
|
1058
|
+
* controller.abort();
|
|
1046
1059
|
*/
|
|
1047
1060
|
withAbortController(controller) {
|
|
1048
1061
|
this.abortController = controller;
|
|
1049
1062
|
return this;
|
|
1050
1063
|
}
|
|
1051
1064
|
/**
|
|
1052
|
-
* Sets the referrer for the request
|
|
1053
|
-
*
|
|
1054
|
-
*
|
|
1065
|
+
* Sets the referrer URL for the request. The referrer is the URL of the page that initiated the request.
|
|
1066
|
+
* This can be used to override the default referrer that the browser would normally send.
|
|
1067
|
+
*
|
|
1068
|
+
* @param referrer - The referrer URL to send with the request. Can be:
|
|
1069
|
+
* - A full URL (e.g., "https://example.com/page")
|
|
1070
|
+
* - An empty string to omit the referrer
|
|
1071
|
+
* - A relative URL (will be resolved relative to the current page)
|
|
1072
|
+
*
|
|
1073
|
+
* @returns The request instance for chaining
|
|
1074
|
+
*
|
|
1075
|
+
* @example
|
|
1076
|
+
* request.withReferrer("https://example.com/previous-page")
|
|
1077
|
+
*
|
|
1078
|
+
* @example
|
|
1079
|
+
* // Omit referrer
|
|
1080
|
+
* request.withReferrer("")
|
|
1055
1081
|
*/
|
|
1056
1082
|
withReferrer(referrer) {
|
|
1057
1083
|
this.requestOptions.referrer = referrer;
|
|
1058
1084
|
return this;
|
|
1059
1085
|
}
|
|
1060
1086
|
/**
|
|
1061
|
-
* Sets referrer policy
|
|
1087
|
+
* Sets the referrer policy for the request, controlling how much referrer information
|
|
1088
|
+
* is sent with the request. This helps balance privacy and functionality.
|
|
1089
|
+
*
|
|
1090
|
+
* @param policy - The referrer policy to use:
|
|
1091
|
+
* - `"no-referrer"` or `ReferrerPolicy.NO_REFERRER`: Never send the referrer header.
|
|
1092
|
+
* - `"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).
|
|
1093
|
+
* - `"origin"` or `ReferrerPolicy.ORIGIN`: Only send the origin (scheme, host, port), not the full URL.
|
|
1094
|
+
* - `"origin-when-cross-origin"` or `ReferrerPolicy.ORIGIN_WHEN_CROSS_ORIGIN`: Send full referrer for same-origin, only origin for cross-origin.
|
|
1095
|
+
* - `"same-origin"` or `ReferrerPolicy.SAME_ORIGIN`: Send full referrer for same-origin requests only, omit for cross-origin.
|
|
1096
|
+
* - `"strict-origin"` or `ReferrerPolicy.STRICT_ORIGIN`: Send origin for HTTPS→HTTPS or HTTP→HTTP, omit for HTTPS→HTTP.
|
|
1097
|
+
* - `"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.
|
|
1098
|
+
* - `"unsafe-url"` or `ReferrerPolicy.UNSAFE_URL`: Always send the full referrer URL (may leak sensitive information).
|
|
1099
|
+
*
|
|
1100
|
+
* @returns The request instance for chaining
|
|
1101
|
+
*
|
|
1062
1102
|
* @example
|
|
1103
|
+
* // Using string values
|
|
1063
1104
|
* request.withReferrerPolicy("no-referrer")
|
|
1105
|
+
*
|
|
1106
|
+
* @example
|
|
1107
|
+
* // Using enum values
|
|
1064
1108
|
* request.withReferrerPolicy(ReferrerPolicy.NO_REFERRER)
|
|
1109
|
+
*
|
|
1110
|
+
* @example
|
|
1111
|
+
* // Using fluent API
|
|
1065
1112
|
* request.withReferrerPolicy.NO_REFERRER()
|
|
1066
1113
|
*/
|
|
1067
1114
|
get withReferrerPolicy() {
|
|
@@ -1077,11 +1124,30 @@ class BaseRequest {
|
|
|
1077
1124
|
});
|
|
1078
1125
|
}
|
|
1079
1126
|
/**
|
|
1080
|
-
* Sets
|
|
1127
|
+
* Sets how the request handles HTTP redirects (3xx status codes).
|
|
1128
|
+
*
|
|
1129
|
+
* @param redirect - The redirect handling mode:
|
|
1130
|
+
* - `"follow"` or `RedirectMode.FOLLOW`: Automatically follow redirects. The fetch will transparently follow redirects and return the final response (default behavior).
|
|
1131
|
+
* - `"error"` or `RedirectMode.ERROR`: Treat redirects as errors. If a redirect occurs, the request will fail with an error.
|
|
1132
|
+
* - `"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.
|
|
1133
|
+
*
|
|
1134
|
+
* @returns The request instance for chaining
|
|
1135
|
+
*
|
|
1081
1136
|
* @example
|
|
1137
|
+
* // Using string values
|
|
1082
1138
|
* request.withRedirect("follow")
|
|
1139
|
+
*
|
|
1140
|
+
* @example
|
|
1141
|
+
* // Using enum values
|
|
1083
1142
|
* request.withRedirect(RedirectMode.FOLLOW)
|
|
1143
|
+
*
|
|
1144
|
+
* @example
|
|
1145
|
+
* // Using fluent API
|
|
1084
1146
|
* request.withRedirect.FOLLOW()
|
|
1147
|
+
*
|
|
1148
|
+
* @example
|
|
1149
|
+
* // Fail on redirects
|
|
1150
|
+
* request.withRedirect.ERROR()
|
|
1085
1151
|
*/
|
|
1086
1152
|
get withRedirect() {
|
|
1087
1153
|
return this.createFluentSetter("redirect", {
|
|
@@ -1091,20 +1157,50 @@ class BaseRequest {
|
|
|
1091
1157
|
});
|
|
1092
1158
|
}
|
|
1093
1159
|
/**
|
|
1094
|
-
* Sets the keepalive flag for the request
|
|
1095
|
-
*
|
|
1096
|
-
*
|
|
1160
|
+
* Sets the keepalive flag for the request. When enabled, the request can continue
|
|
1161
|
+
* even after the page that initiated it is closed. This is useful for analytics,
|
|
1162
|
+
* logging, or other background requests that should complete even if the user navigates away.
|
|
1163
|
+
*
|
|
1164
|
+
* @param keepalive - Whether to allow the request to outlive the page:
|
|
1165
|
+
* - `true`: The request will continue even if the page is closed or navigated away.
|
|
1166
|
+
* - `false`: The request will be cancelled if the page is closed (default).
|
|
1167
|
+
*
|
|
1168
|
+
* @returns The request instance for chaining
|
|
1169
|
+
*
|
|
1170
|
+
* @example
|
|
1171
|
+
* // Send analytics event that should complete even if user navigates away
|
|
1172
|
+
* request.withKeepAlive(true)
|
|
1097
1173
|
*/
|
|
1098
1174
|
withKeepAlive(keepalive) {
|
|
1099
1175
|
this.requestOptions.keepalive = keepalive;
|
|
1100
1176
|
return this;
|
|
1101
1177
|
}
|
|
1102
1178
|
/**
|
|
1103
|
-
* Sets
|
|
1179
|
+
* Sets the priority hint for the request, indicating to the browser how important
|
|
1180
|
+
* this request is relative to other requests. This helps the browser optimize resource loading.
|
|
1181
|
+
*
|
|
1182
|
+
* @param priority - The request priority:
|
|
1183
|
+
* - `"high"` or `RequestPriority.HIGH`: High priority - the browser should prioritize this request.
|
|
1184
|
+
* - `"low"` or `RequestPriority.LOW`: Low priority - the browser can defer this request if needed.
|
|
1185
|
+
* - `"auto"` or `RequestPriority.AUTO`: Automatic priority based on the request type (default).
|
|
1186
|
+
*
|
|
1187
|
+
* @returns The request instance for chaining
|
|
1188
|
+
*
|
|
1104
1189
|
* @example
|
|
1190
|
+
* // Using string values
|
|
1105
1191
|
* request.withPriority("high")
|
|
1192
|
+
*
|
|
1193
|
+
* @example
|
|
1194
|
+
* // Using enum values
|
|
1106
1195
|
* request.withPriority(RequestPriority.HIGH)
|
|
1196
|
+
*
|
|
1197
|
+
* @example
|
|
1198
|
+
* // Using fluent API
|
|
1107
1199
|
* request.withPriority.HIGH()
|
|
1200
|
+
*
|
|
1201
|
+
* @example
|
|
1202
|
+
* // Low priority for non-critical requests
|
|
1203
|
+
* request.withPriority.LOW()
|
|
1108
1204
|
*/
|
|
1109
1205
|
get withPriority() {
|
|
1110
1206
|
return this.createFluentSetter("priority", {
|
|
@@ -1114,22 +1210,62 @@ class BaseRequest {
|
|
|
1114
1210
|
});
|
|
1115
1211
|
}
|
|
1116
1212
|
/**
|
|
1117
|
-
* Sets the integrity hash for
|
|
1118
|
-
*
|
|
1119
|
-
*
|
|
1213
|
+
* Sets the integrity hash for Subresource Integrity (SRI) verification.
|
|
1214
|
+
* This allows the browser to verify that the fetched resource hasn't been tampered with
|
|
1215
|
+
* by comparing its hash against the provided value. If the hashes don't match, the request fails.
|
|
1216
|
+
*
|
|
1217
|
+
* @param integrity - The integrity hash string in the format `"algorithm-hash"`:
|
|
1218
|
+
* - Example: `"sha256-abcdef1234567890..."` (SHA-256 hash)
|
|
1219
|
+
* - Example: `"sha384-abcdef1234567890..."` (SHA-384 hash)
|
|
1220
|
+
* - Example: `"sha512-abcdef1234567890..."` (SHA-512 hash)
|
|
1221
|
+
* - Multiple hashes can be separated by spaces: `"sha256-... sha384-..."`
|
|
1222
|
+
*
|
|
1223
|
+
* @returns The request instance for chaining
|
|
1224
|
+
*
|
|
1120
1225
|
* @example
|
|
1121
1226
|
* request.withIntegrity("sha256-abcdef1234567890...")
|
|
1227
|
+
*
|
|
1228
|
+
* @example
|
|
1229
|
+
* // Multiple algorithms for better compatibility
|
|
1230
|
+
* request.withIntegrity("sha256-... sha384-...")
|
|
1122
1231
|
*/
|
|
1123
1232
|
withIntegrity(integrity) {
|
|
1124
1233
|
this.requestOptions.integrity = integrity;
|
|
1125
1234
|
return this;
|
|
1126
1235
|
}
|
|
1127
1236
|
/**
|
|
1128
|
-
* Sets cache mode
|
|
1237
|
+
* Sets the cache mode for the request, controlling how the browser's HTTP cache
|
|
1238
|
+
* is used for this request.
|
|
1239
|
+
*
|
|
1240
|
+
* @param cache - The cache mode:
|
|
1241
|
+
* - `"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.
|
|
1242
|
+
* - `"no-store"` or `CacheMode.NO_STORE`: Never use the cache and don't store the response in cache. Always fetch from network.
|
|
1243
|
+
* - `"reload"` or `CacheMode.RELOAD`: Bypass the cache but store the response. Always fetch from network, ignoring cached responses.
|
|
1244
|
+
* - `"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.
|
|
1245
|
+
* - `"force-cache"` or `CacheMode.FORCE_CACHE`: Use the cache if available, even if stale. Only fetch from network if not in cache.
|
|
1246
|
+
* - `"only-if-cached"` or `CacheMode.ONLY_IF_CACHED`: Only use the cache. If not in cache, return an error. Never fetch from network.
|
|
1247
|
+
*
|
|
1248
|
+
* @returns The request instance for chaining
|
|
1249
|
+
*
|
|
1129
1250
|
* @example
|
|
1251
|
+
* // Using string values
|
|
1130
1252
|
* request.withCache("no-cache")
|
|
1253
|
+
*
|
|
1254
|
+
* @example
|
|
1255
|
+
* // Using enum values
|
|
1131
1256
|
* request.withCache(CacheMode.NO_CACHE)
|
|
1257
|
+
*
|
|
1258
|
+
* @example
|
|
1259
|
+
* // Using fluent API
|
|
1132
1260
|
* request.withCache.NO_CACHE()
|
|
1261
|
+
*
|
|
1262
|
+
* @example
|
|
1263
|
+
* // Always fetch fresh data
|
|
1264
|
+
* request.withCache.RELOAD()
|
|
1265
|
+
*
|
|
1266
|
+
* @example
|
|
1267
|
+
* // Use cache only, fail if not cached
|
|
1268
|
+
* request.withCache.ONLY_IF_CACHED()
|
|
1133
1269
|
*/
|
|
1134
1270
|
get withCache() {
|
|
1135
1271
|
const cacheOptions = {
|
|
@@ -1178,11 +1314,32 @@ class BaseRequest {
|
|
|
1178
1314
|
return this;
|
|
1179
1315
|
}
|
|
1180
1316
|
/**
|
|
1181
|
-
* Sets request mode
|
|
1317
|
+
* Sets the request mode, which determines the CORS (Cross-Origin Resource Sharing) behavior
|
|
1318
|
+
* for the request. This controls how the browser handles cross-origin requests.
|
|
1319
|
+
*
|
|
1320
|
+
* @param mode - The request mode:
|
|
1321
|
+
* - `"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.
|
|
1322
|
+
* - `"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).
|
|
1323
|
+
* - `"same-origin"` or `RequestMode.SAME_ORIGIN`: Only allow same-origin requests. Cross-origin requests will fail.
|
|
1324
|
+
* - `"navigate"` or `RequestMode.NAVIGATE`: Used for navigation requests (typically only used by the browser itself).
|
|
1325
|
+
*
|
|
1326
|
+
* @returns The request instance for chaining
|
|
1327
|
+
*
|
|
1182
1328
|
* @example
|
|
1329
|
+
* // Using string values
|
|
1183
1330
|
* request.withMode("cors")
|
|
1331
|
+
*
|
|
1332
|
+
* @example
|
|
1333
|
+
* // Using enum values
|
|
1184
1334
|
* request.withMode(RequestMode.CORS)
|
|
1335
|
+
*
|
|
1336
|
+
* @example
|
|
1337
|
+
* // Using fluent API
|
|
1185
1338
|
* request.withMode.CORS()
|
|
1339
|
+
*
|
|
1340
|
+
* @example
|
|
1341
|
+
* // Restrict to same-origin only
|
|
1342
|
+
* request.withMode.SAME_ORIGIN()
|
|
1186
1343
|
*/
|
|
1187
1344
|
get withMode() {
|
|
1188
1345
|
return this.createFluentSetter("mode", {
|
|
@@ -1706,7 +1863,7 @@ class BaseRequest {
|
|
|
1706
1863
|
// No timeout - just use external controller
|
|
1707
1864
|
if (!timeoutMs) {
|
|
1708
1865
|
return {
|
|
1709
|
-
signal: externalController
|
|
1866
|
+
signal: externalController?.signal,
|
|
1710
1867
|
cleanup: () => { },
|
|
1711
1868
|
wasTimeout: () => false,
|
|
1712
1869
|
};
|
|
@@ -1889,9 +2046,11 @@ class BaseRequest {
|
|
|
1889
2046
|
* Base class for requests that can have a body (POST, PUT, PATCH)
|
|
1890
2047
|
*/
|
|
1891
2048
|
class BodyRequest extends BaseRequest {
|
|
2049
|
+
body;
|
|
2050
|
+
bodyType;
|
|
2051
|
+
graphQLOptions = undefined;
|
|
1892
2052
|
constructor(url) {
|
|
1893
2053
|
super(url);
|
|
1894
|
-
this.graphQLOptions = undefined;
|
|
1895
2054
|
}
|
|
1896
2055
|
/**
|
|
1897
2056
|
* Sets the body of the request
|
|
@@ -1906,7 +2065,13 @@ class BodyRequest extends BaseRequest {
|
|
|
1906
2065
|
}
|
|
1907
2066
|
else if (body !== null &&
|
|
1908
2067
|
typeof body === "object" &&
|
|
1909
|
-
!(body instanceof FormData ||
|
|
2068
|
+
!(body instanceof FormData ||
|
|
2069
|
+
body instanceof Blob ||
|
|
2070
|
+
body instanceof File ||
|
|
2071
|
+
body instanceof ArrayBuffer ||
|
|
2072
|
+
ArrayBuffer.isView(body) || // Handles TypedArray and DataView
|
|
2073
|
+
body instanceof URLSearchParams ||
|
|
2074
|
+
body instanceof ReadableStream)) {
|
|
1910
2075
|
this.bodyType = BodyType.JSON;
|
|
1911
2076
|
this.setContentTypeIfNeeded("application/json");
|
|
1912
2077
|
// Validate JSON is stringifiable early
|
|
@@ -2036,9 +2201,9 @@ class BodyRequest extends BaseRequest {
|
|
|
2036
2201
|
* const data = await request.getData();
|
|
2037
2202
|
*/
|
|
2038
2203
|
class GetRequest extends BaseRequest {
|
|
2204
|
+
method = exports.HttpMethod.GET;
|
|
2039
2205
|
constructor(url) {
|
|
2040
2206
|
super(url);
|
|
2041
|
-
this.method = exports.HttpMethod.GET;
|
|
2042
2207
|
}
|
|
2043
2208
|
}
|
|
2044
2209
|
/**
|
|
@@ -2050,9 +2215,9 @@ class GetRequest extends BaseRequest {
|
|
|
2050
2215
|
* const response = await request.getResponse();
|
|
2051
2216
|
*/
|
|
2052
2217
|
class HeadRequest extends BaseRequest {
|
|
2218
|
+
method = exports.HttpMethod.HEAD;
|
|
2053
2219
|
constructor(url) {
|
|
2054
2220
|
super(url);
|
|
2055
|
-
this.method = exports.HttpMethod.HEAD;
|
|
2056
2221
|
}
|
|
2057
2222
|
}
|
|
2058
2223
|
/**
|
|
@@ -2064,9 +2229,9 @@ class HeadRequest extends BaseRequest {
|
|
|
2064
2229
|
* const response = await request.getResponse();
|
|
2065
2230
|
*/
|
|
2066
2231
|
class OptionsRequest extends BaseRequest {
|
|
2232
|
+
method = exports.HttpMethod.OPTIONS;
|
|
2067
2233
|
constructor(url) {
|
|
2068
2234
|
super(url);
|
|
2069
|
-
this.method = exports.HttpMethod.OPTIONS;
|
|
2070
2235
|
}
|
|
2071
2236
|
}
|
|
2072
2237
|
/**
|
|
@@ -2078,9 +2243,9 @@ class OptionsRequest extends BaseRequest {
|
|
|
2078
2243
|
* await request.getData();
|
|
2079
2244
|
*/
|
|
2080
2245
|
class DeleteRequest extends BaseRequest {
|
|
2246
|
+
method = exports.HttpMethod.DELETE;
|
|
2081
2247
|
constructor(url) {
|
|
2082
2248
|
super(url);
|
|
2083
|
-
this.method = exports.HttpMethod.DELETE;
|
|
2084
2249
|
}
|
|
2085
2250
|
}
|
|
2086
2251
|
/**
|
|
@@ -2093,9 +2258,9 @@ class DeleteRequest extends BaseRequest {
|
|
|
2093
2258
|
* const data = await request.getData();
|
|
2094
2259
|
*/
|
|
2095
2260
|
class PostRequest extends BodyRequest {
|
|
2261
|
+
method = exports.HttpMethod.POST;
|
|
2096
2262
|
constructor(url) {
|
|
2097
2263
|
super(url);
|
|
2098
|
-
this.method = exports.HttpMethod.POST;
|
|
2099
2264
|
}
|
|
2100
2265
|
}
|
|
2101
2266
|
/**
|
|
@@ -2108,9 +2273,9 @@ class PostRequest extends BodyRequest {
|
|
|
2108
2273
|
* const data = await request.getData();
|
|
2109
2274
|
*/
|
|
2110
2275
|
class PutRequest extends BodyRequest {
|
|
2276
|
+
method = exports.HttpMethod.PUT;
|
|
2111
2277
|
constructor(url) {
|
|
2112
2278
|
super(url);
|
|
2113
|
-
this.method = exports.HttpMethod.PUT;
|
|
2114
2279
|
}
|
|
2115
2280
|
}
|
|
2116
2281
|
/**
|
|
@@ -2123,9 +2288,9 @@ class PutRequest extends BodyRequest {
|
|
|
2123
2288
|
* const data = await request.getData();
|
|
2124
2289
|
*/
|
|
2125
2290
|
class PatchRequest extends BodyRequest {
|
|
2291
|
+
method = exports.HttpMethod.PATCH;
|
|
2126
2292
|
constructor(url) {
|
|
2127
2293
|
super(url);
|
|
2128
|
-
this.method = exports.HttpMethod.PATCH;
|
|
2129
2294
|
}
|
|
2130
2295
|
}
|
|
2131
2296
|
|