zod-codegen 1.0.3 → 1.1.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/PERFORMANCE.md ADDED
@@ -0,0 +1,59 @@
1
+ # Build Performance
2
+
3
+ ## Current Build Time
4
+
5
+ - **Standard TypeScript (`tsc`)**: ~1.2 seconds
6
+ - **Project size**: 19 TypeScript files, ~184KB
7
+
8
+ ## TypeScript Native Preview (TSGO)
9
+
10
+ For even faster builds, you can optionally use the TypeScript Native Preview (TSGO), a Go-based compiler that can be up to 10x faster.
11
+
12
+ ### Installation
13
+
14
+ ```bash
15
+ npm install -D @typescript/native-preview
16
+ ```
17
+
18
+ ### Usage
19
+
20
+ ```bash
21
+ # Use native compiler for builds
22
+ npm run build:native
23
+
24
+ # Or use directly
25
+ npx tsgo --project tsconfig.json
26
+ ```
27
+
28
+ ### Notes
29
+
30
+ - **Status**: Still in preview/experimental phase
31
+ - **Compatibility**: May not support all TypeScript features yet
32
+ - **Best for**: Large codebases where build time is a bottleneck
33
+ - **Current project**: Build is already fast (~1.2s), so the benefit is minimal
34
+
35
+ ### When to Use TSGO
36
+
37
+ ✅ **Use TSGO if:**
38
+
39
+ - You have a large codebase (>100 files)
40
+ - Build time is >5 seconds
41
+ - You're willing to test experimental features
42
+
43
+ ❌ **Stick with `tsc` if:**
44
+
45
+ - Your build is already fast (<2 seconds)
46
+ - You need 100% feature compatibility
47
+ - You're in production
48
+
49
+ ### Benchmarking
50
+
51
+ To compare performance:
52
+
53
+ ```bash
54
+ # Standard TypeScript
55
+ time npm run build
56
+
57
+ # Native TypeScript
58
+ time npm run build:native
59
+ ```
package/README.md CHANGED
@@ -10,13 +10,18 @@ A powerful TypeScript code generator that creates **Zod schemas** and **type-saf
10
10
 
11
11
  ## 🚀 Features
12
12
 
13
- - **🔥 Zod Schema Generation**: Automatically generate Zod validation schemas from OpenAPI specs
14
- - **🎯 Type-Safe**: Full TypeScript support with generated types
15
- - **📡 Multiple Formats**: Support for OpenAPI 3.x, Swagger 2.0, JSON, and YAML
16
- - **🌐 Remote Files**: Fetch OpenAPI specs from URLs
13
+ - **🔥 Zod Schema Generation**: Automatically generate Zod validation schemas from OpenAPI component schemas
14
+ - **🎯 Type-Safe Client**: Generate a fully type-safe API client class with methods for each endpoint
15
+ - **📡 Multiple Formats**: Support for OpenAPI 3.x specifications in JSON and YAML formats
16
+ - **🌐 Remote Files**: Fetch OpenAPI specs from URLs using native fetch API
17
17
  - **⚡ Fast**: Optimized for performance with minimal dependencies
18
- - **🔧 Configurable**: Flexible output options and customization
19
- - **📦 CLI & Programmatic**: Use as a CLI tool or integrate into your build process
18
+ - **🔧 Advanced Schema Support**: Handles logical operators (anyOf, oneOf, allOf, not), enums, discriminators, and complex nested schemas
19
+ - **📦 Single File Output**: Generates all schemas and client in one convenient TypeScript file
20
+ - **🛡️ Runtime Validation**: Built-in Zod validation for request/response data
21
+ - **🌍 Form Support**: Supports both JSON and form-urlencoded request bodies
22
+ - **🔐 Extensible**: Override `getBaseRequestOptions()` to add authentication, custom headers, CORS, and other fetch options
23
+ - **🌐 Server Configuration**: Full support for OpenAPI server variables and templating (e.g., `{environment}.example.com`)
24
+ - **⚙️ Flexible Client Options**: Options-based constructor supporting server selection, variable overrides, and custom base URLs
20
25
 
21
26
  ## 📦 Installation
22
27
 
