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