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.
- package/.github/workflows/ci.yml +50 -48
- package/.github/workflows/release.yml +13 -3
- package/.husky/commit-msg +1 -1
- package/.husky/pre-commit +1 -1
- package/.lintstagedrc.json +5 -1
- package/.nvmrc +1 -1
- package/.prettierrc.json +12 -5
- package/CHANGELOG.md +15 -0
- package/CONTRIBUTING.md +12 -12
- package/EXAMPLES.md +135 -57
- package/PERFORMANCE.md +4 -4
- package/README.md +87 -64
- package/SECURITY.md +1 -1
- package/dist/src/cli.js +11 -18
- package/dist/src/generator.d.ts +2 -2
- package/dist/src/generator.d.ts.map +1 -1
- package/dist/src/generator.js +5 -3
- package/dist/src/interfaces/code-generator.d.ts.map +1 -1
- package/dist/src/services/code-generator.service.d.ts +24 -1
- package/dist/src/services/code-generator.service.d.ts.map +1 -1
- package/dist/src/services/code-generator.service.js +385 -216
- package/dist/src/services/file-reader.service.d.ts.map +1 -1
- package/dist/src/services/file-reader.service.js +1 -1
- package/dist/src/services/file-writer.service.d.ts.map +1 -1
- package/dist/src/services/file-writer.service.js +2 -2
- package/dist/src/services/import-builder.service.d.ts.map +1 -1
- package/dist/src/services/import-builder.service.js +3 -3
- package/dist/src/services/type-builder.service.d.ts.map +1 -1
- package/dist/src/types/generator-options.d.ts.map +1 -1
- package/dist/src/types/openapi.d.ts.map +1 -1
- package/dist/src/types/openapi.js +20 -20
- package/dist/src/utils/error-handler.d.ts.map +1 -1
- package/dist/src/utils/naming-convention.d.ts.map +1 -1
- package/dist/src/utils/naming-convention.js +6 -3
- package/dist/src/utils/signal-handler.d.ts.map +1 -1
- package/dist/tests/integration/cli-comprehensive.test.d.ts +2 -0
- package/dist/tests/integration/cli-comprehensive.test.d.ts.map +1 -0
- package/dist/tests/integration/cli-comprehensive.test.js +110 -0
- package/dist/tests/integration/cli.test.d.ts +2 -0
- package/dist/tests/integration/cli.test.d.ts.map +1 -0
- package/dist/tests/integration/cli.test.js +25 -0
- package/dist/tests/integration/error-scenarios.test.d.ts +2 -0
- package/dist/tests/integration/error-scenarios.test.d.ts.map +1 -0
- package/dist/tests/integration/error-scenarios.test.js +169 -0
- package/dist/tests/integration/snapshots.test.d.ts +2 -0
- package/dist/tests/integration/snapshots.test.d.ts.map +1 -0
- package/dist/tests/integration/snapshots.test.js +100 -0
- package/dist/tests/unit/code-generator-edge-cases.test.d.ts +2 -0
- package/dist/tests/unit/code-generator-edge-cases.test.d.ts.map +1 -0
- package/dist/tests/unit/code-generator-edge-cases.test.js +506 -0
- package/dist/tests/unit/code-generator.test.d.ts +2 -0
- package/dist/tests/unit/code-generator.test.d.ts.map +1 -0
- package/dist/tests/unit/code-generator.test.js +1364 -0
- package/dist/tests/unit/file-reader.test.d.ts +2 -0
- package/dist/tests/unit/file-reader.test.d.ts.map +1 -0
- package/dist/tests/unit/file-reader.test.js +125 -0
- package/dist/tests/unit/generator.test.d.ts +2 -0
- package/dist/tests/unit/generator.test.d.ts.map +1 -0
- package/dist/tests/unit/generator.test.js +119 -0
- package/dist/tests/unit/naming-convention.test.d.ts +2 -0
- package/dist/tests/unit/naming-convention.test.d.ts.map +1 -0
- package/dist/tests/unit/naming-convention.test.js +256 -0
- package/dist/tests/unit/reporter.test.d.ts +2 -0
- package/dist/tests/unit/reporter.test.d.ts.map +1 -0
- package/dist/tests/unit/reporter.test.js +44 -0
- package/dist/tests/unit/type-builder.test.d.ts +2 -0
- package/dist/tests/unit/type-builder.test.d.ts.map +1 -0
- package/dist/tests/unit/type-builder.test.js +108 -0
- package/dist/vitest.config.d.ts.map +1 -1
- package/dist/vitest.config.js +10 -20
- package/eslint.config.mjs +38 -28
- package/examples/.gitkeep +1 -1
- package/examples/README.md +4 -2
- package/examples/petstore/README.md +18 -17
- package/examples/petstore/{type.ts ā api.ts} +158 -74
- package/examples/petstore/authenticated-usage.ts +6 -4
- package/examples/petstore/basic-usage.ts +4 -3
- package/examples/petstore/error-handling-usage.ts +84 -0
- package/examples/petstore/retry-handler-usage.ts +11 -18
- package/examples/petstore/server-variables-usage.ts +10 -10
- package/examples/pokeapi/README.md +8 -8
- package/examples/pokeapi/api.ts +218 -0
- package/examples/pokeapi/basic-usage.ts +3 -2
- package/examples/pokeapi/custom-client.ts +5 -4
- package/package.json +17 -21
- package/src/cli.ts +20 -25
- package/src/generator.ts +13 -11
- package/src/interfaces/code-generator.ts +1 -1
- package/src/services/code-generator.service.ts +989 -1099
- package/src/services/file-reader.service.ts +6 -5
- package/src/services/file-writer.service.ts +7 -7
- package/src/services/import-builder.service.ts +9 -13
- package/src/services/type-builder.service.ts +8 -19
- package/src/types/generator-options.ts +1 -1
- package/src/types/openapi.ts +22 -22
- package/src/utils/error-handler.ts +2 -2
- package/src/utils/naming-convention.ts +13 -10
- package/src/utils/reporter.ts +2 -2
- package/src/utils/signal-handler.ts +7 -8
- package/tests/integration/cli-comprehensive.test.ts +38 -32
- package/tests/integration/cli.test.ts +5 -5
- package/tests/integration/error-scenarios.test.ts +20 -26
- package/tests/integration/snapshots.test.ts +19 -23
- package/tests/unit/code-generator-edge-cases.test.ts +133 -133
- package/tests/unit/code-generator.test.ts +674 -268
- package/tests/unit/file-reader.test.ts +14 -14
- package/tests/unit/generator.test.ts +30 -18
- package/tests/unit/naming-convention.test.ts +27 -27
- package/tests/unit/type-builder.test.ts +2 -2
- package/tsconfig.json +5 -3
- package/vitest.config.ts +11 -21
- package/dist/scripts/update-manifest.d.ts +0 -14
- package/dist/scripts/update-manifest.d.ts.map +0 -1
- package/dist/scripts/update-manifest.js +0 -33
- package/dist/src/assets/manifest.json +0 -5
- package/examples/pokeapi/type.ts +0 -109
- package/generated/type.ts +0 -326
- package/scripts/update-manifest.ts +0 -49
- 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.
|
|
3
|
-
// Latest edit: Mon,
|
|
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
|
-
|
|
177
|
-
|
|
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
|
-
|
|
189
|
-
|
|
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
|
-
|
|
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
|
-
|
|
258
|
-
|
|
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
|
-
|
|
279
|
-
|
|
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
|
-
|
|
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
|
-
|
|
313
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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<
|
|
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
|
}
|