zod-codegen 1.6.2 → 1.7.0

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.
Files changed (119) hide show
  1. package/.github/workflows/ci.yml +50 -48
  2. package/.github/workflows/release.yml +13 -3
  3. package/.husky/commit-msg +1 -1
  4. package/.husky/pre-commit +1 -1
  5. package/.lintstagedrc.json +5 -1
  6. package/.nvmrc +1 -1
  7. package/.prettierrc.json +12 -5
  8. package/CHANGELOG.md +15 -0
  9. package/CONTRIBUTING.md +12 -12
  10. package/EXAMPLES.md +135 -57
  11. package/PERFORMANCE.md +4 -4
  12. package/README.md +87 -64
  13. package/SECURITY.md +1 -1
  14. package/dist/src/cli.js +11 -18
  15. package/dist/src/generator.d.ts +2 -2
  16. package/dist/src/generator.d.ts.map +1 -1
  17. package/dist/src/generator.js +5 -3
  18. package/dist/src/interfaces/code-generator.d.ts.map +1 -1
  19. package/dist/src/services/code-generator.service.d.ts +24 -1
  20. package/dist/src/services/code-generator.service.d.ts.map +1 -1
  21. package/dist/src/services/code-generator.service.js +385 -216
  22. package/dist/src/services/file-reader.service.d.ts.map +1 -1
  23. package/dist/src/services/file-reader.service.js +1 -1
  24. package/dist/src/services/file-writer.service.d.ts.map +1 -1
  25. package/dist/src/services/file-writer.service.js +2 -2
  26. package/dist/src/services/import-builder.service.d.ts.map +1 -1
  27. package/dist/src/services/import-builder.service.js +3 -3
  28. package/dist/src/services/type-builder.service.d.ts.map +1 -1
  29. package/dist/src/types/generator-options.d.ts.map +1 -1
  30. package/dist/src/types/openapi.d.ts.map +1 -1
  31. package/dist/src/types/openapi.js +20 -20
  32. package/dist/src/utils/error-handler.d.ts.map +1 -1
  33. package/dist/src/utils/naming-convention.d.ts.map +1 -1
  34. package/dist/src/utils/naming-convention.js +6 -3
  35. package/dist/src/utils/signal-handler.d.ts.map +1 -1
  36. package/dist/tests/integration/cli-comprehensive.test.d.ts +2 -0
  37. package/dist/tests/integration/cli-comprehensive.test.d.ts.map +1 -0
  38. package/dist/tests/integration/cli-comprehensive.test.js +110 -0
  39. package/dist/tests/integration/cli.test.d.ts +2 -0
  40. package/dist/tests/integration/cli.test.d.ts.map +1 -0
  41. package/dist/tests/integration/cli.test.js +25 -0
  42. package/dist/tests/integration/error-scenarios.test.d.ts +2 -0
  43. package/dist/tests/integration/error-scenarios.test.d.ts.map +1 -0
  44. package/dist/tests/integration/error-scenarios.test.js +169 -0
  45. package/dist/tests/integration/snapshots.test.d.ts +2 -0
  46. package/dist/tests/integration/snapshots.test.d.ts.map +1 -0
  47. package/dist/tests/integration/snapshots.test.js +100 -0
  48. package/dist/tests/unit/code-generator-edge-cases.test.d.ts +2 -0
  49. package/dist/tests/unit/code-generator-edge-cases.test.d.ts.map +1 -0
  50. package/dist/tests/unit/code-generator-edge-cases.test.js +506 -0
  51. package/dist/tests/unit/code-generator.test.d.ts +2 -0
  52. package/dist/tests/unit/code-generator.test.d.ts.map +1 -0
  53. package/dist/tests/unit/code-generator.test.js +1364 -0
  54. package/dist/tests/unit/file-reader.test.d.ts +2 -0
  55. package/dist/tests/unit/file-reader.test.d.ts.map +1 -0
  56. package/dist/tests/unit/file-reader.test.js +125 -0
  57. package/dist/tests/unit/generator.test.d.ts +2 -0
  58. package/dist/tests/unit/generator.test.d.ts.map +1 -0
  59. package/dist/tests/unit/generator.test.js +119 -0
  60. package/dist/tests/unit/naming-convention.test.d.ts +2 -0
  61. package/dist/tests/unit/naming-convention.test.d.ts.map +1 -0
  62. package/dist/tests/unit/naming-convention.test.js +256 -0
  63. package/dist/tests/unit/reporter.test.d.ts +2 -0
  64. package/dist/tests/unit/reporter.test.d.ts.map +1 -0
  65. package/dist/tests/unit/reporter.test.js +44 -0
  66. package/dist/tests/unit/type-builder.test.d.ts +2 -0
  67. package/dist/tests/unit/type-builder.test.d.ts.map +1 -0
  68. package/dist/tests/unit/type-builder.test.js +108 -0
  69. package/dist/vitest.config.d.ts.map +1 -1
  70. package/dist/vitest.config.js +10 -20
  71. package/eslint.config.mjs +38 -28
  72. package/examples/.gitkeep +1 -1
  73. package/examples/README.md +4 -2
  74. package/examples/petstore/README.md +18 -17
  75. package/examples/petstore/{type.ts → api.ts} +158 -74
  76. package/examples/petstore/authenticated-usage.ts +6 -4
  77. package/examples/petstore/basic-usage.ts +4 -3
  78. package/examples/petstore/error-handling-usage.ts +84 -0
  79. package/examples/petstore/retry-handler-usage.ts +11 -18
  80. package/examples/petstore/server-variables-usage.ts +10 -10
  81. package/examples/pokeapi/README.md +8 -8
  82. package/examples/pokeapi/api.ts +218 -0
  83. package/examples/pokeapi/basic-usage.ts +3 -2
  84. package/examples/pokeapi/custom-client.ts +5 -4
  85. package/package.json +17 -21
  86. package/src/cli.ts +20 -25
  87. package/src/generator.ts +13 -11
  88. package/src/interfaces/code-generator.ts +1 -1
  89. package/src/services/code-generator.service.ts +989 -1099
  90. package/src/services/file-reader.service.ts +6 -5
  91. package/src/services/file-writer.service.ts +7 -7
  92. package/src/services/import-builder.service.ts +9 -13
  93. package/src/services/type-builder.service.ts +8 -19
  94. package/src/types/generator-options.ts +1 -1
  95. package/src/types/openapi.ts +22 -22
  96. package/src/utils/error-handler.ts +2 -2
  97. package/src/utils/naming-convention.ts +13 -10
  98. package/src/utils/reporter.ts +2 -2
  99. package/src/utils/signal-handler.ts +7 -8
  100. package/tests/integration/cli-comprehensive.test.ts +38 -32
  101. package/tests/integration/cli.test.ts +5 -5
  102. package/tests/integration/error-scenarios.test.ts +20 -26
  103. package/tests/integration/snapshots.test.ts +19 -23
  104. package/tests/unit/code-generator-edge-cases.test.ts +133 -133
  105. package/tests/unit/code-generator.test.ts +674 -268
  106. package/tests/unit/file-reader.test.ts +14 -14
  107. package/tests/unit/generator.test.ts +30 -18
  108. package/tests/unit/naming-convention.test.ts +27 -27
  109. package/tests/unit/type-builder.test.ts +2 -2
  110. package/tsconfig.json +5 -3
  111. package/vitest.config.ts +11 -21
  112. package/dist/scripts/update-manifest.d.ts +0 -14
  113. package/dist/scripts/update-manifest.d.ts.map +0 -1
  114. package/dist/scripts/update-manifest.js +0 -33
  115. package/dist/src/assets/manifest.json +0 -5
  116. package/examples/pokeapi/type.ts +0 -109
  117. package/generated/type.ts +0 -326
  118. package/scripts/update-manifest.ts +0 -49
  119. package/src/assets/manifest.json +0 -5