@@ -61,35 +66,85 @@ zod-codegen -i ./swagger.yaml -o ./src/generated
61
66
  ```typescript
62
67
  import {Generator} from 'zod-codegen';
63
68
 
64
- const generator = new Generator();
65
-
66
- // Generate from local file
67
- await generator.generate({
68
- input: './openapi.json',
69
- output: './generated',
70
- });
71
-
72
- // Generate from URL
73
- await generator.generate({
74
- input: 'https://api.example.com/openapi.json',
75
- output: './api',
76
- });
69
+ // Create a simple reporter object
70
+ const reporter = {
71
+ log: (...args: unknown[]) => console.log(...args),
72
+ error: (...args: unknown[]) => console.error(...args),
73
+ };
74
+
75
+ // Create generator instance
76
+ const generator = new Generator(
77
+ 'my-app',
78
+ '1.0.0',
79
+ reporter,
80
+ './openapi.json', // Input path or URL
81
+ './generated', // Output directory
82
+ );
83
+
84
+ // Run the generator
85
+ const exitCode = await generator.run();
77
86
  ```
78
87
 
79
88
  ## 📁 Generated Output
80
89
 
81
- The generator creates the following structure:
90
+ The generator creates a single TypeScript file (`type.ts`) containing:
91
+
92
+ - **Zod Schemas**: Exported Zod validation schemas for all component schemas defined in your OpenAPI spec
93
+ - **API Client Class**: A type-safe client class with methods for each endpoint operation
94
+ - **Server Configuration**: `serverConfigurations` array and `defaultBaseUrl` constant extracted from OpenAPI servers
95
+ - **Client Options Type**: `ClientOptions` type for flexible server selection and variable overrides
96
+ - **Protected Extension Point**: A `getBaseRequestOptions()` method that can be overridden for customization
97
+
98
+ ### Generated Client Structure
99
+
100
+ The generated client class includes:
101
+
102
+ ```typescript
103
+ export class YourAPI {
104
+ readonly #baseUrl: string;
105
+
106
+ // Options-based constructor (if servers are defined in OpenAPI spec)
107
+ constructor(options: ClientOptions);
108
+
109
+ // Or simple baseUrl constructor (if no servers defined)
110
+ constructor(baseUrl: string = '/', _?: unknown);
111
+
112
+ // Protected method - override to customize request options
113
+ protected getBaseRequestOptions(): Partial<Omit<RequestInit, 'method' | 'body'>>;
114
+
115
+ // Private method - handles all HTTP requests
116
+ async #makeRequest<T>(method: string, path: string, options: {...}): Promise<T>;
117
+
118
+ // Generated endpoint methods (one per operationId)
119
+ async yourEndpointMethod(...): Promise<ResponseType>;
120
+ }
121
+
122
+ // ClientOptions type (when servers are defined)
123
+ export type ClientOptions = {
124
+ baseUrl?: string; // Override base URL directly
125
+ serverIndex?: number; // Select server by index (0-based)
126
+ serverVariables?: Record<string, string>; // Override server template variables
127
+ };
128
+
129
+ // Server configuration (when servers are defined)
130
+ export const serverConfigurations: Array<{
131
+ url: string;
132
+ description?: string;
133
+ variables?: Record<string, {
134
+ default: string;
135
+ enum?: string[];
136
+ description?: string;
137
+ }>;
138
+ }>;
139
+
140
+ export const defaultBaseUrl: string; // First server with default variables
141
+ ```
142
+
143
+ ### File Structure
82
144
 
83
145
  ```
84
146
  generated/
85
- ├── schemas/ # Zod validation schemas
86
- │ ├── user.schema.ts
87
- │ └── product.schema.ts
88
- ├── types/ # TypeScript type definitions
89
- │ ├── user.types.ts
90
- │ └── product.types.ts
91
- └── client/ # Type-safe API client
92
- └── api.client.ts
147
+ └── type.ts # All schemas and client in one file
93
148
  ```
94
149
 
95
150
  ## 🎯 Example
@@ -101,69 +156,228 @@ openapi: 3.0.0
101
156
  info:
102
157
  title: User API
103
158
  version: 1.0.0
159
+ servers:
160
+ - url: https://api.example.com
104
161
  paths:
105
- /users:
162
+ /users/{id}:
106
163
  get:
164
+ operationId: getUserById
165
+ parameters:
166
+ - name: id
167
+ in: path
168
+ required: true
169
+ schema:
170
+ type: integer
107
171
  responses:
108
172
  '200':
109
173
  content:
110
174
  application/json:
111
175
  schema:
112
- type: object
113
- properties:
114
- id:
115
- type: integer
116
- name:
117
- type: string
118
- email:
119
- type: string
120
- format: email
121
- required: [id, name, email]
176
+ $ref: '#/components/schemas/User'
177
+ components:
178
+ schemas:
179
+ User:
180
+ type: object
181
+ properties:
182
+ id:
183
+ type: integer
184
+ name:
185
+ type: string
186
+ email:
187
+ type: string
188
+ format: email
189
+ required: [id, name, email]
122
190
  ```
123
191
 
124
- **Generated Zod Schema** (`schemas/user.schema.ts`):
192
+ **Generated Output** (`generated/type.ts`):
125
193
 
126
194
  ```typescript
