zod-codegen 1.0.3 → 1.1.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/EXAMPLES.md ADDED
@@ -0,0 +1,704 @@
1
+ # Usage Examples
2
+
3
+ ## Extending the Generated Client for Authentication and Configuration
4
+
5
+ The generated client class includes a protected `getBaseRequestOptions()` method that you can override to customize request options. This method returns `Partial<Omit<RequestInit, 'method' | 'body'>>`, allowing you to set:
6
+
7
+ - **Headers**: Authentication tokens, User-Agent, custom headers
8
+ - **CORS**: `mode`, `credentials` for cross-origin requests
9
+ - **Request Options**: `signal` (AbortController), `cache`, `redirect`, `referrer`, etc.
10
+
11
+ All examples below demonstrate how to extend the generated client class to add these features.
12
+
13
+ ### Example: Adding Bearer Token Authentication
14
+
15
+ ```typescript
16
+ import {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type.js';
17
+
18
+ class AuthenticatedPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
19
+ private accessToken: string | null = null;
20
+
21
+ constructor(options: ClientOptions = {}) {
22
+ super(options);
23
+ }
24
+
25
+ // Override getBaseRequestOptions to add Authorization header
26
+ protected getBaseRequestOptions(): Partial<Omit<RequestInit, 'method' | 'body'>> {
27
+ const options = super.getBaseRequestOptions();
28
+ return {
29
+ ...options,
30
+ headers: {
31
+ ...((options.headers as Record<string, string>) || {}),
32
+ ...(this.accessToken ? {Authorization: `Bearer ${this.accessToken}`} : {}),
33
+ },
34
+ };
35
+ }
36
+
37
+ // Helper method to set the access token
38
+ setAccessToken(token: string): void {
39
+ this.accessToken = token;
40
+ }
41
+
42
+ // Helper method to clear the token
43
+ clearAccessToken(): void {
44
+ this.accessToken = null;
45
+ }
46
+ }
47
+
48
+ // Usage
49
+ async function main() {
50
+ const client = new AuthenticatedPetstoreAPI({});
51
+
52
+ // Set authentication token
53
+ client.setAccessToken('your-token-here');
54
+
55
+ // All subsequent requests will include the Authorization header
56
+ const pets = await client.findPetsByStatus('available');
57
+ console.log(pets);
58
+
59
+ // You can also manually set/update the token
60
+ client.setAccessToken('new-token-here');
61
+
62
+ // Or clear it
63
+ client.clearAccessToken();
64
+ }
65
+
66
+ void main();
67
+ ```
68
+
69
+ ### Example: Session Management with Token Refresh
70
+
71
+ ```typescript
72
+ import {SwaggerPetstoreOpenAPI30} from './generated/type.js';
73
+
74
+ class SessionManagedPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
75
+ private accessToken: string | null = null;
76
+ private refreshToken: string | null = null;
77
+ private tokenExpiry: Date | null = null;
78
+
79
+ protected getBaseRequestOptions(): Partial<Omit<RequestInit, 'method' | 'body'>> {
80
+ const options = super.getBaseRequestOptions();
81
+
82
+ // Check if token is expired and refresh if needed
83
+ if (this.tokenExpiry && this.tokenExpiry <= new Date()) {
84
+ this.refreshAccessToken().catch(console.error);
85
+ }
86
+
87
+ return {
88
+ ...options,
89
+ headers: {
90
+ ...((options.headers as Record<string, string>) || {}),
91
+ ...(this.accessToken ? {Authorization: `Bearer ${this.accessToken}`} : {}),
92
+ },
93
+ };
94
+ }
95
+
96
+ async login(username: string, password: string): Promise<void> {
97
+ // Example: If your API has a login endpoint
98
+ // const response = await this.loginUser({ username, password });
99
+ // this.setTokens(response.access_token, response.refresh_token, response.expires_in);
100
+
101
+ // For demonstration, setting tokens directly
102
+ this.setTokens('access-token-here', 'refresh-token-here', 3600);
103
+ }
104
+
105
+ private async refreshAccessToken(): Promise<void> {
106
+ if (!this.refreshToken) {
107
+ throw new Error('No refresh token available');
108
+ }
109
+
110
+ // Example: If your API has a refresh endpoint
111
+ // const response = await this.refreshToken({ refresh_token: this.refreshToken });
112
+ // this.setTokens(response.access_token, response.refresh_token, response.expires_in);
113
+
114
+ // For demonstration
115
+ this.setTokens('new-access-token', this.refreshToken, 3600);
116
+ }
117
+
118
+ private setTokens(accessToken: string, refreshToken?: string, expiresIn?: number): void {
119
+ this.accessToken = accessToken;
120
+ if (refreshToken) {
121
+ this.refreshToken = refreshToken;
122
+ }
123
+ if (expiresIn) {
124
+ this.tokenExpiry = new Date(Date.now() + expiresIn * 1000);
125
+ }
126
+ }
127
+ }
128
+ ```
129
+
130
+ ### Example: Custom Headers Per Request
131
+
132
+ If you need to pass custom headers for specific requests, you can extend the client and add helper methods:
133
+
134
+ ```typescript
135
+ import {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type.js';
136
+
137
+ class CustomHeadersPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
138
+ constructor(options: ClientOptions = {}) {
139
+ super(options);
140
+ }
141
+ protected getBaseRequestOptions(): Partial<Omit<RequestInit, 'method' | 'body'>> {
142
+ const options = super.getBaseRequestOptions();
143
+ return {
144
+ ...options,
145
+ headers: {
146
+ ...((options.headers as Record<string, string>) || {}),
147
+ 'X-Custom-Header': 'custom-value',
148
+ 'X-Request-ID': this.generateRequestId(),
149
+ },
150
+ };
151
+ }
152
+
153
+ private generateRequestId(): string {
154
+ return `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
155
+ }
156
+ }
157
+ ```
158
+
159
+ ### Example: API Key Authentication
160
+
161
+ ```typescript
162
+ import {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type.js';
163
+
164
+ class ApiKeyAuthenticatedPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
165
+ constructor(options: ClientOptions & {apiKey: string}) {
166
+ super(options);
167
+ this.apiKey = options.apiKey;
168
+ }
169
+
170
+ private apiKey: string;
171
+
172
+ protected getBaseRequestOptions(): Partial<Omit<RequestInit, 'method' | 'body'>> {
173
+ const options = super.getBaseRequestOptions();
174
+ return {
175
+ ...options,
176
+ headers: {
177
+ ...((options.headers as Record<string, string>) || {}),
178
+ 'X-API-Key': this.apiKey,
179
+ },
180
+ };
181
+ }
182
+ }
183
+ ```
184
+
185
+ ### Example: Using AbortController for Request Cancellation
186
+
187
+ ```typescript
188
+ import {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type.js';
189
+
190
+ class CancellablePetstoreAPI extends SwaggerPetstoreOpenAPI30 {
191
+ constructor(options: ClientOptions = {}) {
192
+ super(options);
193
+ }
194
+ private abortController: AbortController | null = null;
195
+
196
+ protected getBaseRequestOptions(): Partial<Omit<RequestInit, 'method' | 'body'>> {
197
+ const options = super.getBaseRequestOptions();
198
+ this.abortController = new AbortController();
199
+ return {
200
+ ...options,
201
+ signal: this.abortController.signal,
202
+ };
203
+ }
204
+
205
+ cancelRequests(): void {
206
+ if (this.abortController) {
207
+ this.abortController.abort();
208
+ this.abortController = null;
209
+ }
210
+ }
211
+ }
212
+
213
+ // Usage
214
+ const client = new CancellablePetstoreAPI();
215
+ const promise = client.findPetsByStatus('available');
216
+ // Later, cancel the request
217
+ client.cancelRequests();
218
+ ```
219
+
220
+ ### Example: Custom Credentials and CORS Mode
221
+
222
+ ```typescript
223
+ import {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type.js';
224
+
225
+ class CustomCorsPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
226
+ constructor(options: ClientOptions = {}) {
227
+ super(options);
228
+ }
229
+
230
+ protected getBaseRequestOptions(): Partial<Omit<RequestInit, 'method' | 'body'>> {
231
+ const options = super.getBaseRequestOptions();
232
+ return {
233
+ ...options,
234
+ credentials: 'include', // Include cookies in CORS requests
235
+ mode: 'cors', // Enable CORS
236
+ };
237
+ }
238
+ }
239
+
240
+ // Usage
241
+ const client = new CustomCorsPetstoreAPI({});
242
+ ```
243
+
244
+ <|tool▁call▁begin|>
245
+ grep
246
+
247
+ ### Example: Complete Configuration (CORS, User-Agent, Authentication)
248
+
249
+ Here's a comprehensive example showing how to combine CORS settings, custom User-Agent, and authentication:
250
+
251
+ ```typescript
252
+ import {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type.js';
253
+
254
+ class FullyConfiguredPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
255
+ private accessToken: string | null = null;
256
+ private readonly userAgent: string;
257
+
258
+ constructor(options: ClientOptions & {userAgent?: string} = {}) {
259
+ super(options);
260
+ this.userAgent = options.userAgent || 'MyApp/1.0.0 (https://myapp.com)';
261
+ }
262
+
263
+ protected getBaseRequestOptions(): Partial<Omit<RequestInit, 'method' | 'body'>> {
264
+ const options = super.getBaseRequestOptions();
265
+
266
+ // Build headers object
267
+ const headers: Record<string, string> = {
268
+ ...((options.headers as Record<string, string>) || {}),
269
+ 'User-Agent': this.userAgent,
270
+ ...(this.accessToken ? {Authorization: `Bearer ${this.accessToken}`} : {}),
271
+ };
272
+
273
+ return {
274
+ ...options,
275
+ headers,
276
+ // CORS configuration
277
+ mode: 'cors', // Enable CORS
278
+ credentials: 'include', // Include cookies and credentials in CORS requests
279
+ // Cache control
280
+ cache: 'no-cache', // Don't cache requests
281
+ // Redirect handling
282
+ redirect: 'follow', // Follow redirects automatically
283
+ };
284
+ }
285
+
286
+ setAccessToken(token: string): void {
287
+ this.accessToken = token;
288
+ }
289
+
290
+ clearAccessToken(): void {
291
+ this.accessToken = null;
292
+ }
293
+ }
294
+
295
+ // Usage
296
+ const client = new FullyConfiguredPetstoreAPI({
297
+ userAgent: 'MyCustomApp/2.0.0 (Custom User Agent)',
298
+ });
299
+
300
+ // Set authentication token
301
+ client.setAccessToken('your-bearer-token-here');
302
+
303
+ // All requests will now include:
304
+ // - Custom User-Agent header
305
+ // - Authorization header (Bearer token)
306
+ // - CORS mode enabled
307
+ // - Credentials included
308
+ // - No caching
309
+ // - Automatic redirect following
310
+ const pets = await client.findPetsByStatus('available');
311
+ ```
312
+
313
+ ### Example: Environment-Specific Configuration
314
+
315
+ You can also create different configurations for different environments:
316
+
317
+ ```typescript
318
+ import {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type.js';
319
+
320
+ interface ClientConfig {
321
+ userAgent?: string;
322
+ enableCors?: boolean;
323
+ includeCredentials?: boolean;
324
+ cachePolicy?: RequestCache;
325
+ }
326
+
327
+ class ConfigurablePetstoreAPI extends SwaggerPetstoreOpenAPI30 {
328
+ private accessToken: string | null = null;
329
+ private readonly config: Required<ClientConfig>;
330
+
331
+ constructor(options: ClientOptions & {config?: ClientConfig} = {}) {
332
+ super(options);
333
+ const config = options.config || {};
334
+ this.config = {
335
+ userAgent: config.userAgent || 'PetstoreAPIClient/1.0.0',
336
+ enableCors: config.enableCors ?? true,
337
+ includeCredentials: config.includeCredentials ?? true,
338
+ cachePolicy: config.cachePolicy || 'no-cache',
339
+ };
340
+ }
341
+
342
+ protected getBaseRequestOptions(): Partial<Omit<RequestInit, 'method' | 'body'>> {
343
+ const options = super.getBaseRequestOptions();
344
+
345
+ const headers: Record<string, string> = {
346
+ ...((options.headers as Record<string, string>) || {}),
347
+ 'User-Agent': this.config.userAgent,
348
+ ...(this.accessToken ? {Authorization: `Bearer ${this.accessToken}`} : {}),
349
+ };
350
+
351
+ const requestOptions: Partial<Omit<RequestInit, 'method' | 'body'>> = {
352
+ ...options,
353
+ headers,
354
+ cache: this.config.cachePolicy,
355
+ };
356
+
357
+ // Conditionally add CORS options
358
+ if (this.config.enableCors) {
359
+ requestOptions.mode = 'cors';
360
+ if (this.config.includeCredentials) {
361
+ requestOptions.credentials = 'include';
362
+ }
363
+ }
364
+
365
+ return requestOptions;
366
+ }
367
+
368
+ setAccessToken(token: string): void {
369
+ this.accessToken = token;
370
+ }
371
+ }
372
+
373
+ // Usage for production
374
+ const productionClient = new ConfigurablePetstoreAPI({
375
+ config: {
376
+ userAgent: 'MyApp/1.0.0 Production',
377
+ enableCors: true,
378
+ includeCredentials: true,
379
+ cachePolicy: 'default',
380
+ },
381
+ });
382
+
383
+ // Usage for development
384
+ const devClient = new ConfigurablePetstoreAPI({
385
+ config: {
386
+ userAgent: 'MyApp/1.0.0 Development',
387
+ enableCors: true,
388
+ includeCredentials: false, // Don't send credentials in dev
389
+ cachePolicy: 'no-cache',
390
+ },
391
+ });
392
+ ```
393
+
394
+ ## How It Works
395
+
396
+ ### Architecture Overview
397
+
398
+ The generated client uses a layered approach to request configuration:
399
+
400
+ 1. **Base Options Layer**: `getBaseRequestOptions()` - Override this to set default options for all requests
401
+ 2. **Request-Specific Layer**: Options passed to individual endpoint methods (via `options.headers`)
402
+ 3. **Generated Layer**: Method, body, and Content-Type headers are set automatically
403
+
404
+ ### Request Options Merging Order
405
+
406
+ When a request is made, options are merged in this order (later values override earlier ones):
407
+
408
+ 1. **Base Options** from `getBaseRequestOptions()` (headers, signal, credentials, etc.)
409
+ 2. **Content-Type Header** (automatically set based on request body: `application/json` or `application/x-www-form-urlencoded`)
410
+ 3. **Request-Specific Headers** from `options.headers` parameter (if provided)
411
+ 4. **Method and Body** (always set by generated code, cannot be overridden)
412
+
413
+ ### Type Safety
414
+
415
+ The `getBaseRequestOptions()` method returns `Partial<Omit<RequestInit, 'method' | 'body'>>`, which means:
416
+
417
+ - ✅ **You CAN set**: `headers`, `signal`, `credentials`, `mode`, `cache`, `redirect`, `referrer`, `referrerPolicy`, `integrity`, `keepalive`
418
+ - ❌ **You CANNOT set**: `method` (controlled by endpoint), `body` (controlled by request data)
419
+
420
+ This ensures type safety while preventing accidental overrides of critical request properties.
421
+
422
+ ### Header Merging Details
423
+
424
+ Headers are merged using `Object.assign()` with this priority:
425
+
426
+ ```typescript
427
+ const finalHeaders = Object.assign(
428
+ {}, // Start with empty object
429
+ baseOptions.headers || {}, // 1. Base headers from getBaseRequestOptions()
430
+ {'Content-Type': contentType}, // 2. Content-Type (may override base)
431
+ options.headers || {}, // 3. Request-specific headers (highest priority)
432
+ );
433
+ ```
434
+
435
+ **Important**: Always return `Record<string, string>` for headers in `getBaseRequestOptions()` for predictable merging behavior.
436
+
437
+ ### Request Flow
438
+
439
+ ```
440
+ User calls endpoint method
441
+
442
+ getBaseRequestOptions() called → Returns base options
443
+
444
+ #makeRequest() merges:
445
+ - Base options (headers, signal, credentials, etc.)
446
+ - Content-Type header
447
+ - Request-specific headers
448
+ - Method and body
449
+
450
+ fetch() called with merged options
451
+
452
+ Response returned and validated with Zod
453
+ ```
454
+
455
+ ## Best Practices
456
+
457
+ ### 1. Always Call Super Method
458
+
459
+ ```typescript
460
+ protected getBaseRequestOptions() {
461
+ const options = super.getBaseRequestOptions(); // ✅ Preserve base options
462
+ return { ...options, /* your additions */ };
463
+ }
464
+ ```
465
+
466
+ ### 2. Proper Header Merging
467
+
468
+ ```typescript
469
+ protected getBaseRequestOptions() {
470
+ const options = super.getBaseRequestOptions();
471
+ return {
472
+ ...options,
473
+ headers: {
474
+ ...(options.headers as Record<string, string> || {}), // ✅ Handle undefined
475
+ 'Authorization': `Bearer ${this.token}`,
476
+ },
477
+ };
478
+ }
479
+ ```
480
+
481
+ ### 3. Store Sensitive Data Privately
482
+
483
+ ```typescript
484
+ class SecureAPI extends YourAPI {
485
+ private accessToken: string | null = null; // ✅ Private property
486
+ // Never expose tokens in public methods
487
+ }
488
+ ```
489
+
490
+ ### 4. Handle Token Expiration
491
+
492
+ ```typescript
493
+ protected getBaseRequestOptions() {
494
+ // ✅ Check expiration before using token
495
+ if (this.tokenExpiry && this.tokenExpiry <= new Date()) {
496
+ this.refreshToken();
497
+ }
498
+ // ... rest of implementation
499
+ }
500
+ ```
501
+
502
+ ### 5. Provide Helper Methods
503
+
504
+ ```typescript
505
+ class UserFriendlyAPI extends YourAPI {
506
+ // ✅ Provide convenient methods
507
+ async login(username: string, password: string) {
508
+ const response = await this.auth_login_post({username, password});
509
+ this.setAccessToken(response.token);
510
+ }
511
+
512
+ logout() {
513
+ this.clearAccessToken();
514
+ }
515
+ }
516
+ ```
517
+
518
+ ### 6. Use TypeScript Strictly
519
+
520
+ ```typescript
521
+ // ✅ Type your headers explicitly
522
+ const headers: Record<string, string> = {
523
+ 'User-Agent': this.userAgent,
524
+ ...(this.token ? {Authorization: `Bearer ${this.token}`} : {}),
525
+ };
526
+ ```
527
+
528
+ ### 7. Environment-Specific Configuration
529
+
530
+ ```typescript
531
+ // ✅ Different configs for different environments
532
+ const prodClient = new ConfigurableAPI({
533
+ config: {
534
+ userAgent: 'MyApp/1.0.0 Production',
535
+ enableCors: true,
536
+ },
537
+ });
538
+
539
+ const devClient = new ConfigurableAPI({
540
+ config: {
541
+ userAgent: 'MyApp/1.0.0 Development',
542
+ enableCors: false,
543
+ },
544
+ });
545
+ ```
546
+
547
+ ### 8. Error Handling
548
+
549
+ ```typescript
550
+ class RobustAPI extends YourAPI {
551
+ protected getBaseRequestOptions() {
552
+ const options = super.getBaseRequestOptions();
553
+ return {
554
+ ...options,
555
+ signal: this.createAbortSignal(), // ✅ Handle cancellation
556
+ };
557
+ }
558
+
559
+ private createAbortSignal(): AbortSignal {
560
+ const controller = new AbortController();
561
+ // Set timeout, etc.
562
+ return controller.signal;
563
+ }
564
+ }
565
+ ```
566
+
567
+ ## Common Patterns
568
+
569
+ ### Pattern 1: Simple Authentication
570
+
571
+ ```typescript
572
+ import {YourAPI, ClientOptions} from './generated/type.js';
573
+
574
+ class SimpleAuthAPI extends YourAPI {
575
+ private token: string | null = null;
576
+
577
+ constructor(options: ClientOptions = {}) {
578
+ super(options);
579
+ }
580
+
581
+ protected getBaseRequestOptions() {
582
+ const options = super.getBaseRequestOptions();
583
+ return {
584
+ ...options,
585
+ headers: {
586
+ ...((options.headers as Record<string, string>) || {}),
587
+ ...(this.token ? {Authorization: `Bearer ${this.token}`} : {}),
588
+ },
589
+ };
590
+ }
591
+ }
592
+ ```
593
+
594
+ ### Pattern 2: Multiple Headers
595
+
596
+ ```typescript
597
+ import {YourAPI, ClientOptions} from './generated/type.js';
598
+
599
+ class MultiHeaderAPI extends YourAPI {
600
+ constructor(options: ClientOptions = {}) {
601
+ super(options);
602
+ }
603
+
604
+ protected getBaseRequestOptions() {
605
+ const options = super.getBaseRequestOptions();
606
+ return {
607
+ ...options,
608
+ headers: {
609
+ ...((options.headers as Record<string, string>) || {}),
610
+ 'User-Agent': 'MyApp/1.0.0',
611
+ 'X-API-Version': 'v2',
612
+ 'X-Request-ID': this.generateId(),
613
+ },
614
+ };
615
+ }
616
+ }
617
+ ```
618
+
619
+ ### Pattern 3: Conditional Options
620
+
621
+ ```typescript
622
+ import {YourAPI, ClientOptions} from './generated/type.js';
623
+
624
+ class ConditionalAPI extends YourAPI {
625
+ constructor(options: ClientOptions = {}) {
626
+ super(options);
627
+ }
628
+
629
+ protected getBaseRequestOptions() {
630
+ const options = super.getBaseRequestOptions();
631
+ const config: Partial<Omit<RequestInit, 'method' | 'body'>> = {...options};
632
+
633
+ if (this.needsCors) {
634
+ config.mode = 'cors';
635
+ config.credentials = 'include';
636
+ }
637
+
638
+ return config;
639
+ }
640
+ }
641
+ ```
642
+
643
+ ## Troubleshooting
644
+
645
+ ### Headers Not Being Applied
646
+
647
+ **Problem**: Custom headers aren't appearing in requests.
648
+
649
+ **Solution**: Ensure you're spreading base options and handling undefined:
650
+
651
+ ```typescript
652
+ headers: {
653
+ ...(options.headers as Record<string, string> || {}), // ✅ Handle undefined
654
+ 'Your-Header': 'value',
655
+ }
656
+ ```
657
+
658
+ ### CORS Errors
659
+
660
+ **Problem**: CORS errors when making requests.
661
+
662
+ **Solution**: Set CORS options in `getBaseRequestOptions()`:
663
+
664
+ ```typescript
665
+ return {
666
+ ...options,
667
+ mode: 'cors',
668
+ credentials: 'include', // If needed
669
+ };
670
+ ```
671
+
672
+ ### Token Not Persisting
673
+
674
+ **Problem**: Token is lost between requests.
675
+
676
+ **Solution**: Store token as instance property:
677
+
678
+ ```typescript
679
+ import {YourAPI, ClientOptions} from './generated/type.js';
680
+
681
+ class MyAPI extends YourAPI {
682
+ private token: string | null = null; // ✅ Instance property
683
+
684
+ constructor(options: ClientOptions = {}) {
685
+ super(options);
686
+ }
687
+
688
+ setToken(token: string) {
689
+ this.token = token; // ✅ Persists across requests
690
+ }
691
+ }
692
+ ```
693
+
694
+ ### Type Errors
695
+
696
+ **Problem**: TypeScript errors when overriding.
697
+
698
+ **Solution**: Ensure return type matches exactly:
699
+
700
+ ```typescript
701
+ protected getBaseRequestOptions(): Partial<Omit<RequestInit, 'method' | 'body'>> {
702
+ // ✅ Correct return type
703
+ }
704
+ ```