@alt-stack/zod-openapi 1.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 ADDED
@@ -0,0 +1,331 @@
1
+ # zod-openapi
2
+
3
+ Convert OpenAPI schemas to Zod schemas with TypeScript code generation.
4
+
5
+ ## Features
6
+
7
+ - Converts OpenAPI 3.x schemas to Zod validation schemas
8
+ - Generates TypeScript code with Zod schemas and inferred types
9
+ - Handles complex types: objects, arrays, unions (oneOf), intersections (allOf)
10
+ - Supports custom string formats via registry system
11
+ - Automatically resolves schema dependencies and generates code in correct order
12
+ - Supports nullable schemas, enums, validation constraints, and more
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ pnpm add zod-openapi
18
+ # or
19
+ npm install zod-openapi
20
+ # or
21
+ yarn add zod-openapi
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ### Basic Example
27
+
28
+ ```typescript
29
+ import { openApiToZodTsCode } from "zod-openapi";
30
+
31
+ const openApiSpec = {
32
+ components: {
33
+ schemas: {
34
+ User: {
35
+ type: "object",
36
+ properties: {
37
+ id: { type: "integer" },
38
+ name: { type: "string" },
39
+ email: { type: "string", format: "email" },
40
+ },
41
+ required: ["id", "name", "email"],
42
+ },
43
+ },
44
+ },
45
+ };
46
+
47
+ const generatedCode = openApiToZodTsCode(openApiSpec);
48
+ console.log(generatedCode);
49
+ ```
50
+
51
+ Generated output:
52
+
53
+ ```typescript
54
+ /**
55
+ * This file was automatically generated from OpenAPI schema
56
+ * Do not manually edit this file
57
+ */
58
+
59
+ import { z } from 'zod';
60
+
61
+ export const UserSchema = z.object({
62
+ id: z.number().int(),
63
+ name: z.string(),
64
+ email: z.string().email(),
65
+ });
66
+ export type User = z.infer<typeof UserSchema>;
67
+ ```
68
+
69
+ ### Custom String Formats
70
+
71
+ Register custom Zod schemas for OpenAPI string formats:
72
+
73
+ ```typescript
74
+ import {
75
+ registerZodSchemaToOpenApiSchema,
76
+ openApiToZodTsCode,
77
+ } from "zod-openapi";
78
+ import { z } from "zod";
79
+
80
+ // Register a custom UUID schema
81
+ const uuidSchema = z.string().uuid();
82
+ registerZodSchemaToOpenApiSchema(uuidSchema, {
83
+ schemaExportedVariableName: "uuidSchema",
84
+ type: "string",
85
+ format: "uuid",
86
+ });
87
+
88
+ // Now OpenAPI schemas with format: "uuid" will use your custom schema
89
+ const openApiSpec = {
90
+ components: {
91
+ schemas: {
92
+ User: {
93
+ type: "object",
94
+ properties: {
95
+ id: { type: "string", format: "uuid" },
96
+ },
97
+ },
98
+ },
99
+ },
100
+ };
101
+
102
+ const code = openApiToZodTsCode(openApiSpec, [
103
+ 'import { uuidSchema } from "./custom-schemas";',
104
+ ]);
105
+ ```
106
+
107
+ ### Request and Response Lookup Objects
108
+
109
+ Generate route lookup objects with Zod schemas for request and response validation:
110
+
111
+ ```typescript
112
+ import { openApiToZodTsCode } from "zod-openapi";
113
+
114
+ const openApiSpec = {
115
+ components: {
116
+ schemas: {
117
+ User: {
118
+ type: "object",
119
+ properties: {
120
+ id: { type: "string" },
121
+ name: { type: "string" },
122
+ },
123
+ required: ["id", "name"],
124
+ },
125
+ CreateUser: {
126
+ type: "object",
127
+ properties: {
128
+ name: { type: "string" },
129
+ },
130
+ required: ["name"],
131
+ },
132
+ },
133
+ },
134
+ paths: {
135
+ "/users/{id}": {
136
+ get: {
137
+ parameters: [
138
+ {
139
+ name: "id",
140
+ in: "path",
141
+ required: true,
142
+ schema: { type: "string" },
143
+ },
144
+ ],
145
+ responses: {
146
+ "200": {
147
+ content: {
148
+ "application/json": {
149
+ schema: {
150
+ $ref: "#/components/schemas/User",
151
+ },
152
+ },
153
+ },
154
+ },
155
+ },
156
+ },
157
+ },
158
+ "/users": {
159
+ post: {
160
+ requestBody: {
161
+ content: {
162
+ "application/json": {
163
+ schema: {
164
+ $ref: "#/components/schemas/CreateUser",
165
+ },
166
+ },
167
+ },
168
+ },
169
+ responses: {
170
+ "201": {
171
+ content: {
172
+ "application/json": {
173
+ schema: {
174
+ $ref: "#/components/schemas/User",
175
+ },
176
+ },
177
+ },
178
+ },
179
+ },
180
+ },
181
+ },
182
+ },
183
+ };
184
+
185
+ const generatedCode = openApiToZodTsCode(openApiSpec, undefined, {
186
+ includeRoutes: true,
187
+ });
188
+ ```
189
+
190
+ Generated output includes `Request` and `Response` lookup objects:
191
+
192
+ ```typescript
193
+ // ... component schemas ...
194
+
195
+ // Route schemas
196
+ export const GetUsersIdParamsSchema = z.object({
197
+ id: z.string(),
198
+ });
199
+ export const GetUsersIdResponseSchema = UserSchema;
200
+ export const PostUsersBodySchema = CreateUserSchema;
201
+ export const PostUsersResponseSchema = UserSchema;
202
+
203
+ // Request and Response lookup objects
204
+ export const Request = {
205
+ '/users/{id}': {
206
+ GET: {
207
+ params: GetUsersIdParamsSchema,
208
+ },
209
+ },
210
+ '/users': {
211
+ POST: {
212
+ body: PostUsersBodySchema,
213
+ },
214
+ },
215
+ } as const;
216
+
217
+ export const Response = {
218
+ '/users/{id}': {
219
+ GET: GetUsersIdResponseSchema,
220
+ },
221
+ '/users': {
222
+ POST: PostUsersResponseSchema,
223
+ },
224
+ } as const;
225
+ ```
226
+
227
+ Usage example:
228
+
229
+ ```typescript
230
+ const endpoint = "/users/{id}" as const;
231
+
232
+ // Access request schemas
233
+ const { params: ParamsSchema } = Request[endpoint]["GET"];
234
+
235
+ // Access response schema
236
+ const ResponseSchema = Response[endpoint]["GET"];
237
+
238
+ // Use schemas for validation
239
+ const params = ParamsSchema.parse({ id: "123" });
240
+ const response = ResponseSchema.parse({ id: "123", name: "John" });
241
+ ```
242
+
243
+ ### Supported String Formats
244
+
245
+ The following string formats are supported out of the box:
246
+
247
+ - `color-hex`
248
+ - `date`
249
+ - `date-time`
250
+ - `email`
251
+ - `iso-date`
252
+ - `iso-date-time`
253
+ - `objectid`
254
+ - `uri`
255
+ - `url`
256
+ - `uuid`
257
+
258
+ ## API Reference
259
+
260
+ ### `openApiToZodTsCode(openapi, customImportLines?, options?)`
261
+
262
+ Converts an OpenAPI specification to TypeScript code containing Zod schemas.
263
+
264
+ **Parameters:**
265
+ - `openapi`: OpenAPI specification object (must contain `components.schemas`)
266
+ - `customImportLines`: Optional array of custom import statements to include
267
+ - `options`: Optional configuration object
268
+ - `includeRoutes`: If `true`, generates `Request` and `Response` lookup objects from OpenAPI paths (default: `false`)
269
+
270
+ **Returns:** `string` - Generated TypeScript code
271
+
272
+ ### `convertSchemaToZodString(schema)`
273
+
274
+ Converts a single OpenAPI schema to a Zod expression string.
275
+
276
+ **Parameters:**
277
+ - `schema`: OpenAPI schema object
278
+
279
+ **Returns:** `string` - Zod expression as a string (e.g., `"z.string()"`)
280
+
281
+ ### `registerZodSchemaToOpenApiSchema(schema, registration)`
282
+
283
+ Registers a Zod schema with its OpenAPI representation for custom string formats.
284
+
285
+ **Parameters:**
286
+ - `schema`: Zod schema instance
287
+ - `registration`: Registration object describing the OpenAPI type/format
288
+
289
+ ### `extractSchemaDependencies(schema)`
290
+
291
+ Extracts all schema references from an OpenAPI schema.
292
+
293
+ **Parameters:**
294
+ - `schema`: OpenAPI schema object
295
+
296
+ **Returns:** `string[]` - Array of referenced schema names
297
+
298
+ ### `topologicalSortSchemas(schemas)`
299
+
300
+ Sorts schemas topologically to ensure correct generation order.
301
+
302
+ **Parameters:**
303
+ - `schemas`: Record mapping schema names to OpenAPI schema definitions
304
+
305
+ **Returns:** `string[]` - Schema names in topological order
306
+
307
+ ## Supported OpenAPI Schema Features
308
+
309
+ - ✅ Basic types: `string`, `number`, `integer`, `boolean`
310
+ - ✅ Objects with `properties` and `required`
311
+ - ✅ Arrays with `items`
312
+ - ✅ Unions (`oneOf`)
313
+ - ✅ Intersections (`allOf`)
314
+ - ✅ Nullable schemas
315
+ - ✅ Enums
316
+ - ✅ String formats (email, date, uuid, etc.)
317
+ - ✅ Validation constraints (`minLength`, `maxLength`, `pattern`, `minimum`, `maximum`, etc.)
318
+ - ✅ Schema references (`$ref`)
319
+ - ✅ Additional properties
320
+ - ✅ Route request/response lookup objects (`Request` and `Response`)
321
+
322
+ ## Development
323
+
324
+ ```bash
325
+ # Run tests
326
+ pnpm test
327
+
328
+ # Type check
329
+ pnpm check-types
330
+ ```
331
+
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+ import { fileURLToPath } from 'url';
3
+ import { dirname, join } from 'path';
4
+ import { createRequire } from 'module';
5
+ import { spawn } from 'child_process';
6
+
7
+ const require = createRequire(import.meta.url);
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+
10
+ // Resolve tsx and CLI paths
11
+ const tsxPath = require.resolve('tsx/cli.mjs');
12
+ const cliPath = join(__dirname, '../src/cli.ts');
13
+
14
+ // Spawn tsx to run the CLI
15
+ const child = spawn('node', [tsxPath, cliPath, ...process.argv.slice(2)], {
16
+ stdio: 'inherit',
17
+ cwd: process.cwd(),
18
+ });
19
+
20
+ child.on('exit', (code) => {
21
+ process.exit(code ?? 0);
22
+ });
23
+
24
+ child.on('error', (error) => {
25
+ console.error('Error running CLI:', error.message);
26
+ process.exit(1);
27
+ });
28
+
@@ -0,0 +1,120 @@
1
+ import { z } from 'zod';
2
+
3
+ declare const openApiToZodTsCode: (openapi: Record<string, unknown>, customImportLines?: string[], options?: {
4
+ includeRoutes?: boolean;
5
+ }) => string;
6
+
7
+ declare const SUPPORTED_STRING_FORMATS_MAP: {
8
+ readonly "color-hex": 1;
9
+ readonly date: 1;
10
+ readonly "date-time": 1;
11
+ readonly email: 1;
12
+ readonly "iso-date": 1;
13
+ readonly "iso-date-time": 1;
14
+ readonly objectid: 1;
15
+ readonly uri: 1;
16
+ readonly url: 1;
17
+ readonly uuid: 1;
18
+ };
19
+ declare const SUPPORTED_STRING_FORMATS: keyof typeof SUPPORTED_STRING_FORMATS_MAP;
20
+ type SupportedStringFormat = typeof SUPPORTED_STRING_FORMATS;
21
+ type ZodOpenApiRegistrationString<F extends SupportedStringFormat = SupportedStringFormat> = {
22
+ /** The name of the schema variable, IMPORTANT: must be named the same as the variable name */
23
+ schemaExportedVariableName: string;
24
+ type: "string";
25
+ description?: string;
26
+ format: F;
27
+ };
28
+ type ZodOpenApiRegistrationStrings<Fs extends readonly SupportedStringFormat[] = readonly SupportedStringFormat[]> = {
29
+ /** The name of the schema variable, IMPORTANT: must be named the same as the variable name */
30
+ schemaExportedVariableName: string;
31
+ type: "string";
32
+ description?: string;
33
+ formats: Fs;
34
+ };
35
+ type ZodOpenApiRegistrationPrimitive = {
36
+ /** The name of the schema variable, IMPORTANT: must be named the same as the variable name */
37
+ schemaExportedVariableName: string;
38
+ description?: string;
39
+ type: "number" | "integer" | "boolean";
40
+ };
41
+ type ZodOpenApiRegistration = ZodOpenApiRegistrationString | ZodOpenApiRegistrationStrings | ZodOpenApiRegistrationPrimitive;
42
+ /**
43
+ * Global registry for mapping Zod schemas to OpenAPI schema representations
44
+ */
45
+ declare class ZodSchemaRegistry {
46
+ private readonly map;
47
+ /**
48
+ * Register a Zod schema with its OpenAPI representation
49
+ */
50
+ register<F extends SupportedStringFormat>(schema: z.ZodTypeAny, registration: ZodOpenApiRegistrationString<F>): void;
51
+ register<Fs extends readonly SupportedStringFormat[]>(schema: z.ZodTypeAny, registration: ZodOpenApiRegistrationStrings<Fs>): void;
52
+ register(schema: z.ZodTypeAny, registration: ZodOpenApiRegistrationPrimitive): void;
53
+ /**
54
+ * Get the OpenAPI schema for a given Zod schema
55
+ */
56
+ getOpenApiSchema(schema: z.ZodTypeAny): ZodOpenApiRegistration | undefined;
57
+ /**
58
+ * Check if a Zod schema is registered
59
+ */
60
+ isRegistered(schema: z.ZodTypeAny): boolean;
61
+ /**
62
+ * Clear all registered schemas
63
+ */
64
+ clear(): void;
65
+ /**
66
+ * Reverse-lookup helper: given a string format, return the registered schema's exported variable name
67
+ */
68
+ getSchemaExportedVariableNameForStringFormat(format: SupportedStringFormat): string | undefined;
69
+ }
70
+ declare const schemaRegistry: ZodSchemaRegistry;
71
+ /**
72
+ * Helper function to register a Zod schema with its OpenAPI representation
73
+ */
74
+ declare function registerZodSchemaToOpenApiSchema(schema: z.ZodTypeAny, openApiSchema: ZodOpenApiRegistration): void;
75
+ /**
76
+ * Convenience helper to get an exported schema variable name for a given string format
77
+ */
78
+ declare function getSchemaExportedVariableNameForStringFormat(format: SupportedStringFormat): string | undefined;
79
+ /**
80
+ * Clear all registered schemas in the global registry
81
+ */
82
+ declare function clearZodSchemaToOpenApiSchemaRegistry(): void;
83
+
84
+ type AnySchema = Record<string, any>;
85
+ type OpenAPIObjectSchema = {
86
+ type: "object";
87
+ properties?: Record<string, AnySchema>;
88
+ required?: string[];
89
+ additionalProperties?: boolean | AnySchema;
90
+ maxProperties?: number;
91
+ minProperties?: number;
92
+ };
93
+
94
+ declare function convertSchemaToZodString(schema: AnySchema): string;
95
+
96
+ type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
97
+ interface RouteParameter {
98
+ name: string;
99
+ in: "path" | "query" | "header" | "cookie";
100
+ required: boolean;
101
+ schema: AnySchema;
102
+ }
103
+ interface RouteInfo {
104
+ path: string;
105
+ method: HttpMethod;
106
+ parameters: RouteParameter[];
107
+ requestBody?: AnySchema;
108
+ responses: Record<string, AnySchema>;
109
+ }
110
+ interface RouteSchemaNames {
111
+ paramsSchemaName?: string;
112
+ querySchemaName?: string;
113
+ headersSchemaName?: string;
114
+ bodySchemaName?: string;
115
+ responseSchemaName?: string;
116
+ }
117
+ declare function parseOpenApiPaths(openapi: Record<string, unknown>): RouteInfo[];
118
+ declare function generateRouteSchemaNames(route: RouteInfo): RouteSchemaNames;
119
+
120
+ export { type AnySchema, type HttpMethod, type OpenAPIObjectSchema, type RouteInfo, type RouteParameter, type RouteSchemaNames, SUPPORTED_STRING_FORMATS, type ZodOpenApiRegistration, type ZodOpenApiRegistrationPrimitive, type ZodOpenApiRegistrationString, type ZodOpenApiRegistrationStrings, clearZodSchemaToOpenApiSchemaRegistry, convertSchemaToZodString, generateRouteSchemaNames, getSchemaExportedVariableNameForStringFormat, openApiToZodTsCode, parseOpenApiPaths, registerZodSchemaToOpenApiSchema, schemaRegistry };
@@ -0,0 +1,120 @@
1
+ import { z } from 'zod';
2
+
3
+ declare const openApiToZodTsCode: (openapi: Record<string, unknown>, customImportLines?: string[], options?: {
4
+ includeRoutes?: boolean;
5
+ }) => string;
6
+
7
+ declare const SUPPORTED_STRING_FORMATS_MAP: {
8
+ readonly "color-hex": 1;
9
+ readonly date: 1;
10
+ readonly "date-time": 1;
11
+ readonly email: 1;
12
+ readonly "iso-date": 1;
13
+ readonly "iso-date-time": 1;
14
+ readonly objectid: 1;
15
+ readonly uri: 1;
16
+ readonly url: 1;
17
+ readonly uuid: 1;
18
+ };
19
+ declare const SUPPORTED_STRING_FORMATS: keyof typeof SUPPORTED_STRING_FORMATS_MAP;
20
+ type SupportedStringFormat = typeof SUPPORTED_STRING_FORMATS;
21
+ type ZodOpenApiRegistrationString<F extends SupportedStringFormat = SupportedStringFormat> = {
22
+ /** The name of the schema variable, IMPORTANT: must be named the same as the variable name */
23
+ schemaExportedVariableName: string;
24
+ type: "string";
25
+ description?: string;
26
+ format: F;
27
+ };
28
+ type ZodOpenApiRegistrationStrings<Fs extends readonly SupportedStringFormat[] = readonly SupportedStringFormat[]> = {
29
+ /** The name of the schema variable, IMPORTANT: must be named the same as the variable name */
30
+ schemaExportedVariableName: string;
31
+ type: "string";
32
+ description?: string;
33
+ formats: Fs;
34
+ };
35
+ type ZodOpenApiRegistrationPrimitive = {
36
+ /** The name of the schema variable, IMPORTANT: must be named the same as the variable name */
37
+ schemaExportedVariableName: string;
38
+ description?: string;
39
+ type: "number" | "integer" | "boolean";
40
+ };
41
+ type ZodOpenApiRegistration = ZodOpenApiRegistrationString | ZodOpenApiRegistrationStrings | ZodOpenApiRegistrationPrimitive;
42
+ /**
43
+ * Global registry for mapping Zod schemas to OpenAPI schema representations
44
+ */
45
+ declare class ZodSchemaRegistry {
46
+ private readonly map;
47
+ /**
48
+ * Register a Zod schema with its OpenAPI representation
49
+ */
50
+ register<F extends SupportedStringFormat>(schema: z.ZodTypeAny, registration: ZodOpenApiRegistrationString<F>): void;
51
+ register<Fs extends readonly SupportedStringFormat[]>(schema: z.ZodTypeAny, registration: ZodOpenApiRegistrationStrings<Fs>): void;
52
+ register(schema: z.ZodTypeAny, registration: ZodOpenApiRegistrationPrimitive): void;
53
+ /**
54
+ * Get the OpenAPI schema for a given Zod schema
55
+ */
56
+ getOpenApiSchema(schema: z.ZodTypeAny): ZodOpenApiRegistration | undefined;
57
+ /**
58
+ * Check if a Zod schema is registered
59
+ */
60
+ isRegistered(schema: z.ZodTypeAny): boolean;
61
+ /**
62
+ * Clear all registered schemas
63
+ */
64
+ clear(): void;
65
+ /**
66
+ * Reverse-lookup helper: given a string format, return the registered schema's exported variable name
67
+ */
68
+ getSchemaExportedVariableNameForStringFormat(format: SupportedStringFormat): string | undefined;
69
+ }
70
+ declare const schemaRegistry: ZodSchemaRegistry;
71
+ /**
72
+ * Helper function to register a Zod schema with its OpenAPI representation
73
+ */
74
+ declare function registerZodSchemaToOpenApiSchema(schema: z.ZodTypeAny, openApiSchema: ZodOpenApiRegistration): void;
75
+ /**
76
+ * Convenience helper to get an exported schema variable name for a given string format
77
+ */
78
+ declare function getSchemaExportedVariableNameForStringFormat(format: SupportedStringFormat): string | undefined;
79
+ /**
80
+ * Clear all registered schemas in the global registry
81
+ */
82
+ declare function clearZodSchemaToOpenApiSchemaRegistry(): void;
83
+
84
+ type AnySchema = Record<string, any>;
85
+ type OpenAPIObjectSchema = {
86
+ type: "object";
87
+ properties?: Record<string, AnySchema>;
88
+ required?: string[];
89
+ additionalProperties?: boolean | AnySchema;
90
+ maxProperties?: number;
91
+ minProperties?: number;
92
+ };
93
+
94
+ declare function convertSchemaToZodString(schema: AnySchema): string;
95
+
96
+ type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
97
+ interface RouteParameter {
98
+ name: string;
99
+ in: "path" | "query" | "header" | "cookie";
100
+ required: boolean;
101
+ schema: AnySchema;
102
+ }
103
+ interface RouteInfo {
104
+ path: string;
105
+ method: HttpMethod;
106
+ parameters: RouteParameter[];
107
+ requestBody?: AnySchema;
108
+ responses: Record<string, AnySchema>;
109
+ }
110
+ interface RouteSchemaNames {
111
+ paramsSchemaName?: string;
112
+ querySchemaName?: string;
113
+ headersSchemaName?: string;
114
+ bodySchemaName?: string;
115
+ responseSchemaName?: string;
116
+ }
117
+ declare function parseOpenApiPaths(openapi: Record<string, unknown>): RouteInfo[];
118
+ declare function generateRouteSchemaNames(route: RouteInfo): RouteSchemaNames;
119
+
120
+ export { type AnySchema, type HttpMethod, type OpenAPIObjectSchema, type RouteInfo, type RouteParameter, type RouteSchemaNames, SUPPORTED_STRING_FORMATS, type ZodOpenApiRegistration, type ZodOpenApiRegistrationPrimitive, type ZodOpenApiRegistrationString, type ZodOpenApiRegistrationStrings, clearZodSchemaToOpenApiSchemaRegistry, convertSchemaToZodString, generateRouteSchemaNames, getSchemaExportedVariableNameForStringFormat, openApiToZodTsCode, parseOpenApiPaths, registerZodSchemaToOpenApiSchema, schemaRegistry };