aspi 2.4.0 → 2.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/dist/index.cjs +133 -10
- package/dist/index.d.cts +106 -1
- package/dist/index.d.ts +106 -1
- package/dist/index.js +133 -10
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -284,6 +284,8 @@ function pipe(a, ab, bc, cd, de, ef, fg, gh, hi) {
|
|
|
284
284
|
return bc(ab(a));
|
|
285
285
|
case 4:
|
|
286
286
|
return cd(bc(ab(a)));
|
|
287
|
+
case 5:
|
|
288
|
+
return de(cd(bc(ab(a))));
|
|
287
289
|
case 6:
|
|
288
290
|
return ef(de(cd(bc(ab(a)))));
|
|
289
291
|
case 7:
|
|
@@ -312,6 +314,7 @@ var Request = class {
|
|
|
312
314
|
#schema = null;
|
|
313
315
|
#bodySchema = null;
|
|
314
316
|
#retryConfig;
|
|
317
|
+
#timeoutMs;
|
|
315
318
|
#shouldBeResult = false;
|
|
316
319
|
#bodySchemaIssues = [];
|
|
317
320
|
#throwOnError = false;
|
|
@@ -361,6 +364,24 @@ var Request = class {
|
|
|
361
364
|
};
|
|
362
365
|
return this;
|
|
363
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* Sets a timeout for the request in milliseconds.
|
|
369
|
+
*
|
|
370
|
+
* When the timeout expires, the request is aborted with an `AbortError`.
|
|
371
|
+
* If a signal was already provided, the timeout is chained so that either
|
|
372
|
+
* the external signal or the timeout can abort the request.
|
|
373
|
+
*
|
|
374
|
+
* @param {number} ms - Timeout duration in milliseconds.
|
|
375
|
+
* @returns {this} The current {@link Request} instance for method chaining.
|
|
376
|
+
*
|
|
377
|
+
* @example
|
|
378
|
+
* const request = new Request('/slow-endpoint', config);
|
|
379
|
+
* request.timeout(5000); // abort after 5 seconds
|
|
380
|
+
*/
|
|
381
|
+
timeout(ms) {
|
|
382
|
+
this.#timeoutMs = ms;
|
|
383
|
+
return this;
|
|
384
|
+
}
|
|
364
385
|
/**
|
|
365
386
|
* Merges the provided headers into the request configuration.
|
|
366
387
|
*
|
|
@@ -944,6 +965,92 @@ var Request = class {
|
|
|
944
965
|
const output = await this.#makeRequest((response) => response.blob());
|
|
945
966
|
return this.#mapResponse(output);
|
|
946
967
|
}
|
|
968
|
+
/**
|
|
969
|
+
* Executes the request and returns the response body as an {@link ArrayBuffer}.
|
|
970
|
+
*
|
|
971
|
+
* The shape of the returned {@link Promise} depends on the request mode:
|
|
972
|
+
*
|
|
973
|
+
* - **Result mode** (`withResult()`): resolves to a {@link Result.Result} containing
|
|
974
|
+
* either an {@link AspiResultOk} with `ArrayBuffer` data or an error variant.
|
|
975
|
+
* - **Throwable mode** (`throwable()`): resolves directly to an {@link ArrayBuffer}
|
|
976
|
+
* (wrapped in {@link AspiPlainResponse}) and throws on failure.
|
|
977
|
+
* - **Default mode**: resolves to a tuple `[value, error]` where exactly one element
|
|
978
|
+
* is `null`.
|
|
979
|
+
*
|
|
980
|
+
* @returns {Promise<
|
|
981
|
+
* Opts['withResult'] extends true
|
|
982
|
+
* ? Result.Result<
|
|
983
|
+
* AspiResultOk<TRequest, ArrayBuffer>,
|
|
984
|
+
* | AspiError<TRequest>
|
|
985
|
+
* | (Opts extends { error: any }
|
|
986
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
987
|
+
* : never)
|
|
988
|
+
* >
|
|
989
|
+
* : Opts['throwable'] extends true
|
|
990
|
+
* ? AspiPlainResponse<TRequest, ArrayBuffer>
|
|
991
|
+
* : [
|
|
992
|
+
* AspiResultOk<TRequest, ArrayBuffer> | null,
|
|
993
|
+
* (
|
|
994
|
+
* | (
|
|
995
|
+
* | AspiError<TRequest>
|
|
996
|
+
* | (Opts extends { error: any }
|
|
997
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
998
|
+
* : never)
|
|
999
|
+
* )
|
|
1000
|
+
* | null
|
|
1001
|
+
* ),
|
|
1002
|
+
* ]
|
|
1003
|
+
* >}
|
|
1004
|
+
*/
|
|
1005
|
+
async arrayBuffer() {
|
|
1006
|
+
const output = await this.#makeRequest(
|
|
1007
|
+
(response) => response.arrayBuffer()
|
|
1008
|
+
);
|
|
1009
|
+
return this.#mapResponse(output);
|
|
1010
|
+
}
|
|
1011
|
+
/**
|
|
1012
|
+
* Executes the request and returns the response body as {@link FormData}.
|
|
1013
|
+
*
|
|
1014
|
+
* The shape of the returned {@link Promise} depends on the request mode:
|
|
1015
|
+
*
|
|
1016
|
+
* - **Result mode** (`withResult()`): resolves to a {@link Result.Result} containing
|
|
1017
|
+
* either an {@link AspiResultOk} with `FormData` or an error variant.
|
|
1018
|
+
* - **Throwable mode** (`throwable()`): resolves directly to {@link FormData}
|
|
1019
|
+
* (wrapped in {@link AspiPlainResponse}) and throws on failure.
|
|
1020
|
+
* - **Default mode**: resolves to a tuple `[value, error]` where exactly one element
|
|
1021
|
+
* is `null`.
|
|
1022
|
+
*
|
|
1023
|
+
* @returns {Promise<
|
|
1024
|
+
* Opts['withResult'] extends true
|
|
1025
|
+
* ? Result.Result<
|
|
1026
|
+
* AspiResultOk<TRequest, FormData>,
|
|
1027
|
+
* | AspiError<TRequest>
|
|
1028
|
+
* | (Opts extends { error: any }
|
|
1029
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1030
|
+
* : never)
|
|
1031
|
+
* >
|
|
1032
|
+
* : Opts['throwable'] extends true
|
|
1033
|
+
* ? AspiPlainResponse<TRequest, FormData>
|
|
1034
|
+
* : [
|
|
1035
|
+
* AspiResultOk<TRequest, FormData> | null,
|
|
1036
|
+
* (
|
|
1037
|
+
* | (
|
|
1038
|
+
* | AspiError<TRequest>
|
|
1039
|
+
* | (Opts extends { error: any }
|
|
1040
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1041
|
+
* : never)
|
|
1042
|
+
* )
|
|
1043
|
+
* | null
|
|
1044
|
+
* ),
|
|
1045
|
+
* ]
|
|
1046
|
+
* >}
|
|
1047
|
+
*/
|
|
1048
|
+
async formData() {
|
|
1049
|
+
const output = await this.#makeRequest(
|
|
1050
|
+
(response) => response.formData()
|
|
1051
|
+
);
|
|
1052
|
+
return this.#mapResponse(output);
|
|
1053
|
+
}
|
|
947
1054
|
#url() {
|
|
948
1055
|
if (this.#path.startsWith("http://") || this.#path.startsWith("https://")) {
|
|
949
1056
|
const absolute = new URL(this.#path);
|
|
@@ -961,7 +1068,6 @@ var Request = class {
|
|
|
961
1068
|
let path = rawPath || "";
|
|
962
1069
|
path = path.replace(/^\/+/, "");
|
|
963
1070
|
path = path.replace(/\/{2,}/g, "/");
|
|
964
|
-
path = path.replace(/\/+$/, "");
|
|
965
1071
|
if (path) {
|
|
966
1072
|
path = "/" + path.replace(/^\/+/, "");
|
|
967
1073
|
}
|
|
@@ -979,7 +1085,6 @@ var Request = class {
|
|
|
979
1085
|
if (fragment) {
|
|
980
1086
|
url += `#${fragment}`;
|
|
981
1087
|
}
|
|
982
|
-
url = url.replace(/\/+$/, "");
|
|
983
1088
|
return url;
|
|
984
1089
|
}
|
|
985
1090
|
/**
|
|
@@ -1077,16 +1182,32 @@ var Request = class {
|
|
|
1077
1182
|
});
|
|
1078
1183
|
let responseData = null;
|
|
1079
1184
|
while (attempts <= retries) {
|
|
1185
|
+
let timeoutTimer;
|
|
1080
1186
|
try {
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1187
|
+
const attemptInit = { ...requestInit };
|
|
1188
|
+
if (this.#timeoutMs && this.#timeoutMs > 0) {
|
|
1189
|
+
const timeoutController = new AbortController();
|
|
1190
|
+
timeoutTimer = setTimeout(
|
|
1191
|
+
() => timeoutController.abort(),
|
|
1192
|
+
this.#timeoutMs
|
|
1193
|
+
);
|
|
1194
|
+
if (attemptInit.signal) {
|
|
1195
|
+
attemptInit.signal.addEventListener(
|
|
1196
|
+
"abort",
|
|
1197
|
+
() => timeoutController.abort(),
|
|
1198
|
+
{ once: true }
|
|
1085
1199
|
);
|
|
1086
1200
|
}
|
|
1087
|
-
|
|
1088
|
-
|
|
1201
|
+
attemptInit.signal = timeoutController.signal;
|
|
1202
|
+
}
|
|
1203
|
+
let runner = () => fetch(url, attemptInit);
|
|
1204
|
+
for (let i = this.#capabilities.length - 1; i >= 0; i--) {
|
|
1205
|
+
const cap = this.#capabilities[i];
|
|
1206
|
+
const next = runner;
|
|
1207
|
+
runner = () => cap({ request }).run(next);
|
|
1089
1208
|
}
|
|
1209
|
+
response = await runner();
|
|
1210
|
+
if (timeoutTimer) clearTimeout(timeoutTimer);
|
|
1090
1211
|
responseData = await responseParser(response);
|
|
1091
1212
|
if (responseData instanceof Error) {
|
|
1092
1213
|
return err(responseData);
|
|
@@ -1098,7 +1219,7 @@ var Request = class {
|
|
|
1098
1219
|
if (this.#isSuccessResponse(response) || !retryOn.includes(response.status) && !retryWhileCondition) {
|
|
1099
1220
|
break;
|
|
1100
1221
|
}
|
|
1101
|
-
if (response.status in this.#customErrorCbs
|
|
1222
|
+
if (response.status in this.#customErrorCbs) {
|
|
1102
1223
|
const result = this.#customErrorCbs[response.status].cb({
|
|
1103
1224
|
request,
|
|
1104
1225
|
response: this.#makeResponse(response, responseData)
|
|
@@ -1121,6 +1242,7 @@ var Request = class {
|
|
|
1121
1242
|
await this.#abortDelay(delay, request);
|
|
1122
1243
|
}
|
|
1123
1244
|
} catch (e) {
|
|
1245
|
+
if (timeoutTimer) clearTimeout(timeoutTimer);
|
|
1124
1246
|
if (e instanceof Error && e.name === "AbortError") {
|
|
1125
1247
|
if (500 in this.#customErrorCbs) {
|
|
1126
1248
|
const result = this.#customErrorCbs[response.status].cb({
|
|
@@ -1389,7 +1511,8 @@ var Request = class {
|
|
|
1389
1511
|
* ```
|
|
1390
1512
|
*/
|
|
1391
1513
|
useCapability(capability) {
|
|
1392
|
-
|
|
1514
|
+
this.#capabilities.push(capability);
|
|
1515
|
+
return this;
|
|
1393
1516
|
}
|
|
1394
1517
|
};
|
|
1395
1518
|
|
package/dist/index.d.cts
CHANGED
|
@@ -858,6 +858,21 @@ declare class Request<Method extends HttpMethods, TRequest extends AspiRequestIn
|
|
|
858
858
|
* });
|
|
859
859
|
*/
|
|
860
860
|
setRetry(retry: AspiRetryConfig<TRequest>): this;
|
|
861
|
+
/**
|
|
862
|
+
* Sets a timeout for the request in milliseconds.
|
|
863
|
+
*
|
|
864
|
+
* When the timeout expires, the request is aborted with an `AbortError`.
|
|
865
|
+
* If a signal was already provided, the timeout is chained so that either
|
|
866
|
+
* the external signal or the timeout can abort the request.
|
|
867
|
+
*
|
|
868
|
+
* @param {number} ms - Timeout duration in milliseconds.
|
|
869
|
+
* @returns {this} The current {@link Request} instance for method chaining.
|
|
870
|
+
*
|
|
871
|
+
* @example
|
|
872
|
+
* const request = new Request('/slow-endpoint', config);
|
|
873
|
+
* request.timeout(5000); // abort after 5 seconds
|
|
874
|
+
*/
|
|
875
|
+
timeout(ms: number): this;
|
|
861
876
|
/**
|
|
862
877
|
* Merges the provided headers into the request configuration.
|
|
863
878
|
*
|
|
@@ -1390,6 +1405,96 @@ declare class Request<Method extends HttpMethods, TRequest extends AspiRequestIn
|
|
|
1390
1405
|
error: any;
|
|
1391
1406
|
} ? Opts['error'][keyof Opts['error']] : never)) | null)
|
|
1392
1407
|
]>;
|
|
1408
|
+
/**
|
|
1409
|
+
* Executes the request and returns the response body as an {@link ArrayBuffer}.
|
|
1410
|
+
*
|
|
1411
|
+
* The shape of the returned {@link Promise} depends on the request mode:
|
|
1412
|
+
*
|
|
1413
|
+
* - **Result mode** (`withResult()`): resolves to a {@link Result.Result} containing
|
|
1414
|
+
* either an {@link AspiResultOk} with `ArrayBuffer` data or an error variant.
|
|
1415
|
+
* - **Throwable mode** (`throwable()`): resolves directly to an {@link ArrayBuffer}
|
|
1416
|
+
* (wrapped in {@link AspiPlainResponse}) and throws on failure.
|
|
1417
|
+
* - **Default mode**: resolves to a tuple `[value, error]` where exactly one element
|
|
1418
|
+
* is `null`.
|
|
1419
|
+
*
|
|
1420
|
+
* @returns {Promise<
|
|
1421
|
+
* Opts['withResult'] extends true
|
|
1422
|
+
* ? Result.Result<
|
|
1423
|
+
* AspiResultOk<TRequest, ArrayBuffer>,
|
|
1424
|
+
* | AspiError<TRequest>
|
|
1425
|
+
* | (Opts extends { error: any }
|
|
1426
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1427
|
+
* : never)
|
|
1428
|
+
* >
|
|
1429
|
+
* : Opts['throwable'] extends true
|
|
1430
|
+
* ? AspiPlainResponse<TRequest, ArrayBuffer>
|
|
1431
|
+
* : [
|
|
1432
|
+
* AspiResultOk<TRequest, ArrayBuffer> | null,
|
|
1433
|
+
* (
|
|
1434
|
+
* | (
|
|
1435
|
+
* | AspiError<TRequest>
|
|
1436
|
+
* | (Opts extends { error: any }
|
|
1437
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1438
|
+
* : never)
|
|
1439
|
+
* )
|
|
1440
|
+
* | null
|
|
1441
|
+
* ),
|
|
1442
|
+
* ]
|
|
1443
|
+
* >}
|
|
1444
|
+
*/
|
|
1445
|
+
arrayBuffer(): Promise<Opts['withResult'] extends true ? Result<AspiResultOk<TRequest, ArrayBuffer>, AspiError<TRequest> | (Opts extends {
|
|
1446
|
+
error: any;
|
|
1447
|
+
} ? Opts['error'][keyof Opts['error']] : never)> : Opts['throwable'] extends true ? AspiPlainResponse<TRequest, ArrayBuffer> : [
|
|
1448
|
+
AspiResultOk<TRequest, ArrayBuffer> | null,
|
|
1449
|
+
((AspiError<TRequest> | (Opts extends {
|
|
1450
|
+
error: any;
|
|
1451
|
+
} ? Opts['error'][keyof Opts['error']] : never)) | null)
|
|
1452
|
+
]>;
|
|
1453
|
+
/**
|
|
1454
|
+
* Executes the request and returns the response body as {@link FormData}.
|
|
1455
|
+
*
|
|
1456
|
+
* The shape of the returned {@link Promise} depends on the request mode:
|
|
1457
|
+
*
|
|
1458
|
+
* - **Result mode** (`withResult()`): resolves to a {@link Result.Result} containing
|
|
1459
|
+
* either an {@link AspiResultOk} with `FormData` or an error variant.
|
|
1460
|
+
* - **Throwable mode** (`throwable()`): resolves directly to {@link FormData}
|
|
1461
|
+
* (wrapped in {@link AspiPlainResponse}) and throws on failure.
|
|
1462
|
+
* - **Default mode**: resolves to a tuple `[value, error]` where exactly one element
|
|
1463
|
+
* is `null`.
|
|
1464
|
+
*
|
|
1465
|
+
* @returns {Promise<
|
|
1466
|
+
* Opts['withResult'] extends true
|
|
1467
|
+
* ? Result.Result<
|
|
1468
|
+
* AspiResultOk<TRequest, FormData>,
|
|
1469
|
+
* | AspiError<TRequest>
|
|
1470
|
+
* | (Opts extends { error: any }
|
|
1471
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1472
|
+
* : never)
|
|
1473
|
+
* >
|
|
1474
|
+
* : Opts['throwable'] extends true
|
|
1475
|
+
* ? AspiPlainResponse<TRequest, FormData>
|
|
1476
|
+
* : [
|
|
1477
|
+
* AspiResultOk<TRequest, FormData> | null,
|
|
1478
|
+
* (
|
|
1479
|
+
* | (
|
|
1480
|
+
* | AspiError<TRequest>
|
|
1481
|
+
* | (Opts extends { error: any }
|
|
1482
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1483
|
+
* : never)
|
|
1484
|
+
* )
|
|
1485
|
+
* | null
|
|
1486
|
+
* ),
|
|
1487
|
+
* ]
|
|
1488
|
+
* >}
|
|
1489
|
+
*/
|
|
1490
|
+
formData(): Promise<Opts['withResult'] extends true ? Result<AspiResultOk<TRequest, FormData>, AspiError<TRequest> | (Opts extends {
|
|
1491
|
+
error: any;
|
|
1492
|
+
} ? Opts['error'][keyof Opts['error']] : never)> : Opts['throwable'] extends true ? AspiPlainResponse<TRequest, FormData> : [
|
|
1493
|
+
AspiResultOk<TRequest, FormData> | null,
|
|
1494
|
+
((AspiError<TRequest> | (Opts extends {
|
|
1495
|
+
error: any;
|
|
1496
|
+
} ? Opts['error'][keyof Opts['error']] : never)) | null)
|
|
1497
|
+
]>;
|
|
1393
1498
|
/**
|
|
1394
1499
|
* Returns the fully‑qualified URL that will be used for the request.
|
|
1395
1500
|
*
|
|
@@ -1549,7 +1654,7 @@ declare class Request<Method extends HttpMethods, TRequest extends AspiRequestIn
|
|
|
1549
1654
|
* .json();
|
|
1550
1655
|
* ```
|
|
1551
1656
|
*/
|
|
1552
|
-
useCapability(capability: Capability<TRequest>):
|
|
1657
|
+
useCapability(capability: Capability<TRequest>): this;
|
|
1553
1658
|
}
|
|
1554
1659
|
|
|
1555
1660
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -858,6 +858,21 @@ declare class Request<Method extends HttpMethods, TRequest extends AspiRequestIn
|
|
|
858
858
|
* });
|
|
859
859
|
*/
|
|
860
860
|
setRetry(retry: AspiRetryConfig<TRequest>): this;
|
|
861
|
+
/**
|
|
862
|
+
* Sets a timeout for the request in milliseconds.
|
|
863
|
+
*
|
|
864
|
+
* When the timeout expires, the request is aborted with an `AbortError`.
|
|
865
|
+
* If a signal was already provided, the timeout is chained so that either
|
|
866
|
+
* the external signal or the timeout can abort the request.
|
|
867
|
+
*
|
|
868
|
+
* @param {number} ms - Timeout duration in milliseconds.
|
|
869
|
+
* @returns {this} The current {@link Request} instance for method chaining.
|
|
870
|
+
*
|
|
871
|
+
* @example
|
|
872
|
+
* const request = new Request('/slow-endpoint', config);
|
|
873
|
+
* request.timeout(5000); // abort after 5 seconds
|
|
874
|
+
*/
|
|
875
|
+
timeout(ms: number): this;
|
|
861
876
|
/**
|
|
862
877
|
* Merges the provided headers into the request configuration.
|
|
863
878
|
*
|
|
@@ -1390,6 +1405,96 @@ declare class Request<Method extends HttpMethods, TRequest extends AspiRequestIn
|
|
|
1390
1405
|
error: any;
|
|
1391
1406
|
} ? Opts['error'][keyof Opts['error']] : never)) | null)
|
|
1392
1407
|
]>;
|
|
1408
|
+
/**
|
|
1409
|
+
* Executes the request and returns the response body as an {@link ArrayBuffer}.
|
|
1410
|
+
*
|
|
1411
|
+
* The shape of the returned {@link Promise} depends on the request mode:
|
|
1412
|
+
*
|
|
1413
|
+
* - **Result mode** (`withResult()`): resolves to a {@link Result.Result} containing
|
|
1414
|
+
* either an {@link AspiResultOk} with `ArrayBuffer` data or an error variant.
|
|
1415
|
+
* - **Throwable mode** (`throwable()`): resolves directly to an {@link ArrayBuffer}
|
|
1416
|
+
* (wrapped in {@link AspiPlainResponse}) and throws on failure.
|
|
1417
|
+
* - **Default mode**: resolves to a tuple `[value, error]` where exactly one element
|
|
1418
|
+
* is `null`.
|
|
1419
|
+
*
|
|
1420
|
+
* @returns {Promise<
|
|
1421
|
+
* Opts['withResult'] extends true
|
|
1422
|
+
* ? Result.Result<
|
|
1423
|
+
* AspiResultOk<TRequest, ArrayBuffer>,
|
|
1424
|
+
* | AspiError<TRequest>
|
|
1425
|
+
* | (Opts extends { error: any }
|
|
1426
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1427
|
+
* : never)
|
|
1428
|
+
* >
|
|
1429
|
+
* : Opts['throwable'] extends true
|
|
1430
|
+
* ? AspiPlainResponse<TRequest, ArrayBuffer>
|
|
1431
|
+
* : [
|
|
1432
|
+
* AspiResultOk<TRequest, ArrayBuffer> | null,
|
|
1433
|
+
* (
|
|
1434
|
+
* | (
|
|
1435
|
+
* | AspiError<TRequest>
|
|
1436
|
+
* | (Opts extends { error: any }
|
|
1437
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1438
|
+
* : never)
|
|
1439
|
+
* )
|
|
1440
|
+
* | null
|
|
1441
|
+
* ),
|
|
1442
|
+
* ]
|
|
1443
|
+
* >}
|
|
1444
|
+
*/
|
|
1445
|
+
arrayBuffer(): Promise<Opts['withResult'] extends true ? Result<AspiResultOk<TRequest, ArrayBuffer>, AspiError<TRequest> | (Opts extends {
|
|
1446
|
+
error: any;
|
|
1447
|
+
} ? Opts['error'][keyof Opts['error']] : never)> : Opts['throwable'] extends true ? AspiPlainResponse<TRequest, ArrayBuffer> : [
|
|
1448
|
+
AspiResultOk<TRequest, ArrayBuffer> | null,
|
|
1449
|
+
((AspiError<TRequest> | (Opts extends {
|
|
1450
|
+
error: any;
|
|
1451
|
+
} ? Opts['error'][keyof Opts['error']] : never)) | null)
|
|
1452
|
+
]>;
|
|
1453
|
+
/**
|
|
1454
|
+
* Executes the request and returns the response body as {@link FormData}.
|
|
1455
|
+
*
|
|
1456
|
+
* The shape of the returned {@link Promise} depends on the request mode:
|
|
1457
|
+
*
|
|
1458
|
+
* - **Result mode** (`withResult()`): resolves to a {@link Result.Result} containing
|
|
1459
|
+
* either an {@link AspiResultOk} with `FormData` or an error variant.
|
|
1460
|
+
* - **Throwable mode** (`throwable()`): resolves directly to {@link FormData}
|
|
1461
|
+
* (wrapped in {@link AspiPlainResponse}) and throws on failure.
|
|
1462
|
+
* - **Default mode**: resolves to a tuple `[value, error]` where exactly one element
|
|
1463
|
+
* is `null`.
|
|
1464
|
+
*
|
|
1465
|
+
* @returns {Promise<
|
|
1466
|
+
* Opts['withResult'] extends true
|
|
1467
|
+
* ? Result.Result<
|
|
1468
|
+
* AspiResultOk<TRequest, FormData>,
|
|
1469
|
+
* | AspiError<TRequest>
|
|
1470
|
+
* | (Opts extends { error: any }
|
|
1471
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1472
|
+
* : never)
|
|
1473
|
+
* >
|
|
1474
|
+
* : Opts['throwable'] extends true
|
|
1475
|
+
* ? AspiPlainResponse<TRequest, FormData>
|
|
1476
|
+
* : [
|
|
1477
|
+
* AspiResultOk<TRequest, FormData> | null,
|
|
1478
|
+
* (
|
|
1479
|
+
* | (
|
|
1480
|
+
* | AspiError<TRequest>
|
|
1481
|
+
* | (Opts extends { error: any }
|
|
1482
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1483
|
+
* : never)
|
|
1484
|
+
* )
|
|
1485
|
+
* | null
|
|
1486
|
+
* ),
|
|
1487
|
+
* ]
|
|
1488
|
+
* >}
|
|
1489
|
+
*/
|
|
1490
|
+
formData(): Promise<Opts['withResult'] extends true ? Result<AspiResultOk<TRequest, FormData>, AspiError<TRequest> | (Opts extends {
|
|
1491
|
+
error: any;
|
|
1492
|
+
} ? Opts['error'][keyof Opts['error']] : never)> : Opts['throwable'] extends true ? AspiPlainResponse<TRequest, FormData> : [
|
|
1493
|
+
AspiResultOk<TRequest, FormData> | null,
|
|
1494
|
+
((AspiError<TRequest> | (Opts extends {
|
|
1495
|
+
error: any;
|
|
1496
|
+
} ? Opts['error'][keyof Opts['error']] : never)) | null)
|
|
1497
|
+
]>;
|
|
1393
1498
|
/**
|
|
1394
1499
|
* Returns the fully‑qualified URL that will be used for the request.
|
|
1395
1500
|
*
|
|
@@ -1549,7 +1654,7 @@ declare class Request<Method extends HttpMethods, TRequest extends AspiRequestIn
|
|
|
1549
1654
|
* .json();
|
|
1550
1655
|
* ```
|
|
1551
1656
|
*/
|
|
1552
|
-
useCapability(capability: Capability<TRequest>):
|
|
1657
|
+
useCapability(capability: Capability<TRequest>): this;
|
|
1553
1658
|
}
|
|
1554
1659
|
|
|
1555
1660
|
/**
|
package/dist/index.js
CHANGED
|
@@ -256,6 +256,8 @@ function pipe(a, ab, bc, cd, de, ef, fg, gh, hi) {
|
|
|
256
256
|
return bc(ab(a));
|
|
257
257
|
case 4:
|
|
258
258
|
return cd(bc(ab(a)));
|
|
259
|
+
case 5:
|
|
260
|
+
return de(cd(bc(ab(a))));
|
|
259
261
|
case 6:
|
|
260
262
|
return ef(de(cd(bc(ab(a)))));
|
|
261
263
|
case 7:
|
|
@@ -284,6 +286,7 @@ var Request = class {
|
|
|
284
286
|
#schema = null;
|
|
285
287
|
#bodySchema = null;
|
|
286
288
|
#retryConfig;
|
|
289
|
+
#timeoutMs;
|
|
287
290
|
#shouldBeResult = false;
|
|
288
291
|
#bodySchemaIssues = [];
|
|
289
292
|
#throwOnError = false;
|
|
@@ -333,6 +336,24 @@ var Request = class {
|
|
|
333
336
|
};
|
|
334
337
|
return this;
|
|
335
338
|
}
|
|
339
|
+
/**
|
|
340
|
+
* Sets a timeout for the request in milliseconds.
|
|
341
|
+
*
|
|
342
|
+
* When the timeout expires, the request is aborted with an `AbortError`.
|
|
343
|
+
* If a signal was already provided, the timeout is chained so that either
|
|
344
|
+
* the external signal or the timeout can abort the request.
|
|
345
|
+
*
|
|
346
|
+
* @param {number} ms - Timeout duration in milliseconds.
|
|
347
|
+
* @returns {this} The current {@link Request} instance for method chaining.
|
|
348
|
+
*
|
|
349
|
+
* @example
|
|
350
|
+
* const request = new Request('/slow-endpoint', config);
|
|
351
|
+
* request.timeout(5000); // abort after 5 seconds
|
|
352
|
+
*/
|
|
353
|
+
timeout(ms) {
|
|
354
|
+
this.#timeoutMs = ms;
|
|
355
|
+
return this;
|
|
356
|
+
}
|
|
336
357
|
/**
|
|
337
358
|
* Merges the provided headers into the request configuration.
|
|
338
359
|
*
|
|
@@ -916,6 +937,92 @@ var Request = class {
|
|
|
916
937
|
const output = await this.#makeRequest((response) => response.blob());
|
|
917
938
|
return this.#mapResponse(output);
|
|
918
939
|
}
|
|
940
|
+
/**
|
|
941
|
+
* Executes the request and returns the response body as an {@link ArrayBuffer}.
|
|
942
|
+
*
|
|
943
|
+
* The shape of the returned {@link Promise} depends on the request mode:
|
|
944
|
+
*
|
|
945
|
+
* - **Result mode** (`withResult()`): resolves to a {@link Result.Result} containing
|
|
946
|
+
* either an {@link AspiResultOk} with `ArrayBuffer` data or an error variant.
|
|
947
|
+
* - **Throwable mode** (`throwable()`): resolves directly to an {@link ArrayBuffer}
|
|
948
|
+
* (wrapped in {@link AspiPlainResponse}) and throws on failure.
|
|
949
|
+
* - **Default mode**: resolves to a tuple `[value, error]` where exactly one element
|
|
950
|
+
* is `null`.
|
|
951
|
+
*
|
|
952
|
+
* @returns {Promise<
|
|
953
|
+
* Opts['withResult'] extends true
|
|
954
|
+
* ? Result.Result<
|
|
955
|
+
* AspiResultOk<TRequest, ArrayBuffer>,
|
|
956
|
+
* | AspiError<TRequest>
|
|
957
|
+
* | (Opts extends { error: any }
|
|
958
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
959
|
+
* : never)
|
|
960
|
+
* >
|
|
961
|
+
* : Opts['throwable'] extends true
|
|
962
|
+
* ? AspiPlainResponse<TRequest, ArrayBuffer>
|
|
963
|
+
* : [
|
|
964
|
+
* AspiResultOk<TRequest, ArrayBuffer> | null,
|
|
965
|
+
* (
|
|
966
|
+
* | (
|
|
967
|
+
* | AspiError<TRequest>
|
|
968
|
+
* | (Opts extends { error: any }
|
|
969
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
970
|
+
* : never)
|
|
971
|
+
* )
|
|
972
|
+
* | null
|
|
973
|
+
* ),
|
|
974
|
+
* ]
|
|
975
|
+
* >}
|
|
976
|
+
*/
|
|
977
|
+
async arrayBuffer() {
|
|
978
|
+
const output = await this.#makeRequest(
|
|
979
|
+
(response) => response.arrayBuffer()
|
|
980
|
+
);
|
|
981
|
+
return this.#mapResponse(output);
|
|
982
|
+
}
|
|
983
|
+
/**
|
|
984
|
+
* Executes the request and returns the response body as {@link FormData}.
|
|
985
|
+
*
|
|
986
|
+
* The shape of the returned {@link Promise} depends on the request mode:
|
|
987
|
+
*
|
|
988
|
+
* - **Result mode** (`withResult()`): resolves to a {@link Result.Result} containing
|
|
989
|
+
* either an {@link AspiResultOk} with `FormData` or an error variant.
|
|
990
|
+
* - **Throwable mode** (`throwable()`): resolves directly to {@link FormData}
|
|
991
|
+
* (wrapped in {@link AspiPlainResponse}) and throws on failure.
|
|
992
|
+
* - **Default mode**: resolves to a tuple `[value, error]` where exactly one element
|
|
993
|
+
* is `null`.
|
|
994
|
+
*
|
|
995
|
+
* @returns {Promise<
|
|
996
|
+
* Opts['withResult'] extends true
|
|
997
|
+
* ? Result.Result<
|
|
998
|
+
* AspiResultOk<TRequest, FormData>,
|
|
999
|
+
* | AspiError<TRequest>
|
|
1000
|
+
* | (Opts extends { error: any }
|
|
1001
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1002
|
+
* : never)
|
|
1003
|
+
* >
|
|
1004
|
+
* : Opts['throwable'] extends true
|
|
1005
|
+
* ? AspiPlainResponse<TRequest, FormData>
|
|
1006
|
+
* : [
|
|
1007
|
+
* AspiResultOk<TRequest, FormData> | null,
|
|
1008
|
+
* (
|
|
1009
|
+
* | (
|
|
1010
|
+
* | AspiError<TRequest>
|
|
1011
|
+
* | (Opts extends { error: any }
|
|
1012
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1013
|
+
* : never)
|
|
1014
|
+
* )
|
|
1015
|
+
* | null
|
|
1016
|
+
* ),
|
|
1017
|
+
* ]
|
|
1018
|
+
* >}
|
|
1019
|
+
*/
|
|
1020
|
+
async formData() {
|
|
1021
|
+
const output = await this.#makeRequest(
|
|
1022
|
+
(response) => response.formData()
|
|
1023
|
+
);
|
|
1024
|
+
return this.#mapResponse(output);
|
|
1025
|
+
}
|
|
919
1026
|
#url() {
|
|
920
1027
|
if (this.#path.startsWith("http://") || this.#path.startsWith("https://")) {
|
|
921
1028
|
const absolute = new URL(this.#path);
|
|
@@ -933,7 +1040,6 @@ var Request = class {
|
|
|
933
1040
|
let path = rawPath || "";
|
|
934
1041
|
path = path.replace(/^\/+/, "");
|
|
935
1042
|
path = path.replace(/\/{2,}/g, "/");
|
|
936
|
-
path = path.replace(/\/+$/, "");
|
|
937
1043
|
if (path) {
|
|
938
1044
|
path = "/" + path.replace(/^\/+/, "");
|
|
939
1045
|
}
|
|
@@ -951,7 +1057,6 @@ var Request = class {
|
|
|
951
1057
|
if (fragment) {
|
|
952
1058
|
url += `#${fragment}`;
|
|
953
1059
|
}
|
|
954
|
-
url = url.replace(/\/+$/, "");
|
|
955
1060
|
return url;
|
|
956
1061
|
}
|
|
957
1062
|
/**
|
|
@@ -1049,16 +1154,32 @@ var Request = class {
|
|
|
1049
1154
|
});
|
|
1050
1155
|
let responseData = null;
|
|
1051
1156
|
while (attempts <= retries) {
|
|
1157
|
+
let timeoutTimer;
|
|
1052
1158
|
try {
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1159
|
+
const attemptInit = { ...requestInit };
|
|
1160
|
+
if (this.#timeoutMs && this.#timeoutMs > 0) {
|
|
1161
|
+
const timeoutController = new AbortController();
|
|
1162
|
+
timeoutTimer = setTimeout(
|
|
1163
|
+
() => timeoutController.abort(),
|
|
1164
|
+
this.#timeoutMs
|
|
1165
|
+
);
|
|
1166
|
+
if (attemptInit.signal) {
|
|
1167
|
+
attemptInit.signal.addEventListener(
|
|
1168
|
+
"abort",
|
|
1169
|
+
() => timeoutController.abort(),
|
|
1170
|
+
{ once: true }
|
|
1057
1171
|
);
|
|
1058
1172
|
}
|
|
1059
|
-
|
|
1060
|
-
|
|
1173
|
+
attemptInit.signal = timeoutController.signal;
|
|
1174
|
+
}
|
|
1175
|
+
let runner = () => fetch(url, attemptInit);
|
|
1176
|
+
for (let i = this.#capabilities.length - 1; i >= 0; i--) {
|
|
1177
|
+
const cap = this.#capabilities[i];
|
|
1178
|
+
const next = runner;
|
|
1179
|
+
runner = () => cap({ request }).run(next);
|
|
1061
1180
|
}
|
|
1181
|
+
response = await runner();
|
|
1182
|
+
if (timeoutTimer) clearTimeout(timeoutTimer);
|
|
1062
1183
|
responseData = await responseParser(response);
|
|
1063
1184
|
if (responseData instanceof Error) {
|
|
1064
1185
|
return err(responseData);
|
|
@@ -1070,7 +1191,7 @@ var Request = class {
|
|
|
1070
1191
|
if (this.#isSuccessResponse(response) || !retryOn.includes(response.status) && !retryWhileCondition) {
|
|
1071
1192
|
break;
|
|
1072
1193
|
}
|
|
1073
|
-
if (response.status in this.#customErrorCbs
|
|
1194
|
+
if (response.status in this.#customErrorCbs) {
|
|
1074
1195
|
const result = this.#customErrorCbs[response.status].cb({
|
|
1075
1196
|
request,
|
|
1076
1197
|
response: this.#makeResponse(response, responseData)
|
|
@@ -1093,6 +1214,7 @@ var Request = class {
|
|
|
1093
1214
|
await this.#abortDelay(delay, request);
|
|
1094
1215
|
}
|
|
1095
1216
|
} catch (e) {
|
|
1217
|
+
if (timeoutTimer) clearTimeout(timeoutTimer);
|
|
1096
1218
|
if (e instanceof Error && e.name === "AbortError") {
|
|
1097
1219
|
if (500 in this.#customErrorCbs) {
|
|
1098
1220
|
const result = this.#customErrorCbs[response.status].cb({
|
|
@@ -1361,7 +1483,8 @@ var Request = class {
|
|
|
1361
1483
|
* ```
|
|
1362
1484
|
*/
|
|
1363
1485
|
useCapability(capability) {
|
|
1364
|
-
|
|
1486
|
+
this.#capabilities.push(capability);
|
|
1487
|
+
return this;
|
|
1365
1488
|
}
|
|
1366
1489
|
};
|
|
1367
1490
|
|
package/package.json
CHANGED