api-def 0.12.1 → 0.14.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/bin/index.js +312 -313
- package/cjs/Api.d.ts +1 -1
- package/cjs/Api.js +99 -145
- package/cjs/ApiTypes.d.ts +3 -2
- package/cjs/ApiUtils.js +30 -32
- package/cjs/Endpoint.d.ts +1 -1
- package/cjs/Endpoint.js +112 -175
- package/cjs/EndpointBuilder.d.ts +17 -0
- package/cjs/EndpointBuilder.js +55 -36
- package/cjs/QueryHandling.js +12 -13
- package/cjs/RequestConfig.js +54 -36
- package/cjs/RequestContext.d.ts +2 -1
- package/cjs/RequestContext.js +143 -180
- package/cjs/RequestError.js +16 -19
- package/cjs/Requester.js +231 -312
- package/cjs/TextDecoding.js +31 -31
- package/cjs/Utils.d.ts +1 -0
- package/cjs/Utils.js +74 -25
- package/cjs/Validation.d.ts +8 -1
- package/cjs/backend/AxiosRequestBackend.d.ts +1 -1
- package/cjs/backend/AxiosRequestBackend.js +65 -172
- package/cjs/backend/FetchRequestBackend.d.ts +1 -1
- package/cjs/backend/FetchRequestBackend.js +108 -154
- package/cjs/backend/MockRequestBackend.d.ts +1 -1
- package/cjs/backend/MockRequestBackend.js +146 -208
- package/cjs/cache/ClientCaching.js +26 -74
- package/cjs/cache/LocalForageClientCacheBackend.js +15 -83
- package/cjs/cache/LocalStorageClientCacheBackend.js +14 -73
- package/cjs/index.d.ts +11 -11
- package/cjs/index.js +24 -21
- package/cjs/middleware/ClientCacheMiddleware.js +80 -103
- package/cjs/middleware/LoggingMiddleware.js +71 -42
- package/cjs/util/retry/index.js +42 -9
- package/cjs/util/retry/lib/retry.js +25 -27
- package/cjs/util/retry/lib/retryOperation.d.ts +1 -26
- package/cjs/util/retry/lib/retryOperation.js +21 -23
- package/esm/Api.d.ts +6 -6
- package/esm/Api.js +12 -25
- package/esm/ApiConstants.d.ts +1 -1
- package/esm/ApiTypes.d.ts +7 -6
- package/esm/ApiUtils.d.ts +2 -2
- package/esm/ApiUtils.js +4 -5
- package/esm/Endpoint.d.ts +6 -6
- package/esm/Endpoint.js +22 -28
- package/esm/EndpointBuilder.d.ts +21 -4
- package/esm/EndpointBuilder.js +38 -10
- package/esm/MockingTypes.d.ts +1 -1
- package/esm/QueryHandling.d.ts +1 -1
- package/esm/QueryHandling.js +2 -3
- package/esm/RequestConfig.d.ts +1 -1
- package/esm/RequestConfig.js +6 -7
- package/esm/RequestContext.d.ts +8 -7
- package/esm/RequestContext.js +33 -27
- package/esm/RequestError.d.ts +3 -3
- package/esm/RequestError.js +2 -3
- package/esm/Requester.d.ts +2 -2
- package/esm/Requester.js +45 -50
- package/esm/UtilTypes.d.ts +1 -1
- package/esm/Utils.d.ts +1 -0
- package/esm/Utils.js +54 -2
- package/esm/Validation.d.ts +8 -1
- package/esm/backend/AxiosRequestBackend.d.ts +4 -4
- package/esm/backend/AxiosRequestBackend.js +47 -84
- package/esm/backend/FetchRequestBackend.d.ts +5 -5
- package/esm/backend/FetchRequestBackend.js +50 -64
- package/esm/backend/MockRequestBackend.d.ts +4 -4
- package/esm/backend/MockRequestBackend.js +105 -136
- package/esm/backend/RequestBackend.d.ts +2 -2
- package/esm/cache/ClientCaching.d.ts +1 -1
- package/esm/cache/ClientCaching.js +8 -17
- package/esm/cache/LocalForageClientCacheBackend.d.ts +1 -1
- package/esm/cache/LocalForageClientCacheBackend.js +8 -25
- package/esm/cache/LocalStorageClientCacheBackend.d.ts +1 -1
- package/esm/cache/LocalStorageClientCacheBackend.js +9 -26
- package/esm/index.d.ts +16 -16
- package/esm/index.js +15 -15
- package/esm/middleware/ClientCacheMiddleware.d.ts +1 -1
- package/esm/middleware/ClientCacheMiddleware.js +8 -17
- package/esm/middleware/LoggingMiddleware.d.ts +1 -1
- package/esm/middleware/LoggingMiddleware.js +5 -6
- package/esm/util/retry/index.js +1 -1
- package/esm/util/retry/lib/retry.d.ts +1 -1
- package/esm/util/retry/lib/retry.js +13 -7
- package/esm/util/retry/lib/retryOperation.d.ts +1 -26
- package/esm/util/retry/lib/retryOperation.js +1 -1
- package/package.json +67 -28
package/esm/RequestConfig.js
CHANGED
|
@@ -1,24 +1,23 @@
|
|
|
1
|
-
import { COMPUTED_CONFIG_SYMBOL, } from "./ApiTypes";
|
|
2
|
-
import { DEFAULT_QUERY_PARSE, DEFAULT_QUERY_STRINGIFY } from "./QueryHandling";
|
|
3
|
-
import * as Utils from "./Utils";
|
|
1
|
+
import { COMPUTED_CONFIG_SYMBOL, } from "./ApiTypes.js";
|
|
2
|
+
import { DEFAULT_QUERY_PARSE, DEFAULT_QUERY_STRINGIFY } from "./QueryHandling.js";
|
|
3
|
+
import * as Utils from "./Utils.js";
|
|
4
4
|
const MERGED_CONFIG_KEYS = ["headers"];
|
|
5
5
|
export const processRequestConfigs = (configs) => {
|
|
6
|
-
var _a, _b;
|
|
7
6
|
const computedConfig = Utils.assign({
|
|
8
7
|
[COMPUTED_CONFIG_SYMBOL]: true,
|
|
9
8
|
}, ...configs);
|
|
10
9
|
// merge other values
|
|
11
10
|
for (const key of MERGED_CONFIG_KEYS) {
|
|
12
11
|
computedConfig[key] = Utils.assign({}, ...configs.reduce((acc, config) => {
|
|
13
|
-
if (config
|
|
12
|
+
if (config?.[key]) {
|
|
14
13
|
acc.push(config[key]);
|
|
15
14
|
}
|
|
16
15
|
return acc;
|
|
17
16
|
}, []));
|
|
18
17
|
}
|
|
19
18
|
computedConfig.queryHandling = {
|
|
20
|
-
parse:
|
|
21
|
-
stringify:
|
|
19
|
+
parse: computedConfig.queryHandling?.parse || DEFAULT_QUERY_PARSE,
|
|
20
|
+
stringify: computedConfig.queryHandling?.stringify || computedConfig.queryParser || DEFAULT_QUERY_STRINGIFY,
|
|
22
21
|
};
|
|
23
22
|
computedConfig.queryParser = undefined;
|
|
24
23
|
const query = computedConfig.query;
|
package/esm/RequestContext.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { Api } from "./Api";
|
|
2
|
-
import type { RequestEvent, RequestMethod, ResponseType } from "./ApiConstants";
|
|
3
|
-
import type { ApiResponse, Body, ComputedRequestConfig, EventResult, Params, Query, RawHeaders, RequestCacheInfo, RequestEventHandlers, RequestHost, RequestStats, State } from "./ApiTypes";
|
|
4
|
-
import type
|
|
5
|
-
import type {
|
|
6
|
-
import type {
|
|
7
|
-
import type
|
|
1
|
+
import type { Api } from "./Api.js";
|
|
2
|
+
import type { RequestEvent, RequestMethod, ResponseType } from "./ApiConstants.js";
|
|
3
|
+
import type { ApiResponse, Body, ComputedRequestConfig, EventResult, Params, Query, RawHeaders, RequestCacheInfo, RequestEventHandlers, RequestHost, RequestStats, State } from "./ApiTypes.js";
|
|
4
|
+
import type RequestBackend from "./backend/RequestBackend.js";
|
|
5
|
+
import type { EndpointMockingConfig } from "./MockingTypes.js";
|
|
6
|
+
import type { RequestError } from "./RequestError.js";
|
|
7
|
+
import type { Validation } from "./Validation.js";
|
|
8
8
|
export default class RequestContext<TResponse = any, TParams extends Params | undefined = Params | undefined, TQuery extends Query | undefined = Query | undefined, TBody extends Body | undefined = Body | undefined, TState extends State = State> {
|
|
9
9
|
readonly id: number;
|
|
10
10
|
readonly key: string;
|
|
@@ -42,6 +42,7 @@ export default class RequestContext<TResponse = any, TParams extends Params | un
|
|
|
42
42
|
updateParams(params: Partial<Record<string, string>>): this;
|
|
43
43
|
private parseRequestBody;
|
|
44
44
|
getParsedBody(): any;
|
|
45
|
+
parseBody(): void;
|
|
45
46
|
updateQuery(newQuery: Partial<TQuery>): this;
|
|
46
47
|
triggerEvent(eventType: RequestEvent): Promise<EventResult<TResponse> | undefined>;
|
|
47
48
|
addCanceller(canceler: () => void): void;
|
package/esm/RequestContext.js
CHANGED
|
@@ -1,14 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
import { resolvePathParams, resolveUrl } from "./ApiUtils";
|
|
11
|
-
import * as Utils from "./Utils";
|
|
1
|
+
import { resolvePathParams, resolveUrl } from "./ApiUtils.js";
|
|
2
|
+
import * as Utils from "./Utils.js";
|
|
12
3
|
let contextIdCounter = 0;
|
|
13
4
|
export default class RequestContext {
|
|
14
5
|
constructor(backend, host, config, rawPath, mocking) {
|
|
@@ -100,18 +91,32 @@ export default class RequestContext {
|
|
|
100
91
|
return this;
|
|
101
92
|
}
|
|
102
93
|
parseRequestBody() {
|
|
103
|
-
var _a;
|
|
104
94
|
if (this.requestConfig.body && this.requestConfig.headers) {
|
|
105
95
|
const contentTypeKey = Object.keys(this.requestConfig.headers).find((key) => key.toLowerCase() === "content-type");
|
|
106
|
-
const contentType = contentTypeKey &&
|
|
96
|
+
const contentType = contentTypeKey && this.requestConfig.headers[contentTypeKey]?.toString().split(";")[0].trim();
|
|
97
|
+
const bodyEncoding = this.validation.bodyEncoding ?? "application/json";
|
|
98
|
+
if (!contentTypeKey && bodyEncoding === "application/x-www-form-urlencoded") {
|
|
99
|
+
this.requestConfig.headers["Content-Type"] = "application/x-www-form-urlencoded";
|
|
100
|
+
}
|
|
107
101
|
if (contentType === "application/x-www-form-urlencoded") {
|
|
108
102
|
const searchParams = new URLSearchParams();
|
|
109
103
|
for (const key of Object.keys(this.requestConfig.body)) {
|
|
110
104
|
const value = this.requestConfig.body[key];
|
|
111
|
-
searchParams.append(key, value
|
|
105
|
+
searchParams.append(key, value?.toString());
|
|
112
106
|
}
|
|
113
107
|
this.parsedBody = searchParams;
|
|
114
108
|
}
|
|
109
|
+
else if (bodyEncoding === "application/x-www-form-urlencoded") {
|
|
110
|
+
const searchParams = new URLSearchParams();
|
|
111
|
+
for (const key of Object.keys(this.requestConfig.body)) {
|
|
112
|
+
const value = this.requestConfig.body[key];
|
|
113
|
+
searchParams.append(key, value?.toString());
|
|
114
|
+
}
|
|
115
|
+
this.parsedBody = searchParams;
|
|
116
|
+
}
|
|
117
|
+
else if (bodyEncoding === "multipart/form-data") {
|
|
118
|
+
this.parsedBody = Utils.toFormData(this.requestConfig.body);
|
|
119
|
+
}
|
|
115
120
|
else {
|
|
116
121
|
this.parsedBody = this.requestConfig.body;
|
|
117
122
|
}
|
|
@@ -123,25 +128,26 @@ export default class RequestContext {
|
|
|
123
128
|
getParsedBody() {
|
|
124
129
|
return this.parsedBody;
|
|
125
130
|
}
|
|
131
|
+
parseBody() {
|
|
132
|
+
this.parseRequestBody();
|
|
133
|
+
}
|
|
126
134
|
updateQuery(newQuery) {
|
|
127
135
|
this.requestConfig.queryObject = Utils.assign({}, this.requestConfig.queryObject, newQuery);
|
|
128
136
|
this.computedRequestUrl = this.resolveRequestUrl();
|
|
129
137
|
return this;
|
|
130
138
|
}
|
|
131
|
-
triggerEvent(eventType) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
return eventResult;
|
|
140
|
-
}
|
|
139
|
+
async triggerEvent(eventType) {
|
|
140
|
+
const eventHandlers = this.eventHandlers[eventType];
|
|
141
|
+
if (eventHandlers) {
|
|
142
|
+
for (let i = 0; i < eventHandlers.length; i++) {
|
|
143
|
+
const eventHandler = eventHandlers[i];
|
|
144
|
+
const eventResult = await eventHandler(this);
|
|
145
|
+
if (eventResult) {
|
|
146
|
+
return eventResult;
|
|
141
147
|
}
|
|
142
148
|
}
|
|
143
|
-
|
|
144
|
-
|
|
149
|
+
}
|
|
150
|
+
return undefined;
|
|
145
151
|
}
|
|
146
152
|
addCanceller(canceler) {
|
|
147
153
|
this.canceler = canceler;
|
|
@@ -170,7 +176,7 @@ export default class RequestContext {
|
|
|
170
176
|
this.computedMethod = method;
|
|
171
177
|
}
|
|
172
178
|
updateBody(body) {
|
|
173
|
-
// @ts-
|
|
179
|
+
// @ts-expect-error
|
|
174
180
|
this.requestConfig.body = body;
|
|
175
181
|
this.parseRequestBody();
|
|
176
182
|
}
|
package/esm/RequestError.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ApiResponse } from "./ApiTypes";
|
|
2
|
-
import type RequestContext from "./RequestContext";
|
|
3
|
-
import type { EnumOf } from "./Utils";
|
|
1
|
+
import type { ApiResponse } from "./ApiTypes.js";
|
|
2
|
+
import type RequestContext from "./RequestContext.js";
|
|
3
|
+
import type { EnumOf } from "./Utils.js";
|
|
4
4
|
export declare const RequestErrorCode: {
|
|
5
5
|
readonly MISC_UNKNOWN_ERROR: "misc/unknown-error";
|
|
6
6
|
readonly REQUEST_NETWORK_ERROR: "request/network-error";
|
package/esm/RequestError.js
CHANGED
|
@@ -46,7 +46,7 @@ export const convertToRequestError = (config) => {
|
|
|
46
46
|
});
|
|
47
47
|
try {
|
|
48
48
|
Object.defineProperty(resultError, "message", {
|
|
49
|
-
value: `A ${context.method.toUpperCase()} request to '${context.requestUrl.href}' failed${
|
|
49
|
+
value: `A ${context.method.toUpperCase()} request to '${context.requestUrl.href}' failed${response?.status ? ` with status code ${response.status}` : ""} [${code}]: ${resultError.message}`,
|
|
50
50
|
});
|
|
51
51
|
}
|
|
52
52
|
catch (e) {
|
|
@@ -57,9 +57,8 @@ export const convertToRequestError = (config) => {
|
|
|
57
57
|
return resultError;
|
|
58
58
|
};
|
|
59
59
|
export const getErrorResponse = (error) => {
|
|
60
|
-
var _a;
|
|
61
60
|
if (isRequestError(error)) {
|
|
62
|
-
return
|
|
61
|
+
return error.response ?? undefined;
|
|
63
62
|
}
|
|
64
63
|
return undefined;
|
|
65
64
|
};
|
package/esm/Requester.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { ApiResponse, Body, Params, Query, RequestConfig, RequestHost, State } from "./ApiTypes";
|
|
2
|
-
import type { EndpointMockingConfig } from "./MockingTypes";
|
|
1
|
+
import type { ApiResponse, Body, Params, Query, RequestConfig, RequestHost, State } from "./ApiTypes.js";
|
|
2
|
+
import type { EndpointMockingConfig } from "./MockingTypes.js";
|
|
3
3
|
export declare const submit: <TResponse, TParams extends Params | undefined, TQuery extends Query | undefined, TBody extends Body | undefined, TState extends State>(host: RequestHost, config: RequestConfig<TParams, TQuery, TBody, TState>, mocking: EndpointMockingConfig<TResponse, TParams, TQuery, TBody, TState> | null | undefined) => Promise<ApiResponse<TResponse>>;
|
package/esm/Requester.js
CHANGED
|
@@ -1,23 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
import { EventResultType, RequestEvent } from "./ApiConstants";
|
|
11
|
-
import { inferResponseType, isAcceptableStatus, isNetworkError } from "./ApiUtils";
|
|
12
|
-
import RequestContext from "./RequestContext";
|
|
13
|
-
import { RequestErrorCode, convertToRequestError, isRequestError } from "./RequestError";
|
|
14
|
-
import { textDecode } from "./TextDecoding";
|
|
15
|
-
import MockRequestBackend from "./backend/MockRequestBackend";
|
|
16
|
-
import retry from "./util/retry";
|
|
1
|
+
import { EventResultType, RequestEvent } from "./ApiConstants.js";
|
|
2
|
+
import { inferResponseType, isAcceptableStatus, isNetworkError } from "./ApiUtils.js";
|
|
3
|
+
import MockRequestBackend from "./backend/MockRequestBackend.js";
|
|
4
|
+
import RequestContext from "./RequestContext.js";
|
|
5
|
+
import { convertToRequestError, isRequestError, RequestErrorCode } from "./RequestError.js";
|
|
6
|
+
import { textDecode } from "./TextDecoding.js";
|
|
7
|
+
import retry from "./util/retry/index.js";
|
|
17
8
|
const locks = {};
|
|
18
9
|
const runningOperations = {};
|
|
19
10
|
const MOCK_REQUEST_BACKEND = new MockRequestBackend();
|
|
20
|
-
export const submit = (host, config, mocking) =>
|
|
11
|
+
export const submit = async (host, config, mocking) => {
|
|
21
12
|
const computedConfig = host.computeConfig(config);
|
|
22
13
|
const backend = mocking ? MOCK_REQUEST_BACKEND : host.getRequestBackend();
|
|
23
14
|
const context = new RequestContext(backend, host, computedConfig, host.path, mocking);
|
|
@@ -39,8 +30,8 @@ export const submit = (host, config, mocking) => __awaiter(void 0, void 0, void
|
|
|
39
30
|
locks[lock] = context;
|
|
40
31
|
}
|
|
41
32
|
try {
|
|
42
|
-
let response =
|
|
43
|
-
const successEventResult =
|
|
33
|
+
let response = await (runningOperations[key] = makeRequest(context));
|
|
34
|
+
const successEventResult = await context.triggerEvent(RequestEvent.SUCCESS);
|
|
44
35
|
if (successEventResult && successEventResult.type === EventResultType.RESPOND) {
|
|
45
36
|
context.response = response = successEventResult.response;
|
|
46
37
|
}
|
|
@@ -56,7 +47,7 @@ export const submit = (host, config, mocking) => __awaiter(void 0, void 0, void
|
|
|
56
47
|
delete locks[lock];
|
|
57
48
|
}
|
|
58
49
|
}
|
|
59
|
-
}
|
|
50
|
+
};
|
|
60
51
|
const parseRetryOptions = (retryConfig) => {
|
|
61
52
|
if (retryConfig && typeof retryConfig === "object") {
|
|
62
53
|
return retryConfig;
|
|
@@ -66,9 +57,8 @@ const parseRetryOptions = (retryConfig) => {
|
|
|
66
57
|
}
|
|
67
58
|
return { maxAttempts: 0 };
|
|
68
59
|
};
|
|
69
|
-
const makeRequest = (context) =>
|
|
70
|
-
|
|
71
|
-
const beforeSendEventResult = yield context.triggerEvent(RequestEvent.BEFORE_SEND);
|
|
60
|
+
const makeRequest = async (context) => {
|
|
61
|
+
const beforeSendEventResult = await context.triggerEvent(RequestEvent.BEFORE_SEND);
|
|
72
62
|
if (beforeSendEventResult && beforeSendEventResult.type === EventResultType.RESPOND) {
|
|
73
63
|
return (context.response = beforeSendEventResult.response);
|
|
74
64
|
}
|
|
@@ -98,22 +88,23 @@ const makeRequest = (context) => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
98
88
|
});
|
|
99
89
|
}
|
|
100
90
|
}
|
|
101
|
-
|
|
91
|
+
context.parseBody();
|
|
92
|
+
const retryOptions = parseRetryOptions(context.requestConfig?.retry);
|
|
102
93
|
const internalRetryOptions = {
|
|
103
94
|
retries: retryOptions.maxAttempts,
|
|
104
95
|
// assume most users won't want to tune the delay between retries
|
|
105
|
-
minTimeout:
|
|
106
|
-
maxTimeout:
|
|
96
|
+
minTimeout: retryOptions.minDelay ?? 200,
|
|
97
|
+
maxTimeout: retryOptions.maxDelay ?? 1000,
|
|
107
98
|
randomize: true,
|
|
108
99
|
};
|
|
109
100
|
context.stats.attempt = 0;
|
|
110
|
-
const performRequest = (fnBail, attemptCount) =>
|
|
101
|
+
const performRequest = async (fnBail, attemptCount) => {
|
|
111
102
|
context.stats.attempt++;
|
|
112
103
|
try {
|
|
113
104
|
const { promise, canceler } = context.backend.makeRequest(context);
|
|
114
105
|
context.addCanceller(canceler);
|
|
115
|
-
const response =
|
|
116
|
-
const parsedResponse = (
|
|
106
|
+
const response = await promise;
|
|
107
|
+
const parsedResponse = (await parseResponse(context, response));
|
|
117
108
|
if (!isAcceptableStatus(parsedResponse.status, context.requestConfig.acceptableStatus)) {
|
|
118
109
|
throw convertToRequestError({
|
|
119
110
|
error: new Error(`[api-def] Invalid response status code '${parsedResponse.status}'`),
|
|
@@ -130,12 +121,12 @@ const makeRequest = (context) => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
130
121
|
if (context.cancelled) {
|
|
131
122
|
rawError.isCancelledRequest = true;
|
|
132
123
|
}
|
|
133
|
-
const error =
|
|
124
|
+
const error = await parseError(context, rawError);
|
|
134
125
|
context.error = error;
|
|
135
126
|
context.response = error.response;
|
|
136
127
|
context.stats.endTimestamp = Date.now();
|
|
137
|
-
const errorEventResult =
|
|
138
|
-
if (
|
|
128
|
+
const errorEventResult = await context.triggerEvent(RequestEvent.ERROR);
|
|
129
|
+
if (errorEventResult?.type === EventResultType.RESPOND) {
|
|
139
130
|
return errorEventResult.response;
|
|
140
131
|
}
|
|
141
132
|
// allow retry logic to handle network errors
|
|
@@ -144,7 +135,7 @@ const makeRequest = (context) => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
144
135
|
// throw error;
|
|
145
136
|
//}
|
|
146
137
|
// if we have an event that tells us to retry, we must do it
|
|
147
|
-
const forceRetry =
|
|
138
|
+
const forceRetry = errorEventResult?.type === EventResultType.RETRY;
|
|
148
139
|
if (forceRetry) {
|
|
149
140
|
return performRequest(fnBail, attemptCount);
|
|
150
141
|
}
|
|
@@ -153,7 +144,7 @@ const makeRequest = (context) => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
153
144
|
throw error;
|
|
154
145
|
}
|
|
155
146
|
// error is unrecoverable, bail
|
|
156
|
-
const unrecoverableErrorEventResult =
|
|
147
|
+
const unrecoverableErrorEventResult = await context.triggerEvent(RequestEvent.UNRECOVERABLE_ERROR);
|
|
157
148
|
if (unrecoverableErrorEventResult) {
|
|
158
149
|
if (unrecoverableErrorEventResult.type === EventResultType.RESPOND) {
|
|
159
150
|
return unrecoverableErrorEventResult.response;
|
|
@@ -161,8 +152,8 @@ const makeRequest = (context) => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
161
152
|
}
|
|
162
153
|
fnBail(error);
|
|
163
154
|
}
|
|
164
|
-
}
|
|
165
|
-
const response =
|
|
155
|
+
};
|
|
156
|
+
const response = await retry(performRequest, internalRetryOptions);
|
|
166
157
|
if (context.validation.response) {
|
|
167
158
|
try {
|
|
168
159
|
response.data = context.validation.response.parse(response.data);
|
|
@@ -176,11 +167,10 @@ const makeRequest = (context) => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
176
167
|
}
|
|
177
168
|
}
|
|
178
169
|
return response;
|
|
179
|
-
}
|
|
180
|
-
const parseResponse = (context, response, error) =>
|
|
181
|
-
var _a;
|
|
170
|
+
};
|
|
171
|
+
const parseResponse = async (context, response, error) => {
|
|
182
172
|
if (response) {
|
|
183
|
-
const parsedResponse =
|
|
173
|
+
const parsedResponse = await context.backend.convertResponse(context, response);
|
|
184
174
|
const contentType = parsedResponse.headers.get("content-type");
|
|
185
175
|
const inferredResponseType = inferResponseType(contentType);
|
|
186
176
|
if (!error) {
|
|
@@ -197,7 +187,7 @@ const parseResponse = (context, response, error) => __awaiter(void 0, void 0, vo
|
|
|
197
187
|
if (inferredResponseType === "arraybuffer" && context.responseType === "json") {
|
|
198
188
|
if (parsedResponse.data && typeof parsedResponse.data === "object") {
|
|
199
189
|
const data = response.data;
|
|
200
|
-
if (
|
|
190
|
+
if (data.constructor?.name === "ArrayBuffer") {
|
|
201
191
|
try {
|
|
202
192
|
const decodedData = (response.data = textDecode(data));
|
|
203
193
|
response.data = JSON.parse(decodedData);
|
|
@@ -217,18 +207,17 @@ const parseResponse = (context, response, error) => __awaiter(void 0, void 0, vo
|
|
|
217
207
|
return parsedResponse;
|
|
218
208
|
}
|
|
219
209
|
return response;
|
|
220
|
-
}
|
|
221
|
-
const parseError = (context, rawError) =>
|
|
222
|
-
var _a;
|
|
210
|
+
};
|
|
211
|
+
const parseError = async (context, rawError) => {
|
|
223
212
|
let error;
|
|
224
213
|
if (isRequestError(rawError)) {
|
|
225
214
|
error = rawError;
|
|
226
215
|
}
|
|
227
216
|
else {
|
|
228
|
-
const extractedResponse =
|
|
229
|
-
let errorResponse
|
|
217
|
+
const extractedResponse = await context.backend.extractResponseFromError(rawError);
|
|
218
|
+
let errorResponse;
|
|
230
219
|
if (extractedResponse !== undefined) {
|
|
231
|
-
errorResponse =
|
|
220
|
+
errorResponse = await parseResponse(context, extractedResponse, true);
|
|
232
221
|
}
|
|
233
222
|
let code = isNetworkError(rawError)
|
|
234
223
|
? RequestErrorCode.REQUEST_NETWORK_ERROR
|
|
@@ -239,12 +228,18 @@ const parseError = (context, rawError) => __awaiter(void 0, void 0, void 0, func
|
|
|
239
228
|
}
|
|
240
229
|
}
|
|
241
230
|
else {
|
|
242
|
-
if (rawError.code === "ENOTFOUND" ||
|
|
231
|
+
if (rawError.code === "ENOTFOUND" || rawError.cause?.code === "ENOTFOUND") {
|
|
243
232
|
code = RequestErrorCode.REQUEST_HOST_NAME_NOT_FOUND;
|
|
244
233
|
}
|
|
245
234
|
}
|
|
246
235
|
const errorInfo = context.backend.getErrorInfo(rawError, errorResponse);
|
|
247
|
-
error = convertToRequestError(
|
|
236
|
+
error = convertToRequestError({
|
|
237
|
+
error: rawError,
|
|
238
|
+
response: errorResponse,
|
|
239
|
+
code: code,
|
|
240
|
+
context: context,
|
|
241
|
+
...errorInfo,
|
|
242
|
+
});
|
|
248
243
|
}
|
|
249
244
|
return error;
|
|
250
|
-
}
|
|
245
|
+
};
|
package/esm/UtilTypes.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type Endpoint from "./Endpoint";
|
|
1
|
+
import type Endpoint from "./Endpoint.js";
|
|
2
2
|
export type ResponseOf<E extends Endpoint<any, any, any, any>> = E extends Endpoint<infer R, any, any, any> ? R : never;
|
|
3
3
|
export type ParamsOf<E extends Endpoint<any, any, any, any>> = E extends Endpoint<any, infer P, any, any> ? P : never;
|
|
4
4
|
export type QueryOf<E extends Endpoint<any, any, any, any>> = E extends Endpoint<any, any, infer Q, any> ? Q : never;
|
package/esm/Utils.d.ts
CHANGED
|
@@ -14,3 +14,4 @@ export declare const noop: () => void;
|
|
|
14
14
|
export declare const delayThenReturn: <T>(value: T, delayMs: number) => Promise<T>;
|
|
15
15
|
export declare const randInt: (min: number, max: number) => number;
|
|
16
16
|
export declare const isFormData: (value: any) => value is FormData;
|
|
17
|
+
export declare const toFormData: (body: any) => FormData;
|
package/esm/Utils.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// polyfill from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
|
|
2
|
+
const hasOwn = (object, key) => Object.prototype.hasOwnProperty.call(object, key);
|
|
2
3
|
export const assign = Object.assign ||
|
|
3
4
|
((target, ...varArgs) => {
|
|
4
5
|
if (target === null || target === undefined) {
|
|
@@ -10,7 +11,7 @@ export const assign = Object.assign ||
|
|
|
10
11
|
if (nextSource !== null && nextSource !== undefined) {
|
|
11
12
|
for (const nextKey in nextSource) {
|
|
12
13
|
// Avoid bugs when hasOwnProperty is shadowed
|
|
13
|
-
if (
|
|
14
|
+
if (hasOwn(nextSource, nextKey)) {
|
|
14
15
|
to[nextKey] = nextSource[nextKey];
|
|
15
16
|
}
|
|
16
17
|
}
|
|
@@ -63,5 +64,56 @@ export const randInt = (min, max) => {
|
|
|
63
64
|
return Math.floor(Math.random() * (maxI - minI + 1)) + minI;
|
|
64
65
|
};
|
|
65
66
|
export const isFormData = (value) => {
|
|
66
|
-
return value instanceof FormData;
|
|
67
|
+
return typeof FormData !== "undefined" && value instanceof FormData;
|
|
68
|
+
};
|
|
69
|
+
const isPlainObject = (value) => {
|
|
70
|
+
return Object.prototype.toString.call(value) === "[object Object]";
|
|
71
|
+
};
|
|
72
|
+
const isBlob = (value) => {
|
|
73
|
+
return typeof Blob !== "undefined" && value instanceof Blob;
|
|
74
|
+
};
|
|
75
|
+
const isArrayBuffer = (value) => {
|
|
76
|
+
return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer;
|
|
77
|
+
};
|
|
78
|
+
const isArrayBufferView = (value) => {
|
|
79
|
+
return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value);
|
|
80
|
+
};
|
|
81
|
+
const toFormDataValue = (value) => {
|
|
82
|
+
if (isBlob(value)) {
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
if ((isArrayBuffer(value) || isArrayBufferView(value)) && typeof Blob !== "undefined") {
|
|
86
|
+
return new Blob([value]);
|
|
87
|
+
}
|
|
88
|
+
return String(value);
|
|
89
|
+
};
|
|
90
|
+
export const toFormData = (body) => {
|
|
91
|
+
if (isFormData(body)) {
|
|
92
|
+
return body;
|
|
93
|
+
}
|
|
94
|
+
const formData = new FormData();
|
|
95
|
+
const appendValue = (key, value) => {
|
|
96
|
+
if (value === undefined || value === null) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (Array.isArray(value)) {
|
|
100
|
+
for (const item of value) {
|
|
101
|
+
appendValue(key, item);
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (isPlainObject(value) && !isBlob(value)) {
|
|
106
|
+
for (const nestedKey of Object.keys(value)) {
|
|
107
|
+
appendValue(`${key}.${nestedKey}`, value[nestedKey]);
|
|
108
|
+
}
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
formData.append(key, toFormDataValue(value));
|
|
112
|
+
};
|
|
113
|
+
if (isPlainObject(body)) {
|
|
114
|
+
for (const key of Object.keys(body)) {
|
|
115
|
+
appendValue(key, body[key]);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return formData;
|
|
67
119
|
};
|
package/esm/Validation.d.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import type * as zod from "zod";
|
|
2
|
-
import type { Body, Params, Query, State } from "./ApiTypes";
|
|
2
|
+
import type { Body, Params, Query, RequestBodyEncoding, State } from "./ApiTypes.js";
|
|
3
|
+
export interface ValidationOptions<TValue> {
|
|
4
|
+
schema?: zod.Schema<TValue>;
|
|
5
|
+
}
|
|
6
|
+
export interface BodyValidationOptions<TBody extends Body> extends ValidationOptions<TBody> {
|
|
7
|
+
encoding?: RequestBodyEncoding;
|
|
8
|
+
}
|
|
3
9
|
export interface Validation<TResponse = any, TParams extends Params | undefined = Params | undefined, TQuery extends Query | undefined = Query | undefined, TBody extends Body | undefined = Body | undefined, TState extends State = State> {
|
|
4
10
|
query?: zod.Schema<TQuery>;
|
|
5
11
|
params?: zod.Schema<TParams>;
|
|
6
12
|
body?: zod.Schema<TBody>;
|
|
13
|
+
bodyEncoding?: RequestBodyEncoding;
|
|
7
14
|
response?: zod.Schema<TResponse>;
|
|
8
15
|
state?: zod.Schema<TState>;
|
|
9
16
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { AxiosError, AxiosResponse } from "axios";
|
|
2
|
-
import type { ApiResponse } from "../ApiTypes";
|
|
3
|
-
import type RequestContext from "../RequestContext";
|
|
4
|
-
import type
|
|
5
|
-
import type
|
|
2
|
+
import type { ApiResponse } from "../ApiTypes.js";
|
|
3
|
+
import type RequestContext from "../RequestContext.js";
|
|
4
|
+
import type RequestBackend from "./RequestBackend.js";
|
|
5
|
+
import type { ConvertedApiResponse, RequestBackendErrorInfo, RequestOperation } from "./RequestBackend.js";
|
|
6
6
|
export declare const isAxiosError: (error: Error) => error is AxiosError;
|
|
7
7
|
export default class AxiosRequestBackend implements RequestBackend<AxiosResponse> {
|
|
8
8
|
readonly id = "axios";
|