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
package/EXAMPLES.md
CHANGED
|
@@ -13,7 +13,8 @@ All examples below demonstrate how to extend the generated client class to add t
|
|
|
13
13
|
### Example: Adding Bearer Token Authentication
|
|
14
14
|
|
|
15
15
|
```typescript
|
|
16
|
-
import
|
|
16
|
+
import SwaggerPetstoreOpenAPI30 from './generated/api';
|
|
17
|
+
import type { ClientOptions } from './generated/api';
|
|
17
18
|
|
|
18
19
|
class AuthenticatedPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
19
20
|
private accessToken: string | null = null;
|
|
@@ -29,8 +30,8 @@ class AuthenticatedPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
|
29
30
|
...options,
|
|
30
31
|
headers: {
|
|
31
32
|
...((options.headers as Record<string, string>) || {}),
|
|
32
|
-
...(this.accessToken ? {Authorization: `Bearer ${this.accessToken}`} : {})
|
|
33
|
-
}
|
|
33
|
+
...(this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {})
|
|
34
|
+
}
|
|
34
35
|
};
|
|
35
36
|
}
|
|
36
37
|
|
|
@@ -69,7 +70,7 @@ void main();
|
|
|
69
70
|
### Example: Session Management with Token Refresh
|
|
70
71
|
|
|
71
72
|
```typescript
|
|
72
|
-
import
|
|
73
|
+
import SwaggerPetstoreOpenAPI30 from './generated/api';
|
|
73
74
|
|
|
74
75
|
class SessionManagedPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
75
76
|
private accessToken: string | null = null;
|
|
@@ -88,8 +89,8 @@ class SessionManagedPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
|
88
89
|
...options,
|
|
89
90
|
headers: {
|
|
90
91
|
...((options.headers as Record<string, string>) || {}),
|
|
91
|
-
...(this.accessToken ? {Authorization: `Bearer ${this.accessToken}`} : {})
|
|
92
|
-
}
|
|
92
|
+
...(this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {})
|
|
93
|
+
}
|
|
93
94
|
};
|
|
94
95
|
}
|
|
95
96
|
|
|
@@ -132,7 +133,8 @@ class SessionManagedPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
|
132
133
|
If you need to pass custom headers for specific requests, you can extend the client and add helper methods:
|
|
133
134
|
|
|
134
135
|
```typescript
|
|
135
|
-
import
|
|
136
|
+
import SwaggerPetstoreOpenAPI30 from './generated/api';
|
|
137
|
+
import type { ClientOptions } from './generated/api';
|
|
136
138
|
|
|
137
139
|
class CustomHeadersPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
138
140
|
constructor(options: ClientOptions = {}) {
|
|
@@ -145,8 +147,8 @@ class CustomHeadersPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
|
145
147
|
headers: {
|
|
146
148
|
...((options.headers as Record<string, string>) || {}),
|
|
147
149
|
'X-Custom-Header': 'custom-value',
|
|
148
|
-
'X-Request-ID': this.generateRequestId()
|
|
149
|
-
}
|
|
150
|
+
'X-Request-ID': this.generateRequestId()
|
|
151
|
+
}
|
|
150
152
|
};
|
|
151
153
|
}
|
|
152
154
|
|
|
@@ -159,10 +161,11 @@ class CustomHeadersPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
|
159
161
|
### Example: API Key Authentication
|
|
160
162
|
|
|
161
163
|
```typescript
|
|
162
|
-
import
|
|
164
|
+
import SwaggerPetstoreOpenAPI30 from './generated/api';
|
|
165
|
+
import type { ClientOptions } from './generated/api';
|
|
163
166
|
|
|
164
167
|
class ApiKeyAuthenticatedPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
165
|
-
constructor(options: ClientOptions & {apiKey: string}) {
|
|
168
|
+
constructor(options: ClientOptions & { apiKey: string }) {
|
|
166
169
|
super(options);
|
|
167
170
|
this.apiKey = options.apiKey;
|
|
168
171
|
}
|
|
@@ -175,8 +178,8 @@ class ApiKeyAuthenticatedPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
|
175
178
|
...options,
|
|
176
179
|
headers: {
|
|
177
180
|
...((options.headers as Record<string, string>) || {}),
|
|
178
|
-
'X-API-Key': this.apiKey
|
|
179
|
-
}
|
|
181
|
+
'X-API-Key': this.apiKey
|
|
182
|
+
}
|
|
180
183
|
};
|
|
181
184
|
}
|
|
182
185
|
}
|
|
@@ -185,7 +188,8 @@ class ApiKeyAuthenticatedPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
|
185
188
|
### Example: Using AbortController for Request Cancellation
|
|
186
189
|
|
|
187
190
|
```typescript
|
|
188
|
-
import
|
|
191
|
+
import SwaggerPetstoreOpenAPI30 from './generated/api';
|
|
192
|
+
import type { ClientOptions } from './generated/api';
|
|
189
193
|
|
|
190
194
|
class CancellablePetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
191
195
|
constructor(options: ClientOptions = {}) {
|
|
@@ -198,7 +202,7 @@ class CancellablePetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
|
198
202
|
this.abortController = new AbortController();
|
|
199
203
|
return {
|
|
200
204
|
...options,
|
|
201
|
-
signal: this.abortController.signal
|
|
205
|
+
signal: this.abortController.signal
|
|
202
206
|
};
|
|
203
207
|
}
|
|
204
208
|
|
|
@@ -220,7 +224,8 @@ client.cancelRequests();
|
|
|
220
224
|
### Example: Custom Credentials and CORS Mode
|
|
221
225
|
|
|
222
226
|
```typescript
|
|
223
|
-
import
|
|
227
|
+
import SwaggerPetstoreOpenAPI30 from './generated/api';
|
|
228
|
+
import type { ClientOptions } from './generated/api';
|
|
224
229
|
|
|
225
230
|
class CustomCorsPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
226
231
|
constructor(options: ClientOptions = {}) {
|
|
@@ -232,7 +237,7 @@ class CustomCorsPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
|
232
237
|
return {
|
|
233
238
|
...options,
|
|
234
239
|
credentials: 'include', // Include cookies in CORS requests
|
|
235
|
-
mode: 'cors'
|
|
240
|
+
mode: 'cors' // Enable CORS
|
|
236
241
|
};
|
|
237
242
|
}
|
|
238
243
|
}
|
|
@@ -241,21 +246,19 @@ class CustomCorsPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
|
241
246
|
const client = new CustomCorsPetstoreAPI({});
|
|
242
247
|
```
|
|
243
248
|
|
|
244
|
-
<|tool▁call▁begin|>
|
|
245
|
-
grep
|
|
246
|
-
|
|
247
249
|
### Example: Complete Configuration (CORS, User-Agent, Authentication)
|
|
248
250
|
|
|
249
251
|
Here's a comprehensive example showing how to combine CORS settings, custom User-Agent, and authentication:
|
|
250
252
|
|
|
251
253
|
```typescript
|
|
252
|
-
import
|
|
254
|
+
import SwaggerPetstoreOpenAPI30 from './generated/api';
|
|
255
|
+
import type { ClientOptions } from './generated/api';
|
|
253
256
|
|
|
254
257
|
class FullyConfiguredPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
255
258
|
private accessToken: string | null = null;
|
|
256
259
|
private readonly userAgent: string;
|
|
257
260
|
|
|
258
|
-
constructor(options: ClientOptions & {userAgent?: string} = {}) {
|
|
261
|
+
constructor(options: ClientOptions & { userAgent?: string } = {}) {
|
|
259
262
|
super(options);
|
|
260
263
|
this.userAgent = options.userAgent || 'MyApp/1.0.0 (https://myapp.com)';
|
|
261
264
|
}
|
|
@@ -267,7 +270,7 @@ class FullyConfiguredPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
|
267
270
|
const headers: Record<string, string> = {
|
|
268
271
|
...((options.headers as Record<string, string>) || {}),
|
|
269
272
|
'User-Agent': this.userAgent,
|
|
270
|
-
...(this.accessToken ? {Authorization: `Bearer ${this.accessToken}`} : {})
|
|
273
|
+
...(this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {})
|
|
271
274
|
};
|
|
272
275
|
|
|
273
276
|
return {
|
|
@@ -279,7 +282,7 @@ class FullyConfiguredPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
|
279
282
|
// Cache control
|
|
280
283
|
cache: 'no-cache', // Don't cache requests
|
|
281
284
|
// Redirect handling
|
|
282
|
-
redirect: 'follow'
|
|
285
|
+
redirect: 'follow' // Follow redirects automatically
|
|
283
286
|
};
|
|
284
287
|
}
|
|
285
288
|
|
|
@@ -294,7 +297,7 @@ class FullyConfiguredPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
|
294
297
|
|
|
295
298
|
// Usage
|
|
296
299
|
const client = new FullyConfiguredPetstoreAPI({
|
|
297
|
-
userAgent: 'MyCustomApp/2.0.0 (Custom User Agent)'
|
|
300
|
+
userAgent: 'MyCustomApp/2.0.0 (Custom User Agent)'
|
|
298
301
|
});
|
|
299
302
|
|
|
300
303
|
// Set authentication token
|
|
@@ -315,7 +318,8 @@ const pets = await client.findPetsByStatus('available');
|
|
|
315
318
|
You can also create different configurations for different environments:
|
|
316
319
|
|
|
317
320
|
```typescript
|
|
318
|
-
import
|
|
321
|
+
import SwaggerPetstoreOpenAPI30 from './generated/api';
|
|
322
|
+
import type { ClientOptions } from './generated/api';
|
|
319
323
|
|
|
320
324
|
interface ClientConfig {
|
|
321
325
|
userAgent?: string;
|
|
@@ -328,14 +332,14 @@ class ConfigurablePetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
|
328
332
|
private accessToken: string | null = null;
|
|
329
333
|
private readonly config: Required<ClientConfig>;
|
|
330
334
|
|
|
331
|
-
constructor(options: ClientOptions & {config?: ClientConfig} = {}) {
|
|
335
|
+
constructor(options: ClientOptions & { config?: ClientConfig } = {}) {
|
|
332
336
|
super(options);
|
|
333
337
|
const config = options.config || {};
|
|
334
338
|
this.config = {
|
|
335
339
|
userAgent: config.userAgent || 'PetstoreAPIClient/1.0.0',
|
|
336
340
|
enableCors: config.enableCors ?? true,
|
|
337
341
|
includeCredentials: config.includeCredentials ?? true,
|
|
338
|
-
cachePolicy: config.cachePolicy || 'no-cache'
|
|
342
|
+
cachePolicy: config.cachePolicy || 'no-cache'
|
|
339
343
|
};
|
|
340
344
|
}
|
|
341
345
|
|
|
@@ -345,13 +349,13 @@ class ConfigurablePetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
|
345
349
|
const headers: Record<string, string> = {
|
|
346
350
|
...((options.headers as Record<string, string>) || {}),
|
|
347
351
|
'User-Agent': this.config.userAgent,
|
|
348
|
-
...(this.accessToken ? {Authorization: `Bearer ${this.accessToken}`} : {})
|
|
352
|
+
...(this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {})
|
|
349
353
|
};
|
|
350
354
|
|
|
351
355
|
const requestOptions: Partial<Omit<RequestInit, 'method' | 'body'>> = {
|
|
352
356
|
...options,
|
|
353
357
|
headers,
|
|
354
|
-
cache: this.config.cachePolicy
|
|
358
|
+
cache: this.config.cachePolicy
|
|
355
359
|
};
|
|
356
360
|
|
|
357
361
|
// Conditionally add CORS options
|
|
@@ -376,8 +380,8 @@ const productionClient = new ConfigurablePetstoreAPI({
|
|
|
376
380
|
userAgent: 'MyApp/1.0.0 Production',
|
|
377
381
|
enableCors: true,
|
|
378
382
|
includeCredentials: true,
|
|
379
|
-
cachePolicy: 'default'
|
|
380
|
-
}
|
|
383
|
+
cachePolicy: 'default'
|
|
384
|
+
}
|
|
381
385
|
});
|
|
382
386
|
|
|
383
387
|
// Usage for development
|
|
@@ -386,8 +390,8 @@ const devClient = new ConfigurablePetstoreAPI({
|
|
|
386
390
|
userAgent: 'MyApp/1.0.0 Development',
|
|
387
391
|
enableCors: true,
|
|
388
392
|
includeCredentials: false, // Don't send credentials in dev
|
|
389
|
-
cachePolicy: 'no-cache'
|
|
390
|
-
}
|
|
393
|
+
cachePolicy: 'no-cache'
|
|
394
|
+
}
|
|
391
395
|
});
|
|
392
396
|
```
|
|
393
397
|
|
|
@@ -435,8 +439,8 @@ The final fetch request uses `Object.assign()` to merge options:
|
|
|
435
439
|
const finalHeaders = Object.assign(
|
|
436
440
|
{}, // Start with empty object
|
|
437
441
|
baseOptions.headers || {}, // 1. Base headers from getBaseRequestOptions()
|
|
438
|
-
{'Content-Type': contentType}, // 2. Content-Type (may override base)
|
|
439
|
-
options.headers || {}
|
|
442
|
+
{ 'Content-Type': contentType }, // 2. Content-Type (may override base)
|
|
443
|
+
options.headers || {} // 3. Request-specific headers (highest priority)
|
|
440
444
|
);
|
|
441
445
|
|
|
442
446
|
// Then all options are merged:
|
|
@@ -447,8 +451,8 @@ const finalOptions = Object.assign(
|
|
|
447
451
|
// 2. Request-specific options (override base)
|
|
448
452
|
method, // Always from endpoint
|
|
449
453
|
headers: finalHeaders, // Merged headers
|
|
450
|
-
body
|
|
451
|
-
}
|
|
454
|
+
body // Always from request data
|
|
455
|
+
}
|
|
452
456
|
);
|
|
453
457
|
|
|
454
458
|
fetch(url, finalOptions);
|
|
@@ -475,7 +479,7 @@ getBaseRequestOptions() called → Returns base options
|
|
|
475
479
|
↓
|
|
476
480
|
fetch() called with merged options
|
|
477
481
|
↓
|
|
478
|
-
Response
|
|
482
|
+
Response validated with Zod safeParse and ResponseValidationError
|
|
479
483
|
```
|
|
480
484
|
|
|
481
485
|
## Best Practices
|
|
@@ -531,7 +535,7 @@ protected getBaseRequestOptions() {
|
|
|
531
535
|
class UserFriendlyAPI extends YourAPI {
|
|
532
536
|
// ✅ Provide convenient methods
|
|
533
537
|
async login(username: string, password: string) {
|
|
534
|
-
const response = await this.auth_login_post({username, password});
|
|
538
|
+
const response = await this.auth_login_post({ username, password });
|
|
535
539
|
this.setAccessToken(response.token);
|
|
536
540
|
}
|
|
537
541
|
|
|
@@ -547,7 +551,7 @@ class UserFriendlyAPI extends YourAPI {
|
|
|
547
551
|
// ✅ Type your headers explicitly
|
|
548
552
|
const headers: Record<string, string> = {
|
|
549
553
|
'User-Agent': this.userAgent,
|
|
550
|
-
...(this.token ? {Authorization: `Bearer ${this.token}`} : {})
|
|
554
|
+
...(this.token ? { Authorization: `Bearer ${this.token}` } : {})
|
|
551
555
|
};
|
|
552
556
|
```
|
|
553
557
|
|
|
@@ -558,15 +562,15 @@ const headers: Record<string, string> = {
|
|
|
558
562
|
const prodClient = new ConfigurableAPI({
|
|
559
563
|
config: {
|
|
560
564
|
userAgent: 'MyApp/1.0.0 Production',
|
|
561
|
-
enableCors: true
|
|
562
|
-
}
|
|
565
|
+
enableCors: true
|
|
566
|
+
}
|
|
563
567
|
});
|
|
564
568
|
|
|
565
569
|
const devClient = new ConfigurableAPI({
|
|
566
570
|
config: {
|
|
567
571
|
userAgent: 'MyApp/1.0.0 Development',
|
|
568
|
-
enableCors: false
|
|
569
|
-
}
|
|
572
|
+
enableCors: false
|
|
573
|
+
}
|
|
570
574
|
});
|
|
571
575
|
```
|
|
572
576
|
|
|
@@ -578,7 +582,7 @@ class RobustAPI extends YourAPI {
|
|
|
578
582
|
const options = super.getBaseRequestOptions();
|
|
579
583
|
return {
|
|
580
584
|
...options,
|
|
581
|
-
signal: this.createAbortSignal()
|
|
585
|
+
signal: this.createAbortSignal() // ✅ Handle cancellation
|
|
582
586
|
};
|
|
583
587
|
}
|
|
584
588
|
|
|
@@ -595,7 +599,8 @@ class RobustAPI extends YourAPI {
|
|
|
595
599
|
### Pattern 1: Simple Authentication
|
|
596
600
|
|
|
597
601
|
```typescript
|
|
598
|
-
import
|
|
602
|
+
import YourAPI from './generated/api';
|
|
603
|
+
import type { ClientOptions } from './generated/api';
|
|
599
604
|
|
|
600
605
|
class SimpleAuthAPI extends YourAPI {
|
|
601
606
|
private token: string | null = null;
|
|
@@ -610,8 +615,8 @@ class SimpleAuthAPI extends YourAPI {
|
|
|
610
615
|
...options,
|
|
611
616
|
headers: {
|
|
612
617
|
...((options.headers as Record<string, string>) || {}),
|
|
613
|
-
...(this.token ? {Authorization: `Bearer ${this.token}`} : {})
|
|
614
|
-
}
|
|
618
|
+
...(this.token ? { Authorization: `Bearer ${this.token}` } : {})
|
|
619
|
+
}
|
|
615
620
|
};
|
|
616
621
|
}
|
|
617
622
|
}
|
|
@@ -620,7 +625,8 @@ class SimpleAuthAPI extends YourAPI {
|
|
|
620
625
|
### Pattern 2: Multiple Headers
|
|
621
626
|
|
|
622
627
|
```typescript
|
|
623
|
-
import
|
|
628
|
+
import YourAPI from './generated/api';
|
|
629
|
+
import type { ClientOptions } from './generated/api';
|
|
624
630
|
|
|
625
631
|
class MultiHeaderAPI extends YourAPI {
|
|
626
632
|
constructor(options: ClientOptions = {}) {
|
|
@@ -635,8 +641,8 @@ class MultiHeaderAPI extends YourAPI {
|
|
|
635
641
|
...((options.headers as Record<string, string>) || {}),
|
|
636
642
|
'User-Agent': 'MyApp/1.0.0',
|
|
637
643
|
'X-API-Version': 'v2',
|
|
638
|
-
'X-Request-ID': this.generateId()
|
|
639
|
-
}
|
|
644
|
+
'X-Request-ID': this.generateId()
|
|
645
|
+
}
|
|
640
646
|
};
|
|
641
647
|
}
|
|
642
648
|
}
|
|
@@ -645,7 +651,8 @@ class MultiHeaderAPI extends YourAPI {
|
|
|
645
651
|
### Pattern 3: Conditional Options
|
|
646
652
|
|
|
647
653
|
```typescript
|
|
648
|
-
import
|
|
654
|
+
import YourAPI from './generated/api';
|
|
655
|
+
import type { ClientOptions } from './generated/api';
|
|
649
656
|
|
|
650
657
|
class ConditionalAPI extends YourAPI {
|
|
651
658
|
constructor(options: ClientOptions = {}) {
|
|
@@ -654,7 +661,7 @@ class ConditionalAPI extends YourAPI {
|
|
|
654
661
|
|
|
655
662
|
protected getBaseRequestOptions() {
|
|
656
663
|
const options = super.getBaseRequestOptions();
|
|
657
|
-
const config: Partial<Omit<RequestInit, 'method' | 'body'>> = {...options};
|
|
664
|
+
const config: Partial<Omit<RequestInit, 'method' | 'body'>> = { ...options };
|
|
658
665
|
|
|
659
666
|
if (this.needsCors) {
|
|
660
667
|
config.mode = 'cors';
|
|
@@ -691,7 +698,7 @@ headers: {
|
|
|
691
698
|
return {
|
|
692
699
|
...options,
|
|
693
700
|
mode: 'cors',
|
|
694
|
-
credentials: 'include'
|
|
701
|
+
credentials: 'include' // If needed
|
|
695
702
|
};
|
|
696
703
|
```
|
|
697
704
|
|
|
@@ -702,7 +709,8 @@ return {
|
|
|
702
709
|
**Solution**: Store token as instance property:
|
|
703
710
|
|
|
704
711
|
```typescript
|
|
705
|
-
import
|
|
712
|
+
import YourAPI from './generated/api';
|
|
713
|
+
import type { ClientOptions } from './generated/api';
|
|
706
714
|
|
|
707
715
|
class MyAPI extends YourAPI {
|
|
708
716
|
private token: string | null = null; // ✅ Instance property
|
|
@@ -758,10 +766,80 @@ protected async handleResponse<T>(
|
|
|
758
766
|
): Promise<Response>
|
|
759
767
|
```
|
|
760
768
|
|
|
769
|
+
### Handling 4xx/5xx in handleResponse
|
|
770
|
+
|
|
771
|
+
Because `handleResponse` runs **before** the `!response.ok` check in the generated client, you can override it to catch 4xx/5xx responses and throw custom errors with status, statusText, and optionally the response body. This lets you use `catch (e) { if (e instanceof HttpError && e.status === 404) ... }` for type-safe error handling.
|
|
772
|
+
|
|
773
|
+
**Example override with optional HttpError class:**
|
|
774
|
+
|
|
775
|
+
```typescript
|
|
776
|
+
import SwaggerPetstoreOpenAPI30 from './generated/api';
|
|
777
|
+
|
|
778
|
+
/** Custom error for HTTP 4xx/5xx with status, statusText, and optional body */
|
|
779
|
+
export class HttpError extends Error {
|
|
780
|
+
readonly status: number;
|
|
781
|
+
readonly statusText: string;
|
|
782
|
+
readonly body?: unknown;
|
|
783
|
+
|
|
784
|
+
constructor(message: string, status: number, statusText: string, body?: unknown) {
|
|
785
|
+
super(message);
|
|
786
|
+
this.name = 'HttpError';
|
|
787
|
+
this.status = status;
|
|
788
|
+
this.statusText = statusText;
|
|
789
|
+
this.body = body;
|
|
790
|
+
Object.setPrototypeOf(this, HttpError.prototype);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
class PetstoreClientWithCustomErrors extends SwaggerPetstoreOpenAPI30 {
|
|
795
|
+
protected async handleResponse<T>(
|
|
796
|
+
response: Response,
|
|
797
|
+
_method: string,
|
|
798
|
+
_path: string,
|
|
799
|
+
_options: { params?: Record<string, string | number | boolean>; data?: unknown; contentType?: string; headers?: Record<string, string> }
|
|
800
|
+
): Promise<Response> {
|
|
801
|
+
if (response.ok) {
|
|
802
|
+
return response;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// Optionally read body for error details (with try/catch for non-JSON responses)
|
|
806
|
+
let body: unknown;
|
|
807
|
+
try {
|
|
808
|
+
const contentType = response.headers.get('Content-Type') ?? '';
|
|
809
|
+
body = contentType.includes('application/json') ? await response.json() : await response.text();
|
|
810
|
+
} catch {
|
|
811
|
+
body = undefined;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
throw new HttpError(`HTTP ${response.status}: ${response.statusText}`, response.status, response.statusText, body);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
// Usage with type-safe error handling
|
|
819
|
+
async function main() {
|
|
820
|
+
const client = new PetstoreClientWithCustomErrors({});
|
|
821
|
+
try {
|
|
822
|
+
const pet = await client.getPetById(99999); // May 404
|
|
823
|
+
console.log(pet);
|
|
824
|
+
} catch (e) {
|
|
825
|
+
if (e instanceof HttpError) {
|
|
826
|
+
if (e.status === 404) {
|
|
827
|
+
console.error('Pet not found:', e.body);
|
|
828
|
+
} else if (e.status >= 500) {
|
|
829
|
+
console.error('Server error:', e.status, e.body);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
throw e;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
See [examples/petstore/error-handling-usage.ts](./examples/petstore/error-handling-usage.ts) for a complete runnable example.
|
|
838
|
+
|
|
761
839
|
### Example: Basic Retry Handler
|
|
762
840
|
|
|
763
841
|
```typescript
|
|
764
|
-
import
|
|
842
|
+
import SwaggerPetstoreOpenAPI30 from './generated/api';
|
|
765
843
|
|
|
766
844
|
class PetstoreClientWithRetry extends SwaggerPetstoreOpenAPI30 {
|
|
767
845
|
private retrying = false;
|
package/PERFORMANCE.md
CHANGED
|
@@ -12,14 +12,14 @@ For even faster builds, you can optionally use the TypeScript Native Preview (TS
|
|
|
12
12
|
### Installation
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
|
|
15
|
+
npm install -D @typescript/native-preview
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
### Usage
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
21
|
# Use native compiler for builds
|
|
22
|
-
|
|
22
|
+
npm run build:native
|
|
23
23
|
|
|
24
24
|
# Or use directly
|
|
25
25
|
npx tsgo --project tsconfig.json
|
|
@@ -52,8 +52,8 @@ To compare performance:
|
|
|
52
52
|
|
|
53
53
|
```bash
|
|
54
54
|
# Standard TypeScript
|
|
55
|
-
time
|
|
55
|
+
time npm run build
|
|
56
56
|
|
|
57
57
|
# Native TypeScript
|
|
58
|
-
time
|
|
58
|
+
time npm run build:native
|
|
59
59
|
```
|