127
195
  import {z} from 'zod';
128
196
 
129
- export const UserSchema = z.object({
197
+ // Components schemas
198
+ export const User = z.object({
130
199
  id: z.number().int(),
131
200
  name: z.string(),
132
201
  email: z.string().email(),
133
202
  });
134
203
 
135
- export type User = z.infer<typeof UserSchema>;
204
+ // Server configuration (when servers are defined in OpenAPI spec)
205
+ export const serverConfigurations = [
206
+ {
207
+ url: 'https://api.example.com',
208
+ },
209
+ ];
210
+ export const defaultBaseUrl = 'https://api.example.com';
211
+ export type ClientOptions = {
212
+ baseUrl?: string;
213
+ serverIndex?: number;
214
+ serverVariables?: Record<string, string>;
215
+ };
216
+
217
+ // Client class
218
+ export class UserAPI {
219
+ readonly #baseUrl: string;
220
+
221
+ constructor(options: ClientOptions = {}) {
222
+ this.#baseUrl = options.baseUrl || defaultBaseUrl;
223
+ }
224
+
225
+ protected getBaseRequestOptions(): Partial<Omit<RequestInit, 'method' | 'body'>> {
226
+ return {};
227
+ }
228
+
229
+ async getUserById(id: number): Promise<z.infer<typeof User>> {
230
+ return User.parse(await this.#makeRequest<z.infer<typeof User>>('GET', `/users/${id}`, {}));
231
+ }
232
+
233
+ // ... private #makeRequest method
234
+ }
235
+ ```
236
+
237
+ **Usage:**
238
+
239
+ ```typescript
240
+ import {UserAPI, User} from './generated/type.js';
241
+
242
+ // Use default server from OpenAPI spec
243
+ const client = new UserAPI({});
244
+ const user = await client.getUserById(123);
245
+ // user is fully typed and validated!
136
246
  ```
137
247
 
138
- **Generated Types** (`types/user.types.ts`):
248
+ ### Extending the Client
249
+
250
+ The generated client includes a protected `getBaseRequestOptions()` method that you can override to customize request options. This method returns `Partial<Omit<RequestInit, 'method' | 'body'>>`, allowing you to configure:
251
+
252
+ - **Headers**: Authentication tokens, User-Agent, custom headers
253
+ - **CORS**: `mode`, `credentials` for cross-origin requests
254
+ - **Request Options**: `signal` (AbortController), `cache`, `redirect`, `referrer`, etc.
255
+
256
+ **Important**: Options from `getBaseRequestOptions()` are **merged with** (not replaced by) request-specific options. Base options like `mode`, `credentials`, and `signal` are preserved, while headers are merged (base headers + Content-Type + request headers). See [EXAMPLES.md](EXAMPLES.md) for detailed merging behavior.
257
+
258
+ #### Basic Authentication Example
139
259
 
140
260
  ```typescript
141
- export interface User {
142
- id: number;
143
- name: string;
144
- email: string;
261
+ import {UserAPI, ClientOptions} from './generated/type.js';
262
+
263
+ class AuthenticatedUserAPI extends UserAPI {
264
+ private accessToken: string | null = null;
265
+
266
+ constructor(options: ClientOptions = {}) {
267
+ super(options);
268
+ }
269
+
270
+ protected getBaseRequestOptions(): Partial<Omit<RequestInit, 'method' | 'body'>> {
271
+ const options = super.getBaseRequestOptions();
272
+ return {
273
+ ...options,
274
+ headers: {
275
+ ...((options.headers as Record<string, string>) || {}),
276
+ ...(this.accessToken ? {Authorization: `Bearer ${this.accessToken}`} : {}),
277
+ },
278
+ };
279
+ }
280
+
281
+ setAccessToken(token: string): void {
282
+ this.accessToken = token;
283
+ }
145
284
  }
285
+
286
+ // Usage
287
+ const client = new AuthenticatedUserAPI({});
288
+ client.setAccessToken('your-token-here');
289
+ const user = await client.getUserById(123); // Includes Authorization header
146
290
  ```
147
291
 
148
- **Generated Client** (`client/api.client.ts`):
292
+ #### Complete Configuration Example
149
293
 
150
294
  ```typescript
151
- import {UserSchema, type User} from '../schemas/user.schema.js';
295
+ import {UserAPI, ClientOptions} from './generated/type.js';
296
+
297
+ class FullyConfiguredAPI extends UserAPI {
298
+ private accessToken: string | null = null;
299
+
300
+ constructor(options: ClientOptions = {}) {
301
+ super(options);
302
+ }
152
303
 
153
- export class ApiClient {
154
- async getUsers(): Promise<User[]> {
155
- const response = await fetch('/api/users');
156
- const data = await response.json();
157
- return UserSchema.array().parse(data);
304
+ protected getBaseRequestOptions(): Partial<Omit<RequestInit, 'method' | 'body'>> {
305
+ const options = super.getBaseRequestOptions();
306
+ return {
307
+ ...options,
308
+ headers: {
309
+ ...((options.headers as Record<string, string>) || {}),
310
+ 'User-Agent': 'MyApp/1.0.0',
311
+ ...(this.accessToken ? {Authorization: `Bearer ${this.accessToken}`} : {}),
312
+ },
313
+ mode: 'cors',
314
+ credentials: 'include',
315
+ cache: 'no-cache',
316
+ };
317
+ }
318
+
319
+ setAccessToken(token: string): void {
320
+ this.accessToken = token;
158
321
  }
159
322
  }
323
+
324
+ // Usage
325
+ const client = new FullyConfiguredAPI({});
326
+ client.setAccessToken('your-token');
160
327
  ```
161
328
 
329
+ #### Available Request Options
330
+
331
+ You can set any `RequestInit` option except `method` and `body` (which are controlled by the generated code):
332
+
333
+ | Option | Type | Description |
334
+ | ---------------- | -------------------- | ------------------------------------------------------------------- |
335
+ | `headers` | `HeadersInit` | Request headers (authentication, User-Agent, etc.) |
336
+ | `signal` | `AbortSignal` | AbortController signal for request cancellation |
337
+ | `credentials` | `RequestCredentials` | CORS credentials mode (`'include'`, `'same-origin'`, `'omit'`) |
338
+ | `mode` | `RequestMode` | Request mode (`'cors'`, `'no-cors'`, `'same-origin'`, `'navigate'`) |
339
+ | `cache` | `RequestCache` | Cache mode (`'default'`, `'no-cache'`, `'reload'`, etc.) |
340
+ | `redirect` | `RequestRedirect` | Redirect handling (`'follow'`, `'error'`, `'manual'`) |
341
+ | `referrer` | `string` | Referrer URL |
342
+ | `referrerPolicy` | `ReferrerPolicy` | Referrer policy |
343
+ | `integrity` | `string` | Subresource integrity hash |
344
+ | `keepalive` | `boolean` | Keep connection alive |
345
+
346
+ See [EXAMPLES.md](EXAMPLES.md) for comprehensive examples including:
347
+
348
+ - Bearer token authentication
349
+ - Session management with token refresh
350
+ - Custom User-Agent headers
351
+ - CORS configuration
352
+ - Request cancellation with AbortController
353
+ - Environment-specific configurations
354
+
355
+ ## 📖 Examples
356
+
357
+ Check out the [examples directory](./examples/) for complete, runnable examples:
358
+
359
+ - **[Petstore API](./examples/petstore/)** - Complete example with the Swagger Petstore API
360
+ - **[PokéAPI](./examples/pokeapi/)** - Example with a public REST API
361
+
362
+ Each example includes:
363
+
364
+ - Generated client code
365
+ - Basic usage examples
366
+ - Authentication examples
367
+ - README with instructions
368
+
369
+ ## 📚 Documentation
370
+
371
+ - **[README.md](README.md)** - Project overview and quick start guide
372
+ - **[EXAMPLES.md](EXAMPLES.md)** - Comprehensive examples and patterns for extending the client
373
+ - **[CONTRIBUTING.md](CONTRIBUTING.md)** - Guidelines for contributing to the project
374
+ - **[CHANGELOG.md](CHANGELOG.md)** - Version history and changes
375
+
162
376
  ## 🛠️ Development
163
377
 
164
378
  ### Prerequisites
165
379
 
166
- - Node.js ≥ 18.0.0
380
+ - Node.js ≥ 24.11.1
167
381
  - npm or yarn
168
382
 
169
383
  ### Setup
@@ -223,9 +437,9 @@ npm run test:ui
223
437
 
224
438
  ## 📋 Requirements
225
439
 
226
- - **Node.js**: ≥ 18.0.0
227
- - **TypeScript**: ≥ 5.0.0
228
- - **Zod**: ≥ 3.20.0
440
+ - **Node.js**: ≥ 24.11.1
441
+ - **TypeScript**: ≥ 5.9.3
442
+ - **Zod**: ≥ 4.1.12
229
443
 
230
444
  ## 🤝 Contributing
231
445