@rexeus/typeweaver-types 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +483 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +2424 -0
- package/dist/lib/InvalidResponseStatusCodeError.ts +21 -0
- package/dist/lib/RequestValidator.ts +43 -0
- package/dist/lib/ResponseValidator.ts +39 -0
- package/dist/lib/assert.ts +5 -0
- package/dist/lib/index.ts +4 -0
- package/dist/templates/Request.ejs +93 -0
- package/dist/templates/RequestValidator.ejs +92 -0
- package/dist/templates/Response.ejs +78 -0
- package/dist/templates/ResponseValidator.ejs +142 -0
- package/dist/templates/SharedResponse.ejs +38 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
# @rexeus/typeweaver-types
|
|
2
|
+
|
|
3
|
+
TypeScript type and Zod validator generators for TypeWeaver APIs.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This plugin generates TypeScript types and Zod validators from your TypeWeaver API definitions,
|
|
8
|
+
providing the foundation for type-safe API development. This is the core plugin that's included by
|
|
9
|
+
default in TypeWeaver.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @rexeus/typeweaver-types
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Peer Dependencies:**
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @rexeus/typeweaver-core @rexeus/typeweaver-gen
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
This plugin is included by default and doesn't need to be explicitly specified:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx typeweaver generate --input ./api/definitions --output ./api/generated
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
You can also explicitly include it with other plugins:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx typeweaver generate --input ./api/definitions --output ./api/generated --plugins types,clients,aws-cdk
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Generated Output
|
|
38
|
+
|
|
39
|
+
This plugin generates comprehensive TypeScript types and validators for each API operation.
|
|
40
|
+
|
|
41
|
+
### Generated Files per Operation
|
|
42
|
+
|
|
43
|
+
For each operation (e.g., `GetUser`), the plugin generates:
|
|
44
|
+
|
|
45
|
+
1. **Request Types** (`GetUserRequest.ts`)
|
|
46
|
+
2. **Response Types** (`GetUserResponse.ts`)
|
|
47
|
+
3. **Request Validators** (`GetUserRequestValidator.ts`)
|
|
48
|
+
4. **Response Validators** (`GetUserResponseValidator.ts`)
|
|
49
|
+
|
|
50
|
+
### Shared Response Types
|
|
51
|
+
|
|
52
|
+
For shared error responses, generates:
|
|
53
|
+
|
|
54
|
+
- **Shared Response Types** (in `shared/` directory)
|
|
55
|
+
|
|
56
|
+
## Example Generated Code
|
|
57
|
+
|
|
58
|
+
### Request Types
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
// GetUserRequest.ts
|
|
62
|
+
import { HttpRequest } from "@rexeus/typeweaver-core";
|
|
63
|
+
import { z } from "zod/v4";
|
|
64
|
+
|
|
65
|
+
export const GetUserRequestSchema = z.object({
|
|
66
|
+
param: z.object({
|
|
67
|
+
userId: z.string().uuid(),
|
|
68
|
+
}),
|
|
69
|
+
header: z
|
|
70
|
+
.object({
|
|
71
|
+
Authorization: z.string(),
|
|
72
|
+
Accept: z.literal("application/json"),
|
|
73
|
+
})
|
|
74
|
+
.optional(),
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
export type GetUserRequest = z.infer<typeof GetUserRequestSchema>;
|
|
78
|
+
|
|
79
|
+
export interface IGetUserRequest extends HttpRequest {
|
|
80
|
+
param: {
|
|
81
|
+
userId: string;
|
|
82
|
+
};
|
|
83
|
+
header?: {
|
|
84
|
+
Authorization: string;
|
|
85
|
+
Accept: "application/json";
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export class GetUserRequestCommand {
|
|
90
|
+
constructor(public readonly data: IGetUserRequest) {
|
|
91
|
+
GetUserRequestSchema.parse(data);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Response Types
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
// GetUserResponse.ts
|
|
100
|
+
import { HttpResponse } from "@rexeus/typeweaver-core";
|
|
101
|
+
import { z } from "zod/v4";
|
|
102
|
+
|
|
103
|
+
// Success Response
|
|
104
|
+
export const GetUserSuccessResponseSchema = z.object({
|
|
105
|
+
statusCode: z.literal(200),
|
|
106
|
+
header: z.object({
|
|
107
|
+
"Content-Type": z.literal("application/json"),
|
|
108
|
+
}),
|
|
109
|
+
body: z.object({
|
|
110
|
+
id: z.string().uuid(),
|
|
111
|
+
name: z.string(),
|
|
112
|
+
email: z.string().email(),
|
|
113
|
+
}),
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
export type GetUserSuccessResponse = z.infer<typeof GetUserSuccessResponseSchema>;
|
|
117
|
+
|
|
118
|
+
// Error Responses
|
|
119
|
+
export type GetUserErrorResponse =
|
|
120
|
+
| UserNotFoundErrorResponse
|
|
121
|
+
| ValidationErrorResponse
|
|
122
|
+
| InternalServerErrorResponse;
|
|
123
|
+
|
|
124
|
+
// Union Type
|
|
125
|
+
export type GetUserResponse = GetUserSuccessResponse | GetUserErrorResponse;
|
|
126
|
+
|
|
127
|
+
export interface IGetUserResponse extends HttpResponse {
|
|
128
|
+
statusCode: 200 | 404 | 400 | 500;
|
|
129
|
+
header: {
|
|
130
|
+
"Content-Type": "application/json";
|
|
131
|
+
};
|
|
132
|
+
body:
|
|
133
|
+
| {
|
|
134
|
+
id: string;
|
|
135
|
+
name: string;
|
|
136
|
+
email: string;
|
|
137
|
+
}
|
|
138
|
+
| {
|
|
139
|
+
message: string;
|
|
140
|
+
code: string;
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Request Validators
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// GetUserRequestValidator.ts
|
|
149
|
+
import { RequestValidator } from "@rexeus/typeweaver-core";
|
|
150
|
+
import { GetUserRequestSchema } from "./GetUserRequest";
|
|
151
|
+
|
|
152
|
+
export class GetUserRequestValidator extends RequestValidator<typeof GetUserRequestSchema> {
|
|
153
|
+
constructor() {
|
|
154
|
+
super(GetUserRequestSchema);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
public validate(request: unknown): GetUserRequest {
|
|
158
|
+
return this.schema.parse(request);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
public safeValidate(request: unknown): SafeParseResult<GetUserRequest> {
|
|
162
|
+
return this.schema.safeParse(request);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Response Validators
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
// GetUserResponseValidator.ts
|
|
171
|
+
import { ResponseValidator } from "@rexeus/typeweaver-core";
|
|
172
|
+
import { GetUserResponse } from "./GetUserResponse";
|
|
173
|
+
|
|
174
|
+
export class GetUserResponseValidator extends ResponseValidator {
|
|
175
|
+
public validate(response: unknown): GetUserResponse {
|
|
176
|
+
const statusCode = (response as any)?.statusCode;
|
|
177
|
+
|
|
178
|
+
switch (statusCode) {
|
|
179
|
+
case 200:
|
|
180
|
+
return this.validateSuccessResponse(response);
|
|
181
|
+
case 404:
|
|
182
|
+
return this.validateUserNotFoundError(response);
|
|
183
|
+
case 400:
|
|
184
|
+
return this.validateValidationError(response);
|
|
185
|
+
case 500:
|
|
186
|
+
return this.validateInternalServerError(response);
|
|
187
|
+
default:
|
|
188
|
+
throw new ResponseValidationError(`Unexpected status code: ${statusCode}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private validateSuccessResponse(response: unknown): GetUserSuccessResponse {
|
|
193
|
+
return GetUserSuccessResponseSchema.parse(response);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ... other validation methods
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Type Features
|
|
201
|
+
|
|
202
|
+
### Complete Type Safety
|
|
203
|
+
|
|
204
|
+
- **Request Types** - Fully typed request interfaces
|
|
205
|
+
- **Response Types** - Union types for all possible responses
|
|
206
|
+
- **Parameter Types** - Path, query, header, and body parameters
|
|
207
|
+
- **Validation Types** - Runtime validation with Zod schemas
|
|
208
|
+
|
|
209
|
+
### Zod Integration
|
|
210
|
+
|
|
211
|
+
- **Schema Generation** - Automatic Zod schema creation
|
|
212
|
+
- **Runtime Validation** - Type-safe validation at runtime
|
|
213
|
+
- **Error Handling** - Structured validation errors
|
|
214
|
+
- **Type Inference** - TypeScript types inferred from Zod schemas
|
|
215
|
+
|
|
216
|
+
### Error Response Handling
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
// Shared error responses are reused across operations
|
|
220
|
+
export type UserNotFoundErrorResponse = {
|
|
221
|
+
statusCode: 404;
|
|
222
|
+
body: {
|
|
223
|
+
message: "User not found";
|
|
224
|
+
code: "USER_NOT_FOUND";
|
|
225
|
+
userId: string;
|
|
226
|
+
};
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
// Operation-specific error handling
|
|
230
|
+
export type GetUserErrorResponse =
|
|
231
|
+
| UserNotFoundErrorResponse
|
|
232
|
+
| ValidationErrorResponse
|
|
233
|
+
| InternalServerErrorResponse;
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Usage Examples
|
|
237
|
+
|
|
238
|
+
### Request Validation
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import { GetUserRequestValidator } from "./api/generated";
|
|
242
|
+
|
|
243
|
+
const validator = new GetUserRequestValidator();
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
const validatedRequest = validator.validate({
|
|
247
|
+
param: { userId: "123e4567-e89b-12d3-a456-426614174000" },
|
|
248
|
+
header: { Authorization: "Bearer token" },
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// validatedRequest is fully typed
|
|
252
|
+
console.log(validatedRequest.param.userId);
|
|
253
|
+
} catch (error) {
|
|
254
|
+
console.error("Validation failed:", error.issues);
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Response Validation
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
import { GetUserResponseValidator } from "./api/generated";
|
|
262
|
+
|
|
263
|
+
const validator = new GetUserResponseValidator();
|
|
264
|
+
|
|
265
|
+
try {
|
|
266
|
+
const validatedResponse = validator.validate({
|
|
267
|
+
statusCode: 200,
|
|
268
|
+
header: { "Content-Type": "application/json" },
|
|
269
|
+
body: { id: "123", name: "John", email: "john@example.com" },
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// Response is typed based on status code
|
|
273
|
+
if (validatedResponse.statusCode === 200) {
|
|
274
|
+
console.log(validatedResponse.body.name); // Type-safe access
|
|
275
|
+
}
|
|
276
|
+
} catch (error) {
|
|
277
|
+
console.error("Response validation failed:", error);
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Type Guards
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
import { GetUserResponse } from "./api/generated";
|
|
285
|
+
|
|
286
|
+
function isSuccessResponse(response: GetUserResponse): response is GetUserSuccessResponse {
|
|
287
|
+
return response.statusCode === 200;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function handleResponse(response: GetUserResponse) {
|
|
291
|
+
if (isSuccessResponse(response)) {
|
|
292
|
+
// TypeScript knows this is a success response
|
|
293
|
+
console.log(response.body.name);
|
|
294
|
+
} else {
|
|
295
|
+
// TypeScript knows this is an error response
|
|
296
|
+
console.error(response.body.message);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## Integration with Other Plugins
|
|
302
|
+
|
|
303
|
+
### With Clients Plugin
|
|
304
|
+
|
|
305
|
+
The types plugin provides the foundation for type-safe clients:
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
// Generated by types plugin
|
|
309
|
+
import { GetUserRequestCommand, GetUserResponse } from "./GetUserRequest";
|
|
310
|
+
import { GetUserResponseValidator } from "./GetUserResponseValidator";
|
|
311
|
+
|
|
312
|
+
// Used by clients plugin
|
|
313
|
+
export class UsersClient {
|
|
314
|
+
async send(command: GetUserRequestCommand): Promise<GetUserResponse> {
|
|
315
|
+
const response = await this.makeRequest(command);
|
|
316
|
+
const validator = new GetUserResponseValidator();
|
|
317
|
+
return validator.validate(response);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### With AWS CDK Plugin
|
|
323
|
+
|
|
324
|
+
Types provide the foundation for request/response handling:
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
// In your Lambda handler
|
|
328
|
+
import { GetUserRequestValidator, GetUserResponseValidator } from "./api/generated";
|
|
329
|
+
|
|
330
|
+
export const handler = async (event: APIGatewayProxyEvent) => {
|
|
331
|
+
const requestValidator = new GetUserRequestValidator();
|
|
332
|
+
|
|
333
|
+
try {
|
|
334
|
+
const validatedRequest = requestValidator.validate({
|
|
335
|
+
param: event.pathParameters,
|
|
336
|
+
header: event.headers,
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
// Handle request with type safety
|
|
340
|
+
const user = await userService.getUser(validatedRequest.param.userId);
|
|
341
|
+
|
|
342
|
+
return {
|
|
343
|
+
statusCode: 200,
|
|
344
|
+
body: JSON.stringify(user),
|
|
345
|
+
};
|
|
346
|
+
} catch (error) {
|
|
347
|
+
if (error instanceof ValidationError) {
|
|
348
|
+
return {
|
|
349
|
+
statusCode: 400,
|
|
350
|
+
body: JSON.stringify({ message: "Invalid request", issues: error.issues }),
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
throw error;
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
## Advanced Features
|
|
359
|
+
|
|
360
|
+
### Custom Validators
|
|
361
|
+
|
|
362
|
+
Extend generated validators for custom validation logic:
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
import { GetUserRequestValidator } from "./api/generated";
|
|
366
|
+
|
|
367
|
+
export class CustomGetUserRequestValidator extends GetUserRequestValidator {
|
|
368
|
+
public validate(request: unknown): GetUserRequest {
|
|
369
|
+
const validated = super.validate(request);
|
|
370
|
+
|
|
371
|
+
// Custom validation logic
|
|
372
|
+
if (validated.param.userId.startsWith("test_")) {
|
|
373
|
+
throw new Error("Test users not allowed in production");
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return validated;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### Schema Composition
|
|
382
|
+
|
|
383
|
+
Generated schemas can be composed and extended:
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
import { GetUserRequestSchema } from "./api/generated";
|
|
387
|
+
|
|
388
|
+
// Extend for internal usage
|
|
389
|
+
export const InternalGetUserRequestSchema = GetUserRequestSchema.extend({
|
|
390
|
+
internal: z.object({
|
|
391
|
+
requestId: z.string(),
|
|
392
|
+
userId: z.string(),
|
|
393
|
+
}),
|
|
394
|
+
});
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
## Plugin Architecture
|
|
398
|
+
|
|
399
|
+
This plugin extends the TypeWeaver plugin system:
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
import { BasePlugin, type GeneratorContext } from "@rexeus/typeweaver-gen";
|
|
403
|
+
|
|
404
|
+
export default class TypesPlugin extends BasePlugin {
|
|
405
|
+
public name = "types";
|
|
406
|
+
|
|
407
|
+
public override generate(context: GeneratorContext): void {
|
|
408
|
+
// Generates types and validators for all operations
|
|
409
|
+
SharedResponseGenerator.generate(context);
|
|
410
|
+
RequestGenerator.generate(context);
|
|
411
|
+
RequestValidationGenerator.generate(context);
|
|
412
|
+
ResponseGenerator.generate(context);
|
|
413
|
+
ResponseValidationGenerator.generate(context);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
## Best Practices
|
|
419
|
+
|
|
420
|
+
### Organization
|
|
421
|
+
|
|
422
|
+
- Keep generated types in source control for review
|
|
423
|
+
- Regenerate types when API definitions change
|
|
424
|
+
- Use TypeScript strict mode for maximum safety
|
|
425
|
+
|
|
426
|
+
### Validation Strategy
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
// Validate at boundaries
|
|
430
|
+
export class ApiController {
|
|
431
|
+
async getUser(request: unknown) {
|
|
432
|
+
// Validate input
|
|
433
|
+
const validator = new GetUserRequestValidator();
|
|
434
|
+
const validatedRequest = validator.validate(request);
|
|
435
|
+
|
|
436
|
+
// Business logic with typed data
|
|
437
|
+
const user = await this.userService.getUser(validatedRequest.param.userId);
|
|
438
|
+
|
|
439
|
+
// Return typed response
|
|
440
|
+
return {
|
|
441
|
+
statusCode: 200,
|
|
442
|
+
body: user,
|
|
443
|
+
} as GetUserSuccessResponse;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### Error Handling
|
|
449
|
+
|
|
450
|
+
```typescript
|
|
451
|
+
// Centralized error mapping
|
|
452
|
+
export function mapValidationError(error: ZodError): ValidationErrorResponse {
|
|
453
|
+
return {
|
|
454
|
+
statusCode: 400,
|
|
455
|
+
body: {
|
|
456
|
+
message: "Validation failed",
|
|
457
|
+
issues: error.issues.map(issue => ({
|
|
458
|
+
path: issue.path.join("."),
|
|
459
|
+
message: issue.message,
|
|
460
|
+
})),
|
|
461
|
+
},
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
## Troubleshooting
|
|
467
|
+
|
|
468
|
+
### Common Issues
|
|
469
|
+
|
|
470
|
+
**Type conflicts**: Ensure API definitions are consistent **Validation errors**: Check Zod schema
|
|
471
|
+
compatibility **Import issues**: Verify generated files are properly exported
|
|
472
|
+
|
|
473
|
+
### Debug Mode
|
|
474
|
+
|
|
475
|
+
Enable detailed generation logging:
|
|
476
|
+
|
|
477
|
+
```bash
|
|
478
|
+
DEBUG=typeweaver:types npx typeweaver generate --plugins types
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
## License
|
|
482
|
+
|
|
483
|
+
ISC © Dennis Wentzien 2025
|