package/generated/type.ts DELETED
@@ -1,326 +0,0 @@
1
- // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2
- // Built with zod-codegen@1.6.2
3
- // Latest edit: Mon, 19 Jan 2026 15:14:43 GMT
4
- // Source file: ./samples/swagger-petstore.yaml
5
- /* eslint-disable */
6
- // @ts-nocheck
7
-
8
-
9
- // Imports
10
- ;
11
- import { z } from 'zod';
12
-
13
- // Components schemas
14
- ;
15
- export const Order = z.object({
16
- id: z.number().int().optional(),
17
- petId: z.number().int().optional(),
18
- quantity: z.number().int().optional(),
19
- shipDate: z.iso.datetime({
20
- local: true
21
- }).optional(),
22
- status: z.enum(['placed', 'approved', 'delivered']).optional(),
23
- complete: z.boolean().optional()
24
- });
25
- export const Address = z.object({
26
- street: z.string().optional(),
27
- city: z.string().optional(),
28
- state: z.string().optional(),
29
- zip: z.string().optional()
30
- });
31
- export const Customer = z.object({
32
- id: z.number().int().optional(),
33
- username: z.string().optional(),
34
- address: z.array(Address).optional()
35
- });
36
- export const Category = z.object({
37
- id: z.number().int().optional(),
38
- name: z.string().optional()
39
- });
40
- export const User = z.object({
41
- id: z.number().int().optional(),
42
- username: z.string().optional(),
43
- firstName: z.string().optional(),
44
- lastName: z.string().optional(),
45
- email: z.string().optional(),
46
- password: z.string().optional(),
47
- phone: z.string().optional(),
48
- userStatus: z.number().int().optional()
49
- });
50
- export const Tag = z.object({
51
- id: z.number().int().optional(),
52
- name: z.string().optional()
53
- });
54
- export const Pet = z.object({
55
- id: z.number().int().optional(),
56
- name: z.string(),
57
- category: Category.optional(),
58
- photoUrls: z.array(z.string()),
59
- tags: z.array(Tag).optional(),
60
- status: z.enum(['available', 'pending', 'sold']).optional()
61
- });
62
- export const ApiResponse = z.object({
63
- code: z.number().int().optional(),
64
- type: z.string().optional(),
65
- message: z.string().optional()
66
- });
67
- export type Order = z.infer<typeof Order>;
68
- export type Address = z.infer<typeof Address>;
69
- export type Customer = z.infer<typeof Customer>;
70
- export type Category = z.infer<typeof Category>;
71
- export type User = z.infer<typeof User>;
72
- export type Tag = z.infer<typeof Tag>;
73
- export type Pet = z.infer<typeof Pet>;
74
- export type ApiResponse = z.infer<typeof ApiResponse>;
75
- export const serverConfigurations = [{
76
- url: 'https://petstore3.swagger.io/api/v3'
77
- }];
78
- export const defaultBaseUrl = 'https://petstore3.swagger.io/api/v3';
79
- export type ClientOptions = {
80
- baseUrl?: string;
81
- serverIndex?: number;
82
- serverVariables?: Record<string, string>;
83
- };
84
- function resolveServerUrl(serverIndex?: number | undefined, serverVariables?: Record<string, string> = {}): string {
85
- const configs = [{
86
- url: 'https://petstore3.swagger.io/api/v3'
87
- }];
88
- const idx = serverIndex ?? 0;
89
- if (idx < configs.length) {
90
- const config = configs[idx];
91
- let url = config.url;
92
- if (config.variables && serverVariables) {
93
- for (const [key, value] of Object.entries(serverVariables)) {
94
- url = url.replace(new RegExp("\\{" + key + "\\}", "g"), value);
95
- }
96
- }
97
- return url;
98
- }
99
- return 'https://petstore3.swagger.io/api/v3';
100
- }
101
-
102
- // Client class
103
- ;
104
- export default class SwaggerPetstoreOpenAPI30 {
105
- readonly #baseUrl: string;
106
- constructor(options: ClientOptions) {
107
- const resolvedUrl = options.baseUrl !== null ? options.baseUrl : resolveServerUrl(options.serverIndex, options.serverVariables);
108
- this.#baseUrl = resolvedUrl;
109
- }
110
- protected getBaseRequestOptions(): Partial<Omit<RequestInit, 'method' | 'body'>> {
111
- return {};
112
- }
113
- protected async handleResponse<T>(response: Response, method: string, path: string, options: _params___Record_string__string___number___boolean___data___unknown__contentType___string__headers___Record_string__string__): Promise<Response> {
114
- return response;
115
- }
116
- protected async makeRequest<T>(method: string, path: string, options: _params___Record_string__string___number___boolean___data___unknown__contentType___string__headers___Record_string__string__ = {}): Promise<T> {
117
- const baseUrl = new URL(path, this.#baseUrl);
118
- const url = options.params && Object.keys(options.params).length > 0 ? (() => {
119
- Object.entries(options.params).filter(([, value]) => value !== undefined).forEach(([key, value]) => { baseUrl.searchParams.set(key, String(value)); });
120
- return baseUrl.toString();
121
- })() : baseUrl.toString();
122
- const baseOptions = this.getBaseRequestOptions();
123
- const contentType = options.contentType === 'application/x-www-form-urlencoded' ? 'application/x-www-form-urlencoded' : 'application/json';
124
- const baseHeaders = baseOptions.headers !== undefined ? baseOptions.headers : {};
125
- const headers = Object.assign({}, baseHeaders, { 'Content-Type': contentType }, options.headers !== undefined ? options.headers : {});
126
- const body = options.data !== undefined ? options.contentType === 'application/x-www-form-urlencoded' ? (() => { const params = new URLSearchParams(); Object.entries(options.data).forEach(([key, value]) => { params.set(key, String(value)); }); return params.toString(); })() : JSON.stringify(options.data) : null;
127
- const rawResponse = await fetch(url, Object.assign({}, baseOptions, { method, headers: headers, body: body }));
128
- const response = await this.handleResponse<T>(rawResponse, method, path, options);
129
- if (!response.ok)
130
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
131
- return await response.json();
132
- }
133
- /**
134
- * Add a new pet to the store
135
- * @param body Create a new pet in the store
136
- * @returns {Pet}
137
- */
138
- async addPet(body: Pet): Promise<Pet> {
139
- return Pet.parse(await this.makeRequest('POST', '/pet', { data: body, contentType: 'application/x-www-form-urlencoded' }));
140
- }
141
- /**
142
- * Update an existing pet
143
- *
144
- * Update an existing pet by Id
145
- * @param body Update an existent pet in the store
146
- * @returns {Pet}
147
- */
148
- async updatePet(body: Pet): Promise<Pet> {
149
- return Pet.parse(await this.makeRequest('PUT', '/pet', { data: body, contentType: 'application/x-www-form-urlencoded' }));
150
- }
151
- /**
152
- * Finds Pets by status
153
- *
154
- * Multiple status values can be provided with comma separated strings
155
- *
156
- * @param status Status values that need to be considered for filter
157
- * @returns {Pet[]}
158
- */
159
- async findPetsByStatus(status?: string): Promise<Pet[]> {
160
- return await this.makeRequest('GET', '/pet/findByStatus', { params: { 'status': status } });
161
- }
162
- /**
163
- * Finds Pets by tags
164
- *
165
- * Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.
166
- *
167
- * @param tags Tags to filter by
168
- * @returns {Pet[]}
169
- */
170
- async findPetsByTags(tags?: string[]): Promise<Pet[]> {
171
- return await this.makeRequest('GET', '/pet/findByTags', { params: { 'tags': tags } });
172
- }
173
- /**
174
- * Find pet by ID
175
- *
176
- * Returns a single pet
177
- *
178
- * @param petId ID of pet to return
179
- * @returns {Pet}
180
- */
181
- async getPetById(petId: number): Promise<Pet> {
182
- return Pet.parse(await this.makeRequest('GET', `/pet/${petId}`, {}));
183
- }
184
- /**
185
- * Updates a pet in the store with form data
186
- *
187
- * @param petId ID of pet that needs to be updated
188
- * @param name Name of pet that needs to be updated
189
- * @param status Status of pet that needs to be updated
190
- * @returns {void}
191
- */
192
- async updatePetWithForm(petId: number, name?: string, status?: string): Promise<void> {
193
- return await this.makeRequest('POST', `/pet/${petId}`, { params: { 'name': name, 'status': status } });
194
- }
195
- /**
196
- * Deletes a pet
197
- *
198
- * delete a pet
199
- *
200
- * @param api_key
201
- * @param petId Pet id to delete
202
- * @returns {void}
203
- */
204
- async deletePet(petId: number): Promise<void> {
205
- return await this.makeRequest('DELETE', `/pet/${petId}`, {});
206
- }
207
- /**
208
- * uploads an image
209
- *
210
- * @param petId ID of pet to update
211
- * @param additionalMetadata Additional Metadata
212
- * @param body
213
- * @returns {ApiResponse}
214
- */
215
- async uploadFile(petId: number, additionalMetadata?: string): Promise<ApiResponse> {
216
- return ApiResponse.parse(await this.makeRequest('POST', `/pet/${petId}/uploadImage`, { params: { 'additionalMetadata': additionalMetadata } }));
217
- }
218
- /**
219
- * Returns pet inventories by status
220
- *
221
- * Returns a map of status codes to quantities
222
- * @returns {Record<string, unknown>}
223
- */
224
- async getInventory(): Promise<Record<string, unknown>> {
225
- return await this.makeRequest('GET', '/store/inventory', {});
226
- }
227
- /**
228
- * Place an order for a pet
229
- *
230
- * Place a new order in the store
231
- * @param body
232
- * @returns {Order}
233
- */
234
- async placeOrder(body?: Order): Promise<Order> {
235
- return Order.parse(await this.makeRequest('POST', '/store/order', { data: body, contentType: 'application/x-www-form-urlencoded' }));
236
- }
237
- /**
238
- * Find purchase order by ID
239
- *
240
- * For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions.
241
- *
242
- * @param orderId ID of order that needs to be fetched
243
- * @returns {Order}
244
- */
245
- async getOrderById(orderId: number): Promise<Order> {
246
- return Order.parse(await this.makeRequest('GET', `/store/order/${orderId}`, {}));
247
- }
248
- /**
249
- * Delete purchase order by ID
250
- *
251
- * For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors
252
- *
253
- * @param orderId ID of the order that needs to be deleted
254
- * @returns {void}
255
- */
256
- async deleteOrder(orderId: number): Promise<void> {
257
- return await this.makeRequest('DELETE', `/store/order/${orderId}`, {});
258
- }
259
- /**
260
- * Create user
261
- *
262
- * This can only be done by the logged in user.
263
- * @param body Created user object
264
- * @returns {User}
265
- */
266
- async createUser(body?: User): Promise<User> {
267
- return User.parse(await this.makeRequest('POST', '/user', { data: body, contentType: 'application/x-www-form-urlencoded' }));
268
- }
269
- /**
270
- * Creates list of users with given input array
271
- * @param body
272
- * @returns {User}
273
- */
274
- async createUsersWithListInput(body?: User[]): Promise<User> {
275
- return User.parse(await this.makeRequest('POST', '/user/createWithList', { data: body }));
276
- }
277
- /**
278
- * Logs user into the system
279
- *
280
- * @param username The user name for login
281
- * @param password The password for login in clear text
282
- * @returns {string}
283
- */
284
- async loginUser(username?: string, password?: string): Promise<string> {
285
- return await this.makeRequest('GET', '/user/login', { params: { 'username': username, 'password': password } });
286
- }
287
- /**
288
- * Logs out current logged in user session
289
- * @returns {void}
290
- */
291
- async logoutUser(): Promise<void> {
292
- return await this.makeRequest('GET', '/user/logout', {});
293
- }
294
- /**
295
- * Get user by user name
296
- *
297
- * @param username The name that needs to be fetched. Use user1 for testing.
298
- * @returns {User}
299
- */
300
- async getUserByName(username: string): Promise<User> {
301
- return User.parse(await this.makeRequest('GET', `/user/${username}`, {}));
302
- }
303
- /**
304
- * Update user
305
- *
306
- * This can only be done by the logged in user.
307
- *
308
- * @param username name that need to be deleted
309
- * @param body Update an existent user in the store
310
- * @returns {void}
311
- */
312
- async updateUser(username: string, body?: User): Promise<void> {
313
- return await this.makeRequest('PUT', `/user/${username}`, { data: body, contentType: 'application/x-www-form-urlencoded' });
314
- }
315
- /**
316
- * Delete user
317
- *
318
- * This can only be done by the logged in user.
319
- *
320
- * @param username The name that needs to be deleted
321
- * @returns {void}
322
- */
323
- async deleteUser(username: string): Promise<void> {
324
- return await this.makeRequest('DELETE', `/user/${username}`, {});
325
- }
326
- }
@@ -1,49 +0,0 @@
1
- import {readFileSync, writeFileSync} from 'node:fs';
2
- import {dirname, resolve} from 'node:path';
3
- import {fileURLToPath} from 'node:url';
4
- import {z} from 'zod';
5
-
6
- interface PackageJson {
7
- name: string;
8
- version: string;
9
- description: string;
10
- }
11
-
12
- /**
13
- * Type guard for the package.json object
14
- *
15
- * @param input Unknown input
16
- * @returns true if the input is an event object
17
- */
18
- export function isPackageJson(input: unknown): input is PackageJson {
19
- const event = z
20
- .object({
21
- name: z.string(),
22
- version: z.string(),
23
- description: z.string(),
24
- })
25
- .strict()
26
- .catchall(z.any())
27
- .required();
28
-
29
- const {success} = event.safeParse(input);
30
-
31
- return success;
32
- }
33
-
34
- const __dirname = dirname(fileURLToPath(import.meta.url));
35
- const sourcePath = resolve(__dirname, '..', 'package.json');
36
-
37
- const data: unknown = JSON.parse(readFileSync(sourcePath, 'utf8'));
38
-
39
- if (!isPackageJson(data)) {
40
- process.exit(1);
41
- }
42
-
43
- const {name, version, description} = data;
44
-
45
- const targetPath = resolve(__dirname, '..', 'src', 'assets', 'manifest.json');
46
-
47
- writeFileSync(targetPath, JSON.stringify({name, version, description}, null, 2));
48
-
49
- process.exit(0);
@@ -1,5 +0,0 @@
1
- {
2
- "name": "zod-codegen",
3
- "version": "1.5.0",
4
- "description": "A powerful TypeScript code generator that creates Zod schemas and type-safe clients from OpenAPI specifications"
5
- }