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
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 {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type';
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 {SwaggerPetstoreOpenAPI30} from './generated/type';
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 {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type';
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 {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type';
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 {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type';
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 {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type';
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', // Enable 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 {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type';
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', // Follow redirects automatically
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 {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type';
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 || {}, // 3. Request-specific headers (highest priority)
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, // Always from request data
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 returned and validated with Zod
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(), // ✅ Handle cancellation
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 {YourAPI, ClientOptions} from './generated/type';
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 {YourAPI, ClientOptions} from './generated/type';
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 {YourAPI, ClientOptions} from './generated/type';
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', // If needed
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 {YourAPI, ClientOptions} from './generated/type';
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 {SwaggerPetstoreOpenAPI30} from './generated/type';
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
- yarn add -D @typescript/native-preview
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
- yarn build:native
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 yarn build
55
+ time npm run build
56
56
 
57
57
  # Native TypeScript
58
- time yarn build:native
58
+ time npm run build:native
59
59
  ```