@rexeus/typeweaver-types 0.0.2 → 0.0.4
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 +153 -419
- package/dist/LICENSE +202 -0
- package/dist/NOTICE +4 -0
- package/dist/index.js +169 -2156
- package/dist/lib/RequestValidator.ts +13 -2
- package/dist/lib/ResponseValidator.ts +82 -1
- package/dist/lib/Validator.ts +207 -0
- package/dist/lib/assert.ts +7 -0
- package/dist/lib/index.ts +8 -1
- package/dist/templates/Request.ejs +12 -53
- package/dist/templates/RequestValidator.ejs +12 -2
- package/dist/templates/Response.ejs +8 -0
- package/dist/templates/ResponseValidator.ejs +35 -81
- package/dist/templates/SharedResponse.ejs +8 -0
- package/package.json +15 -10
- package/dist/lib/InvalidResponseStatusCodeError.ts +0 -21
|
@@ -1,8 +1,16 @@
|
|
|
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
|
+
|
|
1
8
|
import type {
|
|
2
9
|
IHttpRequest,
|
|
3
10
|
IRequestValidator,
|
|
4
11
|
SafeRequestValidationResult,
|
|
5
12
|
} from "@rexeus/typeweaver-core";
|
|
13
|
+
import { Validator } from "./Validator";
|
|
6
14
|
|
|
7
15
|
/**
|
|
8
16
|
* Abstract base class for HTTP request validation.
|
|
@@ -17,9 +25,12 @@ import type {
|
|
|
17
25
|
* - Return validated data (for `validate`)
|
|
18
26
|
* - Return success/error result (for `safeValidate`)
|
|
19
27
|
*/
|
|
20
|
-
export abstract class RequestValidator
|
|
28
|
+
export abstract class RequestValidator
|
|
29
|
+
extends Validator
|
|
30
|
+
implements IRequestValidator
|
|
31
|
+
{
|
|
21
32
|
public constructor() {
|
|
22
|
-
|
|
33
|
+
super();
|
|
23
34
|
}
|
|
24
35
|
|
|
25
36
|
/**
|
|
@@ -1,8 +1,20 @@
|
|
|
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
|
+
|
|
1
8
|
import type {
|
|
9
|
+
HttpBodySchema,
|
|
10
|
+
HttpHeaderSchema,
|
|
2
11
|
IHttpResponse,
|
|
3
12
|
IResponseValidator,
|
|
13
|
+
ResponseValidationError,
|
|
4
14
|
SafeResponseValidationResult,
|
|
5
15
|
} from "@rexeus/typeweaver-core";
|
|
16
|
+
import { Validator } from "./Validator";
|
|
17
|
+
import type { ZodSafeParseResult } from "zod/v4";
|
|
6
18
|
|
|
7
19
|
/**
|
|
8
20
|
* Abstract base class for HTTP response validation.
|
|
@@ -16,7 +28,10 @@ import type {
|
|
|
16
28
|
* Response validators are typically used in API clients to ensure
|
|
17
29
|
* responses match the expected format before processing.
|
|
18
30
|
*/
|
|
19
|
-
export abstract class ResponseValidator
|
|
31
|
+
export abstract class ResponseValidator
|
|
32
|
+
extends Validator
|
|
33
|
+
implements IResponseValidator
|
|
34
|
+
{
|
|
20
35
|
/**
|
|
21
36
|
* Validates a response without throwing errors.
|
|
22
37
|
*
|
|
@@ -36,4 +51,70 @@ export abstract class ResponseValidator implements IResponseValidator {
|
|
|
36
51
|
* @throws {ResponseValidationError} If response structure fails validation
|
|
37
52
|
*/
|
|
38
53
|
public abstract validate(response: IHttpResponse): IHttpResponse;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Generic response validation method that validates header and body schemas.
|
|
57
|
+
* This method reduces code duplication across individual response validators.
|
|
58
|
+
*
|
|
59
|
+
* @param responseName - Name of the response type for error reporting
|
|
60
|
+
* @param headerSchema - Zod schema for header validation (optional)
|
|
61
|
+
* @param bodySchema - Zod schema for body validation (optional)
|
|
62
|
+
* @returns Function that validates response and returns result
|
|
63
|
+
*/
|
|
64
|
+
protected validateResponseType<Response extends IHttpResponse>(
|
|
65
|
+
responseName: string,
|
|
66
|
+
headerSchema: HttpHeaderSchema | undefined,
|
|
67
|
+
bodySchema: HttpBodySchema | undefined
|
|
68
|
+
): (
|
|
69
|
+
response: IHttpResponse,
|
|
70
|
+
error: ResponseValidationError
|
|
71
|
+
) => SafeResponseValidationResult<Response> {
|
|
72
|
+
return (response, error) => {
|
|
73
|
+
let isValid = true;
|
|
74
|
+
const validatedResponse: IHttpResponse = {
|
|
75
|
+
statusCode: response.statusCode,
|
|
76
|
+
header: undefined,
|
|
77
|
+
body: undefined,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
if (bodySchema) {
|
|
81
|
+
const validateBodyResult = bodySchema.safeParse(
|
|
82
|
+
response.body
|
|
83
|
+
) as unknown as ZodSafeParseResult<Response["body"]>;
|
|
84
|
+
|
|
85
|
+
if (!validateBodyResult.success) {
|
|
86
|
+
error.addBodyIssues(responseName, validateBodyResult.error.issues);
|
|
87
|
+
isValid = false;
|
|
88
|
+
} else {
|
|
89
|
+
validatedResponse.body = validateBodyResult.data;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (headerSchema) {
|
|
94
|
+
const coercedHeader = this.coerceHeaderToSchema(
|
|
95
|
+
response.header,
|
|
96
|
+
headerSchema.shape
|
|
97
|
+
);
|
|
98
|
+
const validateHeaderResult = headerSchema.safeParse(
|
|
99
|
+
coercedHeader
|
|
100
|
+
) as unknown as ZodSafeParseResult<Response["header"]>;
|
|
101
|
+
|
|
102
|
+
if (!validateHeaderResult.success) {
|
|
103
|
+
error.addHeaderIssues(
|
|
104
|
+
responseName,
|
|
105
|
+
validateHeaderResult.error.issues
|
|
106
|
+
);
|
|
107
|
+
isValid = false;
|
|
108
|
+
} else {
|
|
109
|
+
validatedResponse.header = validateHeaderResult.data;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!isValid) {
|
|
114
|
+
return { isValid: false, error };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return { isValid: true, data: validatedResponse as Response };
|
|
118
|
+
};
|
|
119
|
+
}
|
|
39
120
|
}
|
|
@@ -0,0 +1,207 @@
|
|
|
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
|
+
|
|
8
|
+
import { $ZodArray, $ZodOptional } from "zod/v4/core";
|
|
9
|
+
import type { $ZodShape } from "zod/v4/core";
|
|
10
|
+
|
|
11
|
+
type SchemaInfo = { readonly originalKey: string; readonly isArray: boolean };
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Abstract base class for HTTP validation.
|
|
15
|
+
*
|
|
16
|
+
* This class provides the foundation for request and response validators that:
|
|
17
|
+
* - Analyze Zod schemas for correct single/multi value handling of headers and query parameters
|
|
18
|
+
* - Coerce objects to match schema expectations
|
|
19
|
+
*/
|
|
20
|
+
export abstract class Validator {
|
|
21
|
+
private static readonly schemaCacheCaseSensitive = new WeakMap<
|
|
22
|
+
$ZodShape,
|
|
23
|
+
Map<string, SchemaInfo>
|
|
24
|
+
>();
|
|
25
|
+
private static readonly schemaCacheCaseInsensitive = new WeakMap<
|
|
26
|
+
$ZodShape,
|
|
27
|
+
Map<string, SchemaInfo>
|
|
28
|
+
>();
|
|
29
|
+
/**
|
|
30
|
+
* Analyzes a Zod schema shape to create an efficient lookup map.
|
|
31
|
+
* Results are cached using WeakMap for optimal performance.
|
|
32
|
+
*
|
|
33
|
+
* @param shape - The Zod schema shape to analyze
|
|
34
|
+
* @param caseSensitive - Whether to preserve key casing (true) or normalize to lowercase (false)
|
|
35
|
+
* @returns Map with lookup keys pointing to original key and array type information
|
|
36
|
+
*/
|
|
37
|
+
protected analyzeSchema(
|
|
38
|
+
shape: $ZodShape,
|
|
39
|
+
caseSensitive: boolean
|
|
40
|
+
): Map<string, SchemaInfo> {
|
|
41
|
+
const cache = caseSensitive
|
|
42
|
+
? Validator.schemaCacheCaseSensitive
|
|
43
|
+
: Validator.schemaCacheCaseInsensitive;
|
|
44
|
+
|
|
45
|
+
const cached = cache.get(shape);
|
|
46
|
+
if (cached) {
|
|
47
|
+
return cached;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const schemaMap = this.buildSchemaMap(shape, caseSensitive);
|
|
51
|
+
cache.set(shape, schemaMap);
|
|
52
|
+
return schemaMap;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Builds a schema map by analyzing the Zod shape structure.
|
|
57
|
+
* Extracts type information for each field to support proper coercion.
|
|
58
|
+
*/
|
|
59
|
+
private buildSchemaMap(
|
|
60
|
+
shape: $ZodShape,
|
|
61
|
+
caseSensitive: boolean
|
|
62
|
+
): Map<string, SchemaInfo> {
|
|
63
|
+
const schemaMap = new Map<string, SchemaInfo>();
|
|
64
|
+
for (const [key, zodType] of Object.entries(shape)) {
|
|
65
|
+
if (!zodType) continue;
|
|
66
|
+
|
|
67
|
+
const isArray =
|
|
68
|
+
zodType instanceof $ZodArray ||
|
|
69
|
+
(zodType instanceof $ZodOptional &&
|
|
70
|
+
zodType._zod.def.innerType instanceof $ZodArray);
|
|
71
|
+
|
|
72
|
+
const lookupKey = caseSensitive ? key : key.toLowerCase();
|
|
73
|
+
schemaMap.set(lookupKey, { originalKey: key, isArray });
|
|
74
|
+
}
|
|
75
|
+
return schemaMap;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Coerces objects to match schema expectations with configurable case sensitivity.
|
|
80
|
+
* Values not in the schema are ignored.
|
|
81
|
+
*
|
|
82
|
+
* @param data - The data object to coerce
|
|
83
|
+
* @param shape - The Zod schema shape to match against
|
|
84
|
+
* @param caseSensitive - Whether keys should match exactly (true) or case-insensitively (false)
|
|
85
|
+
* @returns Coerced data object with proper key casing and array coercion
|
|
86
|
+
*/
|
|
87
|
+
protected coerceToSchema(
|
|
88
|
+
data: unknown,
|
|
89
|
+
shape: $ZodShape,
|
|
90
|
+
caseSensitive: boolean
|
|
91
|
+
): unknown {
|
|
92
|
+
if (typeof data !== "object" || data === null) {
|
|
93
|
+
return data;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const schemaMap = this.analyzeSchema(shape, caseSensitive);
|
|
97
|
+
const coerced: Record<string, unknown | unknown[]> = {};
|
|
98
|
+
|
|
99
|
+
for (const [key, value] of Object.entries(data)) {
|
|
100
|
+
const normalizedKey = caseSensitive ? key : key.toLowerCase();
|
|
101
|
+
const schemaInfo = schemaMap.get(normalizedKey);
|
|
102
|
+
|
|
103
|
+
if (schemaInfo) {
|
|
104
|
+
this.addValueToCoerced(
|
|
105
|
+
coerced,
|
|
106
|
+
normalizedKey,
|
|
107
|
+
value,
|
|
108
|
+
schemaInfo.isArray
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
// Headers/params not in schema are ignored (strict validation)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// If case-sensitive, return coerced object as is
|
|
115
|
+
if (caseSensitive) {
|
|
116
|
+
return coerced;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// If case-insensitive, map back to original keys from schema
|
|
120
|
+
return this.mapToOriginalKeys(coerced, schemaMap);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Adds a value to the coerced object, handling collisions when multiple
|
|
125
|
+
* values exist for the same key (e.g., duplicate headers with different casing).
|
|
126
|
+
* Preserves all values as arrays when collisions occur to prevent data loss.
|
|
127
|
+
*/
|
|
128
|
+
private addValueToCoerced(
|
|
129
|
+
coerced: Record<string, unknown | unknown[]>,
|
|
130
|
+
key: string,
|
|
131
|
+
value: unknown,
|
|
132
|
+
expectsArray: boolean
|
|
133
|
+
): void {
|
|
134
|
+
const existing = coerced[key];
|
|
135
|
+
const newValue = this.coerceValueStructure(value, expectsArray);
|
|
136
|
+
|
|
137
|
+
if (existing === undefined) {
|
|
138
|
+
coerced[key] = newValue;
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Merge existing and new values
|
|
143
|
+
const existingArray = Array.isArray(existing) ? existing : [existing];
|
|
144
|
+
const newArray = Array.isArray(newValue) ? newValue : [newValue];
|
|
145
|
+
const merged = [...existingArray, ...newArray];
|
|
146
|
+
|
|
147
|
+
// If schema expects a single value but we have multiple, preserve as array
|
|
148
|
+
// to avoid data loss (validation will catch this later)
|
|
149
|
+
coerced[key] = expectsArray || merged.length > 1 ? merged : merged[0];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Coerces a value's structure to match schema expectations.
|
|
154
|
+
* Wraps single values in arrays when schema expects array type,
|
|
155
|
+
* unwraps single-element arrays when schema expects single value.
|
|
156
|
+
*/
|
|
157
|
+
private coerceValueStructure(
|
|
158
|
+
value: unknown,
|
|
159
|
+
expectsArray: boolean
|
|
160
|
+
): unknown | unknown[] {
|
|
161
|
+
if (expectsArray && !Array.isArray(value)) {
|
|
162
|
+
return [value];
|
|
163
|
+
}
|
|
164
|
+
if (!expectsArray && Array.isArray(value) && value.length === 1) {
|
|
165
|
+
return value[0];
|
|
166
|
+
}
|
|
167
|
+
return value;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Maps normalized (lowercase) keys back to their original casing as defined in the schema.
|
|
172
|
+
* Used for case-insensitive matching where the output should preserve schema-defined casing.
|
|
173
|
+
*/
|
|
174
|
+
private mapToOriginalKeys(
|
|
175
|
+
coerced: Record<string, unknown | unknown[]>,
|
|
176
|
+
schemaMap: Map<string, SchemaInfo>
|
|
177
|
+
): Record<string, unknown | unknown[]> {
|
|
178
|
+
const withOriginalKeys: Record<string, unknown | unknown[]> = {};
|
|
179
|
+
for (const [key, value] of Object.entries(coerced)) {
|
|
180
|
+
const originalKey = schemaMap.get(key)?.originalKey ?? key;
|
|
181
|
+
withOriginalKeys[originalKey] = value;
|
|
182
|
+
}
|
|
183
|
+
return withOriginalKeys;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Coerces header data to match schema expectations with case-insensitive matching.
|
|
188
|
+
*
|
|
189
|
+
* @param header - The header object to coerce
|
|
190
|
+
* @param shape - The Zod schema shape for headers
|
|
191
|
+
* @returns Coerced header object
|
|
192
|
+
*/
|
|
193
|
+
protected coerceHeaderToSchema(header: unknown, shape: $ZodShape): unknown {
|
|
194
|
+
return this.coerceToSchema(header, shape, false); // case-insensitive
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Coerces query data to match schema expectations with case-sensitive matching.
|
|
199
|
+
*
|
|
200
|
+
* @param query - The query object to coerce
|
|
201
|
+
* @param shape - The Zod schema shape for query parameters
|
|
202
|
+
* @returns Coerced query object
|
|
203
|
+
*/
|
|
204
|
+
protected coerceQueryToSchema(query: unknown, shape: $ZodShape): unknown {
|
|
205
|
+
return this.coerceToSchema(query, shape, true); // case-sensitive
|
|
206
|
+
}
|
|
207
|
+
}
|
package/dist/lib/assert.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
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
|
+
|
|
1
8
|
export function assert(value: any, message?: string): asserts value {
|
|
2
9
|
if (!value) {
|
|
3
10
|
throw new Error(message || "Assertion failed");
|
package/dist/lib/index.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
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
|
+
|
|
1
8
|
export * from "./assert";
|
|
2
|
-
export * from "./InvalidResponseStatusCodeError";
|
|
3
9
|
export * from "./RequestValidator";
|
|
4
10
|
export * from "./ResponseValidator";
|
|
11
|
+
export * from "./Validator";
|
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/**
|
|
3
|
+
* This file was automatically generated by typeweaver.
|
|
4
|
+
* DO NOT EDIT. Instead, modify the source definition file and generate again.
|
|
5
|
+
*
|
|
6
|
+
* @generated by @rexeus/typeweaver
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { HttpMethod } from "@rexeus/typeweaver-core";
|
|
5
10
|
import {
|
|
6
11
|
type <%= pascalCaseOperationId %>Response,
|
|
7
|
-
<% for (const ownResponse of
|
|
12
|
+
<% for (const ownResponse of ownErrorResponses) { %>
|
|
8
13
|
<%= ownResponse.name %>Response,
|
|
9
14
|
<% } %>
|
|
10
15
|
} from "<%= responseFile %>";
|
|
11
|
-
<% for (const sharedResponse of
|
|
16
|
+
<% for (const sharedResponse of sharedErrorResponses) { %>
|
|
12
17
|
import { <%= sharedResponse.name %>Response } from "<%= sharedResponse.path %>";
|
|
13
18
|
<% } %>
|
|
14
19
|
|
|
@@ -44,50 +49,4 @@ export type I<%= pascalCaseOperationId %>Request = {
|
|
|
44
49
|
<% } else { %>
|
|
45
50
|
export type Successful<%= pascalCaseOperationId %>Response = Exclude<<%= pascalCaseOperationId %>Response, <%= [...ownErrorResponses, ...sharedErrorResponses].map(({ name }) => `${name}Response`).join(" | ") %>
|
|
46
51
|
>;
|
|
47
|
-
<% } %>
|
|
48
|
-
|
|
49
|
-
export class <%= pascalCaseOperationId %>RequestCommand extends RequestCommand implements I<%= pascalCaseOperationId %>Request {
|
|
50
|
-
public override readonly method = definition.method as HttpMethod.<%= method %>;
|
|
51
|
-
public override readonly path = definition.path;
|
|
52
|
-
|
|
53
|
-
public <%= headerTsType ? `override` : `declare` %> readonly header: <%= headerTsType ? `I${pascalCaseOperationId}RequestHeader` : `undefined` %>;
|
|
54
|
-
public <%= paramTsType ? `override` : `declare` %> readonly param: <%= paramTsType ? `I${pascalCaseOperationId}RequestParam` : `undefined` %>;
|
|
55
|
-
public <%= queryTsType ? `override` : `declare` %> readonly query: <%= queryTsType ? `I${pascalCaseOperationId}RequestQuery` : `undefined` %>;
|
|
56
|
-
public <%= bodyTsType ? `override` : `declare` %> readonly body: <%= bodyTsType ? `I${pascalCaseOperationId}RequestBody` : `undefined` %>;
|
|
57
|
-
|
|
58
|
-
private readonly responseValidator: <%= pascalCaseOperationId %>ResponseValidator;
|
|
59
|
-
|
|
60
|
-
public constructor(input: Omit<I<%= pascalCaseOperationId %>Request, "method" | "path">) {
|
|
61
|
-
super();
|
|
62
|
-
|
|
63
|
-
<% if (headerTsType) { %>
|
|
64
|
-
this.header = input.header;
|
|
65
|
-
<% } %>
|
|
66
|
-
|
|
67
|
-
<% if (paramTsType) { %>
|
|
68
|
-
this.param = input.param;
|
|
69
|
-
<% } %>
|
|
70
|
-
|
|
71
|
-
<% if (queryTsType) { %>
|
|
72
|
-
this.query = input.query;
|
|
73
|
-
<% } %>
|
|
74
|
-
|
|
75
|
-
<% if (bodyTsType) { %>
|
|
76
|
-
this.body = input.body;
|
|
77
|
-
<% } %>
|
|
78
|
-
|
|
79
|
-
this.responseValidator = new <%= pascalCaseOperationId %>ResponseValidator();
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
public processResponse(response: IHttpResponse): Successful<%= pascalCaseOperationId %>Response {
|
|
83
|
-
const result = this.responseValidator.validate(response);
|
|
84
|
-
|
|
85
|
-
<% for (const successResponse of [...ownSuccessResponses, ...sharedSuccessResponses]) { %>
|
|
86
|
-
if (result instanceof <%= successResponse.name %>Response) {
|
|
87
|
-
return result;
|
|
88
|
-
}
|
|
89
|
-
<% } %>
|
|
90
|
-
|
|
91
|
-
throw result;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
52
|
+
<% } %>
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/**
|
|
3
|
+
* This file was automatically generated by typeweaver.
|
|
4
|
+
* DO NOT EDIT. Instead, modify the source definition file and generate again.
|
|
5
|
+
*
|
|
6
|
+
* @generated by @rexeus/typeweaver
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
import definition from "<%= sourcePath %>";
|
|
2
10
|
import {
|
|
3
11
|
type IHttpRequest,
|
|
@@ -33,7 +41,8 @@ export class <%= pascalCaseOperationId %>RequestValidator extends RequestValidat
|
|
|
33
41
|
|
|
34
42
|
<% if(header) { %>
|
|
35
43
|
if (definition.request.header) {
|
|
36
|
-
const
|
|
44
|
+
const coercedHeader = this.coerceHeaderToSchema(request.header, definition.request.header.shape);
|
|
45
|
+
const result = definition.request.header.safeParse(coercedHeader);
|
|
37
46
|
|
|
38
47
|
if (!result.success) {
|
|
39
48
|
error.addHeaderIssues(result.error.issues);
|
|
@@ -57,7 +66,8 @@ export class <%= pascalCaseOperationId %>RequestValidator extends RequestValidat
|
|
|
57
66
|
|
|
58
67
|
<% if(query) { %>
|
|
59
68
|
if (definition.request.query) {
|
|
60
|
-
const
|
|
69
|
+
const coercedQuery = this.coerceQueryToSchema(request.query, definition.request.query.shape);
|
|
70
|
+
const result = definition.request.query.safeParse(coercedQuery);
|
|
61
71
|
|
|
62
72
|
if (!result.success) {
|
|
63
73
|
error.addQueryIssues(result.error.issues);
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/**
|
|
3
|
+
* This file was automatically generated by typeweaver.
|
|
4
|
+
* DO NOT EDIT. Instead, modify the source definition file and generate again.
|
|
5
|
+
*
|
|
6
|
+
* @generated by @rexeus/typeweaver
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
import { HttpResponse, HttpStatusCode } from "<%= coreDir %>";
|
|
2
10
|
<% for(const sharedResponse of sharedResponses) { %>
|
|
3
11
|
import type { I<%= sharedResponse.name %>Response, <%= sharedResponse.name %>Response } from "<%= sharedResponse.path %>";
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/**
|
|
3
|
+
* This file was automatically generated by typeweaver.
|
|
4
|
+
* DO NOT EDIT. Instead, modify the source definition file and generate again.
|
|
5
|
+
*
|
|
6
|
+
* @generated by @rexeus/typeweaver
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
import definition from "<%= sourcePath %>";
|
|
10
|
+
import type { ZodSafeParseResult } from "zod/v4";
|
|
2
11
|
import {
|
|
3
12
|
type IHttpResponse,
|
|
4
13
|
type SafeResponseValidationResult,
|
|
@@ -6,8 +15,6 @@ import {
|
|
|
6
15
|
} from "@rexeus/typeweaver-core";
|
|
7
16
|
import {
|
|
8
17
|
ResponseValidator,
|
|
9
|
-
InvalidResponseStatusCodeError,
|
|
10
|
-
assert,
|
|
11
18
|
} from "../lib/types";
|
|
12
19
|
import {
|
|
13
20
|
type <%= pascalCaseOperationId %>Response,
|
|
@@ -25,42 +32,16 @@ type <%= pascalCaseOperationId %>Response,
|
|
|
25
32
|
|
|
26
33
|
export class <%= pascalCaseOperationId %>ResponseValidator extends ResponseValidator {
|
|
27
34
|
public safeValidate(response: IHttpResponse): SafeResponseValidationResult<<%= pascalCaseOperationId %>Response> {
|
|
28
|
-
const
|
|
29
|
-
const validationResult = this.validateAgainstDefinedResponses(response, error);
|
|
35
|
+
const result = this.validateAgainstDefinedResponses(response);
|
|
30
36
|
|
|
31
|
-
if
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
if(!result.isValid && !result.error.hasResponseIssues()) {
|
|
38
|
+
result.error.addStatusCodeIssue([
|
|
39
|
+
<% const sortedStatusCodes = allStatusCodes.map(statusCode => statusCode.statusCode).sort() %>
|
|
40
|
+
<% for (const statusCode of sortedStatusCodes) { %><%= statusCode %>,<% } %>
|
|
41
|
+
]);
|
|
36
42
|
}
|
|
37
43
|
|
|
38
|
-
|
|
39
|
-
switch (response.statusCode) {
|
|
40
|
-
<% for (const statusCode of allStatusCodes) { %>
|
|
41
|
-
case <%= statusCode.statusCode %>: {
|
|
42
|
-
<%
|
|
43
|
-
for (const response of [...ownResponses, ...sharedResponses]) {
|
|
44
|
-
if (response.statusCode === statusCode.statusCode) {
|
|
45
|
-
%>
|
|
46
|
-
if (validationResult.validResponseName === "<%= response.name %>") {
|
|
47
|
-
data = new <%= response.name %>Response(validationResult.validatedResponse as unknown as I<%= response.name %>Response);
|
|
48
|
-
break;
|
|
49
|
-
}
|
|
50
|
-
<% } } %>
|
|
51
|
-
throw new Error("Could not find a response for status code '<%= statusCode.statusCode %>'");
|
|
52
|
-
}
|
|
53
|
-
<% } %>
|
|
54
|
-
|
|
55
|
-
default: {
|
|
56
|
-
throw new InvalidResponseStatusCodeError(response);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return {
|
|
61
|
-
isValid: true,
|
|
62
|
-
data,
|
|
63
|
-
};
|
|
44
|
+
return result;
|
|
64
45
|
}
|
|
65
46
|
|
|
66
47
|
public validate(response: IHttpResponse): <%= pascalCaseOperationId %>Response {
|
|
@@ -73,12 +54,8 @@ export class <%= pascalCaseOperationId %>ResponseValidator extends ResponseValid
|
|
|
73
54
|
return result.data;
|
|
74
55
|
}
|
|
75
56
|
|
|
76
|
-
private validateAgainstDefinedResponses(response: IHttpResponse
|
|
77
|
-
const
|
|
78
|
-
statusCode: response.statusCode,
|
|
79
|
-
header: undefined,
|
|
80
|
-
body: undefined,
|
|
81
|
-
};
|
|
57
|
+
private validateAgainstDefinedResponses(response: IHttpResponse): SafeResponseValidationResult<<%= pascalCaseOperationId %>Response> {
|
|
58
|
+
const error = new ResponseValidationError(response.statusCode);
|
|
82
59
|
|
|
83
60
|
<% for (const statusCode of allStatusCodes) { %>
|
|
84
61
|
if (response.statusCode === <%= statusCode.statusCode %>) {
|
|
@@ -86,57 +63,34 @@ export class <%= pascalCaseOperationId %>ResponseValidator extends ResponseValid
|
|
|
86
63
|
for (const response of [...ownResponses, ...sharedResponses]) {
|
|
87
64
|
if (response.statusCode === statusCode.statusCode) {
|
|
88
65
|
%>
|
|
89
|
-
const
|
|
90
|
-
if (
|
|
91
|
-
return
|
|
66
|
+
const validate<%= response.name %>ResponseResult = this.validate<%= response.name %>Response(response, error);
|
|
67
|
+
if (validate<%= response.name %>ResponseResult.isValid) {
|
|
68
|
+
return validate<%= response.name %>ResponseResult;
|
|
92
69
|
}
|
|
93
70
|
<% } } %>
|
|
94
71
|
}
|
|
95
72
|
|
|
96
73
|
<% } %>
|
|
97
74
|
|
|
98
|
-
return {
|
|
75
|
+
return {
|
|
76
|
+
isValid: false,
|
|
77
|
+
error
|
|
78
|
+
};
|
|
99
79
|
}
|
|
100
80
|
|
|
101
81
|
<% for (const response of [...ownResponses, ...sharedResponses]) { %>
|
|
102
|
-
private validate<%= response.name %>Response(response: IHttpResponse,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
&& "body" in definition.responses[<%= response.index %>]
|
|
109
|
-
&& definition.responses[<%= response.index %>].body,
|
|
110
|
-
"'<%= response.name %>ResponseBody' has to be defined in the definition"
|
|
111
|
-
);
|
|
112
|
-
const validateBodyResult = definition.responses[<%= response.index %>].body.safeParse(response.body);
|
|
113
|
-
|
|
114
|
-
if (!validateBodyResult.success) {
|
|
115
|
-
error.addBodyIssues(validateBodyResult.error.issues);
|
|
116
|
-
isValid = false;
|
|
117
|
-
} else {
|
|
118
|
-
validatedResponse.body = validateBodyResult.data;
|
|
119
|
-
}
|
|
120
|
-
<% } %>
|
|
82
|
+
private validate<%= response.name %>Response(response: IHttpResponse, error: ResponseValidationError): SafeResponseValidationResult<<%= response.name %>Response> {
|
|
83
|
+
const result = this.validateResponseType<<%= response.name %>Response>(
|
|
84
|
+
"<%= response.name.replace(/Response$/, '') %>",
|
|
85
|
+
<% if(response.hasHeader) { %>definition.responses[<%= response.index %>] && "header" in definition.responses[<%= response.index %>] ? definition.responses[<%= response.index %>]!.header : undefined<% } else { %>undefined<% } %>,
|
|
86
|
+
<% if(response.hasBody) { %>definition.responses[<%= response.index %>] && "body" in definition.responses[<%= response.index %>] ? definition.responses[<%= response.index %>]!.body : undefined<% } else { %>undefined<% } %>,
|
|
87
|
+
)(response, error);
|
|
121
88
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
&& "header" in definition.responses[<%= response.index %>]
|
|
126
|
-
&& definition.responses[<%= response.index %>].header,
|
|
127
|
-
"'<%= response.name %>ResponseHeader' has to be defined in the definition"
|
|
128
|
-
);
|
|
129
|
-
const validateHeaderResult = definition.responses[<%= response.index %>].header.safeParse(response.header);
|
|
130
|
-
|
|
131
|
-
if (!validateHeaderResult.success) {
|
|
132
|
-
error.addHeaderIssues(validateHeaderResult.error.issues);
|
|
133
|
-
isValid = false;
|
|
134
|
-
} else {
|
|
135
|
-
validatedResponse.header = validateHeaderResult.data;
|
|
136
|
-
}
|
|
137
|
-
<% } %>
|
|
89
|
+
if (!result.isValid) {
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
138
92
|
|
|
139
|
-
return isValid;
|
|
93
|
+
return { isValid: true, data: new <%= response.name %>Response(result.data as I<%= response.name %>Response) };
|
|
140
94
|
}
|
|
141
95
|
<% } %>
|
|
142
96
|
}
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/**
|
|
3
|
+
* This file was automatically generated by typeweaver.
|
|
4
|
+
* DO NOT EDIT. Instead, modify the source definition file and generate again.
|
|
5
|
+
*
|
|
6
|
+
* @generated by @rexeus/typeweaver
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
import { HttpResponse, HttpStatusCode } from "<%= coreDir %>";
|
|
2
10
|
|
|
3
11
|
<% if (headerTsType) { %>
|