create-request 1.4.3-rc.4 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +184 -12
- package/dist/library/BaseRequest.d.ts +15 -0
- package/dist/library/RequestError.d.ts +2 -2
- package/dist/library/apiBuilder.d.ts +476 -115
- package/dist/library/index.cjs +191 -290
- package/dist/library/index.cjs.map +1 -1
- package/dist/library/index.d.ts +3 -3
- package/dist/library/index.esm.js +184 -283
- 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/requestFactories.d.ts +3 -3
- package/dist/library/requestMethods.d.ts +3 -3
- package/package.json +1 -1
package/dist/library/index.cjs
CHANGED
|
@@ -146,8 +146,8 @@ class RequestError extends Error {
|
|
|
146
146
|
this.method = method;
|
|
147
147
|
this.status = options.status;
|
|
148
148
|
this.response = options.response;
|
|
149
|
-
this.isTimeout = options.isTimeout;
|
|
150
|
-
this.isAborted = options.isAborted;
|
|
149
|
+
this.isTimeout = !!options.isTimeout;
|
|
150
|
+
this.isAborted = !!options.isAborted;
|
|
151
151
|
// For better stack traces in modern environments
|
|
152
152
|
if (Error.captureStackTrace) {
|
|
153
153
|
Error.captureStackTrace(this, RequestError);
|
|
@@ -169,7 +169,7 @@ class RequestError extends Error {
|
|
|
169
169
|
* ```
|
|
170
170
|
*/
|
|
171
171
|
static timeout(url, method, timeoutMs) {
|
|
172
|
-
return new RequestError(`Timeout
|
|
172
|
+
return new RequestError(`Timeout:${timeoutMs}`, url, method, {
|
|
173
173
|
isTimeout: true,
|
|
174
174
|
});
|
|
175
175
|
}
|
|
@@ -248,16 +248,16 @@ class RequestError extends Error {
|
|
|
248
248
|
// Check for connection errors (but not timeout errors)
|
|
249
249
|
const isConnectionError = !isTimeoutError && (errorCode === "ECONNREFUSED" || errorCode === "ECONNRESET" || stack.includes("ECONNREFUSED") || stack.includes("connect"));
|
|
250
250
|
if (isTimeoutError) {
|
|
251
|
-
message = `Timeout
|
|
251
|
+
message = `Timeout:${url}`;
|
|
252
252
|
}
|
|
253
253
|
else if (isDnsError) {
|
|
254
|
-
message = `DNS
|
|
254
|
+
message = `DNS:${url}`;
|
|
255
255
|
}
|
|
256
256
|
else if (isConnectionError) {
|
|
257
|
-
message = `
|
|
257
|
+
message = `Conn:${url}`;
|
|
258
258
|
}
|
|
259
259
|
else {
|
|
260
|
-
message = `
|
|
260
|
+
message = `Net:${url}`;
|
|
261
261
|
}
|
|
262
262
|
}
|
|
263
263
|
const error = new RequestError(message, url, method, {
|
|
@@ -367,7 +367,7 @@ class ResponseWrapper {
|
|
|
367
367
|
*/
|
|
368
368
|
checkBodyNotConsumed() {
|
|
369
369
|
if (this.response.bodyUsed) {
|
|
370
|
-
throw new RequestError("Body
|
|
370
|
+
throw new RequestError("Body used", this.url || "", this.method || "", {
|
|
371
371
|
status: this.response.status,
|
|
372
372
|
response: this.response,
|
|
373
373
|
});
|
|
@@ -409,7 +409,7 @@ class ResponseWrapper {
|
|
|
409
409
|
return String(x);
|
|
410
410
|
});
|
|
411
411
|
const errorMessage = errorMessages.join(", ");
|
|
412
|
-
throw new RequestError(`
|
|
412
|
+
throw new RequestError(`GQL: ${errorMessage}`, this.url || "", this.method || "", {
|
|
413
413
|
status: this.response.status,
|
|
414
414
|
response: this.response,
|
|
415
415
|
});
|
|
@@ -449,8 +449,7 @@ class ResponseWrapper {
|
|
|
449
449
|
if (error instanceof RequestError) {
|
|
450
450
|
throw error;
|
|
451
451
|
}
|
|
452
|
-
|
|
453
|
-
throw new RequestError(`Invalid JSON: ${errorMessage}`, this.url || "", this.method || "", {
|
|
452
|
+
throw new RequestError(`Bad JSON: ${error instanceof Error ? error.message : String(error)}`, this.url || "", this.method || "", {
|
|
454
453
|
status: this.response.status,
|
|
455
454
|
response: this.response,
|
|
456
455
|
});
|
|
@@ -479,8 +478,7 @@ class ResponseWrapper {
|
|
|
479
478
|
return text;
|
|
480
479
|
}
|
|
481
480
|
catch (e) {
|
|
482
|
-
|
|
483
|
-
throw new RequestError(`Read failed: ${errorMessage}`, this.url || "", this.method || "", {
|
|
481
|
+
throw new RequestError(`Read: ${e instanceof Error ? e.message : String(e)}`, this.url || "", this.method || "", {
|
|
484
482
|
status: this.response.status,
|
|
485
483
|
response: this.response,
|
|
486
484
|
});
|
|
@@ -511,8 +509,7 @@ class ResponseWrapper {
|
|
|
511
509
|
return blob;
|
|
512
510
|
}
|
|
513
511
|
catch (e) {
|
|
514
|
-
|
|
515
|
-
throw new RequestError(`Read failed: ${errorMessage}`, this.url || "", this.method || "", {
|
|
512
|
+
throw new RequestError(`Read: ${e instanceof Error ? e.message : String(e)}`, this.url || "", this.method || "", {
|
|
516
513
|
status: this.response.status,
|
|
517
514
|
response: this.response,
|
|
518
515
|
});
|
|
@@ -544,8 +541,7 @@ class ResponseWrapper {
|
|
|
544
541
|
return arrayBuffer;
|
|
545
542
|
}
|
|
546
543
|
catch (e) {
|
|
547
|
-
|
|
548
|
-
throw new RequestError(`Read failed: ${errorMessage}`, this.url || "", this.method || "", {
|
|
544
|
+
throw new RequestError(`Read: ${e instanceof Error ? e.message : String(e)}`, this.url || "", this.method || "", {
|
|
549
545
|
status: this.response.status,
|
|
550
546
|
response: this.response,
|
|
551
547
|
});
|
|
@@ -611,8 +607,7 @@ class ResponseWrapper {
|
|
|
611
607
|
}
|
|
612
608
|
// Enhance selector errors with context
|
|
613
609
|
if (selector) {
|
|
614
|
-
|
|
615
|
-
throw new RequestError(`Data selector failed: ${errorMessage}`, this.url || "", this.method || "", {
|
|
610
|
+
throw new RequestError(`Selector: ${error instanceof Error ? error.message : String(error)}`, this.url || "", this.method || "", {
|
|
616
611
|
status: this.response.status,
|
|
617
612
|
response: this.response,
|
|
618
613
|
});
|
|
@@ -1058,10 +1053,11 @@ class BaseRequest {
|
|
|
1058
1053
|
return Object.assign(callable, fluent);
|
|
1059
1054
|
}
|
|
1060
1055
|
validateUrl(url) {
|
|
1056
|
+
const errorMessage = "Bad URL";
|
|
1061
1057
|
if (!url?.trim())
|
|
1062
|
-
throw new RequestError(
|
|
1058
|
+
throw new RequestError(errorMessage, url, this.method);
|
|
1063
1059
|
if (url.includes("\0") || url.includes("\r") || url.includes("\n")) {
|
|
1064
|
-
throw new RequestError(
|
|
1060
|
+
throw new RequestError(errorMessage, url, this.method);
|
|
1065
1061
|
}
|
|
1066
1062
|
const trimmed = url.trim();
|
|
1067
1063
|
if (/^https?:\/\//.test(trimmed)) {
|
|
@@ -1069,7 +1065,7 @@ class BaseRequest {
|
|
|
1069
1065
|
new URL(trimmed);
|
|
1070
1066
|
}
|
|
1071
1067
|
catch {
|
|
1072
|
-
throw new RequestError(
|
|
1068
|
+
throw new RequestError(errorMessage, trimmed, this.method);
|
|
1073
1069
|
}
|
|
1074
1070
|
}
|
|
1075
1071
|
}
|
|
@@ -1125,7 +1121,7 @@ class BaseRequest {
|
|
|
1125
1121
|
*/
|
|
1126
1122
|
withTimeout(timeout) {
|
|
1127
1123
|
if (!Number.isFinite(timeout) || timeout <= 0)
|
|
1128
|
-
throw new RequestError("
|
|
1124
|
+
throw new RequestError("Bad timeout", this.url, this.method);
|
|
1129
1125
|
this.requestOptions.timeout = timeout;
|
|
1130
1126
|
return this;
|
|
1131
1127
|
}
|
|
@@ -1164,24 +1160,24 @@ class BaseRequest {
|
|
|
1164
1160
|
withRetries(retries) {
|
|
1165
1161
|
if (typeof retries === "number") {
|
|
1166
1162
|
if (!Number.isInteger(retries) || retries < 0) {
|
|
1167
|
-
throw new RequestError(`
|
|
1163
|
+
throw new RequestError(`Bad retries: ${retries}`, this.url, this.method);
|
|
1168
1164
|
}
|
|
1169
1165
|
this.requestOptions.retries = retries;
|
|
1170
1166
|
}
|
|
1171
1167
|
else {
|
|
1172
1168
|
// Validate RetryConfig
|
|
1173
1169
|
if (!Number.isInteger(retries.attempts) || retries.attempts < 0) {
|
|
1174
|
-
throw new RequestError(`
|
|
1170
|
+
throw new RequestError(`Bad attempts: ${retries.attempts}`, this.url, this.method);
|
|
1175
1171
|
}
|
|
1176
1172
|
// Validate delay if provided
|
|
1177
1173
|
if (retries.delay !== undefined) {
|
|
1178
1174
|
if (typeof retries.delay === "number") {
|
|
1179
1175
|
if (!Number.isFinite(retries.delay) || retries.delay < 0) {
|
|
1180
|
-
throw new RequestError(`
|
|
1176
|
+
throw new RequestError(`Bad delay: ${retries.delay}`, this.url, this.method);
|
|
1181
1177
|
}
|
|
1182
1178
|
}
|
|
1183
1179
|
else if (typeof retries.delay !== "function") {
|
|
1184
|
-
throw new RequestError(`
|
|
1180
|
+
throw new RequestError(`Bad delay: ${typeof retries.delay}`, this.url, this.method);
|
|
1185
1181
|
}
|
|
1186
1182
|
}
|
|
1187
1183
|
this.requestOptions.retries = retries;
|
|
@@ -1664,7 +1660,7 @@ class BaseRequest {
|
|
|
1664
1660
|
if (typeof Buffer !== "undefined")
|
|
1665
1661
|
return Buffer.from(str).toString("base64");
|
|
1666
1662
|
// Fallback (should never happen in modern environments)
|
|
1667
|
-
throw new RequestError("
|
|
1663
|
+
throw new RequestError("No encoder", this.url, this.method);
|
|
1668
1664
|
}
|
|
1669
1665
|
/**
|
|
1670
1666
|
* Sets a Bearer token for authentication.
|
|
@@ -1948,6 +1944,24 @@ class BaseRequest {
|
|
|
1948
1944
|
const response = await this.getResponse();
|
|
1949
1945
|
return response.getBlob();
|
|
1950
1946
|
}
|
|
1947
|
+
/**
|
|
1948
|
+
* Execute the request and get the response body as an ArrayBuffer.
|
|
1949
|
+
* Useful for processing binary data at a low level.
|
|
1950
|
+
*
|
|
1951
|
+
* @returns A promise that resolves to the response body as an ArrayBuffer
|
|
1952
|
+
* @throws {RequestError} When the request fails or reading the response fails
|
|
1953
|
+
*
|
|
1954
|
+
* @example
|
|
1955
|
+
* ```typescript
|
|
1956
|
+
* const buffer = await request.getArrayBuffer();
|
|
1957
|
+
* const uint8Array = new Uint8Array(buffer);
|
|
1958
|
+
* // Process the binary data
|
|
1959
|
+
* ```
|
|
1960
|
+
*/
|
|
1961
|
+
async getArrayBuffer() {
|
|
1962
|
+
const response = await this.getResponse();
|
|
1963
|
+
return response.getArrayBuffer();
|
|
1964
|
+
}
|
|
1951
1965
|
/**
|
|
1952
1966
|
* Execute the request and get the response body as a ReadableStream.
|
|
1953
1967
|
* Note: Unlike other methods, streams cannot be cached. The body can only be consumed once.
|
|
@@ -2083,7 +2097,7 @@ class BaseRequest {
|
|
|
2083
2097
|
const delay = typeof retriesConfig.delay === "function" ? retriesConfig.delay({ attempt: attempt + 1, error: requestError }) : retriesConfig.delay;
|
|
2084
2098
|
// Validate delay result
|
|
2085
2099
|
if (typeof delay !== "number" || !Number.isFinite(delay) || delay < 0) {
|
|
2086
|
-
throw new RequestError(`
|
|
2100
|
+
throw new RequestError(`Bad delay: ${delay}`, url, method);
|
|
2087
2101
|
}
|
|
2088
2102
|
// Wait for the delay
|
|
2089
2103
|
if (delay > 0) {
|
|
@@ -2114,8 +2128,7 @@ class BaseRequest {
|
|
|
2114
2128
|
currentConfig = result;
|
|
2115
2129
|
}
|
|
2116
2130
|
catch (error) {
|
|
2117
|
-
|
|
2118
|
-
throw new RequestError(`Req Interceptor failed: ${errorMessage}`, currentConfig.url, currentConfig.method);
|
|
2131
|
+
throw new RequestError(`ReqI: ${error instanceof Error ? error.message : String(error)}`, currentConfig.url, currentConfig.method);
|
|
2119
2132
|
}
|
|
2120
2133
|
}
|
|
2121
2134
|
return currentConfig;
|
|
@@ -2129,17 +2142,14 @@ class BaseRequest {
|
|
|
2129
2142
|
const globalConfig = Config.getInstance();
|
|
2130
2143
|
const globalInterceptors = globalConfig.getResponseInterceptors();
|
|
2131
2144
|
// Per-request in order, then global in reverse
|
|
2132
|
-
const allInterceptors = [...this.responseInterceptors, ...globalInterceptors.reverse()];
|
|
2145
|
+
const allInterceptors = [...this.responseInterceptors, ...[...globalInterceptors].reverse()];
|
|
2133
2146
|
let currentResponse = response;
|
|
2134
2147
|
for (let i = 0; i < allInterceptors.length; i++) {
|
|
2135
2148
|
try {
|
|
2136
2149
|
currentResponse = await allInterceptors[i](currentResponse);
|
|
2137
2150
|
}
|
|
2138
2151
|
catch (error) {
|
|
2139
|
-
|
|
2140
|
-
const url = currentResponse.url || "";
|
|
2141
|
-
const method = currentResponse.method || "";
|
|
2142
|
-
throw new RequestError(`Res Interceptor failed: ${errorMessage}`, url, method);
|
|
2152
|
+
throw new RequestError(`ResI: ${error instanceof Error ? error.message : String(error)}`, currentResponse.url || "", currentResponse.method || "");
|
|
2143
2153
|
}
|
|
2144
2154
|
}
|
|
2145
2155
|
return currentResponse;
|
|
@@ -2153,7 +2163,7 @@ class BaseRequest {
|
|
|
2153
2163
|
const globalConfig = Config.getInstance();
|
|
2154
2164
|
const globalInterceptors = globalConfig.getErrorInterceptors();
|
|
2155
2165
|
// Per-request in order, then global in reverse
|
|
2156
|
-
const allInterceptors = [...this.errorInterceptors, ...globalInterceptors.reverse()];
|
|
2166
|
+
const allInterceptors = [...this.errorInterceptors, ...[...globalInterceptors].reverse()];
|
|
2157
2167
|
let currentError = error;
|
|
2158
2168
|
for (let i = 0; i < allInterceptors.length; i++) {
|
|
2159
2169
|
try {
|
|
@@ -2170,10 +2180,10 @@ class BaseRequest {
|
|
|
2170
2180
|
currentError = interceptorError;
|
|
2171
2181
|
}
|
|
2172
2182
|
else {
|
|
2173
|
-
const
|
|
2183
|
+
const em = interceptorError instanceof Error ? interceptorError.message : String(interceptorError);
|
|
2174
2184
|
// Always wrap in RequestError when we have context
|
|
2175
2185
|
if (currentError instanceof RequestError) {
|
|
2176
|
-
currentError = new RequestError(`
|
|
2186
|
+
currentError = new RequestError(`ErrI${i + 1}: ${em}`, currentError.url, currentError.method, {
|
|
2177
2187
|
status: currentError.status,
|
|
2178
2188
|
response: currentError.response,
|
|
2179
2189
|
});
|
|
@@ -2350,6 +2360,11 @@ class BaseRequest {
|
|
|
2350
2360
|
// It will check for timeout patterns as a safety net (useful for external AbortControllers)
|
|
2351
2361
|
throw RequestError.networkError(url, method, errorObj);
|
|
2352
2362
|
}
|
|
2363
|
+
// Status 0 indicates the request failed before receiving a proper HTTP response
|
|
2364
|
+
// (e.g., CORS errors, network failures that don't throw). Treat as network error.
|
|
2365
|
+
if (response.status === 0) {
|
|
2366
|
+
throw RequestError.networkError(url, method, new Error("Failed with status 0 (network error or CORS blocked)"));
|
|
2367
|
+
}
|
|
2353
2368
|
if (!response.ok) {
|
|
2354
2369
|
throw RequestError.fromResponse(response, url, method);
|
|
2355
2370
|
}
|
|
@@ -2449,8 +2464,7 @@ class BodyRequest extends BaseRequest {
|
|
|
2449
2464
|
JSON.stringify(body);
|
|
2450
2465
|
}
|
|
2451
2466
|
catch (error) {
|
|
2452
|
-
|
|
2453
|
-
throw new RequestError(`Invalid JSON: ${errorMessage}`, this.url, this.method);
|
|
2467
|
+
throw new RequestError(`Bad JSON: ${error instanceof Error ? error.message : String(error)}`, this.url, this.method);
|
|
2454
2468
|
}
|
|
2455
2469
|
}
|
|
2456
2470
|
else {
|
|
@@ -2497,21 +2511,21 @@ class BodyRequest extends BaseRequest {
|
|
|
2497
2511
|
*/
|
|
2498
2512
|
withGraphQL(query, variables, options) {
|
|
2499
2513
|
if (typeof query !== "string" || query.length === 0) {
|
|
2500
|
-
throw new RequestError("
|
|
2514
|
+
throw new RequestError("Bad query", this.url, this.method);
|
|
2501
2515
|
}
|
|
2502
2516
|
const graphQLBody = {
|
|
2503
2517
|
query: query,
|
|
2504
2518
|
};
|
|
2505
2519
|
if (variables !== undefined) {
|
|
2506
2520
|
if (typeof variables !== "object" || variables === null || Array.isArray(variables)) {
|
|
2507
|
-
throw new RequestError("
|
|
2521
|
+
throw new RequestError("Bad vars", this.url, this.method);
|
|
2508
2522
|
}
|
|
2509
2523
|
graphQLBody.variables = variables;
|
|
2510
2524
|
}
|
|
2511
2525
|
// Store GraphQL options if provided
|
|
2512
2526
|
if (options !== undefined) {
|
|
2513
2527
|
if (typeof options !== "object" || options === null || Array.isArray(options)) {
|
|
2514
|
-
throw new RequestError("
|
|
2528
|
+
throw new RequestError("Bad opts", this.url, this.method);
|
|
2515
2529
|
}
|
|
2516
2530
|
// Store only the known GraphQL options properties
|
|
2517
2531
|
const opts = options;
|
|
@@ -2524,8 +2538,7 @@ class BodyRequest extends BaseRequest {
|
|
|
2524
2538
|
JSON.stringify(graphQLBody);
|
|
2525
2539
|
}
|
|
2526
2540
|
catch (error) {
|
|
2527
|
-
|
|
2528
|
-
throw new RequestError(`Invalid JSON: ${errorMessage}`, this.url, this.method);
|
|
2541
|
+
throw new RequestError(`Bad JSON: ${error instanceof Error ? error.message : String(error)}`, this.url, this.method);
|
|
2529
2542
|
}
|
|
2530
2543
|
this.body = graphQLBody;
|
|
2531
2544
|
this.bodyType = BodyType.JSON;
|
|
@@ -2639,7 +2652,7 @@ class DeleteRequest extends BaseRequest {
|
|
|
2639
2652
|
*
|
|
2640
2653
|
* @example
|
|
2641
2654
|
* const request = new PostRequest('/api/users')
|
|
2642
|
-
* .
|
|
2655
|
+
* .withBody({ name: 'John', email: 'john@example.com' });
|
|
2643
2656
|
* const data = await request.getData();
|
|
2644
2657
|
*/
|
|
2645
2658
|
class PostRequest extends BodyRequest {
|
|
@@ -2654,7 +2667,7 @@ class PostRequest extends BodyRequest {
|
|
|
2654
2667
|
*
|
|
2655
2668
|
* @example
|
|
2656
2669
|
* const request = new PutRequest('/api/users/123')
|
|
2657
|
-
* .
|
|
2670
|
+
* .withBody({ id: '123', name: 'John', email: 'john@example.com' });
|
|
2658
2671
|
* const data = await request.getData();
|
|
2659
2672
|
*/
|
|
2660
2673
|
class PutRequest extends BodyRequest {
|
|
@@ -2669,7 +2682,7 @@ class PutRequest extends BodyRequest {
|
|
|
2669
2682
|
*
|
|
2670
2683
|
* @example
|
|
2671
2684
|
* const request = new PatchRequest('/api/users/123')
|
|
2672
|
-
* .
|
|
2685
|
+
* .withBody({ email: 'new.email@example.com' });
|
|
2673
2686
|
* const data = await request.getData();
|
|
2674
2687
|
*/
|
|
2675
2688
|
class PatchRequest extends BodyRequest {
|
|
@@ -2703,7 +2716,7 @@ function get(url) {
|
|
|
2703
2716
|
*
|
|
2704
2717
|
* @example
|
|
2705
2718
|
* const request = post('/api/users')
|
|
2706
|
-
* .
|
|
2719
|
+
* .withBody({ name: 'John', email: 'john@example.com' });
|
|
2707
2720
|
* const newUser = await request.getData();
|
|
2708
2721
|
*/
|
|
2709
2722
|
function post(url) {
|
|
@@ -2718,7 +2731,7 @@ function post(url) {
|
|
|
2718
2731
|
*
|
|
2719
2732
|
* @example
|
|
2720
2733
|
* const request = put('/api/users/123')
|
|
2721
|
-
* .
|
|
2734
|
+
* .withBody({ name: 'John Updated', email: 'john@example.com' });
|
|
2722
2735
|
* const updatedUser = await request.getData();
|
|
2723
2736
|
*/
|
|
2724
2737
|
function put(url) {
|
|
@@ -2747,7 +2760,7 @@ function del(url) {
|
|
|
2747
2760
|
*
|
|
2748
2761
|
* @example
|
|
2749
2762
|
* const request = patch('/api/users/123')
|
|
2750
|
-
* .
|
|
2763
|
+
* .withBody({ status: 'active' });
|
|
2751
2764
|
* const patchedUser = await request.getData();
|
|
2752
2765
|
*/
|
|
2753
2766
|
function patch(url) {
|
|
@@ -2785,267 +2798,155 @@ function options(url) {
|
|
|
2785
2798
|
}
|
|
2786
2799
|
|
|
2787
2800
|
/**
|
|
2788
|
-
*
|
|
2789
|
-
* If the URL is absolute (starts with http:// or https://), it is returned as-is.
|
|
2790
|
-
* Otherwise, it is resolved relative to the base URL.
|
|
2791
|
-
*
|
|
2792
|
-
* @param baseURL - The base URL to resolve against, or undefined if not set
|
|
2793
|
-
* @param url - The URL to resolve, or undefined to return the base URL
|
|
2794
|
-
* @returns The resolved absolute URL string
|
|
2795
|
-
* @example
|
|
2796
|
-
* resolveURL("https://api.example.com", "/users") // "https://api.example.com/users"
|
|
2797
|
-
* resolveURL("https://api.example.com", "users") // "https://api.example.com/users"
|
|
2798
|
-
* resolveURL("https://api.example.com", "https://other.com") // "https://other.com"
|
|
2799
|
-
*/
|
|
2800
|
-
function resolveURL(baseURL, url) {
|
|
2801
|
-
if (!url)
|
|
2802
|
-
return baseURL || "";
|
|
2803
|
-
if (!baseURL)
|
|
2804
|
-
return url;
|
|
2805
|
-
if (/^https?:\/\//.test(url))
|
|
2806
|
-
return url;
|
|
2807
|
-
try {
|
|
2808
|
-
return new URL(url, baseURL.endsWith("/") ? baseURL : baseURL + "/").toString();
|
|
2809
|
-
}
|
|
2810
|
-
catch {
|
|
2811
|
-
return baseURL.replace(/\/$/, "") + (url.startsWith("/") ? url : "/" + url);
|
|
2812
|
-
}
|
|
2813
|
-
}
|
|
2814
|
-
/**
|
|
2815
|
-
* API builder for creating configured API instances with default settings.
|
|
2816
|
-
* Allows you to set default headers, timeouts, authentication, and other options
|
|
2817
|
-
* that will be applied to all requests created through this builder.
|
|
2818
|
-
*
|
|
2819
|
-
* @example
|
|
2820
|
-
* ```typescript
|
|
2821
|
-
* const api = create.api()
|
|
2822
|
-
* .withBaseURL("https://api.example.com")
|
|
2823
|
-
* .withBearerToken("token123")
|
|
2824
|
-
* .withTimeout(5000);
|
|
2825
|
-
*
|
|
2826
|
-
* // All requests will use the base URL, bearer token, and timeout
|
|
2827
|
-
* await api.get("/users").getJson();
|
|
2828
|
-
* await api.post("/posts").withBody({ title: "Hello" }).getJson();
|
|
2829
|
-
* ```
|
|
2801
|
+
* Internal API builder implementation.
|
|
2830
2802
|
*/
|
|
2831
|
-
class
|
|
2803
|
+
class ApiBuilderImpl {
|
|
2832
2804
|
baseURL;
|
|
2833
|
-
modifiers;
|
|
2834
|
-
|
|
2835
|
-
* Sets the base URL for all requests created through this API builder.
|
|
2836
|
-
* Relative URLs will be resolved against this base URL.
|
|
2837
|
-
*
|
|
2838
|
-
* @param baseURL - The base URL to use for all requests
|
|
2839
|
-
* @returns The API builder instance for method chaining
|
|
2840
|
-
* @example
|
|
2841
|
-
* ```typescript
|
|
2842
|
-
* const api = create.api().withBaseURL("https://api.example.com");
|
|
2843
|
-
* await api.get("/users").getJson(); // Requests https://api.example.com/users
|
|
2844
|
-
* ```
|
|
2845
|
-
*/
|
|
2805
|
+
modifiers = [];
|
|
2806
|
+
proxy;
|
|
2846
2807
|
withBaseURL(baseURL) {
|
|
2847
2808
|
this.baseURL = baseURL;
|
|
2848
|
-
return this;
|
|
2809
|
+
return this.getProxy();
|
|
2849
2810
|
}
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
if (!this.modifiers)
|
|
2859
|
-
this.modifiers = [];
|
|
2860
|
-
this.modifiers.push(modifier);
|
|
2861
|
-
return this;
|
|
2811
|
+
resolveURL(url) {
|
|
2812
|
+
if (!url)
|
|
2813
|
+
return this.baseURL || "";
|
|
2814
|
+
if (/^https?:\/\//.test(url))
|
|
2815
|
+
return url;
|
|
2816
|
+
if (!this.baseURL)
|
|
2817
|
+
return url;
|
|
2818
|
+
return this.baseURL.replace(/\/$/, "") + (url[0] === "/" ? url : "/" + url);
|
|
2862
2819
|
}
|
|
2863
|
-
|
|
2864
|
-
* Creates a GET request with the configured default settings.
|
|
2865
|
-
*
|
|
2866
|
-
* @param url - Optional URL path. If not provided, uses the base URL. If relative, resolves against base URL.
|
|
2867
|
-
* @returns A GetRequest instance ready to be executed
|
|
2868
|
-
* @example
|
|
2869
|
-
* ```typescript
|
|
2870
|
-
* const api = create.api().withBaseURL("https://api.example.com");
|
|
2871
|
-
* await api.get("/users").getJson();
|
|
2872
|
-
* await api.get("https://other.com/data").getJson(); // Absolute URL overrides base
|
|
2873
|
-
* ```
|
|
2874
|
-
*/
|
|
2875
|
-
get = (url) => {
|
|
2876
|
-
const request = get(resolveURL(this.baseURL, url));
|
|
2820
|
+
applyModifiers(request) {
|
|
2877
2821
|
if (this.modifiers)
|
|
2878
2822
|
for (const modifier of this.modifiers)
|
|
2879
2823
|
modifier(request);
|
|
2824
|
+
}
|
|
2825
|
+
get(url) {
|
|
2826
|
+
const request = new GetRequest(this.resolveURL(url));
|
|
2827
|
+
this.applyModifiers(request);
|
|
2880
2828
|
return request;
|
|
2881
|
-
}
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
* @param url - Optional URL path. If not provided, uses the base URL. If relative, resolves against base URL.
|
|
2886
|
-
* @returns A PostRequest instance ready to be executed
|
|
2887
|
-
* @example
|
|
2888
|
-
* ```typescript
|
|
2889
|
-
* const api = create.api().withBaseURL("https://api.example.com");
|
|
2890
|
-
* await api.post("/users").withBody({ name: "John" }).getJson();
|
|
2891
|
-
* ```
|
|
2892
|
-
*/
|
|
2893
|
-
post = (url) => {
|
|
2894
|
-
const request = post(resolveURL(this.baseURL, url));
|
|
2895
|
-
if (this.modifiers)
|
|
2896
|
-
for (const modifier of this.modifiers)
|
|
2897
|
-
modifier(request);
|
|
2829
|
+
}
|
|
2830
|
+
post(url) {
|
|
2831
|
+
const request = new PostRequest(this.resolveURL(url));
|
|
2832
|
+
this.applyModifiers(request);
|
|
2898
2833
|
return request;
|
|
2899
|
-
}
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
* @param url - Optional URL path. If not provided, uses the base URL. If relative, resolves against base URL.
|
|
2904
|
-
* @returns A PutRequest instance ready to be executed
|
|
2905
|
-
* @example
|
|
2906
|
-
* ```typescript
|
|
2907
|
-
* const api = create.api().withBaseURL("https://api.example.com");
|
|
2908
|
-
* await api.put("/users/123").withBody({ name: "Jane" }).getJson();
|
|
2909
|
-
* ```
|
|
2910
|
-
*/
|
|
2911
|
-
put = (url) => {
|
|
2912
|
-
const request = put(resolveURL(this.baseURL, url));
|
|
2913
|
-
if (this.modifiers)
|
|
2914
|
-
for (const modifier of this.modifiers)
|
|
2915
|
-
modifier(request);
|
|
2834
|
+
}
|
|
2835
|
+
put(url) {
|
|
2836
|
+
const request = new PutRequest(this.resolveURL(url));
|
|
2837
|
+
this.applyModifiers(request);
|
|
2916
2838
|
return request;
|
|
2917
|
-
}
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
* @param url - Optional URL path. If not provided, uses the base URL. If relative, resolves against base URL.
|
|
2922
|
-
* @returns A DeleteRequest instance ready to be executed
|
|
2923
|
-
* @example
|
|
2924
|
-
* ```typescript
|
|
2925
|
-
* const api = create.api().withBaseURL("https://api.example.com");
|
|
2926
|
-
* await api.del("/users/123").getResponse();
|
|
2927
|
-
* ```
|
|
2928
|
-
*/
|
|
2929
|
-
del = (url) => {
|
|
2930
|
-
const request = del(resolveURL(this.baseURL, url));
|
|
2931
|
-
if (this.modifiers)
|
|
2932
|
-
for (const modifier of this.modifiers)
|
|
2933
|
-
modifier(request);
|
|
2839
|
+
}
|
|
2840
|
+
del(url) {
|
|
2841
|
+
const request = new DeleteRequest(this.resolveURL(url));
|
|
2842
|
+
this.applyModifiers(request);
|
|
2934
2843
|
return request;
|
|
2935
|
-
}
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
* @param url - Optional URL path. If not provided, uses the base URL. If relative, resolves against base URL.
|
|
2940
|
-
* @returns A PatchRequest instance ready to be executed
|
|
2941
|
-
* @example
|
|
2942
|
-
* ```typescript
|
|
2943
|
-
* const api = create.api().withBaseURL("https://api.example.com");
|
|
2944
|
-
* await api.patch("/users/123").withBody({ name: "Updated" }).getJson();
|
|
2945
|
-
* ```
|
|
2946
|
-
*/
|
|
2947
|
-
patch = (url) => {
|
|
2948
|
-
const request = patch(resolveURL(this.baseURL, url));
|
|
2949
|
-
if (this.modifiers)
|
|
2950
|
-
for (const modifier of this.modifiers)
|
|
2951
|
-
modifier(request);
|
|
2844
|
+
}
|
|
2845
|
+
patch(url) {
|
|
2846
|
+
const request = new PatchRequest(this.resolveURL(url));
|
|
2847
|
+
this.applyModifiers(request);
|
|
2952
2848
|
return request;
|
|
2953
|
-
}
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
* @param url - Optional URL path. If not provided, uses the base URL. If relative, resolves against base URL.
|
|
2958
|
-
* @returns A HeadRequest instance ready to be executed
|
|
2959
|
-
* @example
|
|
2960
|
-
* ```typescript
|
|
2961
|
-
* const api = create.api().withBaseURL("https://api.example.com");
|
|
2962
|
-
* const response = await api.head("/users").getResponse();
|
|
2963
|
-
* ```
|
|
2964
|
-
*/
|
|
2965
|
-
head = (url) => {
|
|
2966
|
-
const request = head(resolveURL(this.baseURL, url));
|
|
2967
|
-
if (this.modifiers)
|
|
2968
|
-
for (const modifier of this.modifiers)
|
|
2969
|
-
modifier(request);
|
|
2849
|
+
}
|
|
2850
|
+
head(url) {
|
|
2851
|
+
const request = new HeadRequest(this.resolveURL(url));
|
|
2852
|
+
this.applyModifiers(request);
|
|
2970
2853
|
return request;
|
|
2971
|
-
}
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
* @param url - Optional URL path. If not provided, uses the base URL. If relative, resolves against base URL.
|
|
2976
|
-
* @returns An OptionsRequest instance ready to be executed
|
|
2977
|
-
* @example
|
|
2978
|
-
* ```typescript
|
|
2979
|
-
* const api = create.api().withBaseURL("https://api.example.com");
|
|
2980
|
-
* await api.options("/users").getResponse();
|
|
2981
|
-
* ```
|
|
2982
|
-
*/
|
|
2983
|
-
options = (url) => {
|
|
2984
|
-
const request = options(resolveURL(this.baseURL, url));
|
|
2985
|
-
if (this.modifiers)
|
|
2986
|
-
for (const modifier of this.modifiers)
|
|
2987
|
-
modifier(request);
|
|
2854
|
+
}
|
|
2855
|
+
options(url) {
|
|
2856
|
+
const request = new OptionsRequest(this.resolveURL(url));
|
|
2857
|
+
this.applyModifiers(request);
|
|
2988
2858
|
return request;
|
|
2989
|
-
}
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2859
|
+
}
|
|
2860
|
+
addModifier(modifier) {
|
|
2861
|
+
this.modifiers.push(modifier);
|
|
2862
|
+
return this.getProxy();
|
|
2863
|
+
}
|
|
2864
|
+
getProxy() {
|
|
2865
|
+
if (!this.proxy) {
|
|
2866
|
+
this.proxy = this.createProxy();
|
|
2867
|
+
}
|
|
2868
|
+
return this.proxy;
|
|
2869
|
+
}
|
|
2870
|
+
createProxy() {
|
|
2871
|
+
const disallowedMethods = new Set(["withBody", "withGraphQL", "withAbortController"]);
|
|
2872
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return
|
|
2873
|
+
return new Proxy(this, {
|
|
2999
2874
|
get(target, prop) {
|
|
3000
|
-
const
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
const result = propertyValue.apply(target, args);
|
|
3005
|
-
return result === target ? proxy : result;
|
|
3006
|
-
};
|
|
3007
|
-
}
|
|
3008
|
-
return propertyValue;
|
|
2875
|
+
const implTarget = target;
|
|
2876
|
+
// Return undefined for disallowed methods
|
|
2877
|
+
if (typeof prop === "string" && disallowedMethods.has(prop)) {
|
|
2878
|
+
return undefined;
|
|
3009
2879
|
}
|
|
3010
|
-
if
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
return proxy;
|
|
3018
|
-
};
|
|
3019
|
-
}
|
|
2880
|
+
// Check if it's an HTTP method - these should be called directly
|
|
2881
|
+
if (prop === "get" || prop === "post" || prop === "put" || prop === "del" || prop === "patch" || prop === "head" || prop === "options") {
|
|
2882
|
+
return implTarget[prop].bind(implTarget);
|
|
2883
|
+
}
|
|
2884
|
+
// Check if it's a configuration method that already exists
|
|
2885
|
+
if (prop === "withBaseURL") {
|
|
2886
|
+
return implTarget[prop].bind(implTarget);
|
|
3020
2887
|
}
|
|
3021
|
-
|
|
2888
|
+
// Check if the property exists on BaseRequest prototype
|
|
2889
|
+
// If it's a 'with...' method or other chainable method, create a modifier for it
|
|
2890
|
+
if (typeof prop === "string" && (prop.startsWith("with") || prop === "onRetry")) {
|
|
2891
|
+
return (...args) => {
|
|
2892
|
+
return implTarget.addModifier((request) => {
|
|
2893
|
+
const method = request[prop];
|
|
2894
|
+
if (typeof method === "function") {
|
|
2895
|
+
method.apply(request, args);
|
|
2896
|
+
}
|
|
2897
|
+
});
|
|
2898
|
+
};
|
|
2899
|
+
}
|
|
2900
|
+
// For other properties, return them directly if they exist
|
|
2901
|
+
const targetValue = implTarget[prop];
|
|
2902
|
+
return targetValue;
|
|
3022
2903
|
},
|
|
3023
2904
|
});
|
|
3024
|
-
|
|
2905
|
+
}
|
|
2906
|
+
static create() {
|
|
2907
|
+
return new ApiBuilderImpl().getProxy();
|
|
3025
2908
|
}
|
|
3026
2909
|
}
|
|
3027
2910
|
/**
|
|
3028
|
-
* Creates a new API builder
|
|
3029
|
-
* The builder allows you to set base
|
|
3030
|
-
* and other options that will be applied to all requests
|
|
2911
|
+
* Creates a new API builder for configuring default request settings.
|
|
2912
|
+
* The API builder allows you to set up a base URL, default headers, timeout,
|
|
2913
|
+
* and other configuration options that will be applied to all requests made through it.
|
|
2914
|
+
*
|
|
2915
|
+
* @returns A new API builder instance
|
|
3031
2916
|
*
|
|
3032
|
-
* @returns A new ApiBuilder instance with all configuration methods available
|
|
3033
2917
|
* @example
|
|
3034
2918
|
* ```typescript
|
|
3035
|
-
* // Create an API instance with
|
|
3036
|
-
* const api =
|
|
3037
|
-
* .withBaseURL(
|
|
3038
|
-
* .withBearerToken(
|
|
3039
|
-
* .withTimeout(5000)
|
|
3040
|
-
* .withHeaders({ "X-Custom": "value" });
|
|
2919
|
+
* // Create an API instance with defaults
|
|
2920
|
+
* const api = api()
|
|
2921
|
+
* .withBaseURL('https://api.example.com')
|
|
2922
|
+
* .withBearerToken('token123')
|
|
2923
|
+
* .withTimeout(5000);
|
|
3041
2924
|
*
|
|
3042
2925
|
* // All requests will use these defaults
|
|
3043
|
-
* const users = await api.get(
|
|
3044
|
-
* const newUser = await api.post(
|
|
2926
|
+
* const users = await api.get('/users').getJson();
|
|
2927
|
+
* const newUser = await api.post('/users').withBody({ name: 'John' }).getJson();
|
|
2928
|
+
* ```
|
|
2929
|
+
*
|
|
2930
|
+
* @example
|
|
2931
|
+
* ```typescript
|
|
2932
|
+
* // Use without URL when baseURL is set
|
|
2933
|
+
* const api = api().withBaseURL('https://api.example.com');
|
|
2934
|
+
* const data = await api.get().getJson(); // Requests to https://api.example.com
|
|
2935
|
+
* ```
|
|
2936
|
+
*
|
|
2937
|
+
* @example
|
|
2938
|
+
* ```typescript
|
|
2939
|
+
* // Override defaults per request
|
|
2940
|
+
* const api = api()
|
|
2941
|
+
* .withBaseURL('https://api.example.com')
|
|
2942
|
+
* .withTimeout(5000);
|
|
2943
|
+
*
|
|
2944
|
+
* // This request uses a longer timeout
|
|
2945
|
+
* await api.get('/slow-endpoint').withTimeout(30000).getJson();
|
|
3045
2946
|
* ```
|
|
3046
2947
|
*/
|
|
3047
2948
|
function api() {
|
|
3048
|
-
return
|
|
2949
|
+
return ApiBuilderImpl.create();
|
|
3049
2950
|
}
|
|
3050
2951
|
|
|
3051
2952
|
/**
|
|
@@ -3061,7 +2962,7 @@ function api() {
|
|
|
3061
2962
|
*
|
|
3062
2963
|
* // POST request with body
|
|
3063
2964
|
* const newUser = await create.post('/api/users')
|
|
3064
|
-
* .
|
|
2965
|
+
* .withBody({ name: 'John', email: 'john@example.com' })
|
|
3065
2966
|
* .getJson();
|
|
3066
2967
|
*
|
|
3067
2968
|
* // Configure API instance with defaults
|
|
@@ -3094,13 +2995,13 @@ exports.PostRequest = PostRequest;
|
|
|
3094
2995
|
exports.PutRequest = PutRequest;
|
|
3095
2996
|
exports.RequestError = RequestError;
|
|
3096
2997
|
exports.ResponseWrapper = ResponseWrapper;
|
|
3097
|
-
exports.
|
|
2998
|
+
exports.createApi = api;
|
|
2999
|
+
exports.createDelete = del;
|
|
3000
|
+
exports.createGet = get;
|
|
3001
|
+
exports.createHead = head;
|
|
3002
|
+
exports.createOptions = options;
|
|
3003
|
+
exports.createPatch = patch;
|
|
3004
|
+
exports.createPost = post;
|
|
3005
|
+
exports.createPut = put;
|
|
3098
3006
|
exports["default"] = create;
|
|
3099
|
-
exports.del = del;
|
|
3100
|
-
exports.get = get;
|
|
3101
|
-
exports.head = head;
|
|
3102
|
-
exports.options = options;
|
|
3103
|
-
exports.patch = patch;
|
|
3104
|
-
exports.post = post;
|
|
3105
|
-
exports.put = put;
|
|
3106
3007
|
//# sourceMappingURL=index.cjs.map
|