create-request 1.6.0 → 1.6.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/dist/library/BaseRequest.d.ts +27 -27
- package/dist/library/BodyRequest.d.ts +6 -7
- package/dist/library/RequestError.d.ts +11 -1
- package/dist/library/ResponseWrapper.d.ts +19 -8
- package/dist/library/enums.d.ts +67 -58
- package/dist/library/index.cjs +429 -595
- package/dist/library/index.cjs.map +1 -1
- package/dist/library/index.esm.js +421 -595
- 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/requestMethods.d.ts +7 -14
- package/dist/library/utils/Config.d.ts +11 -11
- package/package.json +1 -1
package/dist/library/index.cjs
CHANGED
|
@@ -3,98 +3,99 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Enum for HTTP methods
|
|
5
5
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
})(exports.HttpMethod || (exports.HttpMethod = {}));
|
|
6
|
+
const HttpMethod = {
|
|
7
|
+
GET: "GET",
|
|
8
|
+
PUT: "PUT",
|
|
9
|
+
POST: "POST",
|
|
10
|
+
HEAD: "HEAD",
|
|
11
|
+
PATCH: "PATCH",
|
|
12
|
+
DELETE: "DELETE",
|
|
13
|
+
OPTIONS: "OPTIONS",
|
|
14
|
+
};
|
|
16
15
|
/**
|
|
17
16
|
* Enum for request priorities
|
|
18
17
|
*/
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
})(exports.RequestPriority || (exports.RequestPriority = {}));
|
|
18
|
+
const RequestPriority = {
|
|
19
|
+
LOW: "low",
|
|
20
|
+
HIGH: "high",
|
|
21
|
+
AUTO: "auto",
|
|
22
|
+
};
|
|
25
23
|
/**
|
|
26
24
|
* Enum for credentials policies
|
|
27
25
|
*/
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
})(exports.CredentialsPolicy || (exports.CredentialsPolicy = {}));
|
|
26
|
+
const CredentialsPolicy = {
|
|
27
|
+
OMIT: "omit",
|
|
28
|
+
INCLUDE: "include",
|
|
29
|
+
SAME_ORIGIN: "same-origin",
|
|
30
|
+
};
|
|
34
31
|
/**
|
|
35
32
|
* Enum for request modes
|
|
36
33
|
*/
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
})(exports.RequestMode || (exports.RequestMode = {}));
|
|
34
|
+
const RequestMode = {
|
|
35
|
+
CORS: "cors",
|
|
36
|
+
NO_CORS: "no-cors",
|
|
37
|
+
SAME_ORIGIN: "same-origin",
|
|
38
|
+
NAVIGATE: "navigate",
|
|
39
|
+
};
|
|
44
40
|
/**
|
|
45
41
|
* Enum for redirect modes
|
|
46
42
|
*/
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
})(exports.RedirectMode || (exports.RedirectMode = {}));
|
|
43
|
+
const RedirectMode = {
|
|
44
|
+
ERROR: "error",
|
|
45
|
+
FOLLOW: "follow",
|
|
46
|
+
MANUAL: "manual",
|
|
47
|
+
};
|
|
53
48
|
/**
|
|
54
49
|
* Enum for cookie SameSite policies
|
|
55
50
|
*/
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
})(exports.SameSitePolicy || (exports.SameSitePolicy = {}));
|
|
51
|
+
const SameSitePolicy = {
|
|
52
|
+
LAX: "Lax",
|
|
53
|
+
NONE: "None",
|
|
54
|
+
STRICT: "Strict",
|
|
55
|
+
};
|
|
62
56
|
/**
|
|
63
57
|
* Enum for body types
|
|
64
58
|
*/
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
})(BodyType || (BodyType = {}));
|
|
59
|
+
const BodyType = {
|
|
60
|
+
JSON: "json",
|
|
61
|
+
STRING: "string",
|
|
62
|
+
BINARY: "binary",
|
|
63
|
+
};
|
|
71
64
|
/**
|
|
72
65
|
* Referrer policies for fetch requests
|
|
73
66
|
*/
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
})(exports.ReferrerPolicy || (exports.ReferrerPolicy = {}));
|
|
67
|
+
const ReferrerPolicy = {
|
|
68
|
+
ORIGIN: "origin",
|
|
69
|
+
UNSAFE_URL: "unsafe-url",
|
|
70
|
+
SAME_ORIGIN: "same-origin",
|
|
71
|
+
NO_REFERRER: "no-referrer",
|
|
72
|
+
STRICT_ORIGIN: "strict-origin",
|
|
73
|
+
ORIGIN_WHEN_CROSS_ORIGIN: "origin-when-cross-origin",
|
|
74
|
+
NO_REFERRER_WHEN_DOWNGRADE: "no-referrer-when-downgrade",
|
|
75
|
+
STRICT_ORIGIN_WHEN_CROSS_ORIGIN: "strict-origin-when-cross-origin",
|
|
76
|
+
};
|
|
85
77
|
/**
|
|
86
78
|
* Cache modes for fetch requests
|
|
87
79
|
*/
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
})(exports.CacheMode || (exports.CacheMode = {}));
|
|
80
|
+
const CacheMode = {
|
|
81
|
+
RELOAD: "reload",
|
|
82
|
+
DEFAULT: "default",
|
|
83
|
+
NO_CACHE: "no-cache",
|
|
84
|
+
NO_STORE: "no-store",
|
|
85
|
+
FORCE_CACHE: "force-cache",
|
|
86
|
+
ONLY_IF_CACHED: "only-if-cached",
|
|
87
|
+
};
|
|
97
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Extract a message from an unknown thrown value
|
|
91
|
+
* @internal
|
|
92
|
+
*/
|
|
93
|
+
const errorMessage = (e) => (e instanceof Error ? e.message : String(e));
|
|
94
|
+
/**
|
|
95
|
+
* Coerce an unknown thrown value to an Error
|
|
96
|
+
* @internal
|
|
97
|
+
*/
|
|
98
|
+
const toError = (e) => (e instanceof Error ? e : new Error(String(e)));
|
|
98
99
|
/**
|
|
99
100
|
* Error class for HTTP request failures.
|
|
100
101
|
* Extends the standard Error class with additional context about the failed request.
|
|
@@ -135,7 +136,7 @@ class RequestError extends Error {
|
|
|
135
136
|
/** Whether the request was aborted (cancelled) */
|
|
136
137
|
isAborted;
|
|
137
138
|
/** Cached result of parsing `body` as JSON (lazily populated by getJson) */
|
|
138
|
-
|
|
139
|
+
_parsed;
|
|
139
140
|
/**
|
|
140
141
|
* Creates a new RequestError instance.
|
|
141
142
|
*
|
|
@@ -188,15 +189,15 @@ class RequestError extends Error {
|
|
|
188
189
|
* ```
|
|
189
190
|
*/
|
|
190
191
|
getJson() {
|
|
191
|
-
if (this.
|
|
192
|
+
if (this._parsed === undefined && this.body) {
|
|
192
193
|
try {
|
|
193
|
-
this.
|
|
194
|
+
this._parsed = JSON.parse(this.body);
|
|
194
195
|
}
|
|
195
196
|
catch {
|
|
196
197
|
// Body is not valid JSON - leave parsedBody undefined
|
|
197
198
|
}
|
|
198
199
|
}
|
|
199
|
-
return this.
|
|
200
|
+
return this._parsed;
|
|
200
201
|
}
|
|
201
202
|
/**
|
|
202
203
|
* Safely reads the body of a Response as text without consuming it.
|
|
@@ -292,55 +293,29 @@ class RequestError extends Error {
|
|
|
292
293
|
let message = originalError.message;
|
|
293
294
|
// Check for Node.js error codes (e.g., from undici/dns errors)
|
|
294
295
|
const errorCode = originalError.code;
|
|
295
|
-
const errorName = originalError.name;
|
|
296
296
|
const stack = originalError.stack || "";
|
|
297
|
-
const errorMessageLower = message.toLowerCase();
|
|
298
297
|
// Check for timeout errors (Node.js/undici TimeoutError)
|
|
299
298
|
// Note: While explicit timeouts set via withTimeout() are handled in BaseRequest,
|
|
300
|
-
// this detection serves as a safety net for
|
|
301
|
-
//
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
const isTimeoutError = errorName === "TimeoutError" ||
|
|
305
|
-
errorMessageLower.includes("timeout") ||
|
|
306
|
-
errorMessageLower.includes("aborted due to timeout") ||
|
|
299
|
+
// this detection serves as a safety net for timeout errors from external
|
|
300
|
+
// AbortControllers, other runtimes, and network-level timeouts (ETIMEDOUT).
|
|
301
|
+
const isTimeoutError = originalError.name === "TimeoutError" ||
|
|
302
|
+
message.toLowerCase().includes("timeout") ||
|
|
307
303
|
errorCode === "ETIMEDOUT" ||
|
|
308
304
|
stack.includes("TimeoutError") ||
|
|
309
305
|
stack.includes("timeout");
|
|
310
306
|
// If the error message is generic "fetch failed", provide more context
|
|
311
307
|
if (message === "fetch failed" || message === "Failed to fetch") {
|
|
312
308
|
// Check for DNS resolution errors
|
|
313
|
-
const isDnsError = errorCode === "ENOTFOUND" ||
|
|
314
|
-
errorCode === "EAI_AGAIN" ||
|
|
315
|
-
errorCode === "EAI_NODATA" ||
|
|
316
|
-
stack.includes("getaddrinfo") ||
|
|
317
|
-
stack.includes("ENOTFOUND") ||
|
|
318
|
-
stack.includes("EAI_AGAIN");
|
|
309
|
+
const isDnsError = errorCode === "ENOTFOUND" || errorCode === "EAI_AGAIN" || errorCode === "EAI_NODATA" || /getaddrinfo|ENOTFOUND|EAI_AGAIN/.test(stack);
|
|
319
310
|
// Check for connection errors (but not timeout errors)
|
|
320
311
|
const isConnectionError = !isTimeoutError && (errorCode === "ECONNREFUSED" || errorCode === "ECONNRESET" || stack.includes("ECONNREFUSED") || stack.includes("connect"));
|
|
321
|
-
|
|
322
|
-
message = `Timeout:${url}`;
|
|
323
|
-
}
|
|
324
|
-
else if (isDnsError) {
|
|
325
|
-
message = `DNS:${url}`;
|
|
326
|
-
}
|
|
327
|
-
else if (isConnectionError) {
|
|
328
|
-
message = `Conn:${url}`;
|
|
329
|
-
}
|
|
330
|
-
else {
|
|
331
|
-
message = `Net:${url}`;
|
|
332
|
-
}
|
|
312
|
+
message = (isTimeoutError ? "Timeout:" : isDnsError ? "DNS:" : isConnectionError ? "Conn:" : "Net:") + url;
|
|
333
313
|
}
|
|
334
|
-
const error = new RequestError(message, url, method, {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
// Create a proper RequestError stack trace, but append the original stack for debugging
|
|
338
|
-
// This way Node.js will show "RequestError: ..." instead of "TypeError: ..."
|
|
314
|
+
const error = new RequestError(message, url, method, isTimeoutError ? { isTimeout: true } : {});
|
|
315
|
+
// Create a proper RequestError stack trace, but append the original stack for
|
|
316
|
+
// debugging context, so Node.js shows "RequestError: ..." instead of "TypeError: ..."
|
|
339
317
|
if (originalError.stack) {
|
|
340
|
-
|
|
341
|
-
const currentStack = error.stack || "";
|
|
342
|
-
// Append the original error's stack as "Caused by:" for debugging context
|
|
343
|
-
error.stack = `${currentStack}\n\nCaused by: ${originalError.stack}`;
|
|
318
|
+
error.stack = `${error.stack || ""}\n\nCaused by: ${originalError.stack}`;
|
|
344
319
|
}
|
|
345
320
|
return error;
|
|
346
321
|
}
|
|
@@ -384,19 +359,19 @@ class ResponseWrapper {
|
|
|
384
359
|
url;
|
|
385
360
|
/** The HTTP method that was used (if available) */
|
|
386
361
|
method;
|
|
387
|
-
|
|
388
|
-
|
|
362
|
+
_res;
|
|
363
|
+
_gqlOpts;
|
|
389
364
|
// Cache the body as the last used method
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
365
|
+
_blob;
|
|
366
|
+
_text;
|
|
367
|
+
_json;
|
|
368
|
+
_buf;
|
|
394
369
|
constructor(response, url, method, graphQLOptions) {
|
|
395
|
-
this.
|
|
370
|
+
this._res = response;
|
|
396
371
|
this.url = url;
|
|
397
372
|
this.method = method;
|
|
398
373
|
if (graphQLOptions) {
|
|
399
|
-
this.
|
|
374
|
+
this._gqlOpts = {
|
|
400
375
|
throwOnError: graphQLOptions.throwOnError,
|
|
401
376
|
};
|
|
402
377
|
}
|
|
@@ -405,43 +380,65 @@ class ResponseWrapper {
|
|
|
405
380
|
* HTTP status code (e.g., 200, 404, 500)
|
|
406
381
|
*/
|
|
407
382
|
get status() {
|
|
408
|
-
return this.
|
|
383
|
+
return this._res.status;
|
|
409
384
|
}
|
|
410
385
|
/**
|
|
411
386
|
* HTTP status text (e.g., "OK", "Not Found", "Internal Server Error")
|
|
412
387
|
*/
|
|
413
388
|
get statusText() {
|
|
414
|
-
return this.
|
|
389
|
+
return this._res.statusText;
|
|
415
390
|
}
|
|
416
391
|
/**
|
|
417
392
|
* Response headers as a Headers object
|
|
418
393
|
*/
|
|
419
394
|
get headers() {
|
|
420
|
-
return this.
|
|
395
|
+
return this._res.headers;
|
|
421
396
|
}
|
|
422
397
|
/**
|
|
423
398
|
* Whether the response status is in the 200-299 range (successful)
|
|
424
399
|
*/
|
|
425
400
|
get ok() {
|
|
426
|
-
return this.
|
|
401
|
+
return this._res.ok;
|
|
427
402
|
}
|
|
428
403
|
/**
|
|
429
404
|
* The raw Response object from the fetch API.
|
|
430
405
|
* Use this if you need direct access to the underlying Response.
|
|
431
406
|
*/
|
|
432
407
|
get raw() {
|
|
433
|
-
return this.
|
|
408
|
+
return this._res;
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Create a RequestError carrying this response's context
|
|
412
|
+
* @param message - The error message
|
|
413
|
+
* @param withBody - Whether to attach the cached body text to the error
|
|
414
|
+
*/
|
|
415
|
+
_err(message, withBody) {
|
|
416
|
+
return new RequestError(message, this.url || "", this.method || "", {
|
|
417
|
+
status: this._res.status,
|
|
418
|
+
response: this._res,
|
|
419
|
+
body: withBody ? this._text : undefined,
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Read the response body via the given reader, wrapping failures in a RequestError
|
|
424
|
+
* @throws RequestError if the body has already been consumed or reading fails
|
|
425
|
+
*/
|
|
426
|
+
async _read(reader) {
|
|
427
|
+
this._checkUsed();
|
|
428
|
+
try {
|
|
429
|
+
return await reader();
|
|
430
|
+
}
|
|
431
|
+
catch (e) {
|
|
432
|
+
throw this._err(`Read: ${errorMessage(e)}`);
|
|
433
|
+
}
|
|
434
434
|
}
|
|
435
435
|
/**
|
|
436
436
|
* Check if the response body has already been consumed and throw an error if so
|
|
437
437
|
* @throws RequestError if the body has already been consumed
|
|
438
438
|
*/
|
|
439
|
-
|
|
440
|
-
if (this.
|
|
441
|
-
throw
|
|
442
|
-
status: this.response.status,
|
|
443
|
-
response: this.response,
|
|
444
|
-
});
|
|
439
|
+
_checkUsed() {
|
|
440
|
+
if (this._res.bodyUsed) {
|
|
441
|
+
throw this._err("Body used");
|
|
445
442
|
}
|
|
446
443
|
}
|
|
447
444
|
/**
|
|
@@ -449,8 +446,8 @@ class ResponseWrapper {
|
|
|
449
446
|
* @param data - The parsed JSON data
|
|
450
447
|
* @throws RequestError if GraphQL response contains errors and throwOnError is enabled
|
|
451
448
|
*/
|
|
452
|
-
|
|
453
|
-
if (!this.
|
|
449
|
+
_checkGql(data) {
|
|
450
|
+
if (!this._gqlOpts?.throwOnError || typeof data !== "object" || data === null)
|
|
454
451
|
return;
|
|
455
452
|
const responseData = data;
|
|
456
453
|
if (!Array.isArray(responseData.errors) || responseData.errors.length === 0)
|
|
@@ -479,12 +476,7 @@ class ResponseWrapper {
|
|
|
479
476
|
}
|
|
480
477
|
return String(x);
|
|
481
478
|
});
|
|
482
|
-
|
|
483
|
-
throw new RequestError(`GQL: ${errorMessage}`, this.url || "", this.method || "", {
|
|
484
|
-
status: this.response.status,
|
|
485
|
-
response: this.response,
|
|
486
|
-
body: this.cachedText,
|
|
487
|
-
});
|
|
479
|
+
throw this._err(`GQL: ${errorMessages.join(", ")}`, true);
|
|
488
480
|
}
|
|
489
481
|
/**
|
|
490
482
|
* Parse the response body as JSON
|
|
@@ -513,39 +505,35 @@ class ResponseWrapper {
|
|
|
513
505
|
* }
|
|
514
506
|
*/
|
|
515
507
|
async getJson() {
|
|
516
|
-
if (this.
|
|
517
|
-
return this.
|
|
508
|
+
if (this._json !== undefined)
|
|
509
|
+
return this._json;
|
|
518
510
|
// Handle empty responses: 204 No Content or content-length: 0
|
|
519
|
-
const contentLength = this.
|
|
520
|
-
if (this.
|
|
521
|
-
this.
|
|
511
|
+
const contentLength = this._res.headers.get("content-length");
|
|
512
|
+
if (this._res.status === 204 || contentLength === "0") {
|
|
513
|
+
this._json = null;
|
|
522
514
|
return null;
|
|
523
515
|
}
|
|
524
|
-
this.
|
|
516
|
+
this._checkUsed();
|
|
525
517
|
try {
|
|
526
518
|
// Read as text first to handle empty bodies and cache for getText()
|
|
527
|
-
const text = await this.
|
|
528
|
-
this.
|
|
519
|
+
const text = await this._res.text();
|
|
520
|
+
this._text = text;
|
|
529
521
|
// Handle empty or whitespace-only responses
|
|
530
522
|
if (!text || text.trim() === "") {
|
|
531
|
-
this.
|
|
523
|
+
this._json = null;
|
|
532
524
|
return null;
|
|
533
525
|
}
|
|
534
526
|
// Parse the text as JSON
|
|
535
527
|
const parsed = JSON.parse(text);
|
|
536
|
-
this.
|
|
537
|
-
this.
|
|
528
|
+
this._json = parsed;
|
|
529
|
+
this._checkGql(parsed);
|
|
538
530
|
return parsed;
|
|
539
531
|
}
|
|
540
532
|
catch (error) {
|
|
541
533
|
if (error instanceof RequestError) {
|
|
542
534
|
throw error;
|
|
543
535
|
}
|
|
544
|
-
throw
|
|
545
|
-
status: this.response.status,
|
|
546
|
-
response: this.response,
|
|
547
|
-
body: this.cachedText,
|
|
548
|
-
});
|
|
536
|
+
throw this._err(`Bad JSON: ${errorMessage(error)}`, true);
|
|
549
537
|
}
|
|
550
538
|
}
|
|
551
539
|
/**
|
|
@@ -562,20 +550,9 @@ class ResponseWrapper {
|
|
|
562
550
|
* ```
|
|
563
551
|
*/
|
|
564
552
|
async getText() {
|
|
565
|
-
if (this.
|
|
566
|
-
return this.
|
|
567
|
-
this.
|
|
568
|
-
try {
|
|
569
|
-
const text = await this.response.text();
|
|
570
|
-
this.cachedText = text;
|
|
571
|
-
return text;
|
|
572
|
-
}
|
|
573
|
-
catch (e) {
|
|
574
|
-
throw new RequestError(`Read: ${e instanceof Error ? e.message : String(e)}`, this.url || "", this.method || "", {
|
|
575
|
-
status: this.response.status,
|
|
576
|
-
response: this.response,
|
|
577
|
-
});
|
|
578
|
-
}
|
|
553
|
+
if (this._text !== undefined)
|
|
554
|
+
return this._text;
|
|
555
|
+
return (this._text = await this._read(() => this._res.text()));
|
|
579
556
|
}
|
|
580
557
|
/**
|
|
581
558
|
* Get the response body as a Blob.
|
|
@@ -593,20 +570,9 @@ class ResponseWrapper {
|
|
|
593
570
|
* ```
|
|
594
571
|
*/
|
|
595
572
|
async getBlob() {
|
|
596
|
-
if (this.
|
|
597
|
-
return this.
|
|
598
|
-
this.
|
|
599
|
-
try {
|
|
600
|
-
const blob = await this.response.blob();
|
|
601
|
-
this.cachedBlob = blob;
|
|
602
|
-
return blob;
|
|
603
|
-
}
|
|
604
|
-
catch (e) {
|
|
605
|
-
throw new RequestError(`Read: ${e instanceof Error ? e.message : String(e)}`, this.url || "", this.method || "", {
|
|
606
|
-
status: this.response.status,
|
|
607
|
-
response: this.response,
|
|
608
|
-
});
|
|
609
|
-
}
|
|
573
|
+
if (this._blob !== undefined)
|
|
574
|
+
return this._blob;
|
|
575
|
+
return (this._blob = await this._read(() => this._res.blob()));
|
|
610
576
|
}
|
|
611
577
|
/**
|
|
612
578
|
* Get the response body as an ArrayBuffer.
|
|
@@ -624,21 +590,10 @@ class ResponseWrapper {
|
|
|
624
590
|
* ```
|
|
625
591
|
*/
|
|
626
592
|
async getArrayBuffer() {
|
|
627
|
-
if (this.
|
|
628
|
-
return this.
|
|
629
|
-
}
|
|
630
|
-
this.checkBodyNotConsumed();
|
|
631
|
-
try {
|
|
632
|
-
const arrayBuffer = await this.response.arrayBuffer();
|
|
633
|
-
this.cachedArrayBuffer = arrayBuffer;
|
|
634
|
-
return arrayBuffer;
|
|
635
|
-
}
|
|
636
|
-
catch (e) {
|
|
637
|
-
throw new RequestError(`Read: ${e instanceof Error ? e.message : String(e)}`, this.url || "", this.method || "", {
|
|
638
|
-
status: this.response.status,
|
|
639
|
-
response: this.response,
|
|
640
|
-
});
|
|
593
|
+
if (this._buf !== undefined) {
|
|
594
|
+
return this._buf;
|
|
641
595
|
}
|
|
596
|
+
return (this._buf = await this._read(() => this._res.arrayBuffer()));
|
|
642
597
|
}
|
|
643
598
|
/**
|
|
644
599
|
* Get the raw response body as a ReadableStream
|
|
@@ -656,8 +611,8 @@ class ResponseWrapper {
|
|
|
656
611
|
* }
|
|
657
612
|
*/
|
|
658
613
|
getBody() {
|
|
659
|
-
this.
|
|
660
|
-
return this.
|
|
614
|
+
this._checkUsed();
|
|
615
|
+
return this._res.body;
|
|
661
616
|
}
|
|
662
617
|
/**
|
|
663
618
|
* Extract specific data using a selector function
|
|
@@ -707,16 +662,11 @@ class ResponseWrapper {
|
|
|
707
662
|
}
|
|
708
663
|
// Enhance selector errors with context
|
|
709
664
|
if (selector) {
|
|
710
|
-
throw
|
|
711
|
-
status: this.response.status,
|
|
712
|
-
response: this.response,
|
|
713
|
-
body: this.cachedText,
|
|
714
|
-
});
|
|
665
|
+
throw this._err(`Selector: ${errorMessage(error)}`, true);
|
|
715
666
|
}
|
|
716
667
|
// If we get here and it's not a RequestError, wrap it
|
|
717
668
|
// This should rarely happen as getJson() should throw RequestError
|
|
718
|
-
|
|
719
|
-
throw RequestError.networkError(this.url || "", this.method || "", errorObj);
|
|
669
|
+
throw RequestError.networkError(this.url || "", this.method || "", toError(error));
|
|
720
670
|
}
|
|
721
671
|
}
|
|
722
672
|
}
|
|
@@ -728,20 +678,9 @@ class CookieUtils {
|
|
|
728
678
|
* @returns Formatted cookie string for the Cookie header
|
|
729
679
|
*/
|
|
730
680
|
static formatRequestCookies(cookies) {
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
if (typeof valueOrOptions === "string") {
|
|
735
|
-
value = valueOrOptions;
|
|
736
|
-
}
|
|
737
|
-
else {
|
|
738
|
-
// Extract value from options object without validation
|
|
739
|
-
value = valueOrOptions.value;
|
|
740
|
-
}
|
|
741
|
-
// Add the cookie to the request
|
|
742
|
-
cookiePairs.push(`${encodeURIComponent(name)}=${encodeURIComponent(value)}`);
|
|
743
|
-
});
|
|
744
|
-
return cookiePairs.join("; ");
|
|
681
|
+
return Object.entries(cookies)
|
|
682
|
+
.map(([name, valueOrOptions]) => `${encodeURIComponent(name)}=${encodeURIComponent(typeof valueOrOptions === "string" ? valueOrOptions : valueOrOptions.value)}`)
|
|
683
|
+
.join("; ");
|
|
745
684
|
}
|
|
746
685
|
}
|
|
747
686
|
|
|
@@ -795,17 +734,11 @@ class CsrfUtils {
|
|
|
795
734
|
// If token is longer than 10 chars, perform additional security checks
|
|
796
735
|
if (token.length > 10) {
|
|
797
736
|
// Check for valid character set (alphanumeric & common token symbols)
|
|
798
|
-
|
|
799
|
-
if (!validTokenRegex.test(token)) {
|
|
737
|
+
if (!/^[A-Za-z0-9\-_=+/.]+$/.test(token)) {
|
|
800
738
|
return false;
|
|
801
739
|
}
|
|
802
740
|
// For longer tokens, check for sufficient entropy (at least 2 character types)
|
|
803
|
-
|
|
804
|
-
const hasLowerCase = /[a-z]/.test(token);
|
|
805
|
-
const hasNumbers = /[0-9]/.test(token);
|
|
806
|
-
const hasSpecials = /[-_=+/.]/.test(token);
|
|
807
|
-
const characterTypesCount = [hasUpperCase, hasLowerCase, hasNumbers, hasSpecials].filter(Boolean).length;
|
|
808
|
-
return characterTypesCount >= 2;
|
|
741
|
+
return [/[A-Z]/, /[a-z]/, /[0-9]/, /[-_=+/.]/].filter(re => re.test(token)).length >= 2;
|
|
809
742
|
}
|
|
810
743
|
// For shorter tokens (8-10 chars), just return true if we reached here
|
|
811
744
|
return true;
|
|
@@ -816,19 +749,19 @@ class CsrfUtils {
|
|
|
816
749
|
* Global configuration for create-request
|
|
817
750
|
*/
|
|
818
751
|
class Config {
|
|
819
|
-
static
|
|
752
|
+
static _instance;
|
|
820
753
|
// CSRF configuration
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
754
|
+
_csrfHeader = "X-CSRF-Token";
|
|
755
|
+
_xsrfCookie = "XSRF-TOKEN";
|
|
756
|
+
_xsrfHeader = "X-XSRF-TOKEN";
|
|
757
|
+
_csrfToken = null;
|
|
758
|
+
_autoXsrf = true;
|
|
759
|
+
_antiCsrf = true; // X-Requested-With header
|
|
827
760
|
// Interceptor configuration
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
761
|
+
_reqI = [];
|
|
762
|
+
_resI = [];
|
|
763
|
+
_errI = [];
|
|
764
|
+
_nextId = 1;
|
|
832
765
|
constructor() { }
|
|
833
766
|
/**
|
|
834
767
|
* Get the singleton instance of the Config class
|
|
@@ -840,10 +773,10 @@ class Config {
|
|
|
840
773
|
* config.setCsrfToken('token123');
|
|
841
774
|
*/
|
|
842
775
|
static getInstance() {
|
|
843
|
-
if (!Config.
|
|
844
|
-
Config.
|
|
776
|
+
if (!Config._instance) {
|
|
777
|
+
Config._instance = new Config();
|
|
845
778
|
}
|
|
846
|
-
return Config.
|
|
779
|
+
return Config._instance;
|
|
847
780
|
}
|
|
848
781
|
/**
|
|
849
782
|
* Set a global CSRF token to be used for all requests
|
|
@@ -855,7 +788,7 @@ class Config {
|
|
|
855
788
|
* Config.getInstance().setCsrfToken('myToken123');
|
|
856
789
|
*/
|
|
857
790
|
setCsrfToken(token) {
|
|
858
|
-
this.
|
|
791
|
+
this._csrfToken = token;
|
|
859
792
|
return this;
|
|
860
793
|
}
|
|
861
794
|
/**
|
|
@@ -864,7 +797,7 @@ class Config {
|
|
|
864
797
|
* @returns The current CSRF token or null if not set
|
|
865
798
|
*/
|
|
866
799
|
getCsrfToken() {
|
|
867
|
-
return this.
|
|
800
|
+
return this._csrfToken;
|
|
868
801
|
}
|
|
869
802
|
/**
|
|
870
803
|
* Set the CSRF header name used when sending the token
|
|
@@ -876,7 +809,7 @@ class Config {
|
|
|
876
809
|
* Config.getInstance().setCsrfHeaderName('X-My-CSRF-Token');
|
|
877
810
|
*/
|
|
878
811
|
setCsrfHeaderName(name) {
|
|
879
|
-
this.
|
|
812
|
+
this._csrfHeader = name;
|
|
880
813
|
return this;
|
|
881
814
|
}
|
|
882
815
|
/**
|
|
@@ -885,7 +818,7 @@ class Config {
|
|
|
885
818
|
* @returns The current CSRF header name
|
|
886
819
|
*/
|
|
887
820
|
getCsrfHeaderName() {
|
|
888
|
-
return this.
|
|
821
|
+
return this._csrfHeader;
|
|
889
822
|
}
|
|
890
823
|
/**
|
|
891
824
|
* Set the XSRF cookie name to look for when extracting tokens from cookies
|
|
@@ -897,7 +830,7 @@ class Config {
|
|
|
897
830
|
* Config.getInstance().setXsrfCookieName('MY-XSRF-COOKIE');
|
|
898
831
|
*/
|
|
899
832
|
setXsrfCookieName(name) {
|
|
900
|
-
this.
|
|
833
|
+
this._xsrfCookie = name;
|
|
901
834
|
return this;
|
|
902
835
|
}
|
|
903
836
|
/**
|
|
@@ -906,7 +839,7 @@ class Config {
|
|
|
906
839
|
* @returns The current XSRF cookie name
|
|
907
840
|
*/
|
|
908
841
|
getXsrfCookieName() {
|
|
909
|
-
return this.
|
|
842
|
+
return this._xsrfCookie;
|
|
910
843
|
}
|
|
911
844
|
/**
|
|
912
845
|
* Set the XSRF header name for sending tokens extracted from cookies
|
|
@@ -915,7 +848,7 @@ class Config {
|
|
|
915
848
|
* @returns The config instance for chaining
|
|
916
849
|
*/
|
|
917
850
|
setXsrfHeaderName(name) {
|
|
918
|
-
this.
|
|
851
|
+
this._xsrfHeader = name;
|
|
919
852
|
return this;
|
|
920
853
|
}
|
|
921
854
|
/**
|
|
@@ -924,7 +857,7 @@ class Config {
|
|
|
924
857
|
* @returns The current XSRF header name
|
|
925
858
|
*/
|
|
926
859
|
getXsrfHeaderName() {
|
|
927
|
-
return this.
|
|
860
|
+
return this._xsrfHeader;
|
|
928
861
|
}
|
|
929
862
|
/**
|
|
930
863
|
* Enable or disable automatic extraction of XSRF tokens from cookies
|
|
@@ -938,7 +871,7 @@ class Config {
|
|
|
938
871
|
* Config.getInstance().setEnableAutoXsrf(false); // Disable XSRF extraction
|
|
939
872
|
*/
|
|
940
873
|
setEnableAutoXsrf(enable) {
|
|
941
|
-
this.
|
|
874
|
+
this._autoXsrf = enable;
|
|
942
875
|
return this;
|
|
943
876
|
}
|
|
944
877
|
/**
|
|
@@ -947,7 +880,7 @@ class Config {
|
|
|
947
880
|
* @returns True if automatic XSRF is enabled
|
|
948
881
|
*/
|
|
949
882
|
isAutoXsrfEnabled() {
|
|
950
|
-
return this.
|
|
883
|
+
return this._autoXsrf;
|
|
951
884
|
}
|
|
952
885
|
/**
|
|
953
886
|
* Enable or disable automatic addition of anti-CSRF headers
|
|
@@ -957,7 +890,7 @@ class Config {
|
|
|
957
890
|
* @returns The config instance for chaining
|
|
958
891
|
*/
|
|
959
892
|
setEnableAntiCsrf(enable) {
|
|
960
|
-
this.
|
|
893
|
+
this._antiCsrf = enable;
|
|
961
894
|
return this;
|
|
962
895
|
}
|
|
963
896
|
/**
|
|
@@ -966,7 +899,7 @@ class Config {
|
|
|
966
899
|
* @returns True if anti-CSRF protection is enabled
|
|
967
900
|
*/
|
|
968
901
|
isAntiCsrfEnabled() {
|
|
969
|
-
return this.
|
|
902
|
+
return this._antiCsrf;
|
|
970
903
|
}
|
|
971
904
|
/**
|
|
972
905
|
* Add a global request interceptor
|
|
@@ -982,8 +915,8 @@ class Config {
|
|
|
982
915
|
* });
|
|
983
916
|
*/
|
|
984
917
|
addRequestInterceptor(interceptor) {
|
|
985
|
-
const id = this.
|
|
986
|
-
this.
|
|
918
|
+
const id = this._nextId++;
|
|
919
|
+
this._reqI.push([id, interceptor]);
|
|
987
920
|
return id;
|
|
988
921
|
}
|
|
989
922
|
/**
|
|
@@ -1000,8 +933,8 @@ class Config {
|
|
|
1000
933
|
* });
|
|
1001
934
|
*/
|
|
1002
935
|
addResponseInterceptor(interceptor) {
|
|
1003
|
-
const id = this.
|
|
1004
|
-
this.
|
|
936
|
+
const id = this._nextId++;
|
|
937
|
+
this._resI.push([id, interceptor]);
|
|
1005
938
|
return id;
|
|
1006
939
|
}
|
|
1007
940
|
/**
|
|
@@ -1018,8 +951,8 @@ class Config {
|
|
|
1018
951
|
* });
|
|
1019
952
|
*/
|
|
1020
953
|
addErrorInterceptor(interceptor) {
|
|
1021
|
-
const id = this.
|
|
1022
|
-
this.
|
|
954
|
+
const id = this._nextId++;
|
|
955
|
+
this._errI.push([id, interceptor]);
|
|
1023
956
|
return id;
|
|
1024
957
|
}
|
|
1025
958
|
/**
|
|
@@ -1031,7 +964,7 @@ class Config {
|
|
|
1031
964
|
* Config.getInstance().removeRequestInterceptor(id);
|
|
1032
965
|
*/
|
|
1033
966
|
removeRequestInterceptor(id) {
|
|
1034
|
-
this.
|
|
967
|
+
this._reqI = this._reqI.filter(item => item[0] !== id);
|
|
1035
968
|
}
|
|
1036
969
|
/**
|
|
1037
970
|
* Remove a response interceptor by its ID
|
|
@@ -1042,7 +975,7 @@ class Config {
|
|
|
1042
975
|
* Config.getInstance().removeResponseInterceptor(id);
|
|
1043
976
|
*/
|
|
1044
977
|
removeResponseInterceptor(id) {
|
|
1045
|
-
this.
|
|
978
|
+
this._resI = this._resI.filter(item => item[0] !== id);
|
|
1046
979
|
}
|
|
1047
980
|
/**
|
|
1048
981
|
* Remove an error interceptor by its ID
|
|
@@ -1053,7 +986,7 @@ class Config {
|
|
|
1053
986
|
* Config.getInstance().removeErrorInterceptor(id);
|
|
1054
987
|
*/
|
|
1055
988
|
removeErrorInterceptor(id) {
|
|
1056
|
-
this.
|
|
989
|
+
this._errI = this._errI.filter(item => item[0] !== id);
|
|
1057
990
|
}
|
|
1058
991
|
/**
|
|
1059
992
|
* Clear all interceptors (request, response, and error)
|
|
@@ -1062,30 +995,30 @@ class Config {
|
|
|
1062
995
|
* Config.getInstance().clearInterceptors();
|
|
1063
996
|
*/
|
|
1064
997
|
clearInterceptors() {
|
|
1065
|
-
this.
|
|
1066
|
-
this.
|
|
1067
|
-
this.
|
|
998
|
+
this._reqI = [];
|
|
999
|
+
this._resI = [];
|
|
1000
|
+
this._errI = [];
|
|
1068
1001
|
}
|
|
1069
1002
|
/**
|
|
1070
1003
|
* Get all global request interceptors (in registration order)
|
|
1071
1004
|
* @internal
|
|
1072
1005
|
*/
|
|
1073
1006
|
getRequestInterceptors() {
|
|
1074
|
-
return this.
|
|
1007
|
+
return this._reqI.map(item => item[1]);
|
|
1075
1008
|
}
|
|
1076
1009
|
/**
|
|
1077
1010
|
* Get all global response interceptors (in registration order)
|
|
1078
1011
|
* @internal
|
|
1079
1012
|
*/
|
|
1080
1013
|
getResponseInterceptors() {
|
|
1081
|
-
return this.
|
|
1014
|
+
return this._resI.map(item => item[1]);
|
|
1082
1015
|
}
|
|
1083
1016
|
/**
|
|
1084
1017
|
* Get all global error interceptors (in registration order)
|
|
1085
1018
|
* @internal
|
|
1086
1019
|
*/
|
|
1087
1020
|
getErrorInterceptors() {
|
|
1088
|
-
return this.
|
|
1021
|
+
return this._errI.map(item => item[1]);
|
|
1089
1022
|
}
|
|
1090
1023
|
/**
|
|
1091
1024
|
* Reset all configuration options to their default values
|
|
@@ -1096,12 +1029,12 @@ class Config {
|
|
|
1096
1029
|
* Config.getInstance().reset();
|
|
1097
1030
|
*/
|
|
1098
1031
|
reset() {
|
|
1099
|
-
this.
|
|
1100
|
-
this.
|
|
1101
|
-
this.
|
|
1102
|
-
this.
|
|
1103
|
-
this.
|
|
1104
|
-
this.
|
|
1032
|
+
this._csrfToken = null;
|
|
1033
|
+
this._csrfHeader = "X-CSRF-Token";
|
|
1034
|
+
this._antiCsrf = true;
|
|
1035
|
+
this._xsrfCookie = "XSRF-TOKEN";
|
|
1036
|
+
this._xsrfHeader = "X-XSRF-TOKEN";
|
|
1037
|
+
this._autoXsrf = true;
|
|
1105
1038
|
this.clearInterceptors();
|
|
1106
1039
|
return this;
|
|
1107
1040
|
}
|
|
@@ -1112,54 +1045,54 @@ class Config {
|
|
|
1112
1045
|
* Provides the core request building and execution capabilities.
|
|
1113
1046
|
*/
|
|
1114
1047
|
class BaseRequest {
|
|
1115
|
-
|
|
1116
|
-
|
|
1048
|
+
_url;
|
|
1049
|
+
_opts = {
|
|
1117
1050
|
headers: {},
|
|
1118
1051
|
};
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1052
|
+
_ctrl;
|
|
1053
|
+
_fetch;
|
|
1054
|
+
_query = new URLSearchParams();
|
|
1055
|
+
_autoCsrf = true;
|
|
1123
1056
|
// Per-request interceptors
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1057
|
+
_reqI = [];
|
|
1058
|
+
_resI = [];
|
|
1059
|
+
_errI = [];
|
|
1127
1060
|
constructor(url) {
|
|
1128
|
-
this.
|
|
1061
|
+
this._url = url;
|
|
1129
1062
|
}
|
|
1130
1063
|
/**
|
|
1131
1064
|
* Get GraphQL options if set (only for BodyRequest subclasses)
|
|
1132
1065
|
* @returns GraphQL options or undefined
|
|
1133
1066
|
*/
|
|
1134
|
-
|
|
1067
|
+
_gql() {
|
|
1135
1068
|
return undefined;
|
|
1136
1069
|
}
|
|
1137
1070
|
/**
|
|
1138
1071
|
* Creates a fluent API for setting enum-based options
|
|
1139
1072
|
* Combines direct setter with convenience methods
|
|
1140
1073
|
*/
|
|
1141
|
-
|
|
1074
|
+
_fluent(optionName, options) {
|
|
1142
1075
|
const fluent = {};
|
|
1143
1076
|
// Create convenience methods for each enum value
|
|
1144
1077
|
Object.entries(options).forEach(([key, value]) => {
|
|
1145
1078
|
fluent[key] = () => {
|
|
1146
|
-
this.
|
|
1079
|
+
this._opts[optionName] = value;
|
|
1147
1080
|
return this;
|
|
1148
1081
|
};
|
|
1149
1082
|
});
|
|
1150
1083
|
// Create the callable setter
|
|
1151
1084
|
const callable = (value) => {
|
|
1152
|
-
this.
|
|
1085
|
+
this._opts[optionName] = value;
|
|
1153
1086
|
return this;
|
|
1154
1087
|
};
|
|
1155
1088
|
return Object.assign(callable, fluent);
|
|
1156
1089
|
}
|
|
1157
|
-
|
|
1090
|
+
_validateUrl(url) {
|
|
1158
1091
|
const errorMessage = "Bad URL";
|
|
1159
1092
|
if (!url?.trim())
|
|
1160
|
-
throw new RequestError(errorMessage, url, this.
|
|
1093
|
+
throw new RequestError(errorMessage, url, this._method);
|
|
1161
1094
|
if (url.includes("\0") || url.includes("\r") || url.includes("\n")) {
|
|
1162
|
-
throw new RequestError(errorMessage, url, this.
|
|
1095
|
+
throw new RequestError(errorMessage, url, this._method);
|
|
1163
1096
|
}
|
|
1164
1097
|
const trimmed = url.trim();
|
|
1165
1098
|
if (/^https?:\/\//.test(trimmed)) {
|
|
@@ -1167,7 +1100,7 @@ class BaseRequest {
|
|
|
1167
1100
|
new URL(trimmed);
|
|
1168
1101
|
}
|
|
1169
1102
|
catch {
|
|
1170
|
-
throw new RequestError(errorMessage, trimmed, this.
|
|
1103
|
+
throw new RequestError(errorMessage, trimmed, this._method);
|
|
1171
1104
|
}
|
|
1172
1105
|
}
|
|
1173
1106
|
}
|
|
@@ -1185,16 +1118,12 @@ class BaseRequest {
|
|
|
1185
1118
|
*/
|
|
1186
1119
|
withHeaders(headers) {
|
|
1187
1120
|
// Filter out null and undefined values
|
|
1188
|
-
const
|
|
1189
|
-
|
|
1190
|
-
if (value
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
this.requestOptions.headers = {
|
|
1195
|
-
...this.getHeadersRecord(),
|
|
1196
|
-
...filteredHeaders,
|
|
1197
|
-
};
|
|
1121
|
+
const merged = { ...this._headers() };
|
|
1122
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
1123
|
+
if (value != null)
|
|
1124
|
+
merged[key] = value;
|
|
1125
|
+
}
|
|
1126
|
+
this._opts.headers = merged;
|
|
1198
1127
|
return this;
|
|
1199
1128
|
}
|
|
1200
1129
|
/**
|
|
@@ -1223,8 +1152,8 @@ class BaseRequest {
|
|
|
1223
1152
|
*/
|
|
1224
1153
|
withTimeout(timeout) {
|
|
1225
1154
|
if (!Number.isFinite(timeout) || timeout <= 0)
|
|
1226
|
-
throw new RequestError("Bad timeout", this.
|
|
1227
|
-
this.
|
|
1155
|
+
throw new RequestError("Bad timeout", this._url, this._method);
|
|
1156
|
+
this._opts.timeout = timeout;
|
|
1228
1157
|
return this;
|
|
1229
1158
|
}
|
|
1230
1159
|
/**
|
|
@@ -1260,30 +1189,22 @@ class BaseRequest {
|
|
|
1260
1189
|
* });
|
|
1261
1190
|
*/
|
|
1262
1191
|
withRetries(retries) {
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
}
|
|
1267
|
-
this.requestOptions.retries = retries;
|
|
1192
|
+
const isNumber = typeof retries === "number";
|
|
1193
|
+
const attempts = isNumber ? retries : retries.attempts;
|
|
1194
|
+
if (!Number.isInteger(attempts) || attempts < 0) {
|
|
1195
|
+
throw new RequestError(`Bad ${isNumber ? "retries" : "attempts"}: ${attempts}`, this._url, this._method);
|
|
1268
1196
|
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
if (
|
|
1272
|
-
|
|
1197
|
+
if (!isNumber && retries.delay !== undefined) {
|
|
1198
|
+
const delay = retries.delay;
|
|
1199
|
+
if (typeof delay === "number") {
|
|
1200
|
+
if (!Number.isFinite(delay) || delay < 0)
|
|
1201
|
+
throw new RequestError(`Bad delay: ${delay}`, this._url, this._method);
|
|
1273
1202
|
}
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
if (typeof retries.delay === "number") {
|
|
1277
|
-
if (!Number.isFinite(retries.delay) || retries.delay < 0) {
|
|
1278
|
-
throw new RequestError(`Bad delay: ${retries.delay}`, this.url, this.method);
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
else if (typeof retries.delay !== "function") {
|
|
1282
|
-
throw new RequestError(`Bad delay: ${typeof retries.delay}`, this.url, this.method);
|
|
1283
|
-
}
|
|
1203
|
+
else if (typeof delay !== "function") {
|
|
1204
|
+
throw new RequestError(`Bad delay: ${typeof delay}`, this._url, this._method);
|
|
1284
1205
|
}
|
|
1285
|
-
this.requestOptions.retries = retries;
|
|
1286
1206
|
}
|
|
1207
|
+
this._opts.retries = retries;
|
|
1287
1208
|
return this;
|
|
1288
1209
|
}
|
|
1289
1210
|
/**
|
|
@@ -1300,7 +1221,7 @@ class BaseRequest {
|
|
|
1300
1221
|
* });
|
|
1301
1222
|
*/
|
|
1302
1223
|
onRetry(callback) {
|
|
1303
|
-
this.
|
|
1224
|
+
this._opts.onRetry = callback;
|
|
1304
1225
|
return this;
|
|
1305
1226
|
}
|
|
1306
1227
|
/**
|
|
@@ -1327,11 +1248,7 @@ class BaseRequest {
|
|
|
1327
1248
|
* request.withCredentials.INCLUDE()
|
|
1328
1249
|
*/
|
|
1329
1250
|
get withCredentials() {
|
|
1330
|
-
return this.
|
|
1331
|
-
INCLUDE: exports.CredentialsPolicy.INCLUDE,
|
|
1332
|
-
OMIT: exports.CredentialsPolicy.OMIT,
|
|
1333
|
-
SAME_ORIGIN: exports.CredentialsPolicy.SAME_ORIGIN,
|
|
1334
|
-
});
|
|
1251
|
+
return this._fluent("credentials", CredentialsPolicy);
|
|
1335
1252
|
}
|
|
1336
1253
|
/**
|
|
1337
1254
|
* Allows providing an external AbortController to cancel the request.
|
|
@@ -1360,7 +1277,7 @@ class BaseRequest {
|
|
|
1360
1277
|
* controller.abort();
|
|
1361
1278
|
*/
|
|
1362
1279
|
withAbortController(controller) {
|
|
1363
|
-
this.
|
|
1280
|
+
this._ctrl = controller;
|
|
1364
1281
|
return this;
|
|
1365
1282
|
}
|
|
1366
1283
|
/**
|
|
@@ -1394,8 +1311,8 @@ class BaseRequest {
|
|
|
1394
1311
|
*/
|
|
1395
1312
|
withFetch(fetchFn) {
|
|
1396
1313
|
if (typeof fetchFn !== "function")
|
|
1397
|
-
throw new RequestError("Bad fetch", this.
|
|
1398
|
-
this.
|
|
1314
|
+
throw new RequestError("Bad fetch", this._url, this._method);
|
|
1315
|
+
this._fetch = fetchFn;
|
|
1399
1316
|
return this;
|
|
1400
1317
|
}
|
|
1401
1318
|
/**
|
|
@@ -1417,7 +1334,7 @@ class BaseRequest {
|
|
|
1417
1334
|
* request.withReferrer("")
|
|
1418
1335
|
*/
|
|
1419
1336
|
withReferrer(referrer) {
|
|
1420
|
-
this.
|
|
1337
|
+
this._opts.referrer = referrer;
|
|
1421
1338
|
return this;
|
|
1422
1339
|
}
|
|
1423
1340
|
/**
|
|
@@ -1449,16 +1366,7 @@ class BaseRequest {
|
|
|
1449
1366
|
* request.withReferrerPolicy.NO_REFERRER()
|
|
1450
1367
|
*/
|
|
1451
1368
|
get withReferrerPolicy() {
|
|
1452
|
-
return this.
|
|
1453
|
-
ORIGIN: exports.ReferrerPolicy.ORIGIN,
|
|
1454
|
-
UNSAFE_URL: exports.ReferrerPolicy.UNSAFE_URL,
|
|
1455
|
-
SAME_ORIGIN: exports.ReferrerPolicy.SAME_ORIGIN,
|
|
1456
|
-
NO_REFERRER: exports.ReferrerPolicy.NO_REFERRER,
|
|
1457
|
-
STRICT_ORIGIN: exports.ReferrerPolicy.STRICT_ORIGIN,
|
|
1458
|
-
ORIGIN_WHEN_CROSS_ORIGIN: exports.ReferrerPolicy.ORIGIN_WHEN_CROSS_ORIGIN,
|
|
1459
|
-
NO_REFERRER_WHEN_DOWNGRADE: exports.ReferrerPolicy.NO_REFERRER_WHEN_DOWNGRADE,
|
|
1460
|
-
STRICT_ORIGIN_WHEN_CROSS_ORIGIN: exports.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN,
|
|
1461
|
-
});
|
|
1369
|
+
return this._fluent("referrerPolicy", ReferrerPolicy);
|
|
1462
1370
|
}
|
|
1463
1371
|
/**
|
|
1464
1372
|
* Sets how the request handles HTTP redirects (3xx status codes).
|
|
@@ -1487,11 +1395,7 @@ class BaseRequest {
|
|
|
1487
1395
|
* request.withRedirect.ERROR()
|
|
1488
1396
|
*/
|
|
1489
1397
|
get withRedirect() {
|
|
1490
|
-
return this.
|
|
1491
|
-
FOLLOW: exports.RedirectMode.FOLLOW,
|
|
1492
|
-
ERROR: exports.RedirectMode.ERROR,
|
|
1493
|
-
MANUAL: exports.RedirectMode.MANUAL,
|
|
1494
|
-
});
|
|
1398
|
+
return this._fluent("redirect", RedirectMode);
|
|
1495
1399
|
}
|
|
1496
1400
|
/**
|
|
1497
1401
|
* Sets the keepalive flag for the request. When enabled, the request can continue
|
|
@@ -1509,7 +1413,7 @@ class BaseRequest {
|
|
|
1509
1413
|
* request.withKeepAlive(true)
|
|
1510
1414
|
*/
|
|
1511
1415
|
withKeepAlive(keepalive) {
|
|
1512
|
-
this.
|
|
1416
|
+
this._opts.keepalive = keepalive;
|
|
1513
1417
|
return this;
|
|
1514
1418
|
}
|
|
1515
1419
|
/**
|
|
@@ -1540,11 +1444,7 @@ class BaseRequest {
|
|
|
1540
1444
|
* request.withPriority.LOW()
|
|
1541
1445
|
*/
|
|
1542
1446
|
get withPriority() {
|
|
1543
|
-
return this.
|
|
1544
|
-
HIGH: exports.RequestPriority.HIGH,
|
|
1545
|
-
LOW: exports.RequestPriority.LOW,
|
|
1546
|
-
AUTO: exports.RequestPriority.AUTO,
|
|
1547
|
-
});
|
|
1447
|
+
return this._fluent("priority", RequestPriority);
|
|
1548
1448
|
}
|
|
1549
1449
|
/**
|
|
1550
1450
|
* Sets the integrity hash for Subresource Integrity (SRI) verification.
|
|
@@ -1567,7 +1467,7 @@ class BaseRequest {
|
|
|
1567
1467
|
* request.withIntegrity("sha256-... sha384-...")
|
|
1568
1468
|
*/
|
|
1569
1469
|
withIntegrity(integrity) {
|
|
1570
|
-
this.
|
|
1470
|
+
this._opts.integrity = integrity;
|
|
1571
1471
|
return this;
|
|
1572
1472
|
}
|
|
1573
1473
|
/**
|
|
@@ -1605,15 +1505,7 @@ class BaseRequest {
|
|
|
1605
1505
|
* request.withCache.ONLY_IF_CACHED()
|
|
1606
1506
|
*/
|
|
1607
1507
|
get withCache() {
|
|
1608
|
-
|
|
1609
|
-
DEFAULT: "default",
|
|
1610
|
-
NO_STORE: "no-store",
|
|
1611
|
-
RELOAD: "reload",
|
|
1612
|
-
NO_CACHE: "no-cache",
|
|
1613
|
-
FORCE_CACHE: "force-cache",
|
|
1614
|
-
ONLY_IF_CACHED: "only-if-cached",
|
|
1615
|
-
};
|
|
1616
|
-
return this.createFluentSetter("cache", cacheOptions);
|
|
1508
|
+
return this._fluent("cache", CacheMode);
|
|
1617
1509
|
}
|
|
1618
1510
|
/**
|
|
1619
1511
|
* Adds query parameters to the request URL.
|
|
@@ -1652,10 +1544,10 @@ class BaseRequest {
|
|
|
1652
1544
|
}
|
|
1653
1545
|
if (Array.isArray(value)) {
|
|
1654
1546
|
// Handle array values - add multiple entries with the same key
|
|
1655
|
-
value.forEach(v => this.
|
|
1547
|
+
value.forEach(v => this._query.append(key, String(v)));
|
|
1656
1548
|
}
|
|
1657
1549
|
else {
|
|
1658
|
-
this.
|
|
1550
|
+
this._query.append(key, String(value));
|
|
1659
1551
|
}
|
|
1660
1552
|
});
|
|
1661
1553
|
return this;
|
|
@@ -1713,12 +1605,7 @@ class BaseRequest {
|
|
|
1713
1605
|
* request.withMode.SAME_ORIGIN()
|
|
1714
1606
|
*/
|
|
1715
1607
|
get withMode() {
|
|
1716
|
-
return this.
|
|
1717
|
-
CORS: exports.RequestMode.CORS,
|
|
1718
|
-
NO_CORS: exports.RequestMode.NO_CORS,
|
|
1719
|
-
SAME_ORIGIN: exports.RequestMode.SAME_ORIGIN,
|
|
1720
|
-
NAVIGATE: exports.RequestMode.NAVIGATE,
|
|
1721
|
-
});
|
|
1608
|
+
return this._fluent("mode", RequestMode);
|
|
1722
1609
|
}
|
|
1723
1610
|
/**
|
|
1724
1611
|
* Sets the Content-Type header for the request.
|
|
@@ -1776,28 +1663,26 @@ class BaseRequest {
|
|
|
1776
1663
|
* ```
|
|
1777
1664
|
*/
|
|
1778
1665
|
withBasicAuth(username, password) {
|
|
1779
|
-
const credentials = this.
|
|
1666
|
+
const credentials = this._b64(`${username}:${password}`);
|
|
1780
1667
|
return this.withAuthorization(`Basic ${credentials}`);
|
|
1781
1668
|
}
|
|
1782
1669
|
/**
|
|
1783
1670
|
* Cross-environment base64 encoding
|
|
1784
1671
|
* Works in both browser and Node.js environments
|
|
1785
1672
|
*/
|
|
1786
|
-
|
|
1673
|
+
_b64(str) {
|
|
1787
1674
|
// Modern approach using TextEncoder (available in both modern browsers and Node.js)
|
|
1788
|
-
if (typeof
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
}
|
|
1793
|
-
// Browser environment
|
|
1794
|
-
if (typeof btoa === "function")
|
|
1675
|
+
if (typeof btoa === "function") {
|
|
1676
|
+
if (typeof TextEncoder !== "undefined")
|
|
1677
|
+
return btoa(String.fromCharCode(...new TextEncoder().encode(str)));
|
|
1678
|
+
// Browser environment without TextEncoder
|
|
1795
1679
|
return btoa(str);
|
|
1680
|
+
}
|
|
1796
1681
|
// Node.js environment
|
|
1797
1682
|
if (typeof Buffer !== "undefined")
|
|
1798
1683
|
return Buffer.from(str).toString("base64");
|
|
1799
1684
|
// Fallback (should never happen in modern environments)
|
|
1800
|
-
throw new RequestError("No encoder", this.
|
|
1685
|
+
throw new RequestError("No encoder", this._url, this._method);
|
|
1801
1686
|
}
|
|
1802
1687
|
/**
|
|
1803
1688
|
* Sets a Bearer token for authentication.
|
|
@@ -1819,9 +1704,9 @@ class BaseRequest {
|
|
|
1819
1704
|
* Safely get headers as a Record<string, string>
|
|
1820
1705
|
* @returns The headers object
|
|
1821
1706
|
*/
|
|
1822
|
-
|
|
1823
|
-
if (typeof this.
|
|
1824
|
-
return this.
|
|
1707
|
+
_headers() {
|
|
1708
|
+
if (typeof this._opts.headers === "object" && this._opts.headers !== null) {
|
|
1709
|
+
return this._opts.headers;
|
|
1825
1710
|
}
|
|
1826
1711
|
return {};
|
|
1827
1712
|
}
|
|
@@ -1830,8 +1715,8 @@ class BaseRequest {
|
|
|
1830
1715
|
* @param headerName Header name to check
|
|
1831
1716
|
* @returns Boolean indicating if the header exists (case-insensitive)
|
|
1832
1717
|
*/
|
|
1833
|
-
|
|
1834
|
-
const headers = this.
|
|
1718
|
+
_hasHeader(headerName) {
|
|
1719
|
+
const headers = this._headers();
|
|
1835
1720
|
return Object.keys(headers).some(key => key.toLowerCase() === headerName.toLowerCase());
|
|
1836
1721
|
}
|
|
1837
1722
|
/**
|
|
@@ -1860,37 +1745,26 @@ class BaseRequest {
|
|
|
1860
1745
|
* ```
|
|
1861
1746
|
*/
|
|
1862
1747
|
withCookies(cookies) {
|
|
1863
|
-
|
|
1864
|
-
if (cookieEntries.length === 0) {
|
|
1748
|
+
if (Object.keys(cookies || {}).length === 0)
|
|
1865
1749
|
return this;
|
|
1866
|
-
|
|
1867
|
-
const
|
|
1868
|
-
// Get all existing cookie headers with different cases
|
|
1750
|
+
// Collect existing cookie header values (any casing), preserving the first header's case
|
|
1751
|
+
const newHeaders = { ...this._headers() };
|
|
1869
1752
|
const cookieValues = [];
|
|
1870
|
-
|
|
1871
|
-
Object.keys(
|
|
1753
|
+
let headerName = "";
|
|
1754
|
+
for (const key of Object.keys(newHeaders)) {
|
|
1872
1755
|
if (key.toLowerCase() === "cookie") {
|
|
1873
1756
|
// Don't add empty cookie values
|
|
1874
|
-
if (
|
|
1875
|
-
cookieValues.push(
|
|
1876
|
-
|
|
1757
|
+
if (newHeaders[key])
|
|
1758
|
+
cookieValues.push(newHeaders[key]);
|
|
1759
|
+
headerName ||= key;
|
|
1760
|
+
delete newHeaders[key];
|
|
1877
1761
|
}
|
|
1878
|
-
}
|
|
1879
|
-
//
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
const headerName = cookieHeaderKeys.length > 0 ? cookieHeaderKeys[0] : "Cookie";
|
|
1883
|
-
// Combine all existing cookie values with the new ones
|
|
1884
|
-
const combinedCookieValue = [...cookieValues, cookieString].filter(Boolean).join("; ");
|
|
1885
|
-
// Create a new headers object without any cookie headers
|
|
1886
|
-
const newHeaders = { ...currentHeaders };
|
|
1887
|
-
cookieHeaderKeys.forEach(key => {
|
|
1888
|
-
delete newHeaders[key];
|
|
1889
|
-
});
|
|
1890
|
-
// Set the new combined cookie header
|
|
1891
|
-
this.requestOptions.headers = {
|
|
1762
|
+
}
|
|
1763
|
+
// Combine all existing cookie values with the newly formatted ones
|
|
1764
|
+
cookieValues.push(CookieUtils.formatRequestCookies(cookies));
|
|
1765
|
+
this._opts.headers = {
|
|
1892
1766
|
...newHeaders,
|
|
1893
|
-
[headerName]:
|
|
1767
|
+
[headerName || "Cookie"]: cookieValues.filter(Boolean).join("; "),
|
|
1894
1768
|
};
|
|
1895
1769
|
return this;
|
|
1896
1770
|
}
|
|
@@ -1944,7 +1818,7 @@ class BaseRequest {
|
|
|
1944
1818
|
* @returns The instance for chaining
|
|
1945
1819
|
*/
|
|
1946
1820
|
withoutCsrfProtection() {
|
|
1947
|
-
this.
|
|
1821
|
+
this._autoCsrf = false;
|
|
1948
1822
|
return this;
|
|
1949
1823
|
}
|
|
1950
1824
|
/**
|
|
@@ -1968,7 +1842,7 @@ class BaseRequest {
|
|
|
1968
1842
|
* });
|
|
1969
1843
|
*/
|
|
1970
1844
|
withRequestInterceptor(interceptor) {
|
|
1971
|
-
this.
|
|
1845
|
+
this._reqI.push(interceptor);
|
|
1972
1846
|
return this;
|
|
1973
1847
|
}
|
|
1974
1848
|
/**
|
|
@@ -1985,7 +1859,7 @@ class BaseRequest {
|
|
|
1985
1859
|
* });
|
|
1986
1860
|
*/
|
|
1987
1861
|
withResponseInterceptor(interceptor) {
|
|
1988
|
-
this.
|
|
1862
|
+
this._resI.push(interceptor);
|
|
1989
1863
|
return this;
|
|
1990
1864
|
}
|
|
1991
1865
|
/**
|
|
@@ -2002,7 +1876,7 @@ class BaseRequest {
|
|
|
2002
1876
|
* });
|
|
2003
1877
|
*/
|
|
2004
1878
|
withErrorInterceptor(interceptor) {
|
|
2005
|
-
this.
|
|
1879
|
+
this._errI.push(interceptor);
|
|
2006
1880
|
return this;
|
|
2007
1881
|
}
|
|
2008
1882
|
/**
|
|
@@ -2016,13 +1890,13 @@ class BaseRequest {
|
|
|
2016
1890
|
* console.log(response.status);
|
|
2017
1891
|
*/
|
|
2018
1892
|
async getResponse() {
|
|
2019
|
-
const url = this.
|
|
2020
|
-
this.
|
|
1893
|
+
const url = this._fullUrl(this._url);
|
|
1894
|
+
this._applyCsrf();
|
|
2021
1895
|
const fetchOptions = {
|
|
2022
|
-
...this.
|
|
2023
|
-
method: this.
|
|
1896
|
+
...this._opts,
|
|
1897
|
+
method: this._method,
|
|
2024
1898
|
};
|
|
2025
|
-
return !this.
|
|
1899
|
+
return !this._opts.retries ? this._run(url, fetchOptions) : this._retry(url, fetchOptions);
|
|
2026
1900
|
}
|
|
2027
1901
|
/**
|
|
2028
1902
|
* Execute the request and parse the response as JSON
|
|
@@ -2163,8 +2037,8 @@ class BaseRequest {
|
|
|
2163
2037
|
/**
|
|
2164
2038
|
* Apply CSRF protection headers based on configuration
|
|
2165
2039
|
*/
|
|
2166
|
-
|
|
2167
|
-
if (!this.
|
|
2040
|
+
_applyCsrf() {
|
|
2041
|
+
if (!this._autoCsrf)
|
|
2168
2042
|
return;
|
|
2169
2043
|
const config = Config.getInstance();
|
|
2170
2044
|
// Apply anti-CSRF headers if enabled
|
|
@@ -2175,7 +2049,7 @@ class BaseRequest {
|
|
|
2175
2049
|
const globalToken = config.getCsrfToken();
|
|
2176
2050
|
if (globalToken) {
|
|
2177
2051
|
const csrfHeaderName = config.getCsrfHeaderName();
|
|
2178
|
-
const hasLocalToken = this.
|
|
2052
|
+
const hasLocalToken = this._hasHeader("X-CSRF-Token") || this._hasHeader(csrfHeaderName);
|
|
2179
2053
|
if (!hasLocalToken) {
|
|
2180
2054
|
this.withHeader(csrfHeaderName, globalToken);
|
|
2181
2055
|
}
|
|
@@ -2185,7 +2059,7 @@ class BaseRequest {
|
|
|
2185
2059
|
const xsrfToken = CsrfUtils.getTokenFromCookie(config.getXsrfCookieName());
|
|
2186
2060
|
if (xsrfToken && CsrfUtils.isValidToken(xsrfToken)) {
|
|
2187
2061
|
const xsrfHeaderName = config.getXsrfHeaderName();
|
|
2188
|
-
const hasLocalToken = this.
|
|
2062
|
+
const hasLocalToken = this._hasHeader("X-XSRF-TOKEN") || this._hasHeader(xsrfHeaderName);
|
|
2189
2063
|
if (!hasLocalToken) {
|
|
2190
2064
|
this.withHeader(xsrfHeaderName, xsrfToken);
|
|
2191
2065
|
}
|
|
@@ -2197,8 +2071,8 @@ class BaseRequest {
|
|
|
2197
2071
|
* @param url The base URL
|
|
2198
2072
|
* @returns The URL with query parameters appended
|
|
2199
2073
|
*/
|
|
2200
|
-
|
|
2201
|
-
const queryString = this.
|
|
2074
|
+
_fullUrl(url) {
|
|
2075
|
+
const queryString = this._query.toString();
|
|
2202
2076
|
if (!queryString) {
|
|
2203
2077
|
return url;
|
|
2204
2078
|
}
|
|
@@ -2206,7 +2080,7 @@ class BaseRequest {
|
|
|
2206
2080
|
// Try to use the URL constructor (works for absolute URLs)
|
|
2207
2081
|
const urlObj = new URL(url);
|
|
2208
2082
|
// Merge our query params with any that might be in the URL already
|
|
2209
|
-
this.
|
|
2083
|
+
this._query.forEach((value, key) => {
|
|
2210
2084
|
urlObj.searchParams.append(key, value);
|
|
2211
2085
|
});
|
|
2212
2086
|
return urlObj.toString();
|
|
@@ -2225,21 +2099,21 @@ class BaseRequest {
|
|
|
2225
2099
|
* @returns A wrapped response object
|
|
2226
2100
|
* @throws RequestError if the request fails after all retries
|
|
2227
2101
|
*/
|
|
2228
|
-
async
|
|
2229
|
-
const retriesConfig = this.
|
|
2102
|
+
async _retry(url, fetchOptions) {
|
|
2103
|
+
const retriesConfig = this._opts.retries;
|
|
2230
2104
|
const maxRetries = typeof retriesConfig === "number" ? retriesConfig : retriesConfig?.attempts || 0;
|
|
2231
2105
|
const method = typeof fetchOptions.method === "string" ? fetchOptions.method : "GET";
|
|
2232
2106
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
2233
2107
|
try {
|
|
2234
|
-
return await this.
|
|
2108
|
+
return await this._run(url, fetchOptions);
|
|
2235
2109
|
}
|
|
2236
2110
|
catch (error) {
|
|
2237
|
-
const requestError = error instanceof RequestError ? error : RequestError.networkError(url, method,
|
|
2111
|
+
const requestError = error instanceof RequestError ? error : RequestError.networkError(url, method, toError(error));
|
|
2238
2112
|
if (attempt >= maxRetries)
|
|
2239
2113
|
throw requestError;
|
|
2240
2114
|
// Call onRetry callback if provided
|
|
2241
|
-
if (this.
|
|
2242
|
-
await this.
|
|
2115
|
+
if (this._opts.onRetry) {
|
|
2116
|
+
await this._opts.onRetry({ attempt: attempt + 1, error: requestError });
|
|
2243
2117
|
}
|
|
2244
2118
|
// Apply delay if configured
|
|
2245
2119
|
if (typeof retriesConfig === "object" && retriesConfig.delay !== undefined) {
|
|
@@ -2263,9 +2137,9 @@ class BaseRequest {
|
|
|
2263
2137
|
* @param configParam - The request configuration
|
|
2264
2138
|
* @returns Modified config or a Response to short-circuit
|
|
2265
2139
|
*/
|
|
2266
|
-
async
|
|
2140
|
+
async _runReqI(configParam) {
|
|
2267
2141
|
const globalConfig = Config.getInstance();
|
|
2268
|
-
const allInterceptors = [...globalConfig.getRequestInterceptors(), ...this.
|
|
2142
|
+
const allInterceptors = [...globalConfig.getRequestInterceptors(), ...this._reqI];
|
|
2269
2143
|
let currentConfig = configParam;
|
|
2270
2144
|
for (let i = 0; i < allInterceptors.length; i++) {
|
|
2271
2145
|
try {
|
|
@@ -2277,7 +2151,7 @@ class BaseRequest {
|
|
|
2277
2151
|
currentConfig = result;
|
|
2278
2152
|
}
|
|
2279
2153
|
catch (error) {
|
|
2280
|
-
throw new RequestError(`ReqI: ${
|
|
2154
|
+
throw new RequestError(`ReqI: ${errorMessage(error)}`, currentConfig.url, currentConfig.method);
|
|
2281
2155
|
}
|
|
2282
2156
|
}
|
|
2283
2157
|
return currentConfig;
|
|
@@ -2287,18 +2161,18 @@ class BaseRequest {
|
|
|
2287
2161
|
* @param response - The response wrapper
|
|
2288
2162
|
* @returns Modified response wrapper
|
|
2289
2163
|
*/
|
|
2290
|
-
async
|
|
2164
|
+
async _runResI(response) {
|
|
2291
2165
|
const globalConfig = Config.getInstance();
|
|
2292
2166
|
const globalInterceptors = globalConfig.getResponseInterceptors();
|
|
2293
2167
|
// Per-request in order, then global in reverse
|
|
2294
|
-
const allInterceptors = [...this.
|
|
2168
|
+
const allInterceptors = [...this._resI, ...[...globalInterceptors].reverse()];
|
|
2295
2169
|
let currentResponse = response;
|
|
2296
2170
|
for (let i = 0; i < allInterceptors.length; i++) {
|
|
2297
2171
|
try {
|
|
2298
2172
|
currentResponse = await allInterceptors[i](currentResponse);
|
|
2299
2173
|
}
|
|
2300
2174
|
catch (error) {
|
|
2301
|
-
throw new RequestError(`ResI: ${
|
|
2175
|
+
throw new RequestError(`ResI: ${errorMessage(error)}`, currentResponse.url || "", currentResponse.method || "");
|
|
2302
2176
|
}
|
|
2303
2177
|
}
|
|
2304
2178
|
return currentResponse;
|
|
@@ -2308,11 +2182,11 @@ class BaseRequest {
|
|
|
2308
2182
|
* @param error - The error that occurred
|
|
2309
2183
|
* @returns Modified error or a ResponseWrapper to recover
|
|
2310
2184
|
*/
|
|
2311
|
-
async
|
|
2185
|
+
async _runErrI(error) {
|
|
2312
2186
|
const globalConfig = Config.getInstance();
|
|
2313
2187
|
const globalInterceptors = globalConfig.getErrorInterceptors();
|
|
2314
2188
|
// Per-request in order, then global in reverse
|
|
2315
|
-
const allInterceptors = [...this.
|
|
2189
|
+
const allInterceptors = [...this._errI, ...[...globalInterceptors].reverse()];
|
|
2316
2190
|
let currentError = error;
|
|
2317
2191
|
for (let i = 0; i < allInterceptors.length; i++) {
|
|
2318
2192
|
try {
|
|
@@ -2328,24 +2202,20 @@ class BaseRequest {
|
|
|
2328
2202
|
if (interceptorError instanceof RequestError) {
|
|
2329
2203
|
currentError = interceptorError;
|
|
2330
2204
|
}
|
|
2331
|
-
else {
|
|
2332
|
-
const em = interceptorError instanceof Error ? interceptorError.message : String(interceptorError);
|
|
2205
|
+
else if (currentError instanceof RequestError) {
|
|
2333
2206
|
// Always wrap in RequestError when we have context
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
currentError = RequestError.networkError(error.url, error.method, errorObj);
|
|
2347
|
-
/* c8 ignore end */
|
|
2348
|
-
}
|
|
2207
|
+
currentError = new RequestError(`ErrI${i + 1}: ${errorMessage(interceptorError)}`, currentError.url, currentError.method, {
|
|
2208
|
+
status: currentError.status,
|
|
2209
|
+
response: currentError.response,
|
|
2210
|
+
body: currentError.body,
|
|
2211
|
+
});
|
|
2212
|
+
}
|
|
2213
|
+
else {
|
|
2214
|
+
/* c8 ignore start */
|
|
2215
|
+
// Last resort: if we have no context at all, use the original error's context
|
|
2216
|
+
// This shouldn't happen in practice, but handle it gracefully
|
|
2217
|
+
currentError = RequestError.networkError(error.url, error.method, toError(interceptorError));
|
|
2218
|
+
/* c8 ignore end */
|
|
2349
2219
|
}
|
|
2350
2220
|
}
|
|
2351
2221
|
}
|
|
@@ -2355,7 +2225,7 @@ class BaseRequest {
|
|
|
2355
2225
|
* Helper to create an abort signal with timeout support
|
|
2356
2226
|
* Handles various AbortSignal API levels gracefully
|
|
2357
2227
|
*/
|
|
2358
|
-
|
|
2228
|
+
_signal(timeoutMs, externalController) {
|
|
2359
2229
|
let timeoutId;
|
|
2360
2230
|
let timeoutController;
|
|
2361
2231
|
let isTimeout = false;
|
|
@@ -2391,7 +2261,7 @@ class BaseRequest {
|
|
|
2391
2261
|
const finalSignal = externalController && hasAbortSignalAny
|
|
2392
2262
|
? AbortSignal.any([externalController.signal, timeoutSignal])
|
|
2393
2263
|
: externalController
|
|
2394
|
-
? this.
|
|
2264
|
+
? this._combine(externalController.signal, timeoutSignal)
|
|
2395
2265
|
: timeoutSignal;
|
|
2396
2266
|
return {
|
|
2397
2267
|
signal: finalSignal,
|
|
@@ -2406,7 +2276,7 @@ class BaseRequest {
|
|
|
2406
2276
|
* Manually combine two abort signals for older environments
|
|
2407
2277
|
* Returns the first signal and listens to the second
|
|
2408
2278
|
*/
|
|
2409
|
-
|
|
2279
|
+
_combine(signal1, signal2) {
|
|
2410
2280
|
// If either is already aborted, use that one
|
|
2411
2281
|
if (signal1.aborted)
|
|
2412
2282
|
return signal1;
|
|
@@ -2422,9 +2292,9 @@ class BaseRequest {
|
|
|
2422
2292
|
/**
|
|
2423
2293
|
* Convert fetchOptions to RequestConfig with proper typing
|
|
2424
2294
|
*/
|
|
2425
|
-
|
|
2295
|
+
_config(url, fetchOptions) {
|
|
2426
2296
|
const method = typeof fetchOptions.method === "string" ? fetchOptions.method : "GET";
|
|
2427
|
-
const headers = this.
|
|
2297
|
+
const headers = this._headers();
|
|
2428
2298
|
const extendedOptions = fetchOptions;
|
|
2429
2299
|
return {
|
|
2430
2300
|
url,
|
|
@@ -2446,7 +2316,7 @@ class BaseRequest {
|
|
|
2446
2316
|
/**
|
|
2447
2317
|
* Apply interceptor results back to fetchOptions
|
|
2448
2318
|
*/
|
|
2449
|
-
|
|
2319
|
+
_applyConfig(config, fetchOptions) {
|
|
2450
2320
|
fetchOptions.headers = config.headers;
|
|
2451
2321
|
if (config.body !== undefined) {
|
|
2452
2322
|
fetchOptions.body = config.body;
|
|
@@ -2458,54 +2328,54 @@ class BaseRequest {
|
|
|
2458
2328
|
fetchOptions.cache = config.cache;
|
|
2459
2329
|
}
|
|
2460
2330
|
}
|
|
2461
|
-
async
|
|
2331
|
+
async _run(url, fetchOptions) {
|
|
2462
2332
|
const method = typeof fetchOptions.method === "string" ? fetchOptions.method : "GET";
|
|
2463
2333
|
// Setup abort signal with timeout
|
|
2464
|
-
const abortSignal = this.
|
|
2334
|
+
const abortSignal = this._signal(this._opts.timeout, this._ctrl);
|
|
2465
2335
|
try {
|
|
2466
2336
|
// Run request interceptors before making the request
|
|
2467
|
-
const requestConfig = this.
|
|
2468
|
-
const interceptorResult = await this.
|
|
2337
|
+
const requestConfig = this._config(url, fetchOptions);
|
|
2338
|
+
const interceptorResult = await this._runReqI(requestConfig);
|
|
2469
2339
|
// If interceptor returned a Response, short-circuit and wrap it
|
|
2470
2340
|
if (interceptorResult instanceof Response) {
|
|
2471
|
-
const graphQLOptions = this.
|
|
2472
|
-
const wrappedResponse = new ResponseWrapper(interceptorResult, this.
|
|
2473
|
-
return await this.
|
|
2341
|
+
const graphQLOptions = this._gql();
|
|
2342
|
+
const wrappedResponse = new ResponseWrapper(interceptorResult, this._url, this._method, graphQLOptions);
|
|
2343
|
+
return await this._runResI(wrappedResponse);
|
|
2474
2344
|
}
|
|
2475
2345
|
// Update fetchOptions with interceptor modifications
|
|
2476
2346
|
url = interceptorResult.url;
|
|
2477
|
-
this.
|
|
2478
|
-
this.
|
|
2347
|
+
this._validateUrl(url);
|
|
2348
|
+
this._applyConfig(interceptorResult, fetchOptions);
|
|
2479
2349
|
// Set the combined abort signal
|
|
2480
2350
|
if (abortSignal.signal) {
|
|
2481
2351
|
fetchOptions.signal = abortSignal.signal;
|
|
2482
2352
|
}
|
|
2483
2353
|
// Execute fetch (custom implementation if provided, global fetch otherwise)
|
|
2484
|
-
const fetchFn = this.
|
|
2354
|
+
const fetchFn = this._fetch ?? globalThis.fetch;
|
|
2485
2355
|
let response;
|
|
2486
2356
|
try {
|
|
2487
2357
|
response = await fetchFn(url, fetchOptions);
|
|
2488
2358
|
}
|
|
2489
2359
|
catch (error) {
|
|
2490
|
-
const errorObj =
|
|
2360
|
+
const errorObj = toError(error);
|
|
2491
2361
|
const errorName = errorObj.name;
|
|
2492
|
-
const
|
|
2362
|
+
const lowerMessage = errorObj.message.toLowerCase();
|
|
2493
2363
|
// Check if this is a timeout error from our internal timeout
|
|
2494
2364
|
const isOurTimeout = abortSignal.wasTimeout();
|
|
2495
2365
|
// Check if it's an abort error (DOMException in browsers, or AbortSignal abort)
|
|
2496
2366
|
if (error instanceof DOMException && error.name === "AbortError") {
|
|
2497
2367
|
// If it was our timeout that caused the abort, throw timeout error
|
|
2498
|
-
if (isOurTimeout && this.
|
|
2499
|
-
throw RequestError.timeout(url, method, this.
|
|
2368
|
+
if (isOurTimeout && this._opts.timeout) {
|
|
2369
|
+
throw RequestError.timeout(url, method, this._opts.timeout);
|
|
2500
2370
|
}
|
|
2501
2371
|
// Otherwise it's a manual abort
|
|
2502
2372
|
throw RequestError.abortError(url, method);
|
|
2503
2373
|
}
|
|
2504
2374
|
// Check for Node.js/undici TimeoutError or other timeout indicators
|
|
2505
2375
|
// This catches timeout errors that aren't thrown as AbortError
|
|
2506
|
-
const isTimeoutError = isOurTimeout || errorName === "TimeoutError" ||
|
|
2507
|
-
if (isTimeoutError && this.
|
|
2508
|
-
throw RequestError.timeout(url, method, this.
|
|
2376
|
+
const isTimeoutError = isOurTimeout || errorName === "TimeoutError" || lowerMessage.includes("timeout");
|
|
2377
|
+
if (isTimeoutError && this._opts.timeout) {
|
|
2378
|
+
throw RequestError.timeout(url, method, this._opts.timeout);
|
|
2509
2379
|
}
|
|
2510
2380
|
// For other network errors, let RequestError.networkError handle them
|
|
2511
2381
|
// It will check for timeout patterns as a safety net (useful for external AbortControllers)
|
|
@@ -2521,22 +2391,15 @@ class BaseRequest {
|
|
|
2521
2391
|
// (reads from a clone, so error.response remains readable)
|
|
2522
2392
|
throw RequestError.fromResponse(response, url, method, await RequestError.captureBody(response));
|
|
2523
2393
|
}
|
|
2524
|
-
const graphQLOptions = this.
|
|
2394
|
+
const graphQLOptions = this._gql();
|
|
2525
2395
|
const wrappedResponse = new ResponseWrapper(response, url, method, graphQLOptions);
|
|
2526
|
-
return await this.
|
|
2396
|
+
return await this._runResI(wrappedResponse);
|
|
2527
2397
|
}
|
|
2528
2398
|
catch (error) {
|
|
2529
2399
|
// Convert to RequestError if needed
|
|
2530
|
-
|
|
2531
|
-
if (error instanceof RequestError) {
|
|
2532
|
-
requestError = error;
|
|
2533
|
-
}
|
|
2534
|
-
else {
|
|
2535
|
-
const errorObj = error instanceof Error ? error : new Error(String(error));
|
|
2536
|
-
requestError = RequestError.networkError(url, method, errorObj);
|
|
2537
|
-
}
|
|
2400
|
+
const requestError = error instanceof RequestError ? error : RequestError.networkError(url, method, toError(error));
|
|
2538
2401
|
// Run error interceptors
|
|
2539
|
-
const interceptorResult = await this.
|
|
2402
|
+
const interceptorResult = await this._runErrI(requestError);
|
|
2540
2403
|
// If error interceptor returned a ResponseWrapper, recover from error
|
|
2541
2404
|
if (interceptorResult instanceof ResponseWrapper) {
|
|
2542
2405
|
return interceptorResult;
|
|
@@ -2553,12 +2416,9 @@ class BaseRequest {
|
|
|
2553
2416
|
* Base class for requests that can have a body (POST, PUT, PATCH)
|
|
2554
2417
|
*/
|
|
2555
2418
|
class BodyRequest extends BaseRequest {
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
constructor(url) {
|
|
2560
|
-
super(url);
|
|
2561
|
-
}
|
|
2419
|
+
_body;
|
|
2420
|
+
_bodyType;
|
|
2421
|
+
_gqlOpts = undefined;
|
|
2562
2422
|
/**
|
|
2563
2423
|
* Sets the request body. Automatically detects the body type and sets appropriate Content-Type header.
|
|
2564
2424
|
* Supports JSON objects/arrays, strings, FormData, Blob, ArrayBuffer, URLSearchParams, and ReadableStream.
|
|
@@ -2595,11 +2455,11 @@ class BodyRequest extends BaseRequest {
|
|
|
2595
2455
|
* ```
|
|
2596
2456
|
*/
|
|
2597
2457
|
withBody(body) {
|
|
2598
|
-
this.
|
|
2458
|
+
this._body = body;
|
|
2599
2459
|
// Set body type and validate
|
|
2600
2460
|
if (typeof body === "string") {
|
|
2601
|
-
this.
|
|
2602
|
-
this.
|
|
2461
|
+
this._bodyType = BodyType.STRING;
|
|
2462
|
+
this._setCT("text/plain");
|
|
2603
2463
|
}
|
|
2604
2464
|
else if (body !== null &&
|
|
2605
2465
|
typeof body === "object" &&
|
|
@@ -2610,18 +2470,18 @@ class BodyRequest extends BaseRequest {
|
|
|
2610
2470
|
ArrayBuffer.isView(body) || // Handles TypedArray and DataView
|
|
2611
2471
|
body instanceof URLSearchParams ||
|
|
2612
2472
|
body instanceof ReadableStream)) {
|
|
2613
|
-
this.
|
|
2614
|
-
this.
|
|
2473
|
+
this._bodyType = BodyType.JSON;
|
|
2474
|
+
this._setCT("application/json");
|
|
2615
2475
|
// Validate JSON is stringifiable early
|
|
2616
2476
|
try {
|
|
2617
2477
|
JSON.stringify(body);
|
|
2618
2478
|
}
|
|
2619
2479
|
catch (error) {
|
|
2620
|
-
throw new RequestError(`Bad JSON: ${
|
|
2480
|
+
throw new RequestError(`Bad JSON: ${errorMessage(error)}`, this._url, this._method);
|
|
2621
2481
|
}
|
|
2622
2482
|
}
|
|
2623
2483
|
else {
|
|
2624
|
-
this.
|
|
2484
|
+
this._bodyType = BodyType.BINARY;
|
|
2625
2485
|
}
|
|
2626
2486
|
return this;
|
|
2627
2487
|
}
|
|
@@ -2664,25 +2524,25 @@ class BodyRequest extends BaseRequest {
|
|
|
2664
2524
|
*/
|
|
2665
2525
|
withGraphQL(query, variables, options) {
|
|
2666
2526
|
if (typeof query !== "string" || query.length === 0) {
|
|
2667
|
-
throw new RequestError("Bad query", this.
|
|
2527
|
+
throw new RequestError("Bad query", this._url, this._method);
|
|
2668
2528
|
}
|
|
2669
2529
|
const graphQLBody = {
|
|
2670
2530
|
query: query,
|
|
2671
2531
|
};
|
|
2672
2532
|
if (variables !== undefined) {
|
|
2673
2533
|
if (typeof variables !== "object" || variables === null || Array.isArray(variables)) {
|
|
2674
|
-
throw new RequestError("Bad vars", this.
|
|
2534
|
+
throw new RequestError("Bad vars", this._url, this._method);
|
|
2675
2535
|
}
|
|
2676
2536
|
graphQLBody.variables = variables;
|
|
2677
2537
|
}
|
|
2678
2538
|
// Store GraphQL options if provided
|
|
2679
2539
|
if (options !== undefined) {
|
|
2680
2540
|
if (typeof options !== "object" || options === null || Array.isArray(options)) {
|
|
2681
|
-
throw new RequestError("Bad opts", this.
|
|
2541
|
+
throw new RequestError("Bad opts", this._url, this._method);
|
|
2682
2542
|
}
|
|
2683
2543
|
// Store only the known GraphQL options properties
|
|
2684
2544
|
const opts = options;
|
|
2685
|
-
this.
|
|
2545
|
+
this._gqlOpts = {
|
|
2686
2546
|
throwOnError: typeof opts.throwOnError === "boolean" ? opts.throwOnError : undefined,
|
|
2687
2547
|
};
|
|
2688
2548
|
}
|
|
@@ -2691,26 +2551,26 @@ class BodyRequest extends BaseRequest {
|
|
|
2691
2551
|
JSON.stringify(graphQLBody);
|
|
2692
2552
|
}
|
|
2693
2553
|
catch (error) {
|
|
2694
|
-
throw new RequestError(`Bad JSON: ${
|
|
2554
|
+
throw new RequestError(`Bad JSON: ${errorMessage(error)}`, this._url, this._method);
|
|
2695
2555
|
}
|
|
2696
|
-
this.
|
|
2697
|
-
this.
|
|
2698
|
-
this.
|
|
2556
|
+
this._body = graphQLBody;
|
|
2557
|
+
this._bodyType = BodyType.JSON;
|
|
2558
|
+
this._setCT("application/json");
|
|
2699
2559
|
return this;
|
|
2700
2560
|
}
|
|
2701
2561
|
/**
|
|
2702
2562
|
* Check if Content-Type header is already set (case-insensitive)
|
|
2703
2563
|
*/
|
|
2704
|
-
|
|
2705
|
-
const headers = this.
|
|
2564
|
+
_hasCT() {
|
|
2565
|
+
const headers = this._opts.headers;
|
|
2706
2566
|
if (typeof headers === "object" && headers !== null) {
|
|
2707
2567
|
const headersObj = headers;
|
|
2708
2568
|
return Object.keys(headersObj).some(header => header.toLowerCase() === "content-type");
|
|
2709
2569
|
}
|
|
2710
2570
|
return false;
|
|
2711
2571
|
}
|
|
2712
|
-
|
|
2713
|
-
if (!this.
|
|
2572
|
+
_setCT(contentType) {
|
|
2573
|
+
if (!this._hasCT()) {
|
|
2714
2574
|
this.withContentType(contentType);
|
|
2715
2575
|
}
|
|
2716
2576
|
}
|
|
@@ -2718,24 +2578,24 @@ class BodyRequest extends BaseRequest {
|
|
|
2718
2578
|
* Get the GraphQL options if set
|
|
2719
2579
|
* @returns The GraphQL options or undefined
|
|
2720
2580
|
*/
|
|
2721
|
-
|
|
2722
|
-
return this.
|
|
2581
|
+
_gql() {
|
|
2582
|
+
return this._gqlOpts;
|
|
2723
2583
|
}
|
|
2724
2584
|
/**
|
|
2725
2585
|
* Execute the request and return the ResponseWrapper
|
|
2726
2586
|
* Overrides the base implementation to add body handling
|
|
2727
2587
|
*/
|
|
2728
2588
|
async getResponse() {
|
|
2729
|
-
if (this.
|
|
2589
|
+
if (this._body !== undefined) {
|
|
2730
2590
|
// Remove previous body if it exists
|
|
2731
|
-
if (this.
|
|
2732
|
-
delete this.
|
|
2591
|
+
if (this._opts.body)
|
|
2592
|
+
delete this._opts.body;
|
|
2733
2593
|
// Process the body based on its type
|
|
2734
|
-
if (this.
|
|
2735
|
-
this.
|
|
2594
|
+
if (this._bodyType === BodyType.JSON) {
|
|
2595
|
+
this._opts.body = JSON.stringify(this._body);
|
|
2736
2596
|
}
|
|
2737
2597
|
else {
|
|
2738
|
-
this.
|
|
2598
|
+
this._opts.body = this._body;
|
|
2739
2599
|
}
|
|
2740
2600
|
}
|
|
2741
2601
|
return super.getResponse();
|
|
@@ -2752,10 +2612,7 @@ class BodyRequest extends BaseRequest {
|
|
|
2752
2612
|
* const data = await request.getData();
|
|
2753
2613
|
*/
|
|
2754
2614
|
class GetRequest extends BaseRequest {
|
|
2755
|
-
|
|
2756
|
-
constructor(url) {
|
|
2757
|
-
super(url);
|
|
2758
|
-
}
|
|
2615
|
+
_method = HttpMethod.GET;
|
|
2759
2616
|
}
|
|
2760
2617
|
/**
|
|
2761
2618
|
* HTTP HEAD request implementation
|
|
@@ -2766,10 +2623,7 @@ class GetRequest extends BaseRequest {
|
|
|
2766
2623
|
* const response = await request.getResponse();
|
|
2767
2624
|
*/
|
|
2768
2625
|
class HeadRequest extends BaseRequest {
|
|
2769
|
-
|
|
2770
|
-
constructor(url) {
|
|
2771
|
-
super(url);
|
|
2772
|
-
}
|
|
2626
|
+
_method = HttpMethod.HEAD;
|
|
2773
2627
|
}
|
|
2774
2628
|
/**
|
|
2775
2629
|
* HTTP OPTIONS request implementation
|
|
@@ -2780,10 +2634,7 @@ class HeadRequest extends BaseRequest {
|
|
|
2780
2634
|
* const response = await request.getResponse();
|
|
2781
2635
|
*/
|
|
2782
2636
|
class OptionsRequest extends BaseRequest {
|
|
2783
|
-
|
|
2784
|
-
constructor(url) {
|
|
2785
|
-
super(url);
|
|
2786
|
-
}
|
|
2637
|
+
_method = HttpMethod.OPTIONS;
|
|
2787
2638
|
}
|
|
2788
2639
|
/**
|
|
2789
2640
|
* HTTP DELETE request implementation
|
|
@@ -2794,10 +2645,7 @@ class OptionsRequest extends BaseRequest {
|
|
|
2794
2645
|
* await request.getData();
|
|
2795
2646
|
*/
|
|
2796
2647
|
class DeleteRequest extends BaseRequest {
|
|
2797
|
-
|
|
2798
|
-
constructor(url) {
|
|
2799
|
-
super(url);
|
|
2800
|
-
}
|
|
2648
|
+
_method = HttpMethod.DELETE;
|
|
2801
2649
|
}
|
|
2802
2650
|
/**
|
|
2803
2651
|
* HTTP POST request implementation
|
|
@@ -2809,10 +2657,7 @@ class DeleteRequest extends BaseRequest {
|
|
|
2809
2657
|
* const data = await request.getData();
|
|
2810
2658
|
*/
|
|
2811
2659
|
class PostRequest extends BodyRequest {
|
|
2812
|
-
|
|
2813
|
-
constructor(url) {
|
|
2814
|
-
super(url);
|
|
2815
|
-
}
|
|
2660
|
+
_method = HttpMethod.POST;
|
|
2816
2661
|
}
|
|
2817
2662
|
/**
|
|
2818
2663
|
* HTTP PUT request implementation
|
|
@@ -2824,10 +2669,7 @@ class PostRequest extends BodyRequest {
|
|
|
2824
2669
|
* const data = await request.getData();
|
|
2825
2670
|
*/
|
|
2826
2671
|
class PutRequest extends BodyRequest {
|
|
2827
|
-
|
|
2828
|
-
constructor(url) {
|
|
2829
|
-
super(url);
|
|
2830
|
-
}
|
|
2672
|
+
_method = HttpMethod.PUT;
|
|
2831
2673
|
}
|
|
2832
2674
|
/**
|
|
2833
2675
|
* HTTP PATCH request implementation
|
|
@@ -2839,10 +2681,7 @@ class PutRequest extends BodyRequest {
|
|
|
2839
2681
|
* const data = await request.getData();
|
|
2840
2682
|
*/
|
|
2841
2683
|
class PatchRequest extends BodyRequest {
|
|
2842
|
-
|
|
2843
|
-
constructor(url) {
|
|
2844
|
-
super(url);
|
|
2845
|
-
}
|
|
2684
|
+
_method = HttpMethod.PATCH;
|
|
2846
2685
|
}
|
|
2847
2686
|
|
|
2848
2687
|
/**
|
|
@@ -2954,73 +2793,60 @@ function options(url) {
|
|
|
2954
2793
|
* Internal API builder implementation.
|
|
2955
2794
|
*/
|
|
2956
2795
|
class ApiBuilderImpl {
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2796
|
+
_baseURL;
|
|
2797
|
+
_mods = [];
|
|
2798
|
+
_proxy;
|
|
2960
2799
|
withBaseURL(baseURL) {
|
|
2961
|
-
this.
|
|
2962
|
-
return this.
|
|
2800
|
+
this._baseURL = baseURL;
|
|
2801
|
+
return this._getProxy();
|
|
2963
2802
|
}
|
|
2964
|
-
|
|
2803
|
+
_resolve(url) {
|
|
2965
2804
|
if (!url)
|
|
2966
|
-
return this.
|
|
2805
|
+
return this._baseURL || "";
|
|
2967
2806
|
if (/^https?:\/\//.test(url))
|
|
2968
2807
|
return url;
|
|
2969
|
-
if (!this.
|
|
2808
|
+
if (!this._baseURL)
|
|
2970
2809
|
return url;
|
|
2971
|
-
return this.
|
|
2810
|
+
return this._baseURL.replace(/\/$/, "") + (url[0] === "/" ? url : "/" + url);
|
|
2972
2811
|
}
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2812
|
+
_new(Ctor, url) {
|
|
2813
|
+
const request = new Ctor(this._resolve(url));
|
|
2814
|
+
for (const modifier of this._mods)
|
|
2815
|
+
modifier(request);
|
|
2816
|
+
return request;
|
|
2977
2817
|
}
|
|
2978
2818
|
get(url) {
|
|
2979
|
-
|
|
2980
|
-
this.applyModifiers(request);
|
|
2981
|
-
return request;
|
|
2819
|
+
return this._new(GetRequest, url);
|
|
2982
2820
|
}
|
|
2983
2821
|
post(url) {
|
|
2984
|
-
|
|
2985
|
-
this.applyModifiers(request);
|
|
2986
|
-
return request;
|
|
2822
|
+
return this._new(PostRequest, url);
|
|
2987
2823
|
}
|
|
2988
2824
|
put(url) {
|
|
2989
|
-
|
|
2990
|
-
this.applyModifiers(request);
|
|
2991
|
-
return request;
|
|
2825
|
+
return this._new(PutRequest, url);
|
|
2992
2826
|
}
|
|
2993
2827
|
del(url) {
|
|
2994
|
-
|
|
2995
|
-
this.applyModifiers(request);
|
|
2996
|
-
return request;
|
|
2828
|
+
return this._new(DeleteRequest, url);
|
|
2997
2829
|
}
|
|
2998
2830
|
patch(url) {
|
|
2999
|
-
|
|
3000
|
-
this.applyModifiers(request);
|
|
3001
|
-
return request;
|
|
2831
|
+
return this._new(PatchRequest, url);
|
|
3002
2832
|
}
|
|
3003
2833
|
head(url) {
|
|
3004
|
-
|
|
3005
|
-
this.applyModifiers(request);
|
|
3006
|
-
return request;
|
|
2834
|
+
return this._new(HeadRequest, url);
|
|
3007
2835
|
}
|
|
3008
2836
|
options(url) {
|
|
3009
|
-
|
|
3010
|
-
this.applyModifiers(request);
|
|
3011
|
-
return request;
|
|
2837
|
+
return this._new(OptionsRequest, url);
|
|
3012
2838
|
}
|
|
3013
|
-
|
|
3014
|
-
this.
|
|
3015
|
-
return this.
|
|
2839
|
+
_add(modifier) {
|
|
2840
|
+
this._mods.push(modifier);
|
|
2841
|
+
return this._getProxy();
|
|
3016
2842
|
}
|
|
3017
|
-
|
|
3018
|
-
if (!this.
|
|
3019
|
-
this.
|
|
2843
|
+
_getProxy() {
|
|
2844
|
+
if (!this._proxy) {
|
|
2845
|
+
this._proxy = this._mkProxy();
|
|
3020
2846
|
}
|
|
3021
|
-
return this.
|
|
2847
|
+
return this._proxy;
|
|
3022
2848
|
}
|
|
3023
|
-
|
|
2849
|
+
_mkProxy() {
|
|
3024
2850
|
const disallowedMethods = new Set(["withBody", "withGraphQL", "withAbortController"]);
|
|
3025
2851
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
3026
2852
|
return new Proxy(this, {
|
|
@@ -3042,7 +2868,7 @@ class ApiBuilderImpl {
|
|
|
3042
2868
|
// If it's a 'with...' method or other chainable method, create a modifier for it
|
|
3043
2869
|
if (typeof prop === "string" && (prop.startsWith("with") || prop === "onRetry")) {
|
|
3044
2870
|
return (...args) => {
|
|
3045
|
-
return implTarget.
|
|
2871
|
+
return implTarget._add((request) => {
|
|
3046
2872
|
const method = request[prop];
|
|
3047
2873
|
if (typeof method === "function") {
|
|
3048
2874
|
method.apply(request, args);
|
|
@@ -3057,7 +2883,7 @@ class ApiBuilderImpl {
|
|
|
3057
2883
|
});
|
|
3058
2884
|
}
|
|
3059
2885
|
static create() {
|
|
3060
|
-
return new ApiBuilderImpl().
|
|
2886
|
+
return new ApiBuilderImpl()._getProxy();
|
|
3061
2887
|
}
|
|
3062
2888
|
}
|
|
3063
2889
|
/**
|
|
@@ -3138,16 +2964,24 @@ const create = {
|
|
|
3138
2964
|
config: Config.getInstance(),
|
|
3139
2965
|
};
|
|
3140
2966
|
|
|
2967
|
+
exports.CacheMode = CacheMode;
|
|
3141
2968
|
exports.CookieUtils = CookieUtils;
|
|
2969
|
+
exports.CredentialsPolicy = CredentialsPolicy;
|
|
3142
2970
|
exports.DeleteRequest = DeleteRequest;
|
|
3143
2971
|
exports.GetRequest = GetRequest;
|
|
3144
2972
|
exports.HeadRequest = HeadRequest;
|
|
2973
|
+
exports.HttpMethod = HttpMethod;
|
|
3145
2974
|
exports.OptionsRequest = OptionsRequest;
|
|
3146
2975
|
exports.PatchRequest = PatchRequest;
|
|
3147
2976
|
exports.PostRequest = PostRequest;
|
|
3148
2977
|
exports.PutRequest = PutRequest;
|
|
2978
|
+
exports.RedirectMode = RedirectMode;
|
|
2979
|
+
exports.ReferrerPolicy = ReferrerPolicy;
|
|
3149
2980
|
exports.RequestError = RequestError;
|
|
2981
|
+
exports.RequestMode = RequestMode;
|
|
2982
|
+
exports.RequestPriority = RequestPriority;
|
|
3150
2983
|
exports.ResponseWrapper = ResponseWrapper;
|
|
2984
|
+
exports.SameSitePolicy = SameSitePolicy;
|
|
3151
2985
|
exports.createApi = api;
|
|
3152
2986
|
exports.createDelete = del;
|
|
3153
2987
|
exports.createGet = get;
|