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/CHANGELOG.md +13 -0
- package/CONTRIBUTING.md +1 -1
- package/EXAMPLES.md +730 -0
- package/PERFORMANCE.md +59 -0
- package/README.md +272 -58
- package/dist/src/services/code-generator.service.js +249 -45
- package/dist/src/types/openapi.js +1 -1
- package/dist/tests/unit/code-generator.test.js +290 -0
- package/dist/tests/unit/file-reader.test.js +110 -0
- package/dist/tests/unit/generator.test.js +77 -7
- package/eslint.config.mjs +1 -0
- package/examples/.gitkeep +3 -0
- package/examples/README.md +74 -0
- package/examples/petstore/README.md +121 -0
- package/examples/petstore/authenticated-usage.ts +60 -0
- package/examples/petstore/basic-usage.ts +51 -0
- package/examples/petstore/server-variables-usage.ts +63 -0
- package/examples/petstore/type.ts +217 -0
- package/examples/pokeapi/README.md +105 -0
- package/examples/pokeapi/basic-usage.ts +57 -0
- package/examples/pokeapi/custom-client.ts +56 -0
- package/examples/pokeapi/type.ts +109 -0
- package/package.json +4 -2
- package/samples/pokeapi-openapi.json +212 -0
- package/samples/server-variables-example.yaml +49 -0
- package/src/services/code-generator.service.ts +707 -88
- package/src/types/openapi.ts +1 -1
- package/tests/unit/code-generator.test.ts +321 -0
- package/tests/unit/file-reader.test.ts +134 -0
- package/tests/unit/generator.test.ts +99 -7
- package/tsconfig.examples.json +17 -0
- package/tsconfig.json +1 -1
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
|
|
14
|
-
- **🎯 Type-Safe**:
|
|
15
|
-
- **📡 Multiple Formats**: Support for OpenAPI 3.x
|
|
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
|
-
- **🔧
|
|
19
|
-
- **📦
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
|
192
|
+
**Generated Output** (`generated/type.ts`):
|
|
125
193
|
|
|
126
194
|
```typescript
|
|
127
195
|
import {z} from 'zod';
|
|
128
196
|
|
|
129
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
292
|
+
#### Complete Configuration Example
|
|
149
293
|
|
|
150
294
|
```typescript
|
|
151
|
-
import {
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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 ≥
|
|
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**: ≥
|
|
227
|
-
- **TypeScript**: ≥ 5.
|
|
228
|
-
- **Zod**: ≥
|
|
440
|
+
- **Node.js**: ≥ 24.11.1
|
|
441
|
+
- **TypeScript**: ≥ 5.9.3
|
|
442
|
+
- **Zod**: ≥ 4.1.12
|
|
229
443
|
|
|
230
444
|
## 🤝 Contributing
|
|
231
445
|
|