zod-codegen 1.2.0 → 1.2.2

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 CHANGED
@@ -1,3 +1,13 @@
1
+ ## <small>1.2.2 (2025-11-19)</small>
2
+
3
+ - Merge pull request #35 from julienandreu/fix/generate-typescript-type-aliases ([b8d9250](https://github.com/julienandreu/zod-codegen/commit/b8d9250)), closes [#35](https://github.com/julienandreu/zod-codegen/issues/35)
4
+ - fix: generate TypeScript type aliases alongside Zod schemas ([7032a69](https://github.com/julienandreu/zod-codegen/commit/7032a69))
5
+
6
+ ## <small>1.2.1 (2025-11-19)</small>
7
+
8
+ - Merge pull request #33 from julienandreu/fix/add-z-infer-to-response-types ([a14d81c](https://github.com/julienandreu/zod-codegen/commit/a14d81c)), closes [#33](https://github.com/julienandreu/zod-codegen/issues/33)
9
+ - fix: add z.infer to response types in generated methods ([c45ac77](https://github.com/julienandreu/zod-codegen/commit/c45ac77))
10
+
1
11
  ## 1.2.0 (2025-11-19)
2
12
 
3
13
  - Merge pull request #31 from julienandreu/dependabot/npm_and_yarn/dev-dependencies-1dd8918b9f ([97ebb65](https://github.com/julienandreu/zod-codegen/commit/97ebb65)), closes [#31](https://github.com/julienandreu/zod-codegen/issues/31)
@@ -40,6 +40,7 @@ export class TypeScriptCodeGeneratorService {
40
40
  buildAST(openapi) {
41
41
  const imports = this.importBuilder.buildImports();
42
42
  const schemas = this.buildSchemas(openapi);
43
+ const schemaTypeAliases = this.buildSchemaTypeAliases(schemas);
43
44
  const serverConfig = this.buildServerConfiguration(openapi);
44
45
  const clientClass = this.buildClientClass(openapi, schemas);
45
46
  return [
@@ -47,6 +48,7 @@ export class TypeScriptCodeGeneratorService {
47
48
  ...imports,
48
49
  this.createComment('Components schemas'),
49
50
  ...Object.values(schemas),
51
+ ...schemaTypeAliases,
50
52
  ...serverConfig,
51
53
  this.createComment('Client class'),
52
54
  clientClass,
@@ -68,6 +70,12 @@ export class TypeScriptCodeGeneratorService {
68
70
  };
69
71
  }, {});
70
72
  }
73
+ buildSchemaTypeAliases(schemas) {
74
+ return Object.keys(schemas).map((name) => {
75
+ const sanitizedName = this.typeBuilder.sanitizeIdentifier(name);
76
+ return ts.factory.createTypeAliasDeclaration([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createIdentifier(sanitizedName), undefined, ts.factory.createTypeReferenceNode(ts.factory.createQualifiedName(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('infer')), [ts.factory.createTypeQueryNode(ts.factory.createIdentifier(sanitizedName), undefined)]));
77
+ });
78
+ }
71
79
  buildClientClass(openapi, schemas) {
72
80
  const clientName = this.generateClientName(openapi.info.title);
73
81
  const methods = this.buildClientMethods(openapi, schemas);
@@ -516,6 +524,31 @@ export class TypeScriptCodeGeneratorService {
516
524
  }
517
525
  const responseSchema = response.content['application/json'].schema;
518
526
  const typeName = this.getSchemaTypeName(responseSchema, schemas);
527
+ // Handle array types like "Pet[]"
528
+ if (typeName.endsWith('[]')) {
529
+ const itemTypeName = typeName.slice(0, -2);
530
+ const sanitizedItemTypeName = this.typeBuilder.sanitizeIdentifier(itemTypeName);
531
+ // Check if the item type is a custom schema (we have a type alias for it)
532
+ if (schemas[sanitizedItemTypeName]) {
533
+ // Use the type alias directly (it already uses z.infer)
534
+ return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
535
+ ts.factory.createArrayTypeNode(ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(sanitizedItemTypeName), undefined)),
536
+ ]);
537
+ }
538
+ // If it's a primitive array, use the type name as-is
539
+ return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
540
+ ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(typeName), undefined),
541
+ ]);
542
+ }
543
+ const sanitizedTypeName = this.typeBuilder.sanitizeIdentifier(typeName);
544
+ // Check if it's a custom schema type (we have a type alias for it)
545
+ if (schemas[sanitizedTypeName]) {
546
+ // Use the type name directly (we have a type alias that already uses z.infer)
547
+ return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
548
+ ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(sanitizedTypeName), undefined),
549
+ ]);
550
+ }
551
+ // For primitive types and Record types, use the type name directly
519
552
  return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
520
553
  ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(typeName), undefined),
521
554
  ]);
@@ -1,6 +1,6 @@
1
1
  // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2
- // Built with zod-codegen@1.0.1
3
- // Latest edit: Thu, 13 Nov 2025 13:36:35 GMT
2
+ // Built with zod-codegen@1.1.2
3
+ // Latest edit: Wed, 19 Nov 2025 16:26:25 GMT
4
4
  // Source file: ./samples/swagger-petstore.yaml
5
5
  /* eslint-disable */
6
6
  // @ts-nocheck
@@ -144,73 +144,209 @@ export class SwaggerPetstoreOpenAPI30 {
144
144
  if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
145
145
  return await response.json();
146
146
  }
147
- async addPet(body: Pet): Promise<Pet> {
147
+ /**
148
+ * Add a new pet to the store
149
+ * @param body Create a new pet in the store
150
+ * @returns {z.infer<typeof Pet>}
151
+ */
152
+ async addPet(body: Pet): Promise<z.infer<typeof Pet>> {
148
153
  return Pet.parse(
149
154
  await this.#makeRequest('POST', '/pet', {data: body, contentType: 'application/x-www-form-urlencoded'}),
150
155
  );
151
156
  }
152
- async updatePet(body: Pet): Promise<Pet> {
157
+ /**
158
+ * Update an existing pet
159
+ *
160
+ * Update an existing pet by Id
161
+ * @param body Update an existent pet in the store
162
+ * @returns {z.infer<typeof Pet>}
163
+ */
164
+ async updatePet(body: Pet): Promise<z.infer<typeof Pet>> {
153
165
  return Pet.parse(
154
166
  await this.#makeRequest('PUT', '/pet', {data: body, contentType: 'application/x-www-form-urlencoded'}),
155
167
  );
156
168
  }
157
- async findPetsByStatus(status?: string): Promise<Pet[]> {
169
+ /**
170
+ * Finds Pets by status
171
+ *
172
+ * Multiple status values can be provided with comma separated strings
173
+ *
174
+ * @param status Status values that need to be considered for filter
175
+ * @returns {z.infer<typeof Pet>[]}
176
+ */
177
+ async findPetsByStatus(status?: string): Promise<z.infer<typeof Pet>[]> {
158
178
  return await this.#makeRequest('GET', '/pet/findByStatus', {params: {status: status}});
159
179
  }
160
- async findPetsByTags(tags?: string[]): Promise<Pet[]> {
180
+ /**
181
+ * Finds Pets by tags
182
+ *
183
+ * Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.
184
+ *
185
+ * @param tags Tags to filter by
186
+ * @returns {z.infer<typeof Pet>[]}
187
+ */
188
+ async findPetsByTags(tags?: string[]): Promise<z.infer<typeof Pet>[]> {
161
189
  return await this.#makeRequest('GET', '/pet/findByTags', {params: {tags: tags}});
162
190
  }
163
- async getPetById(petId: number): Promise<Pet> {
191
+ /**
192
+ * Find pet by ID
193
+ *
194
+ * Returns a single pet
195
+ *
196
+ * @param petId ID of pet to return
197
+ * @returns {z.infer<typeof Pet>}
198
+ */
199
+ async getPetById(petId: number): Promise<z.infer<typeof Pet>> {
164
200
  return Pet.parse(await this.#makeRequest('GET', `/pet/${petId}`, {}));
165
201
  }
202
+ /**
203
+ * Updates a pet in the store with form data
204
+ *
205
+ * @param petId ID of pet that needs to be updated
206
+ * @param name Name of pet that needs to be updated
207
+ * @param status Status of pet that needs to be updated
208
+ * @returns {void}
209
+ */
166
210
  async updatePetWithForm(petId: number, name?: string, status?: string): Promise<void> {
167
211
  return await this.#makeRequest('POST', `/pet/${petId}`, {params: {name: name, status: status}});
168
212
  }
213
+ /**
214
+ * Deletes a pet
215
+ *
216
+ * delete a pet
217
+ *
218
+ * @param api_key
219
+ * @param petId Pet id to delete
220
+ * @returns {void}
221
+ */
169
222
  async deletePet(petId: number): Promise<void> {
170
223
  return await this.#makeRequest('DELETE', `/pet/${petId}`, {});
171
224
  }
172
- async uploadFile(petId: number, additionalMetadata?: string): Promise<ApiResponse> {
225
+ /**
226
+ * uploads an image
227
+ *
228
+ * @param petId ID of pet to update
229
+ * @param additionalMetadata Additional Metadata
230
+ * @param body
231
+ * @returns {z.infer<typeof ApiResponse>}
232
+ */
233
+ async uploadFile(petId: number, additionalMetadata?: string): Promise<z.infer<typeof ApiResponse>> {
173
234
  return ApiResponse.parse(
174
235
  await this.#makeRequest('POST', `/pet/${petId}/uploadImage`, {params: {additionalMetadata: additionalMetadata}}),
175
236
  );
176
237
  }
238
+ /**
239
+ * Returns pet inventories by status
240
+ *
241
+ * Returns a map of status codes to quantities
242
+ * @returns {Record<string, unknown>}
243
+ */
177
244
  async getInventory(): Promise<Record<string, unknown>> {
178
245
  return await this.#makeRequest('GET', '/store/inventory', {});
179
246
  }
180
- async placeOrder(body?: Order): Promise<Order> {
247
+ /**
248
+ * Place an order for a pet
249
+ *
250
+ * Place a new order in the store
251
+ * @param body
252
+ * @returns {z.infer<typeof Order>}
253
+ */
254
+ async placeOrder(body?: Order): Promise<z.infer<typeof Order>> {
181
255
  return Order.parse(
182
256
  await this.#makeRequest('POST', '/store/order', {data: body, contentType: 'application/x-www-form-urlencoded'}),
183
257
  );
184
258
  }
185
- async getOrderById(orderId: number): Promise<Order> {
259
+ /**
260
+ * Find purchase order by ID
261
+ *
262
+ * For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions.
263
+ *
264
+ * @param orderId ID of order that needs to be fetched
265
+ * @returns {z.infer<typeof Order>}
266
+ */
267
+ async getOrderById(orderId: number): Promise<z.infer<typeof Order>> {
186
268
  return Order.parse(await this.#makeRequest('GET', `/store/order/${orderId}`, {}));
187
269
  }
270
+ /**
271
+ * Delete purchase order by ID
272
+ *
273
+ * For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors
274
+ *
275
+ * @param orderId ID of the order that needs to be deleted
276
+ * @returns {void}
277
+ */
188
278
  async deleteOrder(orderId: number): Promise<void> {
189
279
  return await this.#makeRequest('DELETE', `/store/order/${orderId}`, {});
190
280
  }
191
- async createUser(body?: User): Promise<User> {
281
+ /**
282
+ * Create user
283
+ *
284
+ * This can only be done by the logged in user.
285
+ * @param body Created user object
286
+ * @returns {z.infer<typeof User>}
287
+ */
288
+ async createUser(body?: User): Promise<z.infer<typeof User>> {
192
289
  return User.parse(
193
290
  await this.#makeRequest('POST', '/user', {data: body, contentType: 'application/x-www-form-urlencoded'}),
194
291
  );
195
292
  }
196
- async createUsersWithListInput(body?: User[]): Promise<User> {
293
+ /**
294
+ * Creates list of users with given input array
295
+ * @param body
296
+ * @returns {z.infer<typeof User>}
297
+ */
298
+ async createUsersWithListInput(body?: User[]): Promise<z.infer<typeof User>> {
197
299
  return User.parse(await this.#makeRequest('POST', '/user/createWithList', {data: body}));
198
300
  }
301
+ /**
302
+ * Logs user into the system
303
+ *
304
+ * @param username The user name for login
305
+ * @param password The password for login in clear text
306
+ * @returns {string}
307
+ */
199
308
  async loginUser(username?: string, password?: string): Promise<string> {
200
309
  return await this.#makeRequest('GET', '/user/login', {params: {username: username, password: password}});
201
310
  }
311
+ /**
312
+ * Logs out current logged in user session
313
+ * @returns {void}
314
+ */
202
315
  async logoutUser(): Promise<void> {
203
316
  return await this.#makeRequest('GET', '/user/logout', {});
204
317
  }
205
- async getUserByName(username: string): Promise<User> {
318
+ /**
319
+ * Get user by user name
320
+ *
321
+ * @param username The name that needs to be fetched. Use user1 for testing.
322
+ * @returns {z.infer<typeof User>}
323
+ */
324
+ async getUserByName(username: string): Promise<z.infer<typeof User>> {
206
325
  return User.parse(await this.#makeRequest('GET', `/user/${username}`, {}));
207
326
  }
327
+ /**
328
+ * Update user
329
+ *
330
+ * This can only be done by the logged in user.
331
+ *
332
+ * @param username name that need to be deleted
333
+ * @param body Update an existent user in the store
334
+ * @returns {void}
335
+ */
208
336
  async updateUser(username: string, body?: User): Promise<void> {
209
337
  return await this.#makeRequest('PUT', `/user/${username}`, {
210
338
  data: body,
211
339
  contentType: 'application/x-www-form-urlencoded',
212
340
  });
213
341
  }
342
+ /**
343
+ * Delete user
344
+ *
345
+ * This can only be done by the logged in user.
346
+ *
347
+ * @param username The name that needs to be deleted
348
+ * @returns {void}
349
+ */
214
350
  async deleteUser(username: string): Promise<void> {
215
351
  return await this.#makeRequest('DELETE', `/user/${username}`, {});
216
352
  }
package/package.json CHANGED
@@ -110,5 +110,5 @@
110
110
  "release": "semantic-release",
111
111
  "release:dry": "semantic-release --dry-run"
112
112
  },
113
- "version": "1.2.0"
113
+ "version": "1.2.2"
114
114
  }
@@ -53,6 +53,7 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
53
53
  private buildAST(openapi: OpenApiSpecType): ts.Statement[] {
54
54
  const imports = this.importBuilder.buildImports();
55
55
  const schemas = this.buildSchemas(openapi);
56
+ const schemaTypeAliases = this.buildSchemaTypeAliases(schemas);
56
57
  const serverConfig = this.buildServerConfiguration(openapi);
57
58
  const clientClass = this.buildClientClass(openapi, schemas);
58
59
 
@@ -61,6 +62,7 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
61
62
  ...imports,
62
63
  this.createComment('Components schemas'),
63
64
  ...Object.values(schemas),
65
+ ...schemaTypeAliases,
64
66
  ...serverConfig,
65
67
  this.createComment('Client class'),
66
68
  clientClass,
@@ -97,6 +99,21 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
97
99
  }, {});
98
100
  }
99
101
 
102
+ private buildSchemaTypeAliases(schemas: Record<string, ts.VariableStatement>): ts.TypeAliasDeclaration[] {
103
+ return Object.keys(schemas).map((name) => {
104
+ const sanitizedName = this.typeBuilder.sanitizeIdentifier(name);
105
+ return ts.factory.createTypeAliasDeclaration(
106
+ [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
107
+ ts.factory.createIdentifier(sanitizedName),
108
+ undefined,
109
+ ts.factory.createTypeReferenceNode(
110
+ ts.factory.createQualifiedName(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('infer')),
111
+ [ts.factory.createTypeQueryNode(ts.factory.createIdentifier(sanitizedName), undefined)],
112
+ ),
113
+ );
114
+ });
115
+ }
116
+
100
117
  private buildClientClass(
101
118
  openapi: OpenApiSpecType,
102
119
  schemas: Record<string, ts.VariableStatement>,
@@ -1265,6 +1282,37 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
1265
1282
  const responseSchema = response.content['application/json'].schema;
1266
1283
  const typeName = this.getSchemaTypeName(responseSchema, schemas);
1267
1284
 
1285
+ // Handle array types like "Pet[]"
1286
+ if (typeName.endsWith('[]')) {
1287
+ const itemTypeName = typeName.slice(0, -2);
1288
+ const sanitizedItemTypeName = this.typeBuilder.sanitizeIdentifier(itemTypeName);
1289
+
1290
+ // Check if the item type is a custom schema (we have a type alias for it)
1291
+ if (schemas[sanitizedItemTypeName]) {
1292
+ // Use the type alias directly (it already uses z.infer)
1293
+ return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
1294
+ ts.factory.createArrayTypeNode(
1295
+ ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(sanitizedItemTypeName), undefined),
1296
+ ),
1297
+ ]);
1298
+ }
1299
+ // If it's a primitive array, use the type name as-is
1300
+ return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
1301
+ ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(typeName), undefined),
1302
+ ]);
1303
+ }
1304
+
1305
+ const sanitizedTypeName = this.typeBuilder.sanitizeIdentifier(typeName);
1306
+
1307
+ // Check if it's a custom schema type (we have a type alias for it)
1308
+ if (schemas[sanitizedTypeName]) {
1309
+ // Use the type name directly (we have a type alias that already uses z.infer)
1310
+ return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
1311
+ ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(sanitizedTypeName), undefined),
1312
+ ]);
1313
+ }
1314
+
1315
+ // For primitive types and Record types, use the type name directly
1268
1316
  return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
1269
1317
  ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(typeName), undefined),
1270
1318
  ]);