api-def 0.6.1 → 0.6.2-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/LICENSE +20 -20
- package/README.md +34 -34
- package/cjs/Api.js +1 -1
- package/cjs/ApiUtils.d.ts +1 -1
- package/cjs/Endpoint.js +4 -4
- package/cjs/MockingTypes.d.ts +1 -0
- package/cjs/RequestContext.d.ts +3 -0
- package/cjs/RequestContext.js +29 -2
- package/cjs/Requester.js +14 -14
- package/cjs/backend/AxiosRequestBackend.d.ts +1 -1
- package/cjs/backend/AxiosRequestBackend.js +2 -2
- package/cjs/backend/FetchRequestBackend.d.ts +1 -1
- package/cjs/backend/FetchRequestBackend.js +6 -5
- package/cjs/backend/MockRequestBackend.js +15 -7
- package/cjs/cache/Caching.d.ts +1 -1
- package/cjs/index.js +5 -1
- package/cjs/middleware/LoggingMiddleware.js +7 -7
- package/esm/Api.js +1 -1
- package/esm/ApiUtils.d.ts +1 -1
- package/esm/Endpoint.js +4 -4
- package/esm/MockingTypes.d.ts +1 -0
- package/esm/RequestContext.d.ts +3 -0
- package/esm/RequestContext.js +29 -2
- package/esm/Requester.js +3 -3
- package/esm/backend/AxiosRequestBackend.d.ts +1 -1
- package/esm/backend/AxiosRequestBackend.js +1 -1
- package/esm/backend/FetchRequestBackend.d.ts +1 -1
- package/esm/backend/FetchRequestBackend.js +4 -3
- package/esm/backend/MockRequestBackend.js +11 -3
- package/esm/cache/Caching.d.ts +1 -1
- package/esm/middleware/LoggingMiddleware.js +7 -7
- package/package.json +17 -5
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2019 James Waterhouse
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 James Waterhouse
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
# [api-def](https://github.com/Censkh/api-def/) · [](https://github.com/Censkh/api-def/blob/master/LICENSE) [](https://www.npmjs.com/package/api-def) [](https://github.com/Censkh/api-def/actions)
|
|
2
|
-
|
|
3
|
-
Typed APIs with middleware support
|
|
4
|
-
|
|
5
|
-
API def provides a unified way to type your endpoints allowing for compile time checking of query, body, response and even url parameters
|
|
6
|
-
|
|
7
|
-
``` npm i api-def ```
|
|
8
|
-
|
|
9
|
-
- [Documentation](https://censkh.github.io/api-def/)
|
|
10
|
-
|
|
11
|
-
```typescript
|
|
12
|
-
import {Api} from "api-def";
|
|
13
|
-
|
|
14
|
-
const api = new Api({
|
|
15
|
-
baseUrl: "https://my-api/",
|
|
16
|
-
name: "My API",
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
const fetchData = api.endpoint()
|
|
20
|
-
.queryOf<{ includeAwesome: boolean; }>()
|
|
21
|
-
.responseOf<{ data: {awesome: boolean; } }>()
|
|
22
|
-
.build({
|
|
23
|
-
id: "fetch_data",
|
|
24
|
-
method: "get",
|
|
25
|
-
path: "/data"
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
// calls GET https://my-api/data?includeAwesome=true
|
|
29
|
-
const res = await fetchData.submit({
|
|
30
|
-
query: {includeAwesome: true}
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
console.log(res.data); // { data: { awesome: true } }
|
|
34
|
-
```
|
|
1
|
+
# [api-def](https://github.com/Censkh/api-def/) · [](https://github.com/Censkh/api-def/blob/master/LICENSE) [](https://www.npmjs.com/package/api-def) [](https://github.com/Censkh/api-def/actions)
|
|
2
|
+
|
|
3
|
+
Typed APIs with middleware support
|
|
4
|
+
|
|
5
|
+
API def provides a unified way to type your endpoints allowing for compile time checking of query, body, response and even url parameters
|
|
6
|
+
|
|
7
|
+
``` npm i api-def ```
|
|
8
|
+
|
|
9
|
+
- [Documentation](https://censkh.github.io/api-def/)
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import {Api} from "api-def";
|
|
13
|
+
|
|
14
|
+
const api = new Api({
|
|
15
|
+
baseUrl: "https://my-api/",
|
|
16
|
+
name: "My API",
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const fetchData = api.endpoint()
|
|
20
|
+
.queryOf<{ includeAwesome: boolean; }>()
|
|
21
|
+
.responseOf<{ data: {awesome: boolean; } }>()
|
|
22
|
+
.build({
|
|
23
|
+
id: "fetch_data",
|
|
24
|
+
method: "get",
|
|
25
|
+
path: "/data"
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// calls GET https://my-api/data?includeAwesome=true
|
|
29
|
+
const res = await fetchData.submit({
|
|
30
|
+
query: {includeAwesome: true}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
console.log(res.data); // { data: { awesome: true } }
|
|
34
|
+
```
|
package/cjs/Api.js
CHANGED
|
@@ -77,7 +77,7 @@ var HotRequestHost = /** @class */ (function () {
|
|
|
77
77
|
return Utils.assign({}, apiDefaults, config);
|
|
78
78
|
};
|
|
79
79
|
HotRequestHost.prototype.computePath = function (path, config) {
|
|
80
|
-
return path.startsWith("/") ? path : "/"
|
|
80
|
+
return path.startsWith("/") ? path : "/".concat(path);
|
|
81
81
|
};
|
|
82
82
|
return HotRequestHost;
|
|
83
83
|
}());
|
package/cjs/ApiUtils.d.ts
CHANGED
|
@@ -2,5 +2,5 @@ import { AcceptableStatus, CancelledRequestError } from "./ApiTypes";
|
|
|
2
2
|
import { ResponseType } from "./ApiConstants";
|
|
3
3
|
export declare const isCancelledError: (error: Error) => error is CancelledRequestError;
|
|
4
4
|
export declare const isNetworkError: (error: Error) => boolean;
|
|
5
|
-
export declare const isAcceptableStatus: (status: number, acceptableStatus?: AcceptableStatus[]
|
|
5
|
+
export declare const isAcceptableStatus: (status: number, acceptableStatus?: AcceptableStatus[]) => boolean;
|
|
6
6
|
export declare const inferResponseType: (contentType: string | null | undefined) => ResponseType;
|
package/cjs/Endpoint.js
CHANGED
|
@@ -62,7 +62,7 @@ var Endpoint = /** @class */ (function () {
|
|
|
62
62
|
mockingEnabled = (_a = (typeof apiMocking.enabled === "function" ? apiMocking.enabled() : apiMocking.enabled)) !== null && _a !== void 0 ? _a : false;
|
|
63
63
|
if (mockingEnabled) {
|
|
64
64
|
if (!((_b = this.mocking) === null || _b === void 0 ? void 0 : _b.handler)) {
|
|
65
|
-
throw new Error("[api-def] Endpoint for '"
|
|
65
|
+
throw new Error("[api-def] Endpoint for '".concat(this.path, "' has no mocking"));
|
|
66
66
|
}
|
|
67
67
|
mock = true;
|
|
68
68
|
}
|
|
@@ -72,16 +72,16 @@ var Endpoint = /** @class */ (function () {
|
|
|
72
72
|
});
|
|
73
73
|
};
|
|
74
74
|
Endpoint.prototype.computePath = function (path, request) {
|
|
75
|
-
var computedPath = path.startsWith("/") ? path : "/"
|
|
75
|
+
var computedPath = path.startsWith("/") ? path : "/".concat(path);
|
|
76
76
|
if (request.params) {
|
|
77
77
|
var keys = Object.keys(request.params);
|
|
78
78
|
for (var i = 0; i < keys.length; i++) {
|
|
79
79
|
var argName = keys[i];
|
|
80
|
-
computedPath = computedPath.replace(":"
|
|
80
|
+
computedPath = computedPath.replace(":".concat(argName), request.params[argName]);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
if (computedPath.includes(":")) {
|
|
84
|
-
throw new Error("[api-def] Not all path params have been resolved: '"
|
|
84
|
+
throw new Error("[api-def] Not all path params have been resolved: '".concat(computedPath, "'"));
|
|
85
85
|
}
|
|
86
86
|
return computedPath;
|
|
87
87
|
};
|
package/cjs/MockingTypes.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export interface MockRequest<R = any, P extends Params | undefined = Params | un
|
|
|
11
11
|
export interface MockResponse<R = any, P extends Params | undefined = Params | undefined, Q extends Query | undefined = Query | undefined, B extends Body | undefined = Body | undefined> {
|
|
12
12
|
statusCode: number;
|
|
13
13
|
response: R | undefined;
|
|
14
|
+
headers: Headers;
|
|
14
15
|
status(statusCode: number): this;
|
|
15
16
|
send(response: R): this;
|
|
16
17
|
}
|
package/cjs/RequestContext.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export default class RequestContext<R = any, P extends Params | undefined = Para
|
|
|
19
19
|
cancelled: boolean;
|
|
20
20
|
readonly computedConfig: RequestConfig<P, Q, B>;
|
|
21
21
|
readonly mocking: EndpointMockingConfig<R, P, Q, B> | null | undefined;
|
|
22
|
+
private parsedBody;
|
|
22
23
|
constructor(backend: RequestBackend, host: RequestHost, config: RequestConfig<P, Q, B>, computedPath: string, mocking: EndpointMockingConfig<R, P, Q, B> | null | undefined);
|
|
23
24
|
get method(): RequestMethod;
|
|
24
25
|
get api(): Api;
|
|
@@ -27,6 +28,8 @@ export default class RequestContext<R = any, P extends Params | undefined = Para
|
|
|
27
28
|
private initMiddleware;
|
|
28
29
|
private generateKey;
|
|
29
30
|
updateHeaders(newHeaders: Headers): this;
|
|
31
|
+
private parseRequestBody;
|
|
32
|
+
getParsedBody(): any;
|
|
30
33
|
updateQuery(newQuery: Partial<Q>): this;
|
|
31
34
|
triggerEvent(eventType: RequestEvent): Promise<EventResult<R> | undefined>;
|
|
32
35
|
addCanceller(canceler: () => void): void;
|
package/cjs/RequestContext.js
CHANGED
|
@@ -37,6 +37,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
var Utils = require("./Utils");
|
|
40
|
+
var url_1 = require("url");
|
|
40
41
|
var contextIdCounter = 0;
|
|
41
42
|
var RequestContext = /** @class */ (function () {
|
|
42
43
|
function RequestContext(backend, host, config, computedPath, mocking) {
|
|
@@ -49,7 +50,7 @@ var RequestContext = /** @class */ (function () {
|
|
|
49
50
|
this.id = contextIdCounter++;
|
|
50
51
|
this.host = host;
|
|
51
52
|
this.computedConfig = config;
|
|
52
|
-
Utils.assign({}, this.computedConfig.headers
|
|
53
|
+
Utils.assign({}, this.computedConfig.headers);
|
|
53
54
|
this.computedPath = computedPath;
|
|
54
55
|
this.key = this.generateKey();
|
|
55
56
|
this.stats = {
|
|
@@ -59,6 +60,7 @@ var RequestContext = /** @class */ (function () {
|
|
|
59
60
|
this.eventHandlers = {};
|
|
60
61
|
this.mocking = mocking;
|
|
61
62
|
this.initMiddleware();
|
|
63
|
+
this.parseRequestBody();
|
|
62
64
|
}
|
|
63
65
|
Object.defineProperty(RequestContext.prototype, "method", {
|
|
64
66
|
get: function () {
|
|
@@ -111,7 +113,7 @@ var RequestContext = /** @class */ (function () {
|
|
|
111
113
|
var queryKeys = Object.keys(computedConfig.query);
|
|
112
114
|
for (var i = 0; i < queryKeys.length; i++) {
|
|
113
115
|
var queryKey = queryKeys[i];
|
|
114
|
-
queryStrings.push(queryKey
|
|
116
|
+
queryStrings.push("".concat(queryKey, "=").concat(computedConfig.query[queryKey]));
|
|
115
117
|
}
|
|
116
118
|
}
|
|
117
119
|
if (queryStrings.length > 0) {
|
|
@@ -121,8 +123,33 @@ var RequestContext = /** @class */ (function () {
|
|
|
121
123
|
};
|
|
122
124
|
RequestContext.prototype.updateHeaders = function (newHeaders) {
|
|
123
125
|
this.computedConfig.headers = Utils.assign({}, this.computedConfig.headers, newHeaders);
|
|
126
|
+
this.parseRequestBody();
|
|
124
127
|
return this;
|
|
125
128
|
};
|
|
129
|
+
RequestContext.prototype.parseRequestBody = function () {
|
|
130
|
+
if (this.computedConfig.body && this.computedConfig.headers) {
|
|
131
|
+
var contentTypeKey = Object.keys(this.computedConfig.headers).find(function (key) { return key.toLowerCase() === "content-type"; });
|
|
132
|
+
var contentType = contentTypeKey && this.computedConfig.headers[contentTypeKey];
|
|
133
|
+
if (typeof contentType === "string" && contentType.toLowerCase() === "multipart/form-data") {
|
|
134
|
+
var searchParams = new url_1.URLSearchParams();
|
|
135
|
+
for (var _i = 0, _a = Object.keys(this.computedConfig.body); _i < _a.length; _i++) {
|
|
136
|
+
var key = _a[_i];
|
|
137
|
+
var value = this.computedConfig.body[key];
|
|
138
|
+
searchParams.set(key, value === null || value === void 0 ? void 0 : value.toString());
|
|
139
|
+
}
|
|
140
|
+
this.parsedBody = searchParams.toString();
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
this.parsedBody = this.computedConfig.body;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
this.parsedBody = undefined;
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
RequestContext.prototype.getParsedBody = function () {
|
|
151
|
+
return this.parsedBody;
|
|
152
|
+
};
|
|
126
153
|
RequestContext.prototype.updateQuery = function (newQuery) {
|
|
127
154
|
this.computedConfig.query = Utils.assign(this.computedConfig.query || {}, newQuery);
|
|
128
155
|
return this;
|
package/cjs/Requester.js
CHANGED
|
@@ -156,9 +156,9 @@ var makeRequest = function (context) { return __awaiter(void 0, void 0, void 0,
|
|
|
156
156
|
return [4 /*yield*/, parseResponse(context, response_1)];
|
|
157
157
|
case 3:
|
|
158
158
|
parsedResponse = (_b.sent());
|
|
159
|
-
if (!ApiUtils_1.isAcceptableStatus(parsedResponse.status, context.computedConfig.acceptableStatus)) {
|
|
160
|
-
throw RequestError_1.convertToRequestError({
|
|
161
|
-
error: new Error("[api-def] Invalid response status code '"
|
|
159
|
+
if (!(0, ApiUtils_1.isAcceptableStatus)(parsedResponse.status, context.computedConfig.acceptableStatus)) {
|
|
160
|
+
throw (0, RequestError_1.convertToRequestError)({
|
|
161
|
+
error: new Error("[api-def] Invalid response status code '".concat(parsedResponse.status, "'")),
|
|
162
162
|
response: parsedResponse,
|
|
163
163
|
code: RequestError_1.RequestErrorCode.REQUEST_INVALID_STATUS,
|
|
164
164
|
});
|
|
@@ -203,7 +203,7 @@ var makeRequest = function (context) { return __awaiter(void 0, void 0, void 0,
|
|
|
203
203
|
}
|
|
204
204
|
});
|
|
205
205
|
}); };
|
|
206
|
-
return [4 /*yield*/, retry_1.default(performRequest, retryOpts)];
|
|
206
|
+
return [4 /*yield*/, (0, retry_1.default)(performRequest, retryOpts)];
|
|
207
207
|
case 2:
|
|
208
208
|
response = _b.sent();
|
|
209
209
|
return [2 /*return*/, (response)];
|
|
@@ -226,12 +226,12 @@ var parseResponse = function (context, response, error) { return __awaiter(void
|
|
|
226
226
|
return headers;
|
|
227
227
|
}, {});
|
|
228
228
|
contentType = parsedResponse_1.headers["content-type"];
|
|
229
|
-
inferredResponseType = ApiUtils_1.inferResponseType(contentType);
|
|
229
|
+
inferredResponseType = (0, ApiUtils_1.inferResponseType)(contentType);
|
|
230
230
|
if (!error) {
|
|
231
231
|
// expand to array buffer once we support that in inferResponseType
|
|
232
232
|
if (inferredResponseType === "text" && context.responseType === "json") {
|
|
233
|
-
throw RequestError_1.convertToRequestError({
|
|
234
|
-
error: new Error("[api-def] Expected '"
|
|
233
|
+
throw (0, RequestError_1.convertToRequestError)({
|
|
234
|
+
error: new Error("[api-def] Expected '".concat(context.responseType, "' response, got '").concat(inferredResponseType, "' (from 'Content-Type' of '").concat(contentType, "')")),
|
|
235
235
|
code: RequestError_1.RequestErrorCode.REQUEST_MISMATCH_RESPONSE_TYPE,
|
|
236
236
|
response: parsedResponse_1,
|
|
237
237
|
});
|
|
@@ -243,12 +243,12 @@ var parseResponse = function (context, response, error) { return __awaiter(void
|
|
|
243
243
|
data = response.data;
|
|
244
244
|
if (((_a = data.constructor) === null || _a === void 0 ? void 0 : _a.name) === "ArrayBuffer") {
|
|
245
245
|
try {
|
|
246
|
-
decodedData = (response.data = TextDecoding_1.textDecode(data));
|
|
246
|
+
decodedData = (response.data = (0, TextDecoding_1.textDecode)(data));
|
|
247
247
|
response.data = JSON.parse(decodedData);
|
|
248
248
|
}
|
|
249
249
|
catch (e) {
|
|
250
|
-
throw RequestError_1.convertToRequestError({
|
|
251
|
-
error: new Error("[api-def] Expected '"
|
|
250
|
+
throw (0, RequestError_1.convertToRequestError)({
|
|
251
|
+
error: new Error("[api-def] Expected '".concat(context.responseType, "' response, got '").concat(inferredResponseType, "' (from 'Content-Type' of '").concat(contentType, "')")),
|
|
252
252
|
code: RequestError_1.RequestErrorCode.REQUEST_MISMATCH_RESPONSE_TYPE,
|
|
253
253
|
response: parsedResponse_1,
|
|
254
254
|
});
|
|
@@ -267,7 +267,7 @@ var parseError = function (context, rawError) { return __awaiter(void 0, void 0,
|
|
|
267
267
|
return __generator(this, function (_a) {
|
|
268
268
|
switch (_a.label) {
|
|
269
269
|
case 0:
|
|
270
|
-
if (!RequestError_1.isRequestError(rawError)) return [3 /*break*/, 1];
|
|
270
|
+
if (!(0, RequestError_1.isRequestError)(rawError)) return [3 /*break*/, 1];
|
|
271
271
|
error = rawError;
|
|
272
272
|
return [3 /*break*/, 5];
|
|
273
273
|
case 1: return [4 /*yield*/, context.backend.extractResponseFromError(rawError)];
|
|
@@ -280,14 +280,14 @@ var parseError = function (context, rawError) { return __awaiter(void 0, void 0,
|
|
|
280
280
|
errorResponse = _a.sent();
|
|
281
281
|
_a.label = 4;
|
|
282
282
|
case 4:
|
|
283
|
-
code = ApiUtils_1.isNetworkError(rawError) ? RequestError_1.RequestErrorCode.REQUEST_NETWORK_ERROR : RequestError_1.RequestErrorCode.MISC_UNKNOWN_ERROR;
|
|
283
|
+
code = (0, ApiUtils_1.isNetworkError)(rawError) ? RequestError_1.RequestErrorCode.REQUEST_NETWORK_ERROR : RequestError_1.RequestErrorCode.MISC_UNKNOWN_ERROR;
|
|
284
284
|
if (errorResponse) {
|
|
285
|
-
if (!ApiUtils_1.isAcceptableStatus(errorResponse.status, context.computedConfig.acceptableStatus)) {
|
|
285
|
+
if (!(0, ApiUtils_1.isAcceptableStatus)(errorResponse.status, context.computedConfig.acceptableStatus)) {
|
|
286
286
|
code = RequestError_1.RequestErrorCode.REQUEST_INVALID_STATUS;
|
|
287
287
|
}
|
|
288
288
|
}
|
|
289
289
|
errorInfo = context.backend.getErrorInfo(rawError, errorResponse);
|
|
290
|
-
error = RequestError_1.convertToRequestError(__assign({ error: rawError, response: errorResponse, code: code }, errorInfo));
|
|
290
|
+
error = (0, RequestError_1.convertToRequestError)(__assign({ error: rawError, response: errorResponse, code: code }, errorInfo));
|
|
291
291
|
_a.label = 5;
|
|
292
292
|
case 5: return [2 /*return*/, error];
|
|
293
293
|
}
|
|
@@ -2,7 +2,7 @@ import RequestBackend, { ConvertedApiResponse, RequestBackendErrorInfo, RequestO
|
|
|
2
2
|
import { ApiResponse } from "../ApiTypes";
|
|
3
3
|
import type { AxiosError, AxiosResponse } from "axios";
|
|
4
4
|
import RequestContext from "../RequestContext";
|
|
5
|
-
export declare const isAxiosError: (error: Error) => error is AxiosError<any>;
|
|
5
|
+
export declare const isAxiosError: (error: Error) => error is AxiosError<unknown, any>;
|
|
6
6
|
export default class AxiosRequestBackend implements RequestBackend<AxiosResponse> {
|
|
7
7
|
readonly id = "axios";
|
|
8
8
|
constructor(axiosLibrary: any);
|
|
@@ -51,7 +51,7 @@ var AxiosRequestBackend = /** @class */ (function () {
|
|
|
51
51
|
AxiosRequestBackend.prototype.extractResponseFromError = function (error) {
|
|
52
52
|
return __awaiter(this, void 0, void 0, function () {
|
|
53
53
|
return __generator(this, function (_a) {
|
|
54
|
-
if (exports.isAxiosError(error)) {
|
|
54
|
+
if ((0, exports.isAxiosError)(error)) {
|
|
55
55
|
return [2 /*return*/, error.response ? error.response : null];
|
|
56
56
|
}
|
|
57
57
|
return [2 /*return*/, undefined];
|
|
@@ -77,7 +77,7 @@ var AxiosRequestBackend = /** @class */ (function () {
|
|
|
77
77
|
method: context.method,
|
|
78
78
|
baseURL: context.baseUrl,
|
|
79
79
|
url: context.computedPath,
|
|
80
|
-
data:
|
|
80
|
+
data: context.getParsedBody() || {},
|
|
81
81
|
params: computedConfig.query || {},
|
|
82
82
|
headers: computedConfig.headers || {},
|
|
83
83
|
responseType: context.responseType,
|
|
@@ -3,7 +3,7 @@ import { ApiResponse } from "../ApiTypes";
|
|
|
3
3
|
import RequestContext from "../RequestContext";
|
|
4
4
|
import { Fetch } from "../Utils";
|
|
5
5
|
export default class FetchRequestBackend implements RequestBackend<Response> {
|
|
6
|
-
fetch: (((input: RequestInfo, init?: RequestInit | undefined) => Promise<Response>) & typeof fetch) | undefined;
|
|
6
|
+
fetch: (((input: RequestInfo | URL, init?: RequestInit | undefined) => Promise<Response>) & typeof fetch) | undefined;
|
|
7
7
|
readonly id = "fetch";
|
|
8
8
|
constructor(fetchLibrary?: Fetch);
|
|
9
9
|
extractResponseFromError(error: Error): Promise<Response | null | undefined>;
|
|
@@ -64,7 +64,7 @@ var FetchError = /** @class */ (function (_super) {
|
|
|
64
64
|
}(Error));
|
|
65
65
|
var FetchRequestBackend = /** @class */ (function () {
|
|
66
66
|
function FetchRequestBackend(fetchLibrary) {
|
|
67
|
-
this.fetch = Utils_1.getGlobalFetch();
|
|
67
|
+
this.fetch = (0, Utils_1.getGlobalFetch)();
|
|
68
68
|
this.id = "fetch";
|
|
69
69
|
if (fetchLibrary !== undefined) {
|
|
70
70
|
this.fetch = fetchLibrary;
|
|
@@ -89,7 +89,7 @@ var FetchRequestBackend = /** @class */ (function () {
|
|
|
89
89
|
switch (_b.label) {
|
|
90
90
|
case 0:
|
|
91
91
|
contentType = response.headers.get("Content-Type");
|
|
92
|
-
responseType = ApiUtils_1.inferResponseType(contentType);
|
|
92
|
+
responseType = (0, ApiUtils_1.inferResponseType)(contentType);
|
|
93
93
|
if (!!response.__text) return [3 /*break*/, 2];
|
|
94
94
|
_a = response;
|
|
95
95
|
return [4 /*yield*/, response.clone().text()];
|
|
@@ -115,7 +115,7 @@ var FetchRequestBackend = /** @class */ (function () {
|
|
|
115
115
|
case 6: return [3 /*break*/, 8];
|
|
116
116
|
case 7:
|
|
117
117
|
error_1 = _b.sent();
|
|
118
|
-
throw Object.assign(new Error("[api-def] Invalid '"
|
|
118
|
+
throw Object.assign(new Error("[api-def] Invalid '".concat(context.responseType, "' response, got: '").concat(response.__text, "'")), {
|
|
119
119
|
response: response,
|
|
120
120
|
});
|
|
121
121
|
case 8: return [2 /*return*/, {
|
|
@@ -161,7 +161,8 @@ var FetchRequestBackend = /** @class */ (function () {
|
|
|
161
161
|
var abortSignal = abortController ? abortController.signal : undefined;
|
|
162
162
|
var softAbort = false;
|
|
163
163
|
var responded = false;
|
|
164
|
-
var
|
|
164
|
+
var body = context.getParsedBody();
|
|
165
|
+
var bodyJsonify = body !== null && typeof body === "object";
|
|
165
166
|
var headers = Utils.assign({
|
|
166
167
|
// logic from axios
|
|
167
168
|
"Content-Type": bodyJsonify ? "application/json;charset=utf-8" : "application/x-www-form-urlencoded",
|
|
@@ -175,7 +176,7 @@ var FetchRequestBackend = /** @class */ (function () {
|
|
|
175
176
|
}, {});
|
|
176
177
|
var promise = this.fetch(url.href, {
|
|
177
178
|
method: context.method,
|
|
178
|
-
body: bodyJsonify ? JSON.stringify(
|
|
179
|
+
body: bodyJsonify ? JSON.stringify(body) : body,
|
|
179
180
|
headers: parsedHeaders,
|
|
180
181
|
mode: "cors",
|
|
181
182
|
signal: abortSignal,
|
|
@@ -65,25 +65,26 @@ var MockRequestBackend = /** @class */ (function () {
|
|
|
65
65
|
MockRequestBackend.prototype.runRequest = function (context) {
|
|
66
66
|
var _a, _b, _c, _d;
|
|
67
67
|
return __awaiter(this, void 0, void 0, function () {
|
|
68
|
-
var mockingFunc, req, res, delay, delayMs, min, max, _e;
|
|
68
|
+
var mockingFunc, req, res, delay, delayMs, min, max, _e, parsedHeaders;
|
|
69
69
|
return __generator(this, function (_f) {
|
|
70
70
|
switch (_f.label) {
|
|
71
71
|
case 0:
|
|
72
72
|
mockingFunc = (_a = context.mocking) === null || _a === void 0 ? void 0 : _a.handler;
|
|
73
73
|
if (!mockingFunc) {
|
|
74
|
-
throw RequestError_1.convertToRequestError({
|
|
74
|
+
throw (0, RequestError_1.convertToRequestError)({
|
|
75
75
|
error: new Error("[api-def] Attempted to run mocked request without mocking function"),
|
|
76
76
|
code: RequestError_1.RequestErrorCode.REQUEST_INVALID_CONFIG,
|
|
77
77
|
});
|
|
78
78
|
}
|
|
79
79
|
req = {
|
|
80
|
-
body: context.
|
|
80
|
+
body: context.getParsedBody(),
|
|
81
81
|
params: (_b = context.computedConfig.params) !== null && _b !== void 0 ? _b : {},
|
|
82
82
|
query: context.computedConfig.query,
|
|
83
83
|
headers: (_c = context.computedConfig.headers) !== null && _c !== void 0 ? _c : {},
|
|
84
84
|
};
|
|
85
85
|
res = {
|
|
86
86
|
statusCode: -1,
|
|
87
|
+
headers: {},
|
|
87
88
|
response: undefined,
|
|
88
89
|
status: function (statusCode) {
|
|
89
90
|
res.statusCode = statusCode;
|
|
@@ -91,6 +92,9 @@ var MockRequestBackend = /** @class */ (function () {
|
|
|
91
92
|
},
|
|
92
93
|
send: function (response) {
|
|
93
94
|
res.response = response;
|
|
95
|
+
if (response && typeof response === "object") {
|
|
96
|
+
res.headers["Content-Type"] = "application/json";
|
|
97
|
+
}
|
|
94
98
|
return res;
|
|
95
99
|
},
|
|
96
100
|
};
|
|
@@ -103,12 +107,12 @@ var MockRequestBackend = /** @class */ (function () {
|
|
|
103
107
|
else {
|
|
104
108
|
min = delay[0], max = delay[1];
|
|
105
109
|
if (min > max) {
|
|
106
|
-
throw RequestError_1.convertToRequestError({
|
|
110
|
+
throw (0, RequestError_1.convertToRequestError)({
|
|
107
111
|
error: new Error("[api-def] Min delay cannot be greater than max delay"),
|
|
108
112
|
code: RequestError_1.RequestErrorCode.REQUEST_INVALID_CONFIG,
|
|
109
113
|
});
|
|
110
114
|
}
|
|
111
|
-
delayMs = Utils_1.randInt(min, max);
|
|
115
|
+
delayMs = (0, Utils_1.randInt)(min, max);
|
|
112
116
|
}
|
|
113
117
|
_e = Utils_1.delayThenReturn;
|
|
114
118
|
return [4 /*yield*/, mockingFunc(req, res)];
|
|
@@ -122,13 +126,17 @@ var MockRequestBackend = /** @class */ (function () {
|
|
|
122
126
|
_f.label = 5;
|
|
123
127
|
case 5:
|
|
124
128
|
if (res.response === undefined) {
|
|
125
|
-
throw RequestError_1.convertToRequestError({
|
|
129
|
+
throw (0, RequestError_1.convertToRequestError)({
|
|
126
130
|
error: new Error("[api-def] Mocked API did not respond"),
|
|
127
131
|
code: RequestError_1.RequestErrorCode.REQUEST_INVALID_CONFIG,
|
|
128
132
|
});
|
|
129
133
|
}
|
|
134
|
+
parsedHeaders = Object.keys(res.headers).reduce(function (parsedHeaders, key) {
|
|
135
|
+
parsedHeaders[key] = res.headers[key].toString();
|
|
136
|
+
return parsedHeaders;
|
|
137
|
+
}, {});
|
|
130
138
|
return [2 /*return*/, {
|
|
131
|
-
headers:
|
|
139
|
+
headers: parsedHeaders,
|
|
132
140
|
data: res.response,
|
|
133
141
|
status: res.statusCode,
|
|
134
142
|
}];
|
package/cjs/cache/Caching.d.ts
CHANGED
|
@@ -6,5 +6,5 @@ export interface CacheEntry {
|
|
|
6
6
|
data: any;
|
|
7
7
|
expiry: number | null;
|
|
8
8
|
}
|
|
9
|
-
export declare const setCachedItem: <T>(key: string, value: T, expiry?: number
|
|
9
|
+
export declare const setCachedItem: <T>(key: string, value: T, expiry?: number) => Promise<T>;
|
|
10
10
|
export declare const getCachedItem: <T = any>(key: string) => Promise<T | undefined>;
|
package/cjs/index.js
CHANGED
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
4
|
if (k2 === undefined) k2 = k;
|
|
5
|
-
Object.
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
6
10
|
}) : (function(o, m, k, k2) {
|
|
7
11
|
if (k2 === undefined) k2 = k;
|
|
8
12
|
o[k2] = m[k];
|
|
@@ -30,12 +30,12 @@ var diagnoseError = function (error) {
|
|
|
30
30
|
var _a = error.response, status = _a.status, data = _a.data;
|
|
31
31
|
var code = data === null || data === void 0 ? void 0 : data.code;
|
|
32
32
|
return {
|
|
33
|
-
message: "responded with "
|
|
33
|
+
message: "responded with ".concat(status).concat(code ? " (".concat(code, ")") : ""),
|
|
34
34
|
response: error.response,
|
|
35
35
|
};
|
|
36
36
|
};
|
|
37
37
|
var formatTime = function (time) {
|
|
38
|
-
return Utils.padNumber(time.getHours(), 2)
|
|
38
|
+
return "".concat(Utils.padNumber(time.getHours(), 2), ":").concat(Utils.padNumber(time.getMinutes(), 2), ":").concat(Utils.padNumber(time.getSeconds(), 2), ".").concat(Utils.padNumber(time.getMilliseconds(), 3));
|
|
39
39
|
};
|
|
40
40
|
var log = function (context, type, message, config, objects) {
|
|
41
41
|
if (typeof config.predicate === "function" && !config.predicate()) {
|
|
@@ -45,10 +45,10 @@ var log = function (context, type, message, config, objects) {
|
|
|
45
45
|
var color = COLOR_MAP[type];
|
|
46
46
|
var timestamp = formatTime(new Date());
|
|
47
47
|
var args = [
|
|
48
|
-
"%cnetwork %c["
|
|
48
|
+
"%cnetwork %c[".concat(context.api.name, "] ").concat(context.method.toUpperCase(), " ").concat(computedPath, " %c").concat(message, " %c@ ").concat(timestamp),
|
|
49
49
|
"color:gray",
|
|
50
50
|
"color:auto",
|
|
51
|
-
"color:"
|
|
51
|
+
"color:".concat(color),
|
|
52
52
|
"color:gray",
|
|
53
53
|
];
|
|
54
54
|
/* eslint-disable-next-line no-console */
|
|
@@ -68,18 +68,18 @@ var LoggingMiddleware = function (config) {
|
|
|
68
68
|
_a[ApiConstants_1.RequestEvent.Success] = function (context) {
|
|
69
69
|
var _a;
|
|
70
70
|
var cacheSource = context.cacheInfo.source;
|
|
71
|
-
log(context, LogType.Success, "responded with "
|
|
71
|
+
log(context, LogType.Success, "responded with ".concat((_a = context.response) === null || _a === void 0 ? void 0 : _a.status).concat(cacheSource ? " (cached by ".concat(cacheSource, ")") : ""), config);
|
|
72
72
|
},
|
|
73
73
|
_a[ApiConstants_1.RequestEvent.Error] = function (context) {
|
|
74
74
|
if (context.error) {
|
|
75
75
|
var _a = diagnoseError(context.error), error = _a.error, message = _a.message;
|
|
76
|
-
log(context, LogType.Warn, "error on attempt "
|
|
76
|
+
log(context, LogType.Warn, "error on attempt ".concat(context.stats.attempt, " - ").concat(message), config, { error: error });
|
|
77
77
|
}
|
|
78
78
|
},
|
|
79
79
|
_a[ApiConstants_1.RequestEvent.UnrecoverableError] = function (context) {
|
|
80
80
|
if (context.error) {
|
|
81
81
|
var _a = diagnoseError(context.error), error = _a.error, message = _a.message;
|
|
82
|
-
log(context, LogType.Error, "failed - "
|
|
82
|
+
log(context, LogType.Error, "failed - ".concat(message), config, {
|
|
83
83
|
error: error,
|
|
84
84
|
});
|
|
85
85
|
}
|
package/esm/Api.js
CHANGED
|
@@ -71,7 +71,7 @@ var HotRequestHost = /** @class */ (function () {
|
|
|
71
71
|
return Utils.assign({}, apiDefaults, config);
|
|
72
72
|
};
|
|
73
73
|
HotRequestHost.prototype.computePath = function (path, config) {
|
|
74
|
-
return path.startsWith("/") ? path : "/"
|
|
74
|
+
return path.startsWith("/") ? path : "/".concat(path);
|
|
75
75
|
};
|
|
76
76
|
return HotRequestHost;
|
|
77
77
|
}());
|
package/esm/ApiUtils.d.ts
CHANGED
|
@@ -2,5 +2,5 @@ import { AcceptableStatus, CancelledRequestError } from "./ApiTypes";
|
|
|
2
2
|
import { ResponseType } from "./ApiConstants";
|
|
3
3
|
export declare const isCancelledError: (error: Error) => error is CancelledRequestError;
|
|
4
4
|
export declare const isNetworkError: (error: Error) => boolean;
|
|
5
|
-
export declare const isAcceptableStatus: (status: number, acceptableStatus?: AcceptableStatus[]
|
|
5
|
+
export declare const isAcceptableStatus: (status: number, acceptableStatus?: AcceptableStatus[]) => boolean;
|
|
6
6
|
export declare const inferResponseType: (contentType: string | null | undefined) => ResponseType;
|
package/esm/Endpoint.js
CHANGED
|
@@ -60,7 +60,7 @@ var Endpoint = /** @class */ (function () {
|
|
|
60
60
|
mockingEnabled = (_a = (typeof apiMocking.enabled === "function" ? apiMocking.enabled() : apiMocking.enabled)) !== null && _a !== void 0 ? _a : false;
|
|
61
61
|
if (mockingEnabled) {
|
|
62
62
|
if (!((_b = this.mocking) === null || _b === void 0 ? void 0 : _b.handler)) {
|
|
63
|
-
throw new Error("[api-def] Endpoint for '"
|
|
63
|
+
throw new Error("[api-def] Endpoint for '".concat(this.path, "' has no mocking"));
|
|
64
64
|
}
|
|
65
65
|
mock = true;
|
|
66
66
|
}
|
|
@@ -70,16 +70,16 @@ var Endpoint = /** @class */ (function () {
|
|
|
70
70
|
});
|
|
71
71
|
};
|
|
72
72
|
Endpoint.prototype.computePath = function (path, request) {
|
|
73
|
-
var computedPath = path.startsWith("/") ? path : "/"
|
|
73
|
+
var computedPath = path.startsWith("/") ? path : "/".concat(path);
|
|
74
74
|
if (request.params) {
|
|
75
75
|
var keys = Object.keys(request.params);
|
|
76
76
|
for (var i = 0; i < keys.length; i++) {
|
|
77
77
|
var argName = keys[i];
|
|
78
|
-
computedPath = computedPath.replace(":"
|
|
78
|
+
computedPath = computedPath.replace(":".concat(argName), request.params[argName]);
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
if (computedPath.includes(":")) {
|
|
82
|
-
throw new Error("[api-def] Not all path params have been resolved: '"
|
|
82
|
+
throw new Error("[api-def] Not all path params have been resolved: '".concat(computedPath, "'"));
|
|
83
83
|
}
|
|
84
84
|
return computedPath;
|
|
85
85
|
};
|
package/esm/MockingTypes.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export interface MockRequest<R = any, P extends Params | undefined = Params | un
|
|
|
11
11
|
export interface MockResponse<R = any, P extends Params | undefined = Params | undefined, Q extends Query | undefined = Query | undefined, B extends Body | undefined = Body | undefined> {
|
|
12
12
|
statusCode: number;
|
|
13
13
|
response: R | undefined;
|
|
14
|
+
headers: Headers;
|
|
14
15
|
status(statusCode: number): this;
|
|
15
16
|
send(response: R): this;
|
|
16
17
|
}
|
package/esm/RequestContext.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export default class RequestContext<R = any, P extends Params | undefined = Para
|
|
|
19
19
|
cancelled: boolean;
|
|
20
20
|
readonly computedConfig: RequestConfig<P, Q, B>;
|
|
21
21
|
readonly mocking: EndpointMockingConfig<R, P, Q, B> | null | undefined;
|
|
22
|
+
private parsedBody;
|
|
22
23
|
constructor(backend: RequestBackend, host: RequestHost, config: RequestConfig<P, Q, B>, computedPath: string, mocking: EndpointMockingConfig<R, P, Q, B> | null | undefined);
|
|
23
24
|
get method(): RequestMethod;
|
|
24
25
|
get api(): Api;
|
|
@@ -27,6 +28,8 @@ export default class RequestContext<R = any, P extends Params | undefined = Para
|
|
|
27
28
|
private initMiddleware;
|
|
28
29
|
private generateKey;
|
|
29
30
|
updateHeaders(newHeaders: Headers): this;
|
|
31
|
+
private parseRequestBody;
|
|
32
|
+
getParsedBody(): any;
|
|
30
33
|
updateQuery(newQuery: Partial<Q>): this;
|
|
31
34
|
triggerEvent(eventType: RequestEvent): Promise<EventResult<R> | undefined>;
|
|
32
35
|
addCanceller(canceler: () => void): void;
|
package/esm/RequestContext.js
CHANGED
|
@@ -35,6 +35,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
35
35
|
}
|
|
36
36
|
};
|
|
37
37
|
import * as Utils from "./Utils";
|
|
38
|
+
import { URLSearchParams } from "url";
|
|
38
39
|
var contextIdCounter = 0;
|
|
39
40
|
var RequestContext = /** @class */ (function () {
|
|
40
41
|
function RequestContext(backend, host, config, computedPath, mocking) {
|
|
@@ -47,7 +48,7 @@ var RequestContext = /** @class */ (function () {
|
|
|
47
48
|
this.id = contextIdCounter++;
|
|
48
49
|
this.host = host;
|
|
49
50
|
this.computedConfig = config;
|
|
50
|
-
Utils.assign({}, this.computedConfig.headers
|
|
51
|
+
Utils.assign({}, this.computedConfig.headers);
|
|
51
52
|
this.computedPath = computedPath;
|
|
52
53
|
this.key = this.generateKey();
|
|
53
54
|
this.stats = {
|
|
@@ -57,6 +58,7 @@ var RequestContext = /** @class */ (function () {
|
|
|
57
58
|
this.eventHandlers = {};
|
|
58
59
|
this.mocking = mocking;
|
|
59
60
|
this.initMiddleware();
|
|
61
|
+
this.parseRequestBody();
|
|
60
62
|
}
|
|
61
63
|
Object.defineProperty(RequestContext.prototype, "method", {
|
|
62
64
|
get: function () {
|
|
@@ -109,7 +111,7 @@ var RequestContext = /** @class */ (function () {
|
|
|
109
111
|
var queryKeys = Object.keys(computedConfig.query);
|
|
110
112
|
for (var i = 0; i < queryKeys.length; i++) {
|
|
111
113
|
var queryKey = queryKeys[i];
|
|
112
|
-
queryStrings.push(queryKey
|
|
114
|
+
queryStrings.push("".concat(queryKey, "=").concat(computedConfig.query[queryKey]));
|
|
113
115
|
}
|
|
114
116
|
}
|
|
115
117
|
if (queryStrings.length > 0) {
|
|
@@ -119,8 +121,33 @@ var RequestContext = /** @class */ (function () {
|
|
|
119
121
|
};
|
|
120
122
|
RequestContext.prototype.updateHeaders = function (newHeaders) {
|
|
121
123
|
this.computedConfig.headers = Utils.assign({}, this.computedConfig.headers, newHeaders);
|
|
124
|
+
this.parseRequestBody();
|
|
122
125
|
return this;
|
|
123
126
|
};
|
|
127
|
+
RequestContext.prototype.parseRequestBody = function () {
|
|
128
|
+
if (this.computedConfig.body && this.computedConfig.headers) {
|
|
129
|
+
var contentTypeKey = Object.keys(this.computedConfig.headers).find(function (key) { return key.toLowerCase() === "content-type"; });
|
|
130
|
+
var contentType = contentTypeKey && this.computedConfig.headers[contentTypeKey];
|
|
131
|
+
if (typeof contentType === "string" && contentType.toLowerCase() === "multipart/form-data") {
|
|
132
|
+
var searchParams = new URLSearchParams();
|
|
133
|
+
for (var _i = 0, _a = Object.keys(this.computedConfig.body); _i < _a.length; _i++) {
|
|
134
|
+
var key = _a[_i];
|
|
135
|
+
var value = this.computedConfig.body[key];
|
|
136
|
+
searchParams.set(key, value === null || value === void 0 ? void 0 : value.toString());
|
|
137
|
+
}
|
|
138
|
+
this.parsedBody = searchParams.toString();
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
this.parsedBody = this.computedConfig.body;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
this.parsedBody = undefined;
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
RequestContext.prototype.getParsedBody = function () {
|
|
149
|
+
return this.parsedBody;
|
|
150
|
+
};
|
|
124
151
|
RequestContext.prototype.updateQuery = function (newQuery) {
|
|
125
152
|
this.computedConfig.query = Utils.assign(this.computedConfig.query || {}, newQuery);
|
|
126
153
|
return this;
|
package/esm/Requester.js
CHANGED
|
@@ -154,7 +154,7 @@ var makeRequest = function (context) { return __awaiter(void 0, void 0, void 0,
|
|
|
154
154
|
parsedResponse = (_b.sent());
|
|
155
155
|
if (!isAcceptableStatus(parsedResponse.status, context.computedConfig.acceptableStatus)) {
|
|
156
156
|
throw convertToRequestError({
|
|
157
|
-
error: new Error("[api-def] Invalid response status code '"
|
|
157
|
+
error: new Error("[api-def] Invalid response status code '".concat(parsedResponse.status, "'")),
|
|
158
158
|
response: parsedResponse,
|
|
159
159
|
code: RequestErrorCode.REQUEST_INVALID_STATUS,
|
|
160
160
|
});
|
|
@@ -227,7 +227,7 @@ var parseResponse = function (context, response, error) { return __awaiter(void
|
|
|
227
227
|
// expand to array buffer once we support that in inferResponseType
|
|
228
228
|
if (inferredResponseType === "text" && context.responseType === "json") {
|
|
229
229
|
throw convertToRequestError({
|
|
230
|
-
error: new Error("[api-def] Expected '"
|
|
230
|
+
error: new Error("[api-def] Expected '".concat(context.responseType, "' response, got '").concat(inferredResponseType, "' (from 'Content-Type' of '").concat(contentType, "')")),
|
|
231
231
|
code: RequestErrorCode.REQUEST_MISMATCH_RESPONSE_TYPE,
|
|
232
232
|
response: parsedResponse_1,
|
|
233
233
|
});
|
|
@@ -244,7 +244,7 @@ var parseResponse = function (context, response, error) { return __awaiter(void
|
|
|
244
244
|
}
|
|
245
245
|
catch (e) {
|
|
246
246
|
throw convertToRequestError({
|
|
247
|
-
error: new Error("[api-def] Expected '"
|
|
247
|
+
error: new Error("[api-def] Expected '".concat(context.responseType, "' response, got '").concat(inferredResponseType, "' (from 'Content-Type' of '").concat(contentType, "')")),
|
|
248
248
|
code: RequestErrorCode.REQUEST_MISMATCH_RESPONSE_TYPE,
|
|
249
249
|
response: parsedResponse_1,
|
|
250
250
|
});
|
|
@@ -2,7 +2,7 @@ import RequestBackend, { ConvertedApiResponse, RequestBackendErrorInfo, RequestO
|
|
|
2
2
|
import { ApiResponse } from "../ApiTypes";
|
|
3
3
|
import type { AxiosError, AxiosResponse } from "axios";
|
|
4
4
|
import RequestContext from "../RequestContext";
|
|
5
|
-
export declare const isAxiosError: (error: Error) => error is AxiosError<any>;
|
|
5
|
+
export declare const isAxiosError: (error: Error) => error is AxiosError<unknown, any>;
|
|
6
6
|
export default class AxiosRequestBackend implements RequestBackend<AxiosResponse> {
|
|
7
7
|
readonly id = "axios";
|
|
8
8
|
constructor(axiosLibrary: any);
|
|
@@ -73,7 +73,7 @@ var AxiosRequestBackend = /** @class */ (function () {
|
|
|
73
73
|
method: context.method,
|
|
74
74
|
baseURL: context.baseUrl,
|
|
75
75
|
url: context.computedPath,
|
|
76
|
-
data:
|
|
76
|
+
data: context.getParsedBody() || {},
|
|
77
77
|
params: computedConfig.query || {},
|
|
78
78
|
headers: computedConfig.headers || {},
|
|
79
79
|
responseType: context.responseType,
|
|
@@ -3,7 +3,7 @@ import { ApiResponse } from "../ApiTypes";
|
|
|
3
3
|
import RequestContext from "../RequestContext";
|
|
4
4
|
import { Fetch } from "../Utils";
|
|
5
5
|
export default class FetchRequestBackend implements RequestBackend<Response> {
|
|
6
|
-
fetch: (((input: RequestInfo, init?: RequestInit | undefined) => Promise<Response>) & typeof fetch) | undefined;
|
|
6
|
+
fetch: (((input: RequestInfo | URL, init?: RequestInit | undefined) => Promise<Response>) & typeof fetch) | undefined;
|
|
7
7
|
readonly id = "fetch";
|
|
8
8
|
constructor(fetchLibrary?: Fetch);
|
|
9
9
|
extractResponseFromError(error: Error): Promise<Response | null | undefined>;
|
|
@@ -113,7 +113,7 @@ var FetchRequestBackend = /** @class */ (function () {
|
|
|
113
113
|
case 6: return [3 /*break*/, 8];
|
|
114
114
|
case 7:
|
|
115
115
|
error_1 = _b.sent();
|
|
116
|
-
throw Object.assign(new Error("[api-def] Invalid '"
|
|
116
|
+
throw Object.assign(new Error("[api-def] Invalid '".concat(context.responseType, "' response, got: '").concat(response.__text, "'")), {
|
|
117
117
|
response: response,
|
|
118
118
|
});
|
|
119
119
|
case 8: return [2 /*return*/, {
|
|
@@ -159,7 +159,8 @@ var FetchRequestBackend = /** @class */ (function () {
|
|
|
159
159
|
var abortSignal = abortController ? abortController.signal : undefined;
|
|
160
160
|
var softAbort = false;
|
|
161
161
|
var responded = false;
|
|
162
|
-
var
|
|
162
|
+
var body = context.getParsedBody();
|
|
163
|
+
var bodyJsonify = body !== null && typeof body === "object";
|
|
163
164
|
var headers = Utils.assign({
|
|
164
165
|
// logic from axios
|
|
165
166
|
"Content-Type": bodyJsonify ? "application/json;charset=utf-8" : "application/x-www-form-urlencoded",
|
|
@@ -173,7 +174,7 @@ var FetchRequestBackend = /** @class */ (function () {
|
|
|
173
174
|
}, {});
|
|
174
175
|
var promise = this.fetch(url.href, {
|
|
175
176
|
method: context.method,
|
|
176
|
-
body: bodyJsonify ? JSON.stringify(
|
|
177
|
+
body: bodyJsonify ? JSON.stringify(body) : body,
|
|
177
178
|
headers: parsedHeaders,
|
|
178
179
|
mode: "cors",
|
|
179
180
|
signal: abortSignal,
|
|
@@ -63,7 +63,7 @@ var MockRequestBackend = /** @class */ (function () {
|
|
|
63
63
|
MockRequestBackend.prototype.runRequest = function (context) {
|
|
64
64
|
var _a, _b, _c, _d;
|
|
65
65
|
return __awaiter(this, void 0, void 0, function () {
|
|
66
|
-
var mockingFunc, req, res, delay, delayMs, min, max, _e;
|
|
66
|
+
var mockingFunc, req, res, delay, delayMs, min, max, _e, parsedHeaders;
|
|
67
67
|
return __generator(this, function (_f) {
|
|
68
68
|
switch (_f.label) {
|
|
69
69
|
case 0:
|
|
@@ -75,13 +75,14 @@ var MockRequestBackend = /** @class */ (function () {
|
|
|
75
75
|
});
|
|
76
76
|
}
|
|
77
77
|
req = {
|
|
78
|
-
body: context.
|
|
78
|
+
body: context.getParsedBody(),
|
|
79
79
|
params: (_b = context.computedConfig.params) !== null && _b !== void 0 ? _b : {},
|
|
80
80
|
query: context.computedConfig.query,
|
|
81
81
|
headers: (_c = context.computedConfig.headers) !== null && _c !== void 0 ? _c : {},
|
|
82
82
|
};
|
|
83
83
|
res = {
|
|
84
84
|
statusCode: -1,
|
|
85
|
+
headers: {},
|
|
85
86
|
response: undefined,
|
|
86
87
|
status: function (statusCode) {
|
|
87
88
|
res.statusCode = statusCode;
|
|
@@ -89,6 +90,9 @@ var MockRequestBackend = /** @class */ (function () {
|
|
|
89
90
|
},
|
|
90
91
|
send: function (response) {
|
|
91
92
|
res.response = response;
|
|
93
|
+
if (response && typeof response === "object") {
|
|
94
|
+
res.headers["Content-Type"] = "application/json";
|
|
95
|
+
}
|
|
92
96
|
return res;
|
|
93
97
|
},
|
|
94
98
|
};
|
|
@@ -125,8 +129,12 @@ var MockRequestBackend = /** @class */ (function () {
|
|
|
125
129
|
code: RequestErrorCode.REQUEST_INVALID_CONFIG,
|
|
126
130
|
});
|
|
127
131
|
}
|
|
132
|
+
parsedHeaders = Object.keys(res.headers).reduce(function (parsedHeaders, key) {
|
|
133
|
+
parsedHeaders[key] = res.headers[key].toString();
|
|
134
|
+
return parsedHeaders;
|
|
135
|
+
}, {});
|
|
128
136
|
return [2 /*return*/, {
|
|
129
|
-
headers:
|
|
137
|
+
headers: parsedHeaders,
|
|
130
138
|
data: res.response,
|
|
131
139
|
status: res.statusCode,
|
|
132
140
|
}];
|
package/esm/cache/Caching.d.ts
CHANGED
|
@@ -6,5 +6,5 @@ export interface CacheEntry {
|
|
|
6
6
|
data: any;
|
|
7
7
|
expiry: number | null;
|
|
8
8
|
}
|
|
9
|
-
export declare const setCachedItem: <T>(key: string, value: T, expiry?: number
|
|
9
|
+
export declare const setCachedItem: <T>(key: string, value: T, expiry?: number) => Promise<T>;
|
|
10
10
|
export declare const getCachedItem: <T = any>(key: string) => Promise<T | undefined>;
|
|
@@ -28,12 +28,12 @@ var diagnoseError = function (error) {
|
|
|
28
28
|
var _a = error.response, status = _a.status, data = _a.data;
|
|
29
29
|
var code = data === null || data === void 0 ? void 0 : data.code;
|
|
30
30
|
return {
|
|
31
|
-
message: "responded with "
|
|
31
|
+
message: "responded with ".concat(status).concat(code ? " (".concat(code, ")") : ""),
|
|
32
32
|
response: error.response,
|
|
33
33
|
};
|
|
34
34
|
};
|
|
35
35
|
var formatTime = function (time) {
|
|
36
|
-
return Utils.padNumber(time.getHours(), 2)
|
|
36
|
+
return "".concat(Utils.padNumber(time.getHours(), 2), ":").concat(Utils.padNumber(time.getMinutes(), 2), ":").concat(Utils.padNumber(time.getSeconds(), 2), ".").concat(Utils.padNumber(time.getMilliseconds(), 3));
|
|
37
37
|
};
|
|
38
38
|
var log = function (context, type, message, config, objects) {
|
|
39
39
|
if (typeof config.predicate === "function" && !config.predicate()) {
|
|
@@ -43,10 +43,10 @@ var log = function (context, type, message, config, objects) {
|
|
|
43
43
|
var color = COLOR_MAP[type];
|
|
44
44
|
var timestamp = formatTime(new Date());
|
|
45
45
|
var args = [
|
|
46
|
-
"%cnetwork %c["
|
|
46
|
+
"%cnetwork %c[".concat(context.api.name, "] ").concat(context.method.toUpperCase(), " ").concat(computedPath, " %c").concat(message, " %c@ ").concat(timestamp),
|
|
47
47
|
"color:gray",
|
|
48
48
|
"color:auto",
|
|
49
|
-
"color:"
|
|
49
|
+
"color:".concat(color),
|
|
50
50
|
"color:gray",
|
|
51
51
|
];
|
|
52
52
|
/* eslint-disable-next-line no-console */
|
|
@@ -66,18 +66,18 @@ var LoggingMiddleware = function (config) {
|
|
|
66
66
|
_a[RequestEvent.Success] = function (context) {
|
|
67
67
|
var _a;
|
|
68
68
|
var cacheSource = context.cacheInfo.source;
|
|
69
|
-
log(context, LogType.Success, "responded with "
|
|
69
|
+
log(context, LogType.Success, "responded with ".concat((_a = context.response) === null || _a === void 0 ? void 0 : _a.status).concat(cacheSource ? " (cached by ".concat(cacheSource, ")") : ""), config);
|
|
70
70
|
},
|
|
71
71
|
_a[RequestEvent.Error] = function (context) {
|
|
72
72
|
if (context.error) {
|
|
73
73
|
var _a = diagnoseError(context.error), error = _a.error, message = _a.message;
|
|
74
|
-
log(context, LogType.Warn, "error on attempt "
|
|
74
|
+
log(context, LogType.Warn, "error on attempt ".concat(context.stats.attempt, " - ").concat(message), config, { error: error });
|
|
75
75
|
}
|
|
76
76
|
},
|
|
77
77
|
_a[RequestEvent.UnrecoverableError] = function (context) {
|
|
78
78
|
if (context.error) {
|
|
79
79
|
var _a = diagnoseError(context.error), error = _a.error, message = _a.message;
|
|
80
|
-
log(context, LogType.Error, "failed - "
|
|
80
|
+
log(context, LogType.Error, "failed - ".concat(message), config, {
|
|
81
81
|
error: error,
|
|
82
82
|
});
|
|
83
83
|
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "api-def",
|
|
3
|
-
"version": "0.6.1",
|
|
3
|
+
"version": "0.6.2-alpha.1",
|
|
4
4
|
"description": "Typed API definitions with middleware support",
|
|
5
5
|
"main": "cjs/index.js",
|
|
6
6
|
"types": "esm/index.d.ts",
|
|
7
7
|
"module": "esm/index.js",
|
|
8
8
|
"sideEffects": false,
|
|
9
9
|
"scripts": {
|
|
10
|
+
"prepublishOnly": "npm run test && npm run build",
|
|
10
11
|
"test": "npm run test:types && npm run test:lint && npm run test:unit",
|
|
11
|
-
"test:unit": "
|
|
12
|
+
"test:unit": "ava",
|
|
12
13
|
"test:lint": "npm run lint:strict",
|
|
13
14
|
"test:types": "tsc --noEmit -p .",
|
|
14
15
|
"example:start": "cd example && npm run start",
|
|
@@ -45,19 +46,30 @@
|
|
|
45
46
|
"cjs/"
|
|
46
47
|
],
|
|
47
48
|
"repository": "https://github.com/Censkh/api-def",
|
|
49
|
+
"ava": {
|
|
50
|
+
"extensions": [
|
|
51
|
+
"ts"
|
|
52
|
+
],
|
|
53
|
+
"files": [
|
|
54
|
+
"src/tests/**/*.test.ts"
|
|
55
|
+
],
|
|
56
|
+
"nodeArguments": [
|
|
57
|
+
"--require=@esbuild-kit/cjs-loader"
|
|
58
|
+
]
|
|
59
|
+
},
|
|
48
60
|
"devDependencies": {
|
|
49
61
|
"@babel/preset-env": "7.14.8",
|
|
50
62
|
"@babel/preset-typescript": "7.14.5",
|
|
63
|
+
"@esbuild-kit/cjs-loader": "2.4.0",
|
|
51
64
|
"@types/axios": "0.14.0",
|
|
52
|
-
"@types/jest": "26.0.24",
|
|
53
65
|
"@types/node": "16.4.6",
|
|
54
66
|
"@typescript-eslint/eslint-plugin": "4.28.5",
|
|
55
67
|
"@typescript-eslint/parser": "4.28.5",
|
|
68
|
+
"ava": "5.0.1",
|
|
56
69
|
"cross-env": "7.0.3",
|
|
57
70
|
"eslint": "7.31.0",
|
|
58
71
|
"eslint-watch": "7.0.0",
|
|
59
|
-
"jest": "27.0.6",
|
|
60
72
|
"npm-run-all": "4.1.5",
|
|
61
|
-
"typescript": "4.
|
|
73
|
+
"typescript": "4.8.4"
|
|
62
74
|
}
|
|
63
75
|
}
|