aspi 2.4.0 → 2.6.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 +145 -12
- package/dist/index.d.cts +114 -2
- package/dist/index.d.ts +114 -2
- package/dist/index.js +142 -11
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -28,7 +28,9 @@ __export(index_exports, {
|
|
|
28
28
|
getHttpErrorStatus: () => getHttpErrorStatus,
|
|
29
29
|
httpErrors: () => httpErrors,
|
|
30
30
|
isAspiError: () => isAspiError,
|
|
31
|
-
isCustomError: () => isCustomError
|
|
31
|
+
isCustomError: () => isCustomError,
|
|
32
|
+
isJSONParseError: () => isJSONParseError,
|
|
33
|
+
isParseError: () => isParseError
|
|
32
34
|
});
|
|
33
35
|
module.exports = __toCommonJS(index_exports);
|
|
34
36
|
|
|
@@ -108,6 +110,12 @@ var isAspiError = (error) => {
|
|
|
108
110
|
var isCustomError = (error) => {
|
|
109
111
|
return error instanceof CustomError;
|
|
110
112
|
};
|
|
113
|
+
var isParseError = (error) => {
|
|
114
|
+
return error instanceof CustomError && error.tag === "parseError";
|
|
115
|
+
};
|
|
116
|
+
var isJSONParseError = (error) => {
|
|
117
|
+
return error instanceof CustomError && error.tag === "jsonParseError";
|
|
118
|
+
};
|
|
111
119
|
|
|
112
120
|
// src/result.ts
|
|
113
121
|
var result_exports = {};
|
|
@@ -284,6 +292,8 @@ function pipe(a, ab, bc, cd, de, ef, fg, gh, hi) {
|
|
|
284
292
|
return bc(ab(a));
|
|
285
293
|
case 4:
|
|
286
294
|
return cd(bc(ab(a)));
|
|
295
|
+
case 5:
|
|
296
|
+
return de(cd(bc(ab(a))));
|
|
287
297
|
case 6:
|
|
288
298
|
return ef(de(cd(bc(ab(a)))));
|
|
289
299
|
case 7:
|
|
@@ -312,6 +322,7 @@ var Request = class {
|
|
|
312
322
|
#schema = null;
|
|
313
323
|
#bodySchema = null;
|
|
314
324
|
#retryConfig;
|
|
325
|
+
#timeoutMs;
|
|
315
326
|
#shouldBeResult = false;
|
|
316
327
|
#bodySchemaIssues = [];
|
|
317
328
|
#throwOnError = false;
|
|
@@ -361,6 +372,24 @@ var Request = class {
|
|
|
361
372
|
};
|
|
362
373
|
return this;
|
|
363
374
|
}
|
|
375
|
+
/**
|
|
376
|
+
* Sets a timeout for the request in milliseconds.
|
|
377
|
+
*
|
|
378
|
+
* When the timeout expires, the request is aborted with an `AbortError`.
|
|
379
|
+
* If a signal was already provided, the timeout is chained so that either
|
|
380
|
+
* the external signal or the timeout can abort the request.
|
|
381
|
+
*
|
|
382
|
+
* @param {number} ms - Timeout duration in milliseconds.
|
|
383
|
+
* @returns {this} The current {@link Request} instance for method chaining.
|
|
384
|
+
*
|
|
385
|
+
* @example
|
|
386
|
+
* const request = new Request('/slow-endpoint', config);
|
|
387
|
+
* request.timeout(5000); // abort after 5 seconds
|
|
388
|
+
*/
|
|
389
|
+
timeout(ms) {
|
|
390
|
+
this.#timeoutMs = ms;
|
|
391
|
+
return this;
|
|
392
|
+
}
|
|
364
393
|
/**
|
|
365
394
|
* Merges the provided headers into the request configuration.
|
|
366
395
|
*
|
|
@@ -944,6 +973,92 @@ var Request = class {
|
|
|
944
973
|
const output = await this.#makeRequest((response) => response.blob());
|
|
945
974
|
return this.#mapResponse(output);
|
|
946
975
|
}
|
|
976
|
+
/**
|
|
977
|
+
* Executes the request and returns the response body as an {@link ArrayBuffer}.
|
|
978
|
+
*
|
|
979
|
+
* The shape of the returned {@link Promise} depends on the request mode:
|
|
980
|
+
*
|
|
981
|
+
* - **Result mode** (`withResult()`): resolves to a {@link Result.Result} containing
|
|
982
|
+
* either an {@link AspiResultOk} with `ArrayBuffer` data or an error variant.
|
|
983
|
+
* - **Throwable mode** (`throwable()`): resolves directly to an {@link ArrayBuffer}
|
|
984
|
+
* (wrapped in {@link AspiPlainResponse}) and throws on failure.
|
|
985
|
+
* - **Default mode**: resolves to a tuple `[value, error]` where exactly one element
|
|
986
|
+
* is `null`.
|
|
987
|
+
*
|
|
988
|
+
* @returns {Promise<
|
|
989
|
+
* Opts['withResult'] extends true
|
|
990
|
+
* ? Result.Result<
|
|
991
|
+
* AspiResultOk<TRequest, ArrayBuffer>,
|
|
992
|
+
* | AspiError<TRequest>
|
|
993
|
+
* | (Opts extends { error: any }
|
|
994
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
995
|
+
* : never)
|
|
996
|
+
* >
|
|
997
|
+
* : Opts['throwable'] extends true
|
|
998
|
+
* ? AspiPlainResponse<TRequest, ArrayBuffer>
|
|
999
|
+
* : [
|
|
1000
|
+
* AspiResultOk<TRequest, ArrayBuffer> | null,
|
|
1001
|
+
* (
|
|
1002
|
+
* | (
|
|
1003
|
+
* | AspiError<TRequest>
|
|
1004
|
+
* | (Opts extends { error: any }
|
|
1005
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1006
|
+
* : never)
|
|
1007
|
+
* )
|
|
1008
|
+
* | null
|
|
1009
|
+
* ),
|
|
1010
|
+
* ]
|
|
1011
|
+
* >}
|
|
1012
|
+
*/
|
|
1013
|
+
async arrayBuffer() {
|
|
1014
|
+
const output = await this.#makeRequest(
|
|
1015
|
+
(response) => response.arrayBuffer()
|
|
1016
|
+
);
|
|
1017
|
+
return this.#mapResponse(output);
|
|
1018
|
+
}
|
|
1019
|
+
/**
|
|
1020
|
+
* Executes the request and returns the response body as {@link FormData}.
|
|
1021
|
+
*
|
|
1022
|
+
* The shape of the returned {@link Promise} depends on the request mode:
|
|
1023
|
+
*
|
|
1024
|
+
* - **Result mode** (`withResult()`): resolves to a {@link Result.Result} containing
|
|
1025
|
+
* either an {@link AspiResultOk} with `FormData` or an error variant.
|
|
1026
|
+
* - **Throwable mode** (`throwable()`): resolves directly to {@link FormData}
|
|
1027
|
+
* (wrapped in {@link AspiPlainResponse}) and throws on failure.
|
|
1028
|
+
* - **Default mode**: resolves to a tuple `[value, error]` where exactly one element
|
|
1029
|
+
* is `null`.
|
|
1030
|
+
*
|
|
1031
|
+
* @returns {Promise<
|
|
1032
|
+
* Opts['withResult'] extends true
|
|
1033
|
+
* ? Result.Result<
|
|
1034
|
+
* AspiResultOk<TRequest, FormData>,
|
|
1035
|
+
* | AspiError<TRequest>
|
|
1036
|
+
* | (Opts extends { error: any }
|
|
1037
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1038
|
+
* : never)
|
|
1039
|
+
* >
|
|
1040
|
+
* : Opts['throwable'] extends true
|
|
1041
|
+
* ? AspiPlainResponse<TRequest, FormData>
|
|
1042
|
+
* : [
|
|
1043
|
+
* AspiResultOk<TRequest, FormData> | null,
|
|
1044
|
+
* (
|
|
1045
|
+
* | (
|
|
1046
|
+
* | AspiError<TRequest>
|
|
1047
|
+
* | (Opts extends { error: any }
|
|
1048
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1049
|
+
* : never)
|
|
1050
|
+
* )
|
|
1051
|
+
* | null
|
|
1052
|
+
* ),
|
|
1053
|
+
* ]
|
|
1054
|
+
* >}
|
|
1055
|
+
*/
|
|
1056
|
+
async formData() {
|
|
1057
|
+
const output = await this.#makeRequest(
|
|
1058
|
+
(response) => response.formData()
|
|
1059
|
+
);
|
|
1060
|
+
return this.#mapResponse(output);
|
|
1061
|
+
}
|
|
947
1062
|
#url() {
|
|
948
1063
|
if (this.#path.startsWith("http://") || this.#path.startsWith("https://")) {
|
|
949
1064
|
const absolute = new URL(this.#path);
|
|
@@ -961,7 +1076,6 @@ var Request = class {
|
|
|
961
1076
|
let path = rawPath || "";
|
|
962
1077
|
path = path.replace(/^\/+/, "");
|
|
963
1078
|
path = path.replace(/\/{2,}/g, "/");
|
|
964
|
-
path = path.replace(/\/+$/, "");
|
|
965
1079
|
if (path) {
|
|
966
1080
|
path = "/" + path.replace(/^\/+/, "");
|
|
967
1081
|
}
|
|
@@ -979,7 +1093,6 @@ var Request = class {
|
|
|
979
1093
|
if (fragment) {
|
|
980
1094
|
url += `#${fragment}`;
|
|
981
1095
|
}
|
|
982
|
-
url = url.replace(/\/+$/, "");
|
|
983
1096
|
return url;
|
|
984
1097
|
}
|
|
985
1098
|
/**
|
|
@@ -1077,16 +1190,32 @@ var Request = class {
|
|
|
1077
1190
|
});
|
|
1078
1191
|
let responseData = null;
|
|
1079
1192
|
while (attempts <= retries) {
|
|
1193
|
+
let timeoutTimer;
|
|
1080
1194
|
try {
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1195
|
+
const attemptInit = { ...requestInit };
|
|
1196
|
+
if (this.#timeoutMs && this.#timeoutMs > 0) {
|
|
1197
|
+
const timeoutController = new AbortController();
|
|
1198
|
+
timeoutTimer = setTimeout(
|
|
1199
|
+
() => timeoutController.abort(),
|
|
1200
|
+
this.#timeoutMs
|
|
1201
|
+
);
|
|
1202
|
+
if (attemptInit.signal) {
|
|
1203
|
+
attemptInit.signal.addEventListener(
|
|
1204
|
+
"abort",
|
|
1205
|
+
() => timeoutController.abort(),
|
|
1206
|
+
{ once: true }
|
|
1085
1207
|
);
|
|
1086
1208
|
}
|
|
1087
|
-
|
|
1088
|
-
|
|
1209
|
+
attemptInit.signal = timeoutController.signal;
|
|
1210
|
+
}
|
|
1211
|
+
let runner = () => fetch(url, attemptInit);
|
|
1212
|
+
for (let i = this.#capabilities.length - 1; i >= 0; i--) {
|
|
1213
|
+
const cap = this.#capabilities[i];
|
|
1214
|
+
const next = runner;
|
|
1215
|
+
runner = () => cap({ request }).run(next);
|
|
1089
1216
|
}
|
|
1217
|
+
response = await runner();
|
|
1218
|
+
if (timeoutTimer) clearTimeout(timeoutTimer);
|
|
1090
1219
|
responseData = await responseParser(response);
|
|
1091
1220
|
if (responseData instanceof Error) {
|
|
1092
1221
|
return err(responseData);
|
|
@@ -1098,7 +1227,7 @@ var Request = class {
|
|
|
1098
1227
|
if (this.#isSuccessResponse(response) || !retryOn.includes(response.status) && !retryWhileCondition) {
|
|
1099
1228
|
break;
|
|
1100
1229
|
}
|
|
1101
|
-
if (response.status in this.#customErrorCbs
|
|
1230
|
+
if (response.status in this.#customErrorCbs) {
|
|
1102
1231
|
const result = this.#customErrorCbs[response.status].cb({
|
|
1103
1232
|
request,
|
|
1104
1233
|
response: this.#makeResponse(response, responseData)
|
|
@@ -1121,6 +1250,7 @@ var Request = class {
|
|
|
1121
1250
|
await this.#abortDelay(delay, request);
|
|
1122
1251
|
}
|
|
1123
1252
|
} catch (e) {
|
|
1253
|
+
if (timeoutTimer) clearTimeout(timeoutTimer);
|
|
1124
1254
|
if (e instanceof Error && e.name === "AbortError") {
|
|
1125
1255
|
if (500 in this.#customErrorCbs) {
|
|
1126
1256
|
const result = this.#customErrorCbs[response.status].cb({
|
|
@@ -1389,7 +1519,8 @@ var Request = class {
|
|
|
1389
1519
|
* ```
|
|
1390
1520
|
*/
|
|
1391
1521
|
useCapability(capability) {
|
|
1392
|
-
|
|
1522
|
+
this.#capabilities.push(capability);
|
|
1523
|
+
return this;
|
|
1393
1524
|
}
|
|
1394
1525
|
};
|
|
1395
1526
|
|
|
@@ -1806,5 +1937,7 @@ var Aspi = class {
|
|
|
1806
1937
|
getHttpErrorStatus,
|
|
1807
1938
|
httpErrors,
|
|
1808
1939
|
isAspiError,
|
|
1809
|
-
isCustomError
|
|
1940
|
+
isCustomError,
|
|
1941
|
+
isJSONParseError,
|
|
1942
|
+
isParseError
|
|
1810
1943
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -363,8 +363,15 @@ interface JSONParseError extends CustomError<'jsonParseError', {
|
|
|
363
363
|
message: string;
|
|
364
364
|
}> {
|
|
365
365
|
}
|
|
366
|
+
/**
|
|
367
|
+
* Type alias for a schema validation parse error.
|
|
368
|
+
* Emitted when either the request body schema or the response schema fails validation.
|
|
369
|
+
*/
|
|
370
|
+
type ParseError = CustomError<'parseError', unknown>;
|
|
366
371
|
declare const isAspiError: <TReq extends AspiRequestInit>(error: unknown) => error is AspiError<TReq>;
|
|
367
372
|
declare const isCustomError: <Tag extends string, A>(error: unknown) => error is CustomError<Tag, A>;
|
|
373
|
+
declare const isParseError: (error: unknown) => error is ParseError;
|
|
374
|
+
declare const isJSONParseError: (error: unknown) => error is JSONParseError;
|
|
368
375
|
|
|
369
376
|
/**
|
|
370
377
|
* Arguments passed to a capability factory.
|
|
@@ -858,6 +865,21 @@ declare class Request<Method extends HttpMethods, TRequest extends AspiRequestIn
|
|
|
858
865
|
* });
|
|
859
866
|
*/
|
|
860
867
|
setRetry(retry: AspiRetryConfig<TRequest>): this;
|
|
868
|
+
/**
|
|
869
|
+
* Sets a timeout for the request in milliseconds.
|
|
870
|
+
*
|
|
871
|
+
* When the timeout expires, the request is aborted with an `AbortError`.
|
|
872
|
+
* If a signal was already provided, the timeout is chained so that either
|
|
873
|
+
* the external signal or the timeout can abort the request.
|
|
874
|
+
*
|
|
875
|
+
* @param {number} ms - Timeout duration in milliseconds.
|
|
876
|
+
* @returns {this} The current {@link Request} instance for method chaining.
|
|
877
|
+
*
|
|
878
|
+
* @example
|
|
879
|
+
* const request = new Request('/slow-endpoint', config);
|
|
880
|
+
* request.timeout(5000); // abort after 5 seconds
|
|
881
|
+
*/
|
|
882
|
+
timeout(ms: number): this;
|
|
861
883
|
/**
|
|
862
884
|
* Merges the provided headers into the request configuration.
|
|
863
885
|
*
|
|
@@ -1390,6 +1412,96 @@ declare class Request<Method extends HttpMethods, TRequest extends AspiRequestIn
|
|
|
1390
1412
|
error: any;
|
|
1391
1413
|
} ? Opts['error'][keyof Opts['error']] : never)) | null)
|
|
1392
1414
|
]>;
|
|
1415
|
+
/**
|
|
1416
|
+
* Executes the request and returns the response body as an {@link ArrayBuffer}.
|
|
1417
|
+
*
|
|
1418
|
+
* The shape of the returned {@link Promise} depends on the request mode:
|
|
1419
|
+
*
|
|
1420
|
+
* - **Result mode** (`withResult()`): resolves to a {@link Result.Result} containing
|
|
1421
|
+
* either an {@link AspiResultOk} with `ArrayBuffer` data or an error variant.
|
|
1422
|
+
* - **Throwable mode** (`throwable()`): resolves directly to an {@link ArrayBuffer}
|
|
1423
|
+
* (wrapped in {@link AspiPlainResponse}) and throws on failure.
|
|
1424
|
+
* - **Default mode**: resolves to a tuple `[value, error]` where exactly one element
|
|
1425
|
+
* is `null`.
|
|
1426
|
+
*
|
|
1427
|
+
* @returns {Promise<
|
|
1428
|
+
* Opts['withResult'] extends true
|
|
1429
|
+
* ? Result.Result<
|
|
1430
|
+
* AspiResultOk<TRequest, ArrayBuffer>,
|
|
1431
|
+
* | AspiError<TRequest>
|
|
1432
|
+
* | (Opts extends { error: any }
|
|
1433
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1434
|
+
* : never)
|
|
1435
|
+
* >
|
|
1436
|
+
* : Opts['throwable'] extends true
|
|
1437
|
+
* ? AspiPlainResponse<TRequest, ArrayBuffer>
|
|
1438
|
+
* : [
|
|
1439
|
+
* AspiResultOk<TRequest, ArrayBuffer> | null,
|
|
1440
|
+
* (
|
|
1441
|
+
* | (
|
|
1442
|
+
* | AspiError<TRequest>
|
|
1443
|
+
* | (Opts extends { error: any }
|
|
1444
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1445
|
+
* : never)
|
|
1446
|
+
* )
|
|
1447
|
+
* | null
|
|
1448
|
+
* ),
|
|
1449
|
+
* ]
|
|
1450
|
+
* >}
|
|
1451
|
+
*/
|
|
1452
|
+
arrayBuffer(): Promise<Opts['withResult'] extends true ? Result<AspiResultOk<TRequest, ArrayBuffer>, AspiError<TRequest> | (Opts extends {
|
|
1453
|
+
error: any;
|
|
1454
|
+
} ? Opts['error'][keyof Opts['error']] : never)> : Opts['throwable'] extends true ? AspiPlainResponse<TRequest, ArrayBuffer> : [
|
|
1455
|
+
AspiResultOk<TRequest, ArrayBuffer> | null,
|
|
1456
|
+
((AspiError<TRequest> | (Opts extends {
|
|
1457
|
+
error: any;
|
|
1458
|
+
} ? Opts['error'][keyof Opts['error']] : never)) | null)
|
|
1459
|
+
]>;
|
|
1460
|
+
/**
|
|
1461
|
+
* Executes the request and returns the response body as {@link FormData}.
|
|
1462
|
+
*
|
|
1463
|
+
* The shape of the returned {@link Promise} depends on the request mode:
|
|
1464
|
+
*
|
|
1465
|
+
* - **Result mode** (`withResult()`): resolves to a {@link Result.Result} containing
|
|
1466
|
+
* either an {@link AspiResultOk} with `FormData` or an error variant.
|
|
1467
|
+
* - **Throwable mode** (`throwable()`): resolves directly to {@link FormData}
|
|
1468
|
+
* (wrapped in {@link AspiPlainResponse}) and throws on failure.
|
|
1469
|
+
* - **Default mode**: resolves to a tuple `[value, error]` where exactly one element
|
|
1470
|
+
* is `null`.
|
|
1471
|
+
*
|
|
1472
|
+
* @returns {Promise<
|
|
1473
|
+
* Opts['withResult'] extends true
|
|
1474
|
+
* ? Result.Result<
|
|
1475
|
+
* AspiResultOk<TRequest, FormData>,
|
|
1476
|
+
* | AspiError<TRequest>
|
|
1477
|
+
* | (Opts extends { error: any }
|
|
1478
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1479
|
+
* : never)
|
|
1480
|
+
* >
|
|
1481
|
+
* : Opts['throwable'] extends true
|
|
1482
|
+
* ? AspiPlainResponse<TRequest, FormData>
|
|
1483
|
+
* : [
|
|
1484
|
+
* AspiResultOk<TRequest, FormData> | null,
|
|
1485
|
+
* (
|
|
1486
|
+
* | (
|
|
1487
|
+
* | AspiError<TRequest>
|
|
1488
|
+
* | (Opts extends { error: any }
|
|
1489
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1490
|
+
* : never)
|
|
1491
|
+
* )
|
|
1492
|
+
* | null
|
|
1493
|
+
* ),
|
|
1494
|
+
* ]
|
|
1495
|
+
* >}
|
|
1496
|
+
*/
|
|
1497
|
+
formData(): Promise<Opts['withResult'] extends true ? Result<AspiResultOk<TRequest, FormData>, AspiError<TRequest> | (Opts extends {
|
|
1498
|
+
error: any;
|
|
1499
|
+
} ? Opts['error'][keyof Opts['error']] : never)> : Opts['throwable'] extends true ? AspiPlainResponse<TRequest, FormData> : [
|
|
1500
|
+
AspiResultOk<TRequest, FormData> | null,
|
|
1501
|
+
((AspiError<TRequest> | (Opts extends {
|
|
1502
|
+
error: any;
|
|
1503
|
+
} ? Opts['error'][keyof Opts['error']] : never)) | null)
|
|
1504
|
+
]>;
|
|
1393
1505
|
/**
|
|
1394
1506
|
* Returns the fully‑qualified URL that will be used for the request.
|
|
1395
1507
|
*
|
|
@@ -1549,7 +1661,7 @@ declare class Request<Method extends HttpMethods, TRequest extends AspiRequestIn
|
|
|
1549
1661
|
* .json();
|
|
1550
1662
|
* ```
|
|
1551
1663
|
*/
|
|
1552
|
-
useCapability(capability: Capability<TRequest>):
|
|
1664
|
+
useCapability(capability: Capability<TRequest>): this;
|
|
1553
1665
|
}
|
|
1554
1666
|
|
|
1555
1667
|
/**
|
|
@@ -1893,4 +2005,4 @@ declare class Aspi<TRequest extends AspiRequestInit = AspiRequestInit, Opts exte
|
|
|
1893
2005
|
useCapability(capability: Capability<TRequest>): this;
|
|
1894
2006
|
}
|
|
1895
2007
|
|
|
1896
|
-
export { Aspi, type AspiConfigBase, AspiError, type AspiPlainResponse, type AspiRequest, type AspiRequestInit, type AspiRequestInitWithoutBodyAndMethod, type AspiResponse, type AspiResultOk, type AspiRetryConfig, type BaseURL, type Capability, type CapabilityArgs, CustomError, type CustomErrorCb, type ErrorCallbacks, type HttpErrorCodes, type HttpErrorStatus, type HttpMethods, type JSONParseError, type Merge, type Prettify, Request, type RequestOptions, type RequestTransformer, result as Result, getHttpErrorStatus, httpErrors, isAspiError, isCustomError };
|
|
2008
|
+
export { Aspi, type AspiConfigBase, AspiError, type AspiPlainResponse, type AspiRequest, type AspiRequestInit, type AspiRequestInitWithoutBodyAndMethod, type AspiResponse, type AspiResultOk, type AspiRetryConfig, type BaseURL, type Capability, type CapabilityArgs, CustomError, type CustomErrorCb, type ErrorCallbacks, type HttpErrorCodes, type HttpErrorStatus, type HttpMethods, type JSONParseError, type Merge, type ParseError, type Prettify, Request, type RequestOptions, type RequestTransformer, result as Result, getHttpErrorStatus, httpErrors, isAspiError, isCustomError, isJSONParseError, isParseError };
|
package/dist/index.d.ts
CHANGED
|
@@ -363,8 +363,15 @@ interface JSONParseError extends CustomError<'jsonParseError', {
|
|
|
363
363
|
message: string;
|
|
364
364
|
}> {
|
|
365
365
|
}
|
|
366
|
+
/**
|
|
367
|
+
* Type alias for a schema validation parse error.
|
|
368
|
+
* Emitted when either the request body schema or the response schema fails validation.
|
|
369
|
+
*/
|
|
370
|
+
type ParseError = CustomError<'parseError', unknown>;
|
|
366
371
|
declare const isAspiError: <TReq extends AspiRequestInit>(error: unknown) => error is AspiError<TReq>;
|
|
367
372
|
declare const isCustomError: <Tag extends string, A>(error: unknown) => error is CustomError<Tag, A>;
|
|
373
|
+
declare const isParseError: (error: unknown) => error is ParseError;
|
|
374
|
+
declare const isJSONParseError: (error: unknown) => error is JSONParseError;
|
|
368
375
|
|
|
369
376
|
/**
|
|
370
377
|
* Arguments passed to a capability factory.
|
|
@@ -858,6 +865,21 @@ declare class Request<Method extends HttpMethods, TRequest extends AspiRequestIn
|
|
|
858
865
|
* });
|
|
859
866
|
*/
|
|
860
867
|
setRetry(retry: AspiRetryConfig<TRequest>): this;
|
|
868
|
+
/**
|
|
869
|
+
* Sets a timeout for the request in milliseconds.
|
|
870
|
+
*
|
|
871
|
+
* When the timeout expires, the request is aborted with an `AbortError`.
|
|
872
|
+
* If a signal was already provided, the timeout is chained so that either
|
|
873
|
+
* the external signal or the timeout can abort the request.
|
|
874
|
+
*
|
|
875
|
+
* @param {number} ms - Timeout duration in milliseconds.
|
|
876
|
+
* @returns {this} The current {@link Request} instance for method chaining.
|
|
877
|
+
*
|
|
878
|
+
* @example
|
|
879
|
+
* const request = new Request('/slow-endpoint', config);
|
|
880
|
+
* request.timeout(5000); // abort after 5 seconds
|
|
881
|
+
*/
|
|
882
|
+
timeout(ms: number): this;
|
|
861
883
|
/**
|
|
862
884
|
* Merges the provided headers into the request configuration.
|
|
863
885
|
*
|
|
@@ -1390,6 +1412,96 @@ declare class Request<Method extends HttpMethods, TRequest extends AspiRequestIn
|
|
|
1390
1412
|
error: any;
|
|
1391
1413
|
} ? Opts['error'][keyof Opts['error']] : never)) | null)
|
|
1392
1414
|
]>;
|
|
1415
|
+
/**
|
|
1416
|
+
* Executes the request and returns the response body as an {@link ArrayBuffer}.
|
|
1417
|
+
*
|
|
1418
|
+
* The shape of the returned {@link Promise} depends on the request mode:
|
|
1419
|
+
*
|
|
1420
|
+
* - **Result mode** (`withResult()`): resolves to a {@link Result.Result} containing
|
|
1421
|
+
* either an {@link AspiResultOk} with `ArrayBuffer` data or an error variant.
|
|
1422
|
+
* - **Throwable mode** (`throwable()`): resolves directly to an {@link ArrayBuffer}
|
|
1423
|
+
* (wrapped in {@link AspiPlainResponse}) and throws on failure.
|
|
1424
|
+
* - **Default mode**: resolves to a tuple `[value, error]` where exactly one element
|
|
1425
|
+
* is `null`.
|
|
1426
|
+
*
|
|
1427
|
+
* @returns {Promise<
|
|
1428
|
+
* Opts['withResult'] extends true
|
|
1429
|
+
* ? Result.Result<
|
|
1430
|
+
* AspiResultOk<TRequest, ArrayBuffer>,
|
|
1431
|
+
* | AspiError<TRequest>
|
|
1432
|
+
* | (Opts extends { error: any }
|
|
1433
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1434
|
+
* : never)
|
|
1435
|
+
* >
|
|
1436
|
+
* : Opts['throwable'] extends true
|
|
1437
|
+
* ? AspiPlainResponse<TRequest, ArrayBuffer>
|
|
1438
|
+
* : [
|
|
1439
|
+
* AspiResultOk<TRequest, ArrayBuffer> | null,
|
|
1440
|
+
* (
|
|
1441
|
+
* | (
|
|
1442
|
+
* | AspiError<TRequest>
|
|
1443
|
+
* | (Opts extends { error: any }
|
|
1444
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1445
|
+
* : never)
|
|
1446
|
+
* )
|
|
1447
|
+
* | null
|
|
1448
|
+
* ),
|
|
1449
|
+
* ]
|
|
1450
|
+
* >}
|
|
1451
|
+
*/
|
|
1452
|
+
arrayBuffer(): Promise<Opts['withResult'] extends true ? Result<AspiResultOk<TRequest, ArrayBuffer>, AspiError<TRequest> | (Opts extends {
|
|
1453
|
+
error: any;
|
|
1454
|
+
} ? Opts['error'][keyof Opts['error']] : never)> : Opts['throwable'] extends true ? AspiPlainResponse<TRequest, ArrayBuffer> : [
|
|
1455
|
+
AspiResultOk<TRequest, ArrayBuffer> | null,
|
|
1456
|
+
((AspiError<TRequest> | (Opts extends {
|
|
1457
|
+
error: any;
|
|
1458
|
+
} ? Opts['error'][keyof Opts['error']] : never)) | null)
|
|
1459
|
+
]>;
|
|
1460
|
+
/**
|
|
1461
|
+
* Executes the request and returns the response body as {@link FormData}.
|
|
1462
|
+
*
|
|
1463
|
+
* The shape of the returned {@link Promise} depends on the request mode:
|
|
1464
|
+
*
|
|
1465
|
+
* - **Result mode** (`withResult()`): resolves to a {@link Result.Result} containing
|
|
1466
|
+
* either an {@link AspiResultOk} with `FormData` or an error variant.
|
|
1467
|
+
* - **Throwable mode** (`throwable()`): resolves directly to {@link FormData}
|
|
1468
|
+
* (wrapped in {@link AspiPlainResponse}) and throws on failure.
|
|
1469
|
+
* - **Default mode**: resolves to a tuple `[value, error]` where exactly one element
|
|
1470
|
+
* is `null`.
|
|
1471
|
+
*
|
|
1472
|
+
* @returns {Promise<
|
|
1473
|
+
* Opts['withResult'] extends true
|
|
1474
|
+
* ? Result.Result<
|
|
1475
|
+
* AspiResultOk<TRequest, FormData>,
|
|
1476
|
+
* | AspiError<TRequest>
|
|
1477
|
+
* | (Opts extends { error: any }
|
|
1478
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1479
|
+
* : never)
|
|
1480
|
+
* >
|
|
1481
|
+
* : Opts['throwable'] extends true
|
|
1482
|
+
* ? AspiPlainResponse<TRequest, FormData>
|
|
1483
|
+
* : [
|
|
1484
|
+
* AspiResultOk<TRequest, FormData> | null,
|
|
1485
|
+
* (
|
|
1486
|
+
* | (
|
|
1487
|
+
* | AspiError<TRequest>
|
|
1488
|
+
* | (Opts extends { error: any }
|
|
1489
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1490
|
+
* : never)
|
|
1491
|
+
* )
|
|
1492
|
+
* | null
|
|
1493
|
+
* ),
|
|
1494
|
+
* ]
|
|
1495
|
+
* >}
|
|
1496
|
+
*/
|
|
1497
|
+
formData(): Promise<Opts['withResult'] extends true ? Result<AspiResultOk<TRequest, FormData>, AspiError<TRequest> | (Opts extends {
|
|
1498
|
+
error: any;
|
|
1499
|
+
} ? Opts['error'][keyof Opts['error']] : never)> : Opts['throwable'] extends true ? AspiPlainResponse<TRequest, FormData> : [
|
|
1500
|
+
AspiResultOk<TRequest, FormData> | null,
|
|
1501
|
+
((AspiError<TRequest> | (Opts extends {
|
|
1502
|
+
error: any;
|
|
1503
|
+
} ? Opts['error'][keyof Opts['error']] : never)) | null)
|
|
1504
|
+
]>;
|
|
1393
1505
|
/**
|
|
1394
1506
|
* Returns the fully‑qualified URL that will be used for the request.
|
|
1395
1507
|
*
|
|
@@ -1549,7 +1661,7 @@ declare class Request<Method extends HttpMethods, TRequest extends AspiRequestIn
|
|
|
1549
1661
|
* .json();
|
|
1550
1662
|
* ```
|
|
1551
1663
|
*/
|
|
1552
|
-
useCapability(capability: Capability<TRequest>):
|
|
1664
|
+
useCapability(capability: Capability<TRequest>): this;
|
|
1553
1665
|
}
|
|
1554
1666
|
|
|
1555
1667
|
/**
|
|
@@ -1893,4 +2005,4 @@ declare class Aspi<TRequest extends AspiRequestInit = AspiRequestInit, Opts exte
|
|
|
1893
2005
|
useCapability(capability: Capability<TRequest>): this;
|
|
1894
2006
|
}
|
|
1895
2007
|
|
|
1896
|
-
export { Aspi, type AspiConfigBase, AspiError, type AspiPlainResponse, type AspiRequest, type AspiRequestInit, type AspiRequestInitWithoutBodyAndMethod, type AspiResponse, type AspiResultOk, type AspiRetryConfig, type BaseURL, type Capability, type CapabilityArgs, CustomError, type CustomErrorCb, type ErrorCallbacks, type HttpErrorCodes, type HttpErrorStatus, type HttpMethods, type JSONParseError, type Merge, type Prettify, Request, type RequestOptions, type RequestTransformer, result as Result, getHttpErrorStatus, httpErrors, isAspiError, isCustomError };
|
|
2008
|
+
export { Aspi, type AspiConfigBase, AspiError, type AspiPlainResponse, type AspiRequest, type AspiRequestInit, type AspiRequestInitWithoutBodyAndMethod, type AspiResponse, type AspiResultOk, type AspiRetryConfig, type BaseURL, type Capability, type CapabilityArgs, CustomError, type CustomErrorCb, type ErrorCallbacks, type HttpErrorCodes, type HttpErrorStatus, type HttpMethods, type JSONParseError, type Merge, type ParseError, type Prettify, Request, type RequestOptions, type RequestTransformer, result as Result, getHttpErrorStatus, httpErrors, isAspiError, isCustomError, isJSONParseError, isParseError };
|
package/dist/index.js
CHANGED
|
@@ -80,6 +80,12 @@ var isAspiError = (error) => {
|
|
|
80
80
|
var isCustomError = (error) => {
|
|
81
81
|
return error instanceof CustomError;
|
|
82
82
|
};
|
|
83
|
+
var isParseError = (error) => {
|
|
84
|
+
return error instanceof CustomError && error.tag === "parseError";
|
|
85
|
+
};
|
|
86
|
+
var isJSONParseError = (error) => {
|
|
87
|
+
return error instanceof CustomError && error.tag === "jsonParseError";
|
|
88
|
+
};
|
|
83
89
|
|
|
84
90
|
// src/result.ts
|
|
85
91
|
var result_exports = {};
|
|
@@ -256,6 +262,8 @@ function pipe(a, ab, bc, cd, de, ef, fg, gh, hi) {
|
|
|
256
262
|
return bc(ab(a));
|
|
257
263
|
case 4:
|
|
258
264
|
return cd(bc(ab(a)));
|
|
265
|
+
case 5:
|
|
266
|
+
return de(cd(bc(ab(a))));
|
|
259
267
|
case 6:
|
|
260
268
|
return ef(de(cd(bc(ab(a)))));
|
|
261
269
|
case 7:
|
|
@@ -284,6 +292,7 @@ var Request = class {
|
|
|
284
292
|
#schema = null;
|
|
285
293
|
#bodySchema = null;
|
|
286
294
|
#retryConfig;
|
|
295
|
+
#timeoutMs;
|
|
287
296
|
#shouldBeResult = false;
|
|
288
297
|
#bodySchemaIssues = [];
|
|
289
298
|
#throwOnError = false;
|
|
@@ -333,6 +342,24 @@ var Request = class {
|
|
|
333
342
|
};
|
|
334
343
|
return this;
|
|
335
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* Sets a timeout for the request in milliseconds.
|
|
347
|
+
*
|
|
348
|
+
* When the timeout expires, the request is aborted with an `AbortError`.
|
|
349
|
+
* If a signal was already provided, the timeout is chained so that either
|
|
350
|
+
* the external signal or the timeout can abort the request.
|
|
351
|
+
*
|
|
352
|
+
* @param {number} ms - Timeout duration in milliseconds.
|
|
353
|
+
* @returns {this} The current {@link Request} instance for method chaining.
|
|
354
|
+
*
|
|
355
|
+
* @example
|
|
356
|
+
* const request = new Request('/slow-endpoint', config);
|
|
357
|
+
* request.timeout(5000); // abort after 5 seconds
|
|
358
|
+
*/
|
|
359
|
+
timeout(ms) {
|
|
360
|
+
this.#timeoutMs = ms;
|
|
361
|
+
return this;
|
|
362
|
+
}
|
|
336
363
|
/**
|
|
337
364
|
* Merges the provided headers into the request configuration.
|
|
338
365
|
*
|
|
@@ -916,6 +943,92 @@ var Request = class {
|
|
|
916
943
|
const output = await this.#makeRequest((response) => response.blob());
|
|
917
944
|
return this.#mapResponse(output);
|
|
918
945
|
}
|
|
946
|
+
/**
|
|
947
|
+
* Executes the request and returns the response body as an {@link ArrayBuffer}.
|
|
948
|
+
*
|
|
949
|
+
* The shape of the returned {@link Promise} depends on the request mode:
|
|
950
|
+
*
|
|
951
|
+
* - **Result mode** (`withResult()`): resolves to a {@link Result.Result} containing
|
|
952
|
+
* either an {@link AspiResultOk} with `ArrayBuffer` data or an error variant.
|
|
953
|
+
* - **Throwable mode** (`throwable()`): resolves directly to an {@link ArrayBuffer}
|
|
954
|
+
* (wrapped in {@link AspiPlainResponse}) and throws on failure.
|
|
955
|
+
* - **Default mode**: resolves to a tuple `[value, error]` where exactly one element
|
|
956
|
+
* is `null`.
|
|
957
|
+
*
|
|
958
|
+
* @returns {Promise<
|
|
959
|
+
* Opts['withResult'] extends true
|
|
960
|
+
* ? Result.Result<
|
|
961
|
+
* AspiResultOk<TRequest, ArrayBuffer>,
|
|
962
|
+
* | AspiError<TRequest>
|
|
963
|
+
* | (Opts extends { error: any }
|
|
964
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
965
|
+
* : never)
|
|
966
|
+
* >
|
|
967
|
+
* : Opts['throwable'] extends true
|
|
968
|
+
* ? AspiPlainResponse<TRequest, ArrayBuffer>
|
|
969
|
+
* : [
|
|
970
|
+
* AspiResultOk<TRequest, ArrayBuffer> | null,
|
|
971
|
+
* (
|
|
972
|
+
* | (
|
|
973
|
+
* | AspiError<TRequest>
|
|
974
|
+
* | (Opts extends { error: any }
|
|
975
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
976
|
+
* : never)
|
|
977
|
+
* )
|
|
978
|
+
* | null
|
|
979
|
+
* ),
|
|
980
|
+
* ]
|
|
981
|
+
* >}
|
|
982
|
+
*/
|
|
983
|
+
async arrayBuffer() {
|
|
984
|
+
const output = await this.#makeRequest(
|
|
985
|
+
(response) => response.arrayBuffer()
|
|
986
|
+
);
|
|
987
|
+
return this.#mapResponse(output);
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
990
|
+
* Executes the request and returns the response body as {@link FormData}.
|
|
991
|
+
*
|
|
992
|
+
* The shape of the returned {@link Promise} depends on the request mode:
|
|
993
|
+
*
|
|
994
|
+
* - **Result mode** (`withResult()`): resolves to a {@link Result.Result} containing
|
|
995
|
+
* either an {@link AspiResultOk} with `FormData` or an error variant.
|
|
996
|
+
* - **Throwable mode** (`throwable()`): resolves directly to {@link FormData}
|
|
997
|
+
* (wrapped in {@link AspiPlainResponse}) and throws on failure.
|
|
998
|
+
* - **Default mode**: resolves to a tuple `[value, error]` where exactly one element
|
|
999
|
+
* is `null`.
|
|
1000
|
+
*
|
|
1001
|
+
* @returns {Promise<
|
|
1002
|
+
* Opts['withResult'] extends true
|
|
1003
|
+
* ? Result.Result<
|
|
1004
|
+
* AspiResultOk<TRequest, FormData>,
|
|
1005
|
+
* | AspiError<TRequest>
|
|
1006
|
+
* | (Opts extends { error: any }
|
|
1007
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1008
|
+
* : never)
|
|
1009
|
+
* >
|
|
1010
|
+
* : Opts['throwable'] extends true
|
|
1011
|
+
* ? AspiPlainResponse<TRequest, FormData>
|
|
1012
|
+
* : [
|
|
1013
|
+
* AspiResultOk<TRequest, FormData> | null,
|
|
1014
|
+
* (
|
|
1015
|
+
* | (
|
|
1016
|
+
* | AspiError<TRequest>
|
|
1017
|
+
* | (Opts extends { error: any }
|
|
1018
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
1019
|
+
* : never)
|
|
1020
|
+
* )
|
|
1021
|
+
* | null
|
|
1022
|
+
* ),
|
|
1023
|
+
* ]
|
|
1024
|
+
* >}
|
|
1025
|
+
*/
|
|
1026
|
+
async formData() {
|
|
1027
|
+
const output = await this.#makeRequest(
|
|
1028
|
+
(response) => response.formData()
|
|
1029
|
+
);
|
|
1030
|
+
return this.#mapResponse(output);
|
|
1031
|
+
}
|
|
919
1032
|
#url() {
|
|
920
1033
|
if (this.#path.startsWith("http://") || this.#path.startsWith("https://")) {
|
|
921
1034
|
const absolute = new URL(this.#path);
|
|
@@ -933,7 +1046,6 @@ var Request = class {
|
|
|
933
1046
|
let path = rawPath || "";
|
|
934
1047
|
path = path.replace(/^\/+/, "");
|
|
935
1048
|
path = path.replace(/\/{2,}/g, "/");
|
|
936
|
-
path = path.replace(/\/+$/, "");
|
|
937
1049
|
if (path) {
|
|
938
1050
|
path = "/" + path.replace(/^\/+/, "");
|
|
939
1051
|
}
|
|
@@ -951,7 +1063,6 @@ var Request = class {
|
|
|
951
1063
|
if (fragment) {
|
|
952
1064
|
url += `#${fragment}`;
|
|
953
1065
|
}
|
|
954
|
-
url = url.replace(/\/+$/, "");
|
|
955
1066
|
return url;
|
|
956
1067
|
}
|
|
957
1068
|
/**
|
|
@@ -1049,16 +1160,32 @@ var Request = class {
|
|
|
1049
1160
|
});
|
|
1050
1161
|
let responseData = null;
|
|
1051
1162
|
while (attempts <= retries) {
|
|
1163
|
+
let timeoutTimer;
|
|
1052
1164
|
try {
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1165
|
+
const attemptInit = { ...requestInit };
|
|
1166
|
+
if (this.#timeoutMs && this.#timeoutMs > 0) {
|
|
1167
|
+
const timeoutController = new AbortController();
|
|
1168
|
+
timeoutTimer = setTimeout(
|
|
1169
|
+
() => timeoutController.abort(),
|
|
1170
|
+
this.#timeoutMs
|
|
1171
|
+
);
|
|
1172
|
+
if (attemptInit.signal) {
|
|
1173
|
+
attemptInit.signal.addEventListener(
|
|
1174
|
+
"abort",
|
|
1175
|
+
() => timeoutController.abort(),
|
|
1176
|
+
{ once: true }
|
|
1057
1177
|
);
|
|
1058
1178
|
}
|
|
1059
|
-
|
|
1060
|
-
|
|
1179
|
+
attemptInit.signal = timeoutController.signal;
|
|
1180
|
+
}
|
|
1181
|
+
let runner = () => fetch(url, attemptInit);
|
|
1182
|
+
for (let i = this.#capabilities.length - 1; i >= 0; i--) {
|
|
1183
|
+
const cap = this.#capabilities[i];
|
|
1184
|
+
const next = runner;
|
|
1185
|
+
runner = () => cap({ request }).run(next);
|
|
1061
1186
|
}
|
|
1187
|
+
response = await runner();
|
|
1188
|
+
if (timeoutTimer) clearTimeout(timeoutTimer);
|
|
1062
1189
|
responseData = await responseParser(response);
|
|
1063
1190
|
if (responseData instanceof Error) {
|
|
1064
1191
|
return err(responseData);
|
|
@@ -1070,7 +1197,7 @@ var Request = class {
|
|
|
1070
1197
|
if (this.#isSuccessResponse(response) || !retryOn.includes(response.status) && !retryWhileCondition) {
|
|
1071
1198
|
break;
|
|
1072
1199
|
}
|
|
1073
|
-
if (response.status in this.#customErrorCbs
|
|
1200
|
+
if (response.status in this.#customErrorCbs) {
|
|
1074
1201
|
const result = this.#customErrorCbs[response.status].cb({
|
|
1075
1202
|
request,
|
|
1076
1203
|
response: this.#makeResponse(response, responseData)
|
|
@@ -1093,6 +1220,7 @@ var Request = class {
|
|
|
1093
1220
|
await this.#abortDelay(delay, request);
|
|
1094
1221
|
}
|
|
1095
1222
|
} catch (e) {
|
|
1223
|
+
if (timeoutTimer) clearTimeout(timeoutTimer);
|
|
1096
1224
|
if (e instanceof Error && e.name === "AbortError") {
|
|
1097
1225
|
if (500 in this.#customErrorCbs) {
|
|
1098
1226
|
const result = this.#customErrorCbs[response.status].cb({
|
|
@@ -1361,7 +1489,8 @@ var Request = class {
|
|
|
1361
1489
|
* ```
|
|
1362
1490
|
*/
|
|
1363
1491
|
useCapability(capability) {
|
|
1364
|
-
|
|
1492
|
+
this.#capabilities.push(capability);
|
|
1493
|
+
return this;
|
|
1365
1494
|
}
|
|
1366
1495
|
};
|
|
1367
1496
|
|
|
@@ -1777,5 +1906,7 @@ export {
|
|
|
1777
1906
|
getHttpErrorStatus,
|
|
1778
1907
|
httpErrors,
|
|
1779
1908
|
isAspiError,
|
|
1780
|
-
isCustomError
|
|
1909
|
+
isCustomError,
|
|
1910
|
+
isJSONParseError,
|
|
1911
|
+
isParseError
|
|
1781
1912
|
};
|
package/package.json
CHANGED