api-def 0.8.3 → 0.8.5
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/cjs/Api.js +1 -0
- package/cjs/ApiTypes.d.ts +2 -0
- package/cjs/Endpoint.d.ts +5 -2
- package/cjs/Endpoint.js +1 -0
- package/cjs/EndpointBuilder.d.ts +6 -4
- package/cjs/EndpointBuilder.js +23 -4
- package/cjs/RequestContext.d.ts +2 -0
- package/cjs/RequestContext.js +1 -0
- package/cjs/RequestError.d.ts +4 -0
- package/cjs/RequestError.js +4 -0
- package/cjs/Requester.js +37 -0
- package/cjs/Validation.d.ts +8 -0
- package/cjs/Validation.js +2 -0
- package/cjs/backend/FetchRequestBackend.js +1 -1
- package/esm/Api.js +1 -0
- package/esm/ApiTypes.d.ts +2 -0
- package/esm/Endpoint.d.ts +5 -2
- package/esm/Endpoint.js +1 -0
- package/esm/EndpointBuilder.d.ts +6 -4
- package/esm/EndpointBuilder.js +12 -4
- package/esm/RequestContext.d.ts +2 -0
- package/esm/RequestContext.js +1 -0
- package/esm/RequestError.d.ts +4 -0
- package/esm/RequestError.js +4 -0
- package/esm/Requester.js +37 -0
- package/esm/Validation.d.ts +8 -0
- package/esm/Validation.js +1 -0
- package/esm/backend/FetchRequestBackend.js +1 -1
- package/package.json +3 -2
package/cjs/Api.js
CHANGED
|
@@ -62,6 +62,7 @@ exports.setRequestBackend = setRequestBackend;
|
|
|
62
62
|
var HotRequestHost = /** @class */ (function () {
|
|
63
63
|
function HotRequestHost(api, path, method) {
|
|
64
64
|
this.responseType = undefined;
|
|
65
|
+
this.validation = {};
|
|
65
66
|
this.api = api;
|
|
66
67
|
this.method = method;
|
|
67
68
|
this.path = path;
|
package/cjs/ApiTypes.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import RequestContext from "./RequestContext";
|
|
|
2
2
|
import { Api } from "./Api";
|
|
3
3
|
import { CacheSource, EventResultType, RequestEvent, RequestMethod, ResponseType } from "./ApiConstants";
|
|
4
4
|
import RequestBackend from "./backend/RequestBackend";
|
|
5
|
+
import { Validation } from "./Validation";
|
|
5
6
|
export declare type AcceptableStatus = number | [min: number, max: number];
|
|
6
7
|
export declare type Headers = Record<string, string | number | boolean | null | undefined>;
|
|
7
8
|
export declare type Params = string;
|
|
@@ -87,6 +88,7 @@ export interface RequestHost {
|
|
|
87
88
|
readonly baseUrl: string;
|
|
88
89
|
readonly path: string;
|
|
89
90
|
readonly responseType: ResponseType | undefined;
|
|
91
|
+
readonly validation: Validation;
|
|
90
92
|
computeConfig<P extends Params | undefined, Q extends Query | undefined, B extends Body | undefined>(config: RequestConfig<P, Q, B>): ComputedRequestConfig<P, Q, B>;
|
|
91
93
|
computePath(path: string, config: RequestConfig): string;
|
|
92
94
|
getRequestBackend(): RequestBackend;
|
package/cjs/Endpoint.d.ts
CHANGED
|
@@ -3,10 +3,11 @@ import { ApiResponse, BaseRequestConfig, Body, ComputedRequestConfig, Params, Qu
|
|
|
3
3
|
import * as Mocking from "./MockingTypes";
|
|
4
4
|
import { RequestMethod, ResponseType } from "./ApiConstants";
|
|
5
5
|
import RequestBackend from "./backend/RequestBackend";
|
|
6
|
-
|
|
6
|
+
import { Validation } from "./Validation";
|
|
7
|
+
export interface EndpointConfig<R, P extends Params | undefined, Q extends Query | undefined, B extends Body | undefined, Path extends string = string> {
|
|
7
8
|
readonly id: string;
|
|
8
9
|
readonly method: RequestMethod;
|
|
9
|
-
readonly path:
|
|
10
|
+
readonly path: Path;
|
|
10
11
|
/**
|
|
11
12
|
* Name your endpoint to help with debugging and documentation
|
|
12
13
|
* @default `id` is used as the name if no name is supplied
|
|
@@ -29,6 +30,7 @@ export interface EndpointConfig<R, P extends Params | undefined, Q extends Query
|
|
|
29
30
|
* Enable/disable mocked returns for all endpoints on your API object.
|
|
30
31
|
*/
|
|
31
32
|
readonly mocking?: Mocking.EndpointMockingConfig<R, P, Q, B>;
|
|
33
|
+
readonly validation?: Validation<R, P, Q, B>;
|
|
32
34
|
}
|
|
33
35
|
export default class Endpoint<R = any, P extends Params | undefined = Params | undefined, Q extends Query | undefined = Query | undefined, B extends Body | undefined = Body | undefined> implements EndpointConfig<R, P, Q, B>, RequestHost {
|
|
34
36
|
readonly api: Api;
|
|
@@ -40,6 +42,7 @@ export default class Endpoint<R = any, P extends Params | undefined = Params | u
|
|
|
40
42
|
readonly config?: BaseRequestConfig;
|
|
41
43
|
readonly responseType: ResponseType | undefined;
|
|
42
44
|
readonly mocking?: Mocking.EndpointMockingConfig<R, P, Q, B>;
|
|
45
|
+
readonly validation: Validation<R, P, Q, B>;
|
|
43
46
|
constructor(api: Api, info: EndpointConfig<R, P, Q, B>);
|
|
44
47
|
submit(config: RequestConfig<P, Q, B>): Promise<ApiResponse<R>>;
|
|
45
48
|
computePath(path: string, request: RequestConfig): string;
|
package/cjs/Endpoint.js
CHANGED
|
@@ -49,6 +49,7 @@ var Endpoint = /** @class */ (function () {
|
|
|
49
49
|
this.config = info.config;
|
|
50
50
|
this.responseType = info.responseType;
|
|
51
51
|
this.mocking = info.mocking;
|
|
52
|
+
this.validation = info.validation || {};
|
|
52
53
|
}
|
|
53
54
|
Endpoint.prototype.submit = function (config) {
|
|
54
55
|
var _a, _b;
|
package/cjs/EndpointBuilder.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { Body, Params, Query } from "./ApiTypes";
|
|
2
2
|
import Endpoint, { EndpointConfig } from "./Endpoint";
|
|
3
3
|
import { Api } from "./Api";
|
|
4
|
+
import * as zod from "zod";
|
|
4
5
|
export default class EndpointBuilder<R = any, P extends Params | undefined = undefined, Q extends Query | undefined = undefined, B extends Body | undefined = undefined> {
|
|
5
6
|
private api;
|
|
7
|
+
private readonly validation;
|
|
6
8
|
constructor(api: Api);
|
|
7
|
-
queryOf<Q extends Query>(): EndpointBuilder<R, P, Q, B>;
|
|
9
|
+
queryOf<Q extends Query>(schema?: zod.Schema<Q>): EndpointBuilder<R, P, Q, B>;
|
|
8
10
|
paramsOf<P extends Params>(): EndpointBuilder<R, P, Q, B>;
|
|
9
|
-
bodyOf<B extends Body>(): EndpointBuilder<R, P, Q, B>;
|
|
10
|
-
responseOf<R>(): EndpointBuilder<R, P, Q, B>;
|
|
11
|
-
build(config: EndpointConfig<R, P, Q, B>): Endpoint<R, P, Q, B>;
|
|
11
|
+
bodyOf<B extends Body>(schema?: zod.Schema<B>): EndpointBuilder<R, P, Q, B>;
|
|
12
|
+
responseOf<R>(schema?: zod.Schema<R>): EndpointBuilder<R, P, Q, B>;
|
|
13
|
+
build<Path extends string>(config: Omit<EndpointConfig<R, P, Q, B, Path>, "validation">): Endpoint<R, P, Q, B>;
|
|
12
14
|
}
|
package/cjs/EndpointBuilder.js
CHANGED
|
@@ -1,24 +1,43 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
2
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
14
|
var Endpoint_1 = require("./Endpoint");
|
|
15
|
+
/*type ExtractParams<Path> = Path extends `${infer Segment}/${infer Rest}`
|
|
16
|
+
? Segment extends `:${infer Param}` ? Param | ExtractParams<Rest> : ExtractParams<Rest>
|
|
17
|
+
: Path extends `:${infer Param}` ? Param : undefined;
|
|
18
|
+
*/
|
|
4
19
|
var EndpointBuilder = /** @class */ (function () {
|
|
5
20
|
function EndpointBuilder(api) {
|
|
21
|
+
this.validation = {};
|
|
6
22
|
this.api = api;
|
|
7
23
|
}
|
|
8
|
-
EndpointBuilder.prototype.queryOf = function () {
|
|
24
|
+
EndpointBuilder.prototype.queryOf = function (schema) {
|
|
25
|
+
this.validation.query = schema;
|
|
9
26
|
return this;
|
|
10
27
|
};
|
|
11
28
|
EndpointBuilder.prototype.paramsOf = function () {
|
|
12
29
|
return this;
|
|
13
30
|
};
|
|
14
|
-
EndpointBuilder.prototype.bodyOf = function () {
|
|
31
|
+
EndpointBuilder.prototype.bodyOf = function (schema) {
|
|
32
|
+
this.validation.body = schema;
|
|
15
33
|
return this;
|
|
16
34
|
};
|
|
17
|
-
EndpointBuilder.prototype.responseOf = function () {
|
|
35
|
+
EndpointBuilder.prototype.responseOf = function (schema) {
|
|
36
|
+
this.validation.response = schema;
|
|
18
37
|
return this;
|
|
19
38
|
};
|
|
20
39
|
EndpointBuilder.prototype.build = function (config) {
|
|
21
|
-
var endpoint = new Endpoint_1.default(this.api, config);
|
|
40
|
+
var endpoint = new Endpoint_1.default(this.api, __assign(__assign({}, config), { validation: this.validation }));
|
|
22
41
|
this.api.endpoints[endpoint.id] = endpoint;
|
|
23
42
|
return endpoint;
|
|
24
43
|
};
|
package/cjs/RequestContext.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { RequestEvent, RequestMethod, ResponseType } from "./ApiConstants";
|
|
|
4
4
|
import { EndpointMockingConfig } from "./MockingTypes";
|
|
5
5
|
import { RequestError } from "./RequestError";
|
|
6
6
|
import RequestBackend from "./backend/RequestBackend";
|
|
7
|
+
import { Validation } from "./Validation";
|
|
7
8
|
export default class RequestContext<R = any, P extends Params | undefined = Params | undefined, Q extends Query | undefined = Query | undefined, B extends Body | undefined = Body | undefined> {
|
|
8
9
|
readonly id: number;
|
|
9
10
|
readonly key: string;
|
|
@@ -20,6 +21,7 @@ export default class RequestContext<R = any, P extends Params | undefined = Para
|
|
|
20
21
|
readonly computedConfig: ComputedRequestConfig<P, Q, B>;
|
|
21
22
|
readonly mocking: EndpointMockingConfig<R, P, Q, B> | null | undefined;
|
|
22
23
|
private parsedBody;
|
|
24
|
+
readonly validation: Validation<R, P, Q, B>;
|
|
23
25
|
constructor(backend: RequestBackend, host: RequestHost, config: ComputedRequestConfig<P, Q, B>, computedPath: string, mocking: EndpointMockingConfig<R, P, Q, B> | null | undefined);
|
|
24
26
|
get method(): RequestMethod;
|
|
25
27
|
get api(): Api;
|
package/cjs/RequestContext.js
CHANGED
package/cjs/RequestError.d.ts
CHANGED
|
@@ -7,6 +7,10 @@ export declare const RequestErrorCode: {
|
|
|
7
7
|
readonly REQUEST_INVALID_STATUS: "request/invalid-status";
|
|
8
8
|
readonly REQUEST_INVALID_CONFIG: "request/invalid-config";
|
|
9
9
|
readonly REQUEST_MISMATCH_RESPONSE_TYPE: "request/mismatch-response-type";
|
|
10
|
+
readonly VALIDATION_QUERY_VALIDATE_ERROR: "validation/query-validate-error";
|
|
11
|
+
readonly VALIDATION_PARAMS_VALIDATE_ERROR: "validation/params-validate-error";
|
|
12
|
+
readonly VALIDATION_BODY_VALIDATE_ERROR: "validation/body-validate-error";
|
|
13
|
+
readonly VALIDATION_RESPONSE_VALIDATE_ERROR: "validation/response-validate-error";
|
|
10
14
|
};
|
|
11
15
|
export declare type RequestErrorCode = EnumOf<typeof RequestErrorCode>;
|
|
12
16
|
export interface RequestError extends Error {
|
package/cjs/RequestError.js
CHANGED
|
@@ -7,6 +7,10 @@ exports.RequestErrorCode = {
|
|
|
7
7
|
REQUEST_INVALID_STATUS: "request/invalid-status",
|
|
8
8
|
REQUEST_INVALID_CONFIG: "request/invalid-config",
|
|
9
9
|
REQUEST_MISMATCH_RESPONSE_TYPE: "request/mismatch-response-type",
|
|
10
|
+
VALIDATION_QUERY_VALIDATE_ERROR: "validation/query-validate-error",
|
|
11
|
+
VALIDATION_PARAMS_VALIDATE_ERROR: "validation/params-validate-error",
|
|
12
|
+
VALIDATION_BODY_VALIDATE_ERROR: "validation/body-validate-error",
|
|
13
|
+
VALIDATION_RESPONSE_VALIDATE_ERROR: "validation/response-validate-error",
|
|
10
14
|
};
|
|
11
15
|
var isRequestError = function (error) {
|
|
12
16
|
return "isRequestError" in error;
|
package/cjs/Requester.js
CHANGED
|
@@ -125,6 +125,31 @@ var makeRequest = function (context) { return __awaiter(void 0, void 0, void 0,
|
|
|
125
125
|
beforeSendEventResult.type === ApiConstants_1.EventResultType.Respond) {
|
|
126
126
|
return [2 /*return*/, (context.response = beforeSendEventResult.response)];
|
|
127
127
|
}
|
|
128
|
+
// validation
|
|
129
|
+
if (context.validation.query) {
|
|
130
|
+
try {
|
|
131
|
+
context.computedConfig.queryObject = context.validation.query.parse(context.computedConfig.queryObject);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
throw (0, RequestError_1.convertToRequestError)({
|
|
135
|
+
error: error,
|
|
136
|
+
code: RequestError_1.RequestErrorCode.VALIDATION_QUERY_VALIDATE_ERROR,
|
|
137
|
+
context: context,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (context.validation.body) {
|
|
142
|
+
try {
|
|
143
|
+
context.computedConfig.body = context.validation.body.parse(context.computedConfig.body);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
throw (0, RequestError_1.convertToRequestError)({
|
|
147
|
+
error: error,
|
|
148
|
+
code: RequestError_1.RequestErrorCode.VALIDATION_BODY_VALIDATE_ERROR,
|
|
149
|
+
context: context,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
128
153
|
retryOptions = parseRetryOptions((_a = context.computedConfig) === null || _a === void 0 ? void 0 : _a.retry);
|
|
129
154
|
internalRetryOptions = {
|
|
130
155
|
retries: retryOptions.maxAttempts,
|
|
@@ -202,6 +227,18 @@ var makeRequest = function (context) { return __awaiter(void 0, void 0, void 0,
|
|
|
202
227
|
return [4 /*yield*/, (0, retry_1.default)(performRequest, internalRetryOptions)];
|
|
203
228
|
case 2:
|
|
204
229
|
response = _d.sent();
|
|
230
|
+
if (context.validation.response) {
|
|
231
|
+
try {
|
|
232
|
+
response.data = context.validation.response.parse(response.data);
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
throw (0, RequestError_1.convertToRequestError)({
|
|
236
|
+
error: error,
|
|
237
|
+
code: RequestError_1.RequestErrorCode.VALIDATION_RESPONSE_VALIDATE_ERROR,
|
|
238
|
+
context: context,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
205
242
|
return [2 /*return*/, (response)];
|
|
206
243
|
}
|
|
207
244
|
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as zod from "zod";
|
|
2
|
+
import { Body, Params, Query } from "./ApiTypes";
|
|
3
|
+
export interface Validation<R = any, P extends Params | undefined = Params | undefined, Q extends Query | undefined = Query | undefined, B extends Body | undefined = Body | undefined> {
|
|
4
|
+
query?: zod.Schema<Q>;
|
|
5
|
+
params?: zod.Schema<P>;
|
|
6
|
+
body?: zod.Schema<B>;
|
|
7
|
+
response?: zod.Schema<R>;
|
|
8
|
+
}
|
|
@@ -163,7 +163,7 @@ var FetchRequestBackend = /** @class */ (function () {
|
|
|
163
163
|
}, {});
|
|
164
164
|
var url = context.getRequestUrl();
|
|
165
165
|
var promise = this.fetch(url.href, {
|
|
166
|
-
method: context.method,
|
|
166
|
+
method: context.method.toUpperCase(),
|
|
167
167
|
body: bodyJsonify ? JSON.stringify(body) : body,
|
|
168
168
|
headers: parsedHeaders,
|
|
169
169
|
mode: "cors",
|
package/esm/Api.js
CHANGED
package/esm/ApiTypes.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import RequestContext from "./RequestContext";
|
|
|
2
2
|
import { Api } from "./Api";
|
|
3
3
|
import { CacheSource, EventResultType, RequestEvent, RequestMethod, ResponseType } from "./ApiConstants";
|
|
4
4
|
import RequestBackend from "./backend/RequestBackend";
|
|
5
|
+
import { Validation } from "./Validation";
|
|
5
6
|
export declare type AcceptableStatus = number | [min: number, max: number];
|
|
6
7
|
export declare type Headers = Record<string, string | number | boolean | null | undefined>;
|
|
7
8
|
export declare type Params = string;
|
|
@@ -87,6 +88,7 @@ export interface RequestHost {
|
|
|
87
88
|
readonly baseUrl: string;
|
|
88
89
|
readonly path: string;
|
|
89
90
|
readonly responseType: ResponseType | undefined;
|
|
91
|
+
readonly validation: Validation;
|
|
90
92
|
computeConfig<P extends Params | undefined, Q extends Query | undefined, B extends Body | undefined>(config: RequestConfig<P, Q, B>): ComputedRequestConfig<P, Q, B>;
|
|
91
93
|
computePath(path: string, config: RequestConfig): string;
|
|
92
94
|
getRequestBackend(): RequestBackend;
|
package/esm/Endpoint.d.ts
CHANGED
|
@@ -3,10 +3,11 @@ import { ApiResponse, BaseRequestConfig, Body, ComputedRequestConfig, Params, Qu
|
|
|
3
3
|
import * as Mocking from "./MockingTypes";
|
|
4
4
|
import { RequestMethod, ResponseType } from "./ApiConstants";
|
|
5
5
|
import RequestBackend from "./backend/RequestBackend";
|
|
6
|
-
|
|
6
|
+
import { Validation } from "./Validation";
|
|
7
|
+
export interface EndpointConfig<R, P extends Params | undefined, Q extends Query | undefined, B extends Body | undefined, Path extends string = string> {
|
|
7
8
|
readonly id: string;
|
|
8
9
|
readonly method: RequestMethod;
|
|
9
|
-
readonly path:
|
|
10
|
+
readonly path: Path;
|
|
10
11
|
/**
|
|
11
12
|
* Name your endpoint to help with debugging and documentation
|
|
12
13
|
* @default `id` is used as the name if no name is supplied
|
|
@@ -29,6 +30,7 @@ export interface EndpointConfig<R, P extends Params | undefined, Q extends Query
|
|
|
29
30
|
* Enable/disable mocked returns for all endpoints on your API object.
|
|
30
31
|
*/
|
|
31
32
|
readonly mocking?: Mocking.EndpointMockingConfig<R, P, Q, B>;
|
|
33
|
+
readonly validation?: Validation<R, P, Q, B>;
|
|
32
34
|
}
|
|
33
35
|
export default class Endpoint<R = any, P extends Params | undefined = Params | undefined, Q extends Query | undefined = Query | undefined, B extends Body | undefined = Body | undefined> implements EndpointConfig<R, P, Q, B>, RequestHost {
|
|
34
36
|
readonly api: Api;
|
|
@@ -40,6 +42,7 @@ export default class Endpoint<R = any, P extends Params | undefined = Params | u
|
|
|
40
42
|
readonly config?: BaseRequestConfig;
|
|
41
43
|
readonly responseType: ResponseType | undefined;
|
|
42
44
|
readonly mocking?: Mocking.EndpointMockingConfig<R, P, Q, B>;
|
|
45
|
+
readonly validation: Validation<R, P, Q, B>;
|
|
43
46
|
constructor(api: Api, info: EndpointConfig<R, P, Q, B>);
|
|
44
47
|
submit(config: RequestConfig<P, Q, B>): Promise<ApiResponse<R>>;
|
|
45
48
|
computePath(path: string, request: RequestConfig): string;
|
package/esm/Endpoint.js
CHANGED
package/esm/EndpointBuilder.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { Body, Params, Query } from "./ApiTypes";
|
|
2
2
|
import Endpoint, { EndpointConfig } from "./Endpoint";
|
|
3
3
|
import { Api } from "./Api";
|
|
4
|
+
import * as zod from "zod";
|
|
4
5
|
export default class EndpointBuilder<R = any, P extends Params | undefined = undefined, Q extends Query | undefined = undefined, B extends Body | undefined = undefined> {
|
|
5
6
|
private api;
|
|
7
|
+
private readonly validation;
|
|
6
8
|
constructor(api: Api);
|
|
7
|
-
queryOf<Q extends Query>(): EndpointBuilder<R, P, Q, B>;
|
|
9
|
+
queryOf<Q extends Query>(schema?: zod.Schema<Q>): EndpointBuilder<R, P, Q, B>;
|
|
8
10
|
paramsOf<P extends Params>(): EndpointBuilder<R, P, Q, B>;
|
|
9
|
-
bodyOf<B extends Body>(): EndpointBuilder<R, P, Q, B>;
|
|
10
|
-
responseOf<R>(): EndpointBuilder<R, P, Q, B>;
|
|
11
|
-
build(config: EndpointConfig<R, P, Q, B>): Endpoint<R, P, Q, B>;
|
|
11
|
+
bodyOf<B extends Body>(schema?: zod.Schema<B>): EndpointBuilder<R, P, Q, B>;
|
|
12
|
+
responseOf<R>(schema?: zod.Schema<R>): EndpointBuilder<R, P, Q, B>;
|
|
13
|
+
build<Path extends string>(config: Omit<EndpointConfig<R, P, Q, B, Path>, "validation">): Endpoint<R, P, Q, B>;
|
|
12
14
|
}
|
package/esm/EndpointBuilder.js
CHANGED
|
@@ -1,22 +1,30 @@
|
|
|
1
1
|
import Endpoint from "./Endpoint";
|
|
2
|
+
/*type ExtractParams<Path> = Path extends `${infer Segment}/${infer Rest}`
|
|
3
|
+
? Segment extends `:${infer Param}` ? Param | ExtractParams<Rest> : ExtractParams<Rest>
|
|
4
|
+
: Path extends `:${infer Param}` ? Param : undefined;
|
|
5
|
+
*/
|
|
2
6
|
export default class EndpointBuilder {
|
|
3
7
|
constructor(api) {
|
|
8
|
+
this.validation = {};
|
|
4
9
|
this.api = api;
|
|
5
10
|
}
|
|
6
|
-
queryOf() {
|
|
11
|
+
queryOf(schema) {
|
|
12
|
+
this.validation.query = schema;
|
|
7
13
|
return this;
|
|
8
14
|
}
|
|
9
15
|
paramsOf() {
|
|
10
16
|
return this;
|
|
11
17
|
}
|
|
12
|
-
bodyOf() {
|
|
18
|
+
bodyOf(schema) {
|
|
19
|
+
this.validation.body = schema;
|
|
13
20
|
return this;
|
|
14
21
|
}
|
|
15
|
-
responseOf() {
|
|
22
|
+
responseOf(schema) {
|
|
23
|
+
this.validation.response = schema;
|
|
16
24
|
return this;
|
|
17
25
|
}
|
|
18
26
|
build(config) {
|
|
19
|
-
const endpoint = new Endpoint(this.api, config);
|
|
27
|
+
const endpoint = new Endpoint(this.api, Object.assign(Object.assign({}, config), { validation: this.validation }));
|
|
20
28
|
this.api.endpoints[endpoint.id] = endpoint;
|
|
21
29
|
return endpoint;
|
|
22
30
|
}
|
package/esm/RequestContext.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { RequestEvent, RequestMethod, ResponseType } from "./ApiConstants";
|
|
|
4
4
|
import { EndpointMockingConfig } from "./MockingTypes";
|
|
5
5
|
import { RequestError } from "./RequestError";
|
|
6
6
|
import RequestBackend from "./backend/RequestBackend";
|
|
7
|
+
import { Validation } from "./Validation";
|
|
7
8
|
export default class RequestContext<R = any, P extends Params | undefined = Params | undefined, Q extends Query | undefined = Query | undefined, B extends Body | undefined = Body | undefined> {
|
|
8
9
|
readonly id: number;
|
|
9
10
|
readonly key: string;
|
|
@@ -20,6 +21,7 @@ export default class RequestContext<R = any, P extends Params | undefined = Para
|
|
|
20
21
|
readonly computedConfig: ComputedRequestConfig<P, Q, B>;
|
|
21
22
|
readonly mocking: EndpointMockingConfig<R, P, Q, B> | null | undefined;
|
|
22
23
|
private parsedBody;
|
|
24
|
+
readonly validation: Validation<R, P, Q, B>;
|
|
23
25
|
constructor(backend: RequestBackend, host: RequestHost, config: ComputedRequestConfig<P, Q, B>, computedPath: string, mocking: EndpointMockingConfig<R, P, Q, B> | null | undefined);
|
|
24
26
|
get method(): RequestMethod;
|
|
25
27
|
get api(): Api;
|
package/esm/RequestContext.js
CHANGED
package/esm/RequestError.d.ts
CHANGED
|
@@ -7,6 +7,10 @@ export declare const RequestErrorCode: {
|
|
|
7
7
|
readonly REQUEST_INVALID_STATUS: "request/invalid-status";
|
|
8
8
|
readonly REQUEST_INVALID_CONFIG: "request/invalid-config";
|
|
9
9
|
readonly REQUEST_MISMATCH_RESPONSE_TYPE: "request/mismatch-response-type";
|
|
10
|
+
readonly VALIDATION_QUERY_VALIDATE_ERROR: "validation/query-validate-error";
|
|
11
|
+
readonly VALIDATION_PARAMS_VALIDATE_ERROR: "validation/params-validate-error";
|
|
12
|
+
readonly VALIDATION_BODY_VALIDATE_ERROR: "validation/body-validate-error";
|
|
13
|
+
readonly VALIDATION_RESPONSE_VALIDATE_ERROR: "validation/response-validate-error";
|
|
10
14
|
};
|
|
11
15
|
export declare type RequestErrorCode = EnumOf<typeof RequestErrorCode>;
|
|
12
16
|
export interface RequestError extends Error {
|
package/esm/RequestError.js
CHANGED
|
@@ -4,6 +4,10 @@ export const RequestErrorCode = {
|
|
|
4
4
|
REQUEST_INVALID_STATUS: "request/invalid-status",
|
|
5
5
|
REQUEST_INVALID_CONFIG: "request/invalid-config",
|
|
6
6
|
REQUEST_MISMATCH_RESPONSE_TYPE: "request/mismatch-response-type",
|
|
7
|
+
VALIDATION_QUERY_VALIDATE_ERROR: "validation/query-validate-error",
|
|
8
|
+
VALIDATION_PARAMS_VALIDATE_ERROR: "validation/params-validate-error",
|
|
9
|
+
VALIDATION_BODY_VALIDATE_ERROR: "validation/body-validate-error",
|
|
10
|
+
VALIDATION_RESPONSE_VALIDATE_ERROR: "validation/response-validate-error",
|
|
7
11
|
};
|
|
8
12
|
export const isRequestError = (error) => {
|
|
9
13
|
return "isRequestError" in error;
|
package/esm/Requester.js
CHANGED
|
@@ -74,6 +74,31 @@ const makeRequest = (context) => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
74
74
|
beforeSendEventResult.type === EventResultType.Respond) {
|
|
75
75
|
return (context.response = beforeSendEventResult.response);
|
|
76
76
|
}
|
|
77
|
+
// validation
|
|
78
|
+
if (context.validation.query) {
|
|
79
|
+
try {
|
|
80
|
+
context.computedConfig.queryObject = context.validation.query.parse(context.computedConfig.queryObject);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
throw convertToRequestError({
|
|
84
|
+
error: error,
|
|
85
|
+
code: RequestErrorCode.VALIDATION_QUERY_VALIDATE_ERROR,
|
|
86
|
+
context: context,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (context.validation.body) {
|
|
91
|
+
try {
|
|
92
|
+
context.computedConfig.body = context.validation.body.parse(context.computedConfig.body);
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
throw convertToRequestError({
|
|
96
|
+
error: error,
|
|
97
|
+
code: RequestErrorCode.VALIDATION_BODY_VALIDATE_ERROR,
|
|
98
|
+
context: context,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
77
102
|
const retryOptions = parseRetryOptions((_a = context.computedConfig) === null || _a === void 0 ? void 0 : _a.retry);
|
|
78
103
|
const internalRetryOptions = {
|
|
79
104
|
retries: retryOptions.maxAttempts,
|
|
@@ -137,6 +162,18 @@ const makeRequest = (context) => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
137
162
|
}
|
|
138
163
|
});
|
|
139
164
|
const response = yield retry(performRequest, internalRetryOptions);
|
|
165
|
+
if (context.validation.response) {
|
|
166
|
+
try {
|
|
167
|
+
response.data = context.validation.response.parse(response.data);
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
throw convertToRequestError({
|
|
171
|
+
error: error,
|
|
172
|
+
code: RequestErrorCode.VALIDATION_RESPONSE_VALIDATE_ERROR,
|
|
173
|
+
context: context,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
140
177
|
return (response);
|
|
141
178
|
});
|
|
142
179
|
const parseResponse = (context, response, error) => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as zod from "zod";
|
|
2
|
+
import { Body, Params, Query } from "./ApiTypes";
|
|
3
|
+
export interface Validation<R = any, P extends Params | undefined = Params | undefined, Q extends Query | undefined = Query | undefined, B extends Body | undefined = Body | undefined> {
|
|
4
|
+
query?: zod.Schema<Q>;
|
|
5
|
+
params?: zod.Schema<P>;
|
|
6
|
+
body?: zod.Schema<B>;
|
|
7
|
+
response?: zod.Schema<R>;
|
|
8
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -94,7 +94,7 @@ export default class FetchRequestBackend {
|
|
|
94
94
|
}, {});
|
|
95
95
|
const url = context.getRequestUrl();
|
|
96
96
|
const promise = this.fetch(url.href, {
|
|
97
|
-
method: context.method,
|
|
97
|
+
method: context.method.toUpperCase(),
|
|
98
98
|
body: bodyJsonify ? JSON.stringify(body) : body,
|
|
99
99
|
headers: parsedHeaders,
|
|
100
100
|
mode: "cors",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "api-def",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.5",
|
|
4
4
|
"description": "Typed API definitions with middleware support",
|
|
5
5
|
"main": "cjs/index.js",
|
|
6
6
|
"types": "esm/index.d.ts",
|
|
@@ -73,6 +73,7 @@
|
|
|
73
73
|
"eslint-watch": "7.0.0",
|
|
74
74
|
"npm-run-all": "4.1.5",
|
|
75
75
|
"qs": "6.11.2",
|
|
76
|
-
"typescript": "4.8.4"
|
|
76
|
+
"typescript": "4.8.4",
|
|
77
|
+
"zod": "3.22.4"
|
|
77
78
|
}
|
|
78
79
|
}
|