@rexeus/typeweaver-types 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -21
- package/dist/index.cjs +331 -524
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +327 -515
- package/dist/index.mjs.map +1 -1
- package/dist/lib/ResponseValidator.d.ts +21 -7
- package/dist/lib/ResponseValidator.js +40 -2
- package/dist/lib/definitionLookup.d.ts +29 -0
- package/dist/lib/definitionLookup.js +14 -0
- package/dist/lib/index.ts +1 -1
- package/dist/templates/Request.ejs +0 -24
- package/dist/templates/RequestValidator.ejs +9 -15
- package/dist/templates/Response.ejs +20 -60
- package/dist/templates/ResponseValidator.ejs +23 -85
- package/dist/templates/SharedResponse.ejs +16 -28
- package/package.json +8 -8
- package/dist/lib/assert.d.ts +0 -1
- package/dist/lib/assert.js +0 -11
|
@@ -1,23 +1,37 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
HttpBodySchema,
|
|
3
|
+
HttpHeaderSchema,
|
|
2
4
|
IHttpResponse,
|
|
3
5
|
IResponseValidator,
|
|
6
|
+
ITypedHttpResponse,
|
|
4
7
|
ResponseValidationError,
|
|
5
8
|
SafeResponseValidationResult,
|
|
6
9
|
} from "@rexeus/typeweaver-core";
|
|
7
10
|
import { Validator } from "./Validator";
|
|
8
11
|
|
|
9
|
-
export
|
|
12
|
+
export type ResponseEntry = {
|
|
13
|
+
readonly name: string;
|
|
14
|
+
readonly statusCode: number;
|
|
15
|
+
readonly headerSchema: HttpHeaderSchema | undefined;
|
|
16
|
+
readonly bodySchema: HttpBodySchema | undefined;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export declare abstract class ResponseValidator<
|
|
20
|
+
TResponse extends ITypedHttpResponse = ITypedHttpResponse,
|
|
21
|
+
>
|
|
10
22
|
extends Validator
|
|
11
23
|
implements IResponseValidator
|
|
12
24
|
{
|
|
13
|
-
|
|
25
|
+
protected abstract readonly responseEntries: readonly ResponseEntry[];
|
|
26
|
+
protected abstract readonly expectedStatusCodes: readonly number[];
|
|
27
|
+
public safeValidate(
|
|
14
28
|
response: IHttpResponse
|
|
15
|
-
): SafeResponseValidationResult<
|
|
16
|
-
public
|
|
17
|
-
protected validateResponseType<Response extends
|
|
29
|
+
): SafeResponseValidationResult<TResponse>;
|
|
30
|
+
public validate(response: IHttpResponse): TResponse;
|
|
31
|
+
protected validateResponseType<Response extends ITypedHttpResponse>(
|
|
18
32
|
responseName: string,
|
|
19
|
-
headerSchema:
|
|
20
|
-
bodySchema:
|
|
33
|
+
headerSchema: HttpHeaderSchema | undefined,
|
|
34
|
+
bodySchema: HttpBodySchema | undefined
|
|
21
35
|
): (
|
|
22
36
|
response: IHttpResponse,
|
|
23
37
|
error: ResponseValidationError
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ResponseValidationError } from "@rexeus/typeweaver-core";
|
|
1
2
|
import { Validator } from "./Validator";
|
|
2
3
|
/**
|
|
3
4
|
* Abstract base class for HTTP response validation.
|
|
@@ -10,11 +11,47 @@ import { Validator } from "./Validator";
|
|
|
10
11
|
*
|
|
11
12
|
* Response validators are typically used in API clients to ensure
|
|
12
13
|
* responses match the expected format before processing.
|
|
14
|
+
*
|
|
15
|
+
* Subclasses provide response metadata via `responseEntries` and
|
|
16
|
+
* `expectedStatusCodes`. All validation logic lives in this base class.
|
|
13
17
|
*/
|
|
14
18
|
export class ResponseValidator extends Validator {
|
|
15
19
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
20
|
+
* Validates a response without throwing errors.
|
|
21
|
+
*
|
|
22
|
+
* @param response - The HTTP response to validate
|
|
23
|
+
* @returns A result object containing either the validated response or error details
|
|
24
|
+
*/
|
|
25
|
+
safeValidate(response) {
|
|
26
|
+
const error = new ResponseValidationError(response.statusCode);
|
|
27
|
+
for (const entry of this.responseEntries) {
|
|
28
|
+
if (response.statusCode === entry.statusCode) {
|
|
29
|
+
const result = this.validateResponseType(entry.name, entry.headerSchema, entry.bodySchema)(response, error);
|
|
30
|
+
if (result.isValid) return result;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (!error.hasResponseIssues()) {
|
|
34
|
+
error.addStatusCodeIssue([...this.expectedStatusCodes]);
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
isValid: false,
|
|
38
|
+
error
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Validates a response and throws if validation fails.
|
|
43
|
+
*
|
|
44
|
+
* @param response - The HTTP response to validate
|
|
45
|
+
* @returns The validated response with proper typing
|
|
46
|
+
* @throws {ResponseValidationError} If response structure fails validation
|
|
47
|
+
*/
|
|
48
|
+
validate(response) {
|
|
49
|
+
const result = this.safeValidate(response);
|
|
50
|
+
if (!result.isValid) throw result.error;
|
|
51
|
+
return result.data;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Validates a single response variant against its header and body schemas.
|
|
18
55
|
*
|
|
19
56
|
* @param responseName - Name of the response type for error reporting
|
|
20
57
|
* @param headerSchema - Zod schema for header validation (optional)
|
|
@@ -25,6 +62,7 @@ export class ResponseValidator extends Validator {
|
|
|
25
62
|
return (response, error) => {
|
|
26
63
|
let isValid = true;
|
|
27
64
|
const validatedResponse = {
|
|
65
|
+
type: responseName,
|
|
28
66
|
statusCode: response.statusCode,
|
|
29
67
|
header: undefined,
|
|
30
68
|
body: undefined
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ResponseDefinition,
|
|
3
|
+
SpecDefinition,
|
|
4
|
+
} from "@rexeus/typeweaver-core";
|
|
5
|
+
|
|
6
|
+
type MatchedOperationDefinition<
|
|
7
|
+
TSpec extends SpecDefinition,
|
|
8
|
+
TResourceName extends keyof TSpec["resources"] & string,
|
|
9
|
+
> = NonNullable<TSpec["resources"][TResourceName]>["operations"][number];
|
|
10
|
+
|
|
11
|
+
type MatchedResponseDefinition<
|
|
12
|
+
TResponses extends readonly ResponseDefinition[],
|
|
13
|
+
> = TResponses[number];
|
|
14
|
+
|
|
15
|
+
export declare const getOperationDefinition: <
|
|
16
|
+
TSpec extends SpecDefinition,
|
|
17
|
+
TResourceName extends keyof TSpec["resources"] & string,
|
|
18
|
+
>(
|
|
19
|
+
spec: TSpec,
|
|
20
|
+
resourceName: TResourceName,
|
|
21
|
+
operationId: string
|
|
22
|
+
) => MatchedOperationDefinition<TSpec, TResourceName>;
|
|
23
|
+
|
|
24
|
+
export declare const getResponseDefinition: <
|
|
25
|
+
TResponses extends readonly ResponseDefinition[],
|
|
26
|
+
>(
|
|
27
|
+
responses: TResponses,
|
|
28
|
+
responseName: string
|
|
29
|
+
) => MatchedResponseDefinition<TResponses>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const getOperationDefinition = (spec, resourceName, operationId) => {
|
|
2
|
+
const operation = spec.resources[resourceName]?.operations.find((candidate) => candidate.operationId === operationId);
|
|
3
|
+
if (operation === undefined) {
|
|
4
|
+
throw new Error(`Missing operation definition '${String(resourceName)}.${String(operationId)}'.`);
|
|
5
|
+
}
|
|
6
|
+
return operation;
|
|
7
|
+
};
|
|
8
|
+
export const getResponseDefinition = (responses, responseName) => {
|
|
9
|
+
const response = responses.find((candidate) => candidate.name === responseName);
|
|
10
|
+
if (response === undefined) {
|
|
11
|
+
throw new Error(`Missing response definition '${String(responseName)}'.`);
|
|
12
|
+
}
|
|
13
|
+
return response;
|
|
14
|
+
};
|
package/dist/lib/index.ts
CHANGED
|
@@ -7,18 +7,6 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { HttpMethod } from "@rexeus/typeweaver-core";
|
|
10
|
-
import {
|
|
11
|
-
type <%= pascalCaseOperationId %>Response,
|
|
12
|
-
<% for (const ownResponse of ownErrorResponses) { %>
|
|
13
|
-
<%= ownResponse.name %>Response,
|
|
14
|
-
<% } %>
|
|
15
|
-
} from "<%= responseFile %>";
|
|
16
|
-
<% for (const sharedError of sharedErrorResponses) { %>
|
|
17
|
-
import { <%= sharedError.name %>Response } from "<%= sharedError.path %>";
|
|
18
|
-
<% } %>
|
|
19
|
-
<% for (const entityError of entityErrorResponses) { %>
|
|
20
|
-
import { <%= entityError.name %>Response } from "<%= entityError.path %>";
|
|
21
|
-
<% } %>
|
|
22
10
|
|
|
23
11
|
<% if (headerTsType) { %>
|
|
24
12
|
export type I<%= pascalCaseOperationId %>RequestHeader = <%- headerTsType %>
|
|
@@ -45,15 +33,3 @@ export type I<%= pascalCaseOperationId %>Request = {
|
|
|
45
33
|
<%= bodyTsType ? `body: I${pascalCaseOperationId}RequestBody;` : "" %>
|
|
46
34
|
};
|
|
47
35
|
|
|
48
|
-
<% if (!hasSuccessResponses) { %>
|
|
49
|
-
export type Successful<%= pascalCaseOperationId %>Response = never;
|
|
50
|
-
<% } else if (!hasErrorResponses) { %>
|
|
51
|
-
export type Successful<%= pascalCaseOperationId %>Response = <%= pascalCaseOperationId %>Response;
|
|
52
|
-
<% } else { %>
|
|
53
|
-
export type Successful<%= pascalCaseOperationId %>Response = Exclude<<%= pascalCaseOperationId %>Response, <%= [
|
|
54
|
-
...sharedErrorResponses,
|
|
55
|
-
...ownErrorResponses,
|
|
56
|
-
...entityErrorResponses,
|
|
57
|
-
].map(({ name }) => `${name}Response`).join(" | ") %>
|
|
58
|
-
>;
|
|
59
|
-
<% } %>
|
|
@@ -6,15 +6,17 @@
|
|
|
6
6
|
* @generated by @rexeus/typeweaver
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import
|
|
9
|
+
import spec from "<%= specPath %>";
|
|
10
10
|
import {
|
|
11
11
|
type IHttpRequest,
|
|
12
12
|
type SafeRequestValidationResult,
|
|
13
13
|
RequestValidationError
|
|
14
14
|
} from "@rexeus/typeweaver-core";
|
|
15
|
-
import { RequestValidator } from "../lib/types";
|
|
15
|
+
import { getOperationDefinition, RequestValidator } from "../lib/types";
|
|
16
16
|
import type { I<%= pascalCaseOperationId %>Request } from "<%= requestFile %>";
|
|
17
17
|
|
|
18
|
+
const definition = getOperationDefinition(spec, "<%= resourceName %>", "<%= operationId %>");
|
|
19
|
+
|
|
18
20
|
export class <%= pascalCaseOperationId %>RequestValidator extends RequestValidator {
|
|
19
21
|
public safeValidate(request: IHttpRequest): SafeRequestValidationResult<I<%= pascalCaseOperationId %>Request> {
|
|
20
22
|
const error = new RequestValidationError();
|
|
@@ -41,17 +43,13 @@ export class <%= pascalCaseOperationId %>RequestValidator extends RequestValidat
|
|
|
41
43
|
|
|
42
44
|
<% if(header) { %>
|
|
43
45
|
if (definition.request.header) {
|
|
44
|
-
|
|
45
|
-
const coercedHeader = this.coerceHeaderToSchema(request.header, definition.request.header.shape);
|
|
46
|
-
<% } else { %>
|
|
47
|
-
const coercedHeader = this.coerceHeaderToSchema(request.header, definition.request.header.unwrap().shape);
|
|
48
|
-
<% } %>
|
|
46
|
+
const coercedHeader = this.coerceHeaderToSchema(request.header, this.getSchema(definition.request.header));
|
|
49
47
|
const result = definition.request.header.safeParse(coercedHeader);
|
|
50
48
|
|
|
51
49
|
if (!result.success) {
|
|
52
50
|
error.addHeaderIssues(result.error.issues);
|
|
53
51
|
} else {
|
|
54
|
-
validatedRequest.header = result.data;
|
|
52
|
+
validatedRequest.header = result.data as IHttpRequest["header"];
|
|
55
53
|
}
|
|
56
54
|
}
|
|
57
55
|
<% } %>
|
|
@@ -70,17 +68,13 @@ export class <%= pascalCaseOperationId %>RequestValidator extends RequestValidat
|
|
|
70
68
|
|
|
71
69
|
<% if(query) { %>
|
|
72
70
|
if (definition.request.query) {
|
|
73
|
-
|
|
74
|
-
const coercedQuery = this.coerceQueryToSchema(request.query, definition.request.query.shape);
|
|
75
|
-
<% } else { %>
|
|
76
|
-
const coercedQuery = this.coerceQueryToSchema(request.query, definition.request.query.unwrap().shape);
|
|
77
|
-
<% } %>
|
|
71
|
+
const coercedQuery = this.coerceQueryToSchema(request.query, this.getSchema(definition.request.query));
|
|
78
72
|
const result = definition.request.query.safeParse(coercedQuery);
|
|
79
73
|
|
|
80
74
|
if (!result.success) {
|
|
81
75
|
error.addQueryIssues(result.error.issues);
|
|
82
76
|
} else {
|
|
83
|
-
validatedRequest.query = result.data;
|
|
77
|
+
validatedRequest.query = result.data as IHttpRequest["query"];
|
|
84
78
|
}
|
|
85
79
|
}
|
|
86
80
|
<% } %>
|
|
@@ -107,4 +101,4 @@ export class <%= pascalCaseOperationId %>RequestValidator extends RequestValidat
|
|
|
107
101
|
|
|
108
102
|
return result.data;
|
|
109
103
|
}
|
|
110
|
-
}
|
|
104
|
+
}
|
|
@@ -6,12 +6,15 @@
|
|
|
6
6
|
* @generated by @rexeus/typeweaver
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
<% if (ownResponses.length > 0) { %>
|
|
10
|
+
import { HttpStatusCode } from "<%= coreDir %>";
|
|
11
|
+
import type { ITypedHttpResponse } from "<%= coreDir %>";
|
|
12
|
+
<% } %>
|
|
10
13
|
<% for (const shared of sharedResponses) { %>
|
|
11
|
-
import type { I<%= shared.name %>Response
|
|
14
|
+
import type { I<%= shared.name %>Response } from "<%= shared.path %>";
|
|
12
15
|
<% } %>
|
|
13
16
|
<% for(const entityResponse of entityResponses) { %>
|
|
14
|
-
import type { I<%= entityResponse.name %>Response
|
|
17
|
+
import type { I<%= entityResponse.name %>Response } from "<%= entityResponse.path %>";
|
|
15
18
|
<% } %>
|
|
16
19
|
|
|
17
20
|
<% for (const ownResponse of ownResponses) { %>
|
|
@@ -23,60 +26,28 @@ import type { I<%= entityResponse.name %>Response, <%= entityResponse.name %>Res
|
|
|
23
26
|
export type I<%= ownResponse.name %>ResponseBody = <%- ownResponse.body %>;
|
|
24
27
|
<% } %>
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
<%= ownResponse.header ? `header: I${ownResponse.name}ResponseHeader;` : "" %>
|
|
30
|
-
<%= ownResponse.body ? `body: I${ownResponse.name}ResponseBody;` : "" %>
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export class <%= ownResponse.name %>Response extends HttpResponse<
|
|
29
|
+
export type I<%= ownResponse.name %>Response = ITypedHttpResponse<
|
|
30
|
+
"<%= ownResponse.name %>",
|
|
31
|
+
HttpStatusCode.<%= ownResponse.statusCodeKey %>,
|
|
34
32
|
<%= ownResponse.header ? `I${ownResponse.name}ResponseHeader` : 'undefined' %>,
|
|
35
33
|
<%= ownResponse.body ? `I${ownResponse.name}ResponseBody` : 'undefined' %>
|
|
36
|
-
|
|
37
|
-
public override readonly statusCode = HttpStatusCode.<%= ownResponse.statusCodeKey %>;
|
|
34
|
+
>;
|
|
38
35
|
|
|
39
36
|
<% if (ownResponse.header || ownResponse.body) { %>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
);
|
|
46
|
-
}
|
|
37
|
+
export const create<%= ownResponse.name %>Response = (input: Omit<I<%= ownResponse.name %>Response, "type" | "statusCode">): I<%= ownResponse.name %>Response => ({
|
|
38
|
+
...input,
|
|
39
|
+
type: "<%= ownResponse.name %>",
|
|
40
|
+
statusCode: HttpStatusCode.<%= ownResponse.statusCodeKey %>,
|
|
41
|
+
});
|
|
47
42
|
<% } else { %>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
undefined,
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
<% } %>
|
|
56
|
-
}
|
|
43
|
+
export const create<%= ownResponse.name %>Response = (): I<%= ownResponse.name %>Response => ({
|
|
44
|
+
type: "<%= ownResponse.name %>",
|
|
45
|
+
statusCode: HttpStatusCode.<%= ownResponse.statusCodeKey %>,
|
|
46
|
+
});
|
|
57
47
|
<% } %>
|
|
58
|
-
|
|
59
|
-
<%
|
|
60
|
-
// Identify success responses (status codes 200-299)
|
|
61
|
-
const successResponses = ownResponses.filter(response => response.statusCode >= 200 && response.statusCode < 300);
|
|
62
|
-
const errorResponses = ownResponses.filter(response => response.statusCode < 200 || response.statusCode >= 300);
|
|
63
|
-
%>
|
|
64
|
-
|
|
65
|
-
<% if (successResponses.length > 0) { %>
|
|
66
|
-
export type I<%= pascalCaseOperationId %>SuccessResponses =<% if (successResponses.length === 1) { %> I<%= successResponses[0].name %>Response;<% } else { %>
|
|
67
|
-
<% for (const [index, successResponse] of successResponses.entries()) { %>
|
|
68
|
-
<% if (index === 0) { %> I<%= successResponse.name %>Response<% } else { %>
|
|
69
|
-
| I<%= successResponse.name %>Response<% } %>
|
|
70
|
-
<% } %>;<% } %>
|
|
71
|
-
|
|
72
|
-
export type <%= pascalCaseOperationId %>SuccessResponses =<% if (successResponses.length === 1) { %> <%= successResponses[0].name %>Response;<% } else { %>
|
|
73
|
-
<% for (const [index, successResponse] of successResponses.entries()) { %>
|
|
74
|
-
<% if (index === 0) { %> <%= successResponse.name %>Response<% } else { %>
|
|
75
|
-
| <%= successResponse.name %>Response<% } %>
|
|
76
|
-
<% } %>;<% } %>
|
|
77
48
|
<% } %>
|
|
78
49
|
|
|
79
|
-
export type
|
|
50
|
+
export type <%= pascalCaseOperationId %>Response =
|
|
80
51
|
<% for (const ownResponse of ownResponses) { %>
|
|
81
52
|
| I<%= ownResponse.name %>Response
|
|
82
53
|
<% } %>
|
|
@@ -86,14 +57,3 @@ export type I<%= pascalCaseOperationId %>Response =
|
|
|
86
57
|
<% for (const entityResponse of entityResponses) { %>
|
|
87
58
|
| I<%= entityResponse.name %>Response
|
|
88
59
|
<% } %>;
|
|
89
|
-
|
|
90
|
-
export type <%= pascalCaseOperationId %>Response =
|
|
91
|
-
<% for (const ownResponse of ownResponses) { %>
|
|
92
|
-
| <%= ownResponse.name %>Response
|
|
93
|
-
<% } %>
|
|
94
|
-
<% for (const shared of sharedResponses) { %>
|
|
95
|
-
| <%= shared.name %>Response
|
|
96
|
-
<% } %>
|
|
97
|
-
<% for (const entityResponse of entityResponses) { %>
|
|
98
|
-
| <%= entityResponse.name %>Response
|
|
99
|
-
<% } %>;
|
|
@@ -2,98 +2,36 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* This file was automatically generated by typeweaver.
|
|
4
4
|
* DO NOT EDIT. Instead, modify the source definition file and generate again.
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* @generated by @rexeus/typeweaver
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
type IHttpResponse,
|
|
12
|
-
type SafeResponseValidationResult,
|
|
13
|
-
ResponseValidationError
|
|
14
|
-
} from "@rexeus/typeweaver-core";
|
|
9
|
+
import spec from "<%= specPath %>";
|
|
15
10
|
import {
|
|
11
|
+
getOperationDefinition,
|
|
12
|
+
getResponseDefinition,
|
|
13
|
+
type ResponseEntry,
|
|
16
14
|
ResponseValidator,
|
|
17
15
|
} from "../lib/types";
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
import type { <%= pascalCaseOperationId %>Response } from "<%= responseFile %>";
|
|
17
|
+
|
|
18
|
+
const definition = getOperationDefinition(spec, "<%= resourceName %>", "<%= operationId %>");
|
|
19
|
+
<% for (const response of [...ownResponses, ...sharedResponses]) { %>
|
|
20
|
+
const <%= response.definitionVariableName %> = getResponseDefinition(
|
|
21
|
+
definition.responses,
|
|
22
|
+
"<%= response.name %>"
|
|
23
|
+
);
|
|
23
24
|
<% } %>
|
|
24
|
-
} from "<%= responseFile %>";
|
|
25
|
-
<% for (const sharedResponse of sharedResponses) { %>
|
|
26
|
-
import {
|
|
27
|
-
type I<%= sharedResponse.name %>Response,
|
|
28
|
-
<%= sharedResponse.name %>Response,
|
|
29
|
-
} from "<%= sharedResponse.importPath %>";
|
|
30
|
-
<% } %>
|
|
31
|
-
|
|
32
|
-
export class <%= pascalCaseOperationId %>ResponseValidator extends ResponseValidator {
|
|
33
|
-
public safeValidate(response: IHttpResponse): SafeResponseValidationResult<<%= pascalCaseOperationId %>Response> {
|
|
34
|
-
const result = this.validateAgainstDefinedResponses(response);
|
|
35
|
-
|
|
36
|
-
if(!result.isValid && !result.error.hasResponseIssues()) {
|
|
37
|
-
result.error.addStatusCodeIssue([
|
|
38
|
-
<% const sortedStatusCodes = allStatusCodes.map(statusCode => statusCode.statusCode).sort() %>
|
|
39
|
-
<% for (const statusCode of sortedStatusCodes) { %><%= statusCode %>,<% } %>
|
|
40
|
-
]);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return result;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
public validate(response: IHttpResponse): <%= pascalCaseOperationId %>Response {
|
|
47
|
-
const result = this.safeValidate(response);
|
|
48
|
-
|
|
49
|
-
if (!result.isValid) {
|
|
50
|
-
throw result.error;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return result.data;
|
|
54
|
-
}
|
|
55
25
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
<% for (const statusCode of
|
|
60
|
-
|
|
61
|
-
<%
|
|
62
|
-
for (const response of [...ownResponses, ...sharedResponses]) {
|
|
63
|
-
if (response.statusCode === statusCode.statusCode) {
|
|
64
|
-
%>
|
|
65
|
-
const validate<%= response.name %>ResponseResult = this.validate<%= response.name %>Response(response, error);
|
|
66
|
-
if (validate<%= response.name %>ResponseResult.isValid) {
|
|
67
|
-
return validate<%= response.name %>ResponseResult;
|
|
68
|
-
}
|
|
69
|
-
<% } } %>
|
|
70
|
-
}
|
|
26
|
+
export class <%= pascalCaseOperationId %>ResponseValidator extends ResponseValidator<<%= pascalCaseOperationId %>Response> {
|
|
27
|
+
protected override readonly expectedStatusCodes = [
|
|
28
|
+
<% const sortedStatusCodes = allStatusCodes.map(statusCode => statusCode.statusCode).sort() %>
|
|
29
|
+
<% for (const statusCode of sortedStatusCodes) { %><%= statusCode %>,<% } %>
|
|
30
|
+
];
|
|
71
31
|
|
|
32
|
+
protected override readonly responseEntries: readonly ResponseEntry[] = [
|
|
33
|
+
<% for (const response of [...ownResponses, ...sharedResponses]) { %>
|
|
34
|
+
{ name: "<%= response.name %>", statusCode: <%= response.statusCode %>, headerSchema: <%= response.definitionVariableName %>.header, bodySchema: <%= response.definitionVariableName %>.body },
|
|
72
35
|
<% } %>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
isValid: false,
|
|
76
|
-
error
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
<% for (const response of [...ownResponses, ...sharedResponses]) { %>
|
|
81
|
-
private validate<%= response.name %>Response(response: IHttpResponse, error: ResponseValidationError): SafeResponseValidationResult<<%= response.name %>Response> {
|
|
82
|
-
const result = this.validateResponseType<<%= response.name %>Response>(
|
|
83
|
-
"<%= response.name.replace(/Response$/, '') %>",
|
|
84
|
-
<% if(response.hasHeader) { %>definition.responses[<%= response.index %>] && "header" in definition.responses[<%= response.index %>] ? definition.responses[<%= response.index %>]!.header : undefined<% } else { %>undefined<% } %>,
|
|
85
|
-
<% if(response.hasBody) { %>definition.responses[<%= response.index %>] && "body" in definition.responses[<%= response.index %>] ? definition.responses[<%= response.index %>]!.body : undefined<% } else { %>undefined<% } %>,
|
|
86
|
-
)(response, error);
|
|
87
|
-
|
|
88
|
-
if (!result.isValid) {
|
|
89
|
-
return result;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
<% if (response.hasHeader || response.hasBody) { %>
|
|
93
|
-
return { isValid: true, data: new <%= response.name %>Response(result.data as I<%= response.name %>Response) };
|
|
94
|
-
<% } else { %>
|
|
95
|
-
return { isValid: true, data: new <%= response.name %>Response() };
|
|
96
|
-
<% } %>
|
|
97
|
-
}
|
|
98
|
-
<% } %>
|
|
99
|
-
}
|
|
36
|
+
];
|
|
37
|
+
}
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* This file was automatically generated by typeweaver.
|
|
4
4
|
* DO NOT EDIT. Instead, modify the source definition file and generate again.
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* @generated by @rexeus/typeweaver
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { HttpStatusCode } from "<%= coreDir %>";
|
|
10
|
+
import type { ITypedHttpResponse } from "<%= coreDir %>";
|
|
10
11
|
|
|
11
12
|
<% if (headerTsType) { %>
|
|
12
13
|
export type I<%= pascalCaseName %>ResponseHeader = <%- headerTsType %>;
|
|
@@ -16,35 +17,22 @@ import { HttpResponse, HttpStatusCode } from "<%= coreDir %>";
|
|
|
16
17
|
export type I<%= pascalCaseName %>ResponseBody = <%- bodyTsType %>;
|
|
17
18
|
<% } %>
|
|
18
19
|
|
|
19
|
-
export type I<%= pascalCaseName %>Response =
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
<%= sharedResponse.body ? `body: I${pascalCaseName}ResponseBody;` : "" %>
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export class <%= pascalCaseName %>Response extends HttpResponse<
|
|
20
|
+
export type I<%= pascalCaseName %>Response = ITypedHttpResponse<
|
|
21
|
+
"<%= pascalCaseName %>",
|
|
22
|
+
HttpStatusCode.<%= httpStatusCode[sharedResponse.statusCode] %>,
|
|
26
23
|
<%= sharedResponse.header ? `I${pascalCaseName}ResponseHeader` : 'undefined' %>,
|
|
27
24
|
<%= sharedResponse.body ? `I${pascalCaseName}ResponseBody` : 'undefined' %>
|
|
28
|
-
|
|
29
|
-
public override readonly statusCode = HttpStatusCode.<%= httpStatusCode[sharedResponse.statusCode] %>;
|
|
25
|
+
>;
|
|
30
26
|
|
|
31
27
|
<% if (sharedResponse.header || sharedResponse.body) { %>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
);
|
|
38
|
-
}
|
|
28
|
+
export const create<%= pascalCaseName %>Response = (input: Omit<I<%= pascalCaseName %>Response, "type" | "statusCode">): I<%= pascalCaseName %>Response => ({
|
|
29
|
+
...input,
|
|
30
|
+
type: "<%= pascalCaseName %>",
|
|
31
|
+
statusCode: HttpStatusCode.<%= httpStatusCode[sharedResponse.statusCode] %>,
|
|
32
|
+
});
|
|
39
33
|
<% } else { %>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
undefined,
|
|
45
|
-
);
|
|
46
|
-
}
|
|
34
|
+
export const create<%= pascalCaseName %>Response = (): I<%= pascalCaseName %>Response => ({
|
|
35
|
+
type: "<%= pascalCaseName %>",
|
|
36
|
+
statusCode: HttpStatusCode.<%= httpStatusCode[sharedResponse.statusCode] %>,
|
|
37
|
+
});
|
|
47
38
|
<% } %>
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rexeus/typeweaver-types",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Generates request and response types plus validators aligned with your API contract. Powered by Typeweaver 🧵✨",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -47,21 +47,21 @@
|
|
|
47
47
|
},
|
|
48
48
|
"homepage": "https://github.com/rexeus/typeweaver#readme",
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"@rexeus/typeweaver-core": "^0.
|
|
51
|
-
"@rexeus/typeweaver-gen": "^0.
|
|
50
|
+
"@rexeus/typeweaver-core": "^0.9.0",
|
|
51
|
+
"@rexeus/typeweaver-gen": "^0.9.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"oxc-transform": "^0.
|
|
54
|
+
"oxc-transform": "^0.121.0",
|
|
55
55
|
"test-utils": "file:../test-utils",
|
|
56
|
-
"@rexeus/typeweaver-core": "^0.
|
|
57
|
-
"@rexeus/typeweaver-gen": "^0.
|
|
56
|
+
"@rexeus/typeweaver-core": "^0.9.0",
|
|
57
|
+
"@rexeus/typeweaver-gen": "^0.9.0"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"case": "^1.6.3",
|
|
61
|
-
"@rexeus/typeweaver-zod-to-ts": "^0.
|
|
61
|
+
"@rexeus/typeweaver-zod-to-ts": "^0.9.0"
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|
|
64
|
-
"typecheck": "tsc --noEmit",
|
|
64
|
+
"typecheck": "tsc --noEmit -p tsconfig.typecheck.json",
|
|
65
65
|
"format": "oxfmt",
|
|
66
66
|
"build": "tsdown && mkdir -p ./dist/templates ./dist/lib && cp -r ./src/templates/* ./dist/templates/ && node scripts/compile-lib.mjs && cp src/lib-declarations/*.d.ts dist/lib/ && cp src/lib/index.ts dist/lib/ && cp ../../LICENSE ../../NOTICE ./dist/",
|
|
67
67
|
"test": "vitest --run",
|
package/dist/lib/assert.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function assert(value: unknown, message?: string): asserts value;
|
package/dist/lib/assert.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This file was automatically generated by typeweaver.
|
|
3
|
-
* DO NOT EDIT. Instead, modify the source definition file and generate again.
|
|
4
|
-
*
|
|
5
|
-
* @generated by @rexeus/typeweaver
|
|
6
|
-
*/
|
|
7
|
-
export function assert(value, message) {
|
|
8
|
-
if (!value) {
|
|
9
|
-
throw new Error(message || "Assertion failed");
|
|
10
|
-
}
|
|
11
|
-
}
|