zod-codegen 1.6.3 → 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 +11 -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 +3 -1
  20. package/dist/src/services/code-generator.service.d.ts.map +1 -1
  21. package/dist/src/services/code-generator.service.js +236 -219
  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 +799 -1120
  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 +431 -330
  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 -371
  118. package/scripts/update-manifest.ts +0 -49
  119. package/src/assets/manifest.json +0 -5
@@ -1,42 +1,94 @@
1
1
  // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2
- // Built with zod-codegen@1.6.1
3
- // Latest edit: Mon, 19 Jan 2026 14:27:35 GMT
2
+ // Built with zod-codegen@1.6.3
3
+ // Latest edit: Mon, 09 Mar 2026 20:11:48 GMT
4
4
  // Source file: ./samples/swagger-petstore.yaml
5
5
  /* eslint-disable */
6
6
  // @ts-nocheck
7
7
 
8
8
  // Imports
9
- import {z} from 'zod';
9
+ import { z } from 'zod';
10
+
11
+ // Explicit type declarations
12
+ export interface Order {
13
+ id?: number;
14
+ petId?: number;
15
+ quantity?: number;
16
+ shipDate?: string;
17
+ status?: 'placed' | 'approved' | 'delivered';
18
+ complete?: boolean;
19
+ }
20
+ export interface Address {
21
+ street?: string;
22
+ city?: string;
23
+ state?: string;
24
+ zip?: string;
25
+ }
26
+ export interface Customer {
27
+ id?: number;
28
+ username?: string;
29
+ address?: Address[];
30
+ }
31
+ export interface Category {
32
+ id?: number;
33
+ name?: string;
34
+ }
35
+ export interface User {
36
+ id?: number;
37
+ username?: string;
38
+ firstName?: string;
39
+ lastName?: string;
40
+ email?: string;
41
+ password?: string;
42
+ phone?: string;
43
+ userStatus?: number;
44
+ }
45
+ export interface Tag {
46
+ id?: number;
47
+ name?: string;
48
+ }
49
+ export interface Pet {
50
+ id?: number;
51
+ name: string;
52
+ category?: Category;
53
+ photoUrls: string[];
54
+ tags?: Tag[];
55
+ status?: 'available' | 'pending' | 'sold';
56
+ }
57
+ export interface ApiResponse {
58
+ code?: number;
59
+ type?: string;
60
+ message?: string;
61
+ }
10
62
 
11
63
  // Components schemas
12
- export const Order = z.object({
64
+ export const Order: z.ZodType<Order> = z.object({
13
65
  id: z.number().int().optional(),
14
66
  petId: z.number().int().optional(),
15
67
  quantity: z.number().int().optional(),
16
68
  shipDate: z.iso
17
69
  .datetime({
18
- local: true,
70
+ local: true
19
71
  })
20
72
  .optional(),
21
73
  status: z.enum(['placed', 'approved', 'delivered']).optional(),
22
- complete: z.boolean().optional(),
74
+ complete: z.boolean().optional()
23
75
  });
24
- export const Address = z.object({
76
+ export const Address: z.ZodType<Address> = z.object({
25
77
  street: z.string().optional(),
26
78
  city: z.string().optional(),
27
79
  state: z.string().optional(),
28
- zip: z.string().optional(),
80
+ zip: z.string().optional()
29
81
  });
30
- export const Customer = z.object({
82
+ export const Customer: z.ZodType<Customer> = z.object({
31
83
  id: z.number().int().optional(),
32
84
  username: z.string().optional(),
33
- address: z.array(Address).optional(),
85
+ address: z.array(Address).optional()
34
86
  });
35
- export const Category = z.object({
87
+ export const Category: z.ZodType<Category> = z.object({
36
88
  id: z.number().int().optional(),
37
- name: z.string().optional(),
89
+ name: z.string().optional()
38
90
  });
39
- export const User = z.object({
91
+ export const User: z.ZodType<User> = z.object({
40
92
  id: z.number().int().optional(),
41
93
  username: z.string().optional(),
42
94
  firstName: z.string().optional(),
@@ -44,37 +96,29 @@ export const User = z.object({
44
96
  email: z.string().optional(),
45
97
  password: z.string().optional(),
46
98
  phone: z.string().optional(),
47
- userStatus: z.number().int().optional(),
99
+ userStatus: z.number().int().optional()
48
100
  });
49
- export const Tag = z.object({
101
+ export const Tag: z.ZodType<Tag> = z.object({
50
102
  id: z.number().int().optional(),
51
- name: z.string().optional(),
103
+ name: z.string().optional()
52
104
  });
53
- export const Pet = z.object({
105
+ export const Pet: z.ZodType<Pet> = z.object({
54
106
  id: z.number().int().optional(),
55
107
  name: z.string(),
56
108
  category: Category.optional(),
57
109
  photoUrls: z.array(z.string()),
58
110
  tags: z.array(Tag).optional(),
59
- status: z.enum(['available', 'pending', 'sold']).optional(),
111
+ status: z.enum(['available', 'pending', 'sold']).optional()
60
112
  });
61
- export const ApiResponse = z.object({
113
+ export const ApiResponse: z.ZodType<ApiResponse> = z.object({
62
114
  code: z.number().int().optional(),
63
115
  type: z.string().optional(),
64
- message: z.string().optional(),
116
+ message: z.string().optional()
65
117
  });
66
- export type Order = z.infer<typeof Order>;
67
- export type Address = z.infer<typeof Address>;
68
- export type Customer = z.infer<typeof Customer>;
69
- export type Category = z.infer<typeof Category>;
70
- export type User = z.infer<typeof User>;
71
- export type Tag = z.infer<typeof Tag>;
72
- export type Pet = z.infer<typeof Pet>;
73
- export type ApiResponse = z.infer<typeof ApiResponse>;
74
118
  export const serverConfigurations = [
75
119
  {
76
- url: 'https://petstore3.swagger.io/api/v3',
77
- },
120
+ url: 'https://petstore3.swagger.io/api/v3'
121
+ }
78
122
  ];
79
123
  export const defaultBaseUrl = 'https://petstore3.swagger.io/api/v3';
80
124
  export type ClientOptions = {
@@ -85,8 +129,8 @@ export type ClientOptions = {
85
129
  function resolveServerUrl(serverIndex?: number | undefined, serverVariables?: Record<string, string> = {}): string {
86
130
  const configs = [
87
131
  {
88
- url: 'https://petstore3.swagger.io/api/v3',
89
- },
132
+ url: 'https://petstore3.swagger.io/api/v3'
133
+ }
90
134
  ];
91
135
  const idx = serverIndex ?? 0;
92
136
  if (idx < configs.length) {
@@ -101,13 +145,25 @@ function resolveServerUrl(serverIndex?: number | undefined, serverVariables?: Re
101
145
  }
102
146
  return 'https://petstore3.swagger.io/api/v3';
103
147
  }
148
+ export class ResponseValidationError<T> extends Error {
149
+ readonly response: Response;
150
+ readonly error: z.ZodError<T>;
151
+ constructor(message: string, response: Response, error: z.ZodError<T>) {
152
+ super(message);
153
+ this.name = 'ResponseValidationError' as const;
154
+ this.response = response;
155
+ this.error = error;
156
+ }
157
+ get data(): T {
158
+ return this.response.json() as T;
159
+ }
160
+ }
104
161
 
105
162
  // Client class
106
163
  export default class SwaggerPetstoreOpenAPI30 {
107
164
  readonly #baseUrl: string;
108
165
  constructor(options: ClientOptions) {
109
- const resolvedUrl =
110
- options.baseUrl !== null ? options.baseUrl : resolveServerUrl(options.serverIndex, options.serverVariables);
166
+ const resolvedUrl = options.baseUrl !== null ? options.baseUrl : resolveServerUrl(options.serverIndex, options.serverVariables);
111
167
  this.#baseUrl = resolvedUrl;
112
168
  }
113
169
  protected getBaseRequestOptions(): Partial<Omit<RequestInit, 'method' | 'body'>> {
@@ -117,14 +173,14 @@ export default class SwaggerPetstoreOpenAPI30 {
117
173
  response: Response,
118
174
  method: string,
119
175
  path: string,
120
- options: _params___Record_string__string___number___boolean___data___unknown__contentType___string__headers___Record_string__string__,
176
+ options: _params___Record_string__string___number___boolean___data___unknown__contentType___string__headers___Record_string__string__
121
177
  ): Promise<Response> {
122
178
  return response;
123
179
  }
124
180
  protected async makeRequest<T>(
125
181
  method: string,
126
182
  path: string,
127
- options: _params___Record_string__string___number___boolean___data___unknown__contentType___string__headers___Record_string__string__ = {},
183
+ options: _params___Record_string__string___number___boolean___data___unknown__contentType___string__headers___Record_string__string__ = {}
128
184
  ): Promise<T> {
129
185
  const baseUrl = new URL(path, this.#baseUrl);
130
186
  const url =
@@ -139,17 +195,9 @@ export default class SwaggerPetstoreOpenAPI30 {
139
195
  })()
140
196
  : baseUrl.toString();
141
197
  const baseOptions = this.getBaseRequestOptions();
142
- const contentType =
143
- options.contentType === 'application/x-www-form-urlencoded'
144
- ? 'application/x-www-form-urlencoded'
145
- : 'application/json';
198
+ const contentType = options.contentType === 'application/x-www-form-urlencoded' ? 'application/x-www-form-urlencoded' : 'application/json';
146
199
  const baseHeaders = baseOptions.headers !== undefined ? baseOptions.headers : {};
147
- const headers = Object.assign(
148
- {},
149
- baseHeaders,
150
- {'Content-Type': contentType},
151
- options.headers !== undefined ? options.headers : {},
152
- );
200
+ const headers = Object.assign({}, baseHeaders, { 'Content-Type': contentType }, options.headers !== undefined ? options.headers : {});
153
201
  const body =
154
202
  options.data !== undefined
155
203
  ? options.contentType === 'application/x-www-form-urlencoded'
@@ -162,7 +210,7 @@ export default class SwaggerPetstoreOpenAPI30 {
162
210
  })()
163
211
  : JSON.stringify(options.data)
164
212
  : null;
165
- const rawResponse = await fetch(url, Object.assign({}, baseOptions, {method, headers: headers, body: body}));
213
+ const rawResponse = await fetch(url, Object.assign({}, baseOptions, { method, headers: headers, body: body }));
166
214
  const response = await this.handleResponse<T>(rawResponse, method, path, options);
167
215
  if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
168
216
  return await response.json();
@@ -173,9 +221,12 @@ export default class SwaggerPetstoreOpenAPI30 {
173
221
  * @returns {Pet}
174
222
  */
175
223
  async addPet(body: Pet): Promise<Pet> {
176
- return Pet.parse(
177
- await this.makeRequest('POST', '/pet', {data: body, contentType: 'application/x-www-form-urlencoded'}),
178
- );
224
+ const response = await this.makeRequest('POST', '/pet', { data: body, contentType: 'application/x-www-form-urlencoded' });
225
+ const parsedPet = Pet.safeParse(response);
226
+ if (!parsedPet.success) {
227
+ throw new ResponseValidationError<Pet>(`Invalid pet: ${parsedPet.error.errors.map((error) => error.message).join(', ')}`, response, parsedPet.error);
228
+ }
229
+ return parsedPet.data;
179
230
  }
180
231
  /**
181
232
  * Update an existing pet
@@ -185,9 +236,12 @@ export default class SwaggerPetstoreOpenAPI30 {
185
236
  * @returns {Pet}
186
237
  */
187
238
  async updatePet(body: Pet): Promise<Pet> {
188
- return Pet.parse(
189
- await this.makeRequest('PUT', '/pet', {data: body, contentType: 'application/x-www-form-urlencoded'}),
190
- );
239
+ const response = await this.makeRequest('PUT', '/pet', { data: body, contentType: 'application/x-www-form-urlencoded' });
240
+ const parsedPet = Pet.safeParse(response);
241
+ if (!parsedPet.success) {
242
+ throw new ResponseValidationError<Pet>(`Invalid pet: ${parsedPet.error.errors.map((error) => error.message).join(', ')}`, response, parsedPet.error);
243
+ }
244
+ return parsedPet.data;
191
245
  }
192
246
  /**
193
247
  * Finds Pets by status
@@ -198,7 +252,7 @@ export default class SwaggerPetstoreOpenAPI30 {
198
252
  * @returns {Pet[]}
199
253
  */
200
254
  async findPetsByStatus(status?: string): Promise<Pet[]> {
201
- return await this.makeRequest('GET', '/pet/findByStatus', {params: {status: status}});
255
+ return await this.makeRequest('GET', '/pet/findByStatus', { params: { status: status } });
202
256
  }
203
257
  /**
204
258
  * Finds Pets by tags
@@ -209,7 +263,7 @@ export default class SwaggerPetstoreOpenAPI30 {
209
263
  * @returns {Pet[]}
210
264
  */
211
265
  async findPetsByTags(tags?: string[]): Promise<Pet[]> {
212
- return await this.makeRequest('GET', '/pet/findByTags', {params: {tags: tags}});
266
+ return await this.makeRequest('GET', '/pet/findByTags', { params: { tags: tags } });
213
267
  }
214
268
  /**
215
269
  * Find pet by ID
@@ -220,7 +274,12 @@ export default class SwaggerPetstoreOpenAPI30 {
220
274
  * @returns {Pet}
221
275
  */
222
276
  async getPetById(petId: number): Promise<Pet> {
223
- return Pet.parse(await this.makeRequest('GET', `/pet/${petId}`, {}));
277
+ const response = await this.makeRequest('GET', `/pet/${petId}`, {});
278
+ const parsedPet = Pet.safeParse(response);
279
+ if (!parsedPet.success) {
280
+ throw new ResponseValidationError<Pet>(`Invalid pet: ${parsedPet.error.errors.map((error) => error.message).join(', ')}`, response, parsedPet.error);
281
+ }
282
+ return parsedPet.data;
224
283
  }
225
284
  /**
226
285
  * Updates a pet in the store with form data
@@ -231,7 +290,7 @@ export default class SwaggerPetstoreOpenAPI30 {
231
290
  * @returns {void}
232
291
  */
233
292
  async updatePetWithForm(petId: number, name?: string, status?: string): Promise<void> {
234
- return await this.makeRequest('POST', `/pet/${petId}`, {params: {name: name, status: status}});
293
+ return await this.makeRequest('POST', `/pet/${petId}`, { params: { name: name, status: status } });
235
294
  }
236
295
  /**
237
296
  * Deletes a pet
@@ -254,9 +313,16 @@ export default class SwaggerPetstoreOpenAPI30 {
254
313
  * @returns {ApiResponse}
255
314
  */
256
315
  async uploadFile(petId: number, additionalMetadata?: string): Promise<ApiResponse> {
257
- return ApiResponse.parse(
258
- await this.makeRequest('POST', `/pet/${petId}/uploadImage`, {params: {additionalMetadata: additionalMetadata}}),
259
- );
316
+ const response = await this.makeRequest('POST', `/pet/${petId}/uploadImage`, { params: { additionalMetadata: additionalMetadata } });
317
+ const parsedApiResponse = ApiResponse.safeParse(response);
318
+ if (!parsedApiResponse.success) {
319
+ throw new ResponseValidationError<ApiResponse>(
320
+ `Invalid apiResponse: ${parsedApiResponse.error.errors.map((error) => error.message).join(', ')}`,
321
+ response,
322
+ parsedApiResponse.error
323
+ );
324
+ }
325
+ return parsedApiResponse.data;
260
326
  }
261
327
  /**
262
328
  * Returns pet inventories by status
@@ -275,9 +341,12 @@ export default class SwaggerPetstoreOpenAPI30 {
275
341
  * @returns {Order}
276
342
  */
277
343
  async placeOrder(body?: Order): Promise<Order> {
278
- return Order.parse(
279
- await this.makeRequest('POST', '/store/order', {data: body, contentType: 'application/x-www-form-urlencoded'}),
280
- );
344
+ const response = await this.makeRequest('POST', '/store/order', { data: body, contentType: 'application/x-www-form-urlencoded' });
345
+ const parsedOrder = Order.safeParse(response);
346
+ if (!parsedOrder.success) {
347
+ throw new ResponseValidationError<Order>(`Invalid order: ${parsedOrder.error.errors.map((error) => error.message).join(', ')}`, response, parsedOrder.error);
348
+ }
349
+ return parsedOrder.data;
281
350
  }
282
351
  /**
283
352
  * Find purchase order by ID
@@ -288,7 +357,12 @@ export default class SwaggerPetstoreOpenAPI30 {
288
357
  * @returns {Order}
289
358
  */
290
359
  async getOrderById(orderId: number): Promise<Order> {
291
- return Order.parse(await this.makeRequest('GET', `/store/order/${orderId}`, {}));
360
+ const response = await this.makeRequest('GET', `/store/order/${orderId}`, {});
361
+ const parsedOrder = Order.safeParse(response);
362
+ if (!parsedOrder.success) {
363
+ throw new ResponseValidationError<Order>(`Invalid order: ${parsedOrder.error.errors.map((error) => error.message).join(', ')}`, response, parsedOrder.error);
364
+ }
365
+ return parsedOrder.data;
292
366
  }
293
367
  /**
294
368
  * Delete purchase order by ID
@@ -309,9 +383,12 @@ export default class SwaggerPetstoreOpenAPI30 {
309
383
  * @returns {User}
310
384
  */
311
385
  async createUser(body?: User): Promise<User> {
312
- return User.parse(
313
- await this.makeRequest('POST', '/user', {data: body, contentType: 'application/x-www-form-urlencoded'}),
314
- );
386
+ const response = await this.makeRequest('POST', '/user', { data: body, contentType: 'application/x-www-form-urlencoded' });
387
+ const parsedUser = User.safeParse(response);
388
+ if (!parsedUser.success) {
389
+ throw new ResponseValidationError<User>(`Invalid user: ${parsedUser.error.errors.map((error) => error.message).join(', ')}`, response, parsedUser.error);
390
+ }
391
+ return parsedUser.data;
315
392
  }
316
393
  /**
317
394
  * Creates list of users with given input array
@@ -319,7 +396,12 @@ export default class SwaggerPetstoreOpenAPI30 {
319
396
  * @returns {User}
320
397
  */
321
398
  async createUsersWithListInput(body?: User[]): Promise<User> {
322
- return User.parse(await this.makeRequest('POST', '/user/createWithList', {data: body}));
399
+ const response = await this.makeRequest('POST', '/user/createWithList', { data: body });
400
+ const parsedUser = User.safeParse(response);
401
+ if (!parsedUser.success) {
402
+ throw new ResponseValidationError<User>(`Invalid user: ${parsedUser.error.errors.map((error) => error.message).join(', ')}`, response, parsedUser.error);
403
+ }
404
+ return parsedUser.data;
323
405
  }
324
406
  /**
325
407
  * Logs user into the system
@@ -329,7 +411,7 @@ export default class SwaggerPetstoreOpenAPI30 {
329
411
  * @returns {string}
330
412
  */
331
413
  async loginUser(username?: string, password?: string): Promise<string> {
332
- return await this.makeRequest('GET', '/user/login', {params: {username: username, password: password}});
414
+ return await this.makeRequest('GET', '/user/login', { params: { username: username, password: password } });
333
415
  }
334
416
  /**
335
417
  * Logs out current logged in user session
@@ -345,7 +427,12 @@ export default class SwaggerPetstoreOpenAPI30 {
345
427
  * @returns {User}
346
428
  */
347
429
  async getUserByName(username: string): Promise<User> {
348
- return User.parse(await this.makeRequest('GET', `/user/${username}`, {}));
430
+ const response = await this.makeRequest('GET', `/user/${username}`, {});
431
+ const parsedUser = User.safeParse(response);
432
+ if (!parsedUser.success) {
433
+ throw new ResponseValidationError<User>(`Invalid user: ${parsedUser.error.errors.map((error) => error.message).join(', ')}`, response, parsedUser.error);
434
+ }
435
+ return parsedUser.data;
349
436
  }
350
437
  /**
351
438
  * Update user
@@ -357,10 +444,7 @@ export default class SwaggerPetstoreOpenAPI30 {
357
444
  * @returns {void}
358
445
  */
359
446
  async updateUser(username: string, body?: User): Promise<void> {
360
- return await this.makeRequest('PUT', `/user/${username}`, {
361
- data: body,
362
- contentType: 'application/x-www-form-urlencoded',
363
- });
447
+ return await this.makeRequest('PUT', `/user/${username}`, { data: body, contentType: 'application/x-www-form-urlencoded' });
364
448
  }
365
449
  /**
366
450
  * Delete user
@@ -4,7 +4,8 @@
4
4
  * Run with: npx ts-node examples/petstore/authenticated-usage.ts
5
5
  */
6
6
 
7
- import {SwaggerPetstoreOpenAPI30, ClientOptions} from './type';
7
+ import type { ClientOptions } from './api';
8
+ import SwaggerPetstoreOpenAPI30 from './api';
8
9
 
9
10
  class AuthenticatedPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
10
11
  private apiKey: string | null = null;
@@ -19,9 +20,9 @@ class AuthenticatedPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
19
20
  ...options,
20
21
  headers: {
21
22
  ...((options.headers as Record<string, string>) || {}),
22
- ...(this.apiKey ? {api_key: this.apiKey} : {}),
23
- 'User-Agent': 'PetstoreClient/1.0.0',
24
- },
23
+ ...(this.apiKey ? { api_key: this.apiKey } : {}),
24
+ 'User-Agent': 'PetstoreClient/1.0.0'
25
+ }
25
26
  };
26
27
  }
27
28
 
@@ -53,6 +54,7 @@ async function main() {
53
54
  } else {
54
55
  console.error('āŒ Unknown error:', error);
55
56
  }
57
+
56
58
  process.exit(1);
57
59
  }
58
60
  }
@@ -4,7 +4,7 @@
4
4
  * Run with: npx ts-node examples/petstore/basic-usage.ts
5
5
  */
6
6
 
7
- import {SwaggerPetstoreOpenAPI30} from './type';
7
+ import SwaggerPetstoreOpenAPI30 from './api';
8
8
 
9
9
  async function main() {
10
10
  // Use default server (first server from OpenAPI spec)
@@ -26,7 +26,7 @@ async function main() {
26
26
  try {
27
27
  const taggedPets = await client.findPetsByTags(['friendly']);
28
28
  console.log(`\nšŸ·ļø Found ${taggedPets.length} pets with tags`);
29
- } catch (error) {
29
+ } catch {
30
30
  console.log('\nāš ļø Tags endpoint may not be available');
31
31
  }
32
32
 
@@ -35,7 +35,7 @@ async function main() {
35
35
  const inventory = await client.getInventory();
36
36
  console.log('\nšŸ“¦ Store inventory:');
37
37
  console.log(JSON.stringify(inventory, null, 2));
38
- } catch (error) {
38
+ } catch {
39
39
  console.log('\nāš ļø Inventory endpoint may require authentication');
40
40
  }
41
41
  } catch (error) {
@@ -44,6 +44,7 @@ async function main() {
44
44
  } else {
45
45
  console.error('āŒ Unknown error:', error);
46
46
  }
47
+
47
48
  process.exit(1);
48
49
  }
49
50
  }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Example showing how to catch 4xx/5xx responses and throw custom errors
3
+ * by extending the generated client and overriding the handleResponse method
4
+ *
5
+ * Run with: npx ts-node examples/petstore/error-handling-usage.ts
6
+ */
7
+
8
+ import SwaggerPetstoreOpenAPI30 from './api';
9
+
10
+ /** Custom error for HTTP 4xx/5xx with status, statusText, and optional body */
11
+ export class HttpError extends Error {
12
+ readonly status: number;
13
+ readonly statusText: string;
14
+ readonly body?: unknown;
15
+
16
+ constructor(message: string, status: number, statusText: string, body?: unknown) {
17
+ super(message);
18
+ this.name = 'HttpError';
19
+ this.status = status;
20
+ this.statusText = statusText;
21
+ this.body = body;
22
+ Object.setPrototypeOf(this, HttpError.prototype);
23
+ }
24
+ }
25
+
26
+ class PetstoreClientWithCustomErrors extends SwaggerPetstoreOpenAPI30 {
27
+ protected async handleResponse<_T>(
28
+ response: Response,
29
+ _method: string,
30
+ _path: string,
31
+ _options: {
32
+ params?: Record<string, string | number | boolean>;
33
+ data?: unknown;
34
+ contentType?: string;
35
+ headers?: Record<string, string>;
36
+ }
37
+ ): Promise<Response> {
38
+ if (response.ok) {
39
+ return response;
40
+ }
41
+
42
+ // Optionally read body for error details (with try/catch for non-JSON responses)
43
+ let body: unknown;
44
+ try {
45
+ const contentType = response.headers.get('Content-Type') ?? '';
46
+ body = contentType.includes('application/json') ? await response.json() : await response.text();
47
+ } catch {
48
+ body = undefined;
49
+ }
50
+
51
+ throw new HttpError(`HTTP ${response.status}: ${response.statusText}`, response.status, response.statusText, body);
52
+ }
53
+ }
54
+
55
+ async function main() {
56
+ const client = new PetstoreClientWithCustomErrors({});
57
+
58
+ try {
59
+ console.log('šŸ” Fetching available pets (success case)...\n');
60
+ const availablePets = await client.findPetsByStatus('available');
61
+ console.log(`āœ… Found ${availablePets.length} available pets`);
62
+
63
+ console.log('\nšŸ” Fetching non-existent pet (404 case)...\n');
64
+ await client.getPetById(999999);
65
+ } catch (e) {
66
+ if (e instanceof HttpError) {
67
+ if (e.status === 404) {
68
+ console.error('āŒ Pet not found (404):', e.body ?? e.message);
69
+ } else if (e.status >= 500) {
70
+ console.error('āŒ Server error:', e.status, e.body ?? e.message);
71
+ } else {
72
+ console.error(`āŒ HTTP ${e.status}:`, e.body ?? e.message);
73
+ }
74
+ } else if (e instanceof Error) {
75
+ console.error('āŒ Error:', e.message);
76
+ } else {
77
+ console.error('āŒ Unknown error:', e);
78
+ }
79
+
80
+ process.exit(1);
81
+ }
82
+ }
83
+
84
+ void main();
@@ -5,7 +5,7 @@
5
5
  * Run with: npx ts-node examples/petstore/retry-handler-usage.ts
6
6
  */
7
7
 
8
- import {SwaggerPetstoreOpenAPI30} from './type';
8
+ import SwaggerPetstoreOpenAPI30 from './api';
9
9
 
10
10
  class PetstoreClientWithRetry extends SwaggerPetstoreOpenAPI30 {
11
11
  private maxRetries = 3;
@@ -19,7 +19,7 @@ class PetstoreClientWithRetry extends SwaggerPetstoreOpenAPI30 {
19
19
  * - Modify responses before they're processed
20
20
  * - Implement custom error handling logic
21
21
  */
22
- protected async handleResponse<T>(
22
+ protected async handleResponse<_T>(
23
23
  response: Response,
24
24
  method: string,
25
25
  path: string,
@@ -28,7 +28,7 @@ class PetstoreClientWithRetry extends SwaggerPetstoreOpenAPI30 {
28
28
  data?: unknown;
29
29
  contentType?: string;
30
30
  headers?: Record<string, string>;
31
- },
31
+ }
32
32
  ): Promise<Response> {
33
33
  // Skip retry logic if we're already retrying to avoid infinite loops
34
34
  if (this.retrying) {
@@ -41,9 +41,7 @@ class PetstoreClientWithRetry extends SwaggerPetstoreOpenAPI30 {
41
41
  const delay = retryAfter ? parseInt(retryAfter, 10) * 1000 : this.retryDelay;
42
42
 
43
43
  for (let attempt = 0; attempt < this.maxRetries; attempt++) {
44
- console.log(
45
- `āš ļø Rate limited (429). Retrying in ${delay * (attempt + 1)}ms... (Attempt ${attempt + 1}/${this.maxRetries})`,
46
- );
44
+ console.log(`āš ļø Rate limited (429). Retrying in ${delay * (attempt + 1)}ms... (Attempt ${attempt + 1}/${this.maxRetries})`);
47
45
 
48
46
  // Wait before retrying with exponential backoff
49
47
  await new Promise((resolve) => setTimeout(resolve, delay * (attempt + 1)));
@@ -62,10 +60,12 @@ class PetstoreClientWithRetry extends SwaggerPetstoreOpenAPI30 {
62
60
  console.log(`āœ… Retry successful after ${attempt + 1} attempt(s)`);
63
61
  return retryResponse;
64
62
  }
63
+
65
64
  // If still rate limited, continue to next retry
66
65
  if (retryResponse.status === 429 && attempt < this.maxRetries - 1) {
67
66
  continue;
68
67
  }
68
+
69
69
  // Return the response even if it's an error (will be handled by normal error flow)
70
70
  return retryResponse;
71
71
  } catch (error) {
@@ -101,7 +101,7 @@ class PetstoreClientWithRetry extends SwaggerPetstoreOpenAPI30 {
101
101
  data?: unknown;
102
102
  contentType?: string;
103
103
  headers?: Record<string, string>;
104
- },
104
+ }
105
105
  ): Promise<Response> {
106
106
  // Reconstruct the request - this duplicates logic from makeRequest
107
107
  // but allows us to retry without going through handleResponse again
@@ -118,17 +118,9 @@ class PetstoreClientWithRetry extends SwaggerPetstoreOpenAPI30 {
118
118
  : baseUrl;
119
119
 
120
120
  const baseOptions = this.getBaseRequestOptions();
121
- const contentType =
122
- options.contentType === 'application/x-www-form-urlencoded'
123
- ? 'application/x-www-form-urlencoded'
124
- : 'application/json';
121
+ const contentType = options.contentType === 'application/x-www-form-urlencoded' ? 'application/x-www-form-urlencoded' : 'application/json';
125
122
  const baseHeaders = baseOptions.headers !== undefined ? baseOptions.headers : {};
126
- const headers = Object.assign(
127
- {},
128
- baseHeaders,
129
- {'Content-Type': contentType},
130
- options.headers !== undefined ? options.headers : {},
131
- );
123
+ const headers = Object.assign({}, baseHeaders, { 'Content-Type': contentType }, options.headers !== undefined ? options.headers : {});
132
124
  const body =
133
125
  options.data !== undefined
134
126
  ? options.contentType === 'application/x-www-form-urlencoded'
@@ -142,7 +134,7 @@ class PetstoreClientWithRetry extends SwaggerPetstoreOpenAPI30 {
142
134
  : JSON.stringify(options.data)
143
135
  : null;
144
136
 
145
- return await fetch(url, Object.assign({}, baseOptions, {method, headers: headers, body: body}));
137
+ return await fetch(url, Object.assign({}, baseOptions, { method, headers: headers, body: body }));
146
138
  }
147
139
  }
148
140
 
@@ -166,6 +158,7 @@ async function main() {
166
158
  } else {
167
159
  console.error('āŒ Unknown error:', error);
168
160
  }
161
+
169
162
  process.exit(1);
170
163
  }
171
164
  }