@cerios/openapi-to-zod 0.6.0 → 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/README.md CHANGED
@@ -35,14 +35,12 @@ npm install @cerios/openapi-to-zod
35
35
 
36
36
  ## CLI Usage
37
37
 
38
- > **Breaking Change in v2.0:** All CLI options have been removed. Use configuration files instead.
39
-
40
38
  ### Quick Start
41
39
 
42
40
  #### 1. Initialize Configuration
43
41
 
44
42
  ```bash
45
- npx @cerios/openapi-to-zod --init
43
+ npx @cerios/openapi-to-zod init
46
44
  ```
47
45
 
48
46
  This interactive command will:
@@ -150,13 +148,15 @@ openapi-to-zod [options]
150
148
 
151
149
  Options:
152
150
  -c, --config <path> Path to config file (optional if using auto-discovery)
153
- --init Initialize a new config file
154
151
  -V, --version Output version number
155
152
  -h, --help Display help
156
153
 
154
+ Commands:
155
+ init Initialize a new config file
156
+
157
157
  Examples:
158
158
  # Create config
159
- $ openapi-to-zod --init
159
+ $ openapi-to-zod init
160
160
 
161
161
  # Generate (auto-discover config)
162
162
  $ openapi-to-zod
@@ -165,35 +165,6 @@ Examples:
165
165
  $ openapi-to-zod --config custom.config.ts
166
166
  ```
167
167
 
168
- ### Migration from v1.x
169
-
170
- **Before (v1.x):**
171
- ```bash
172
- openapi-to-zod -i openapi.yaml -o schemas.ts --mode strict --prefix Api
173
- ```
174
-
175
- **After (v2.0):**
176
- ```typescript
177
- // openapi-to-zod.config.ts
178
- import { defineConfig } from '@cerios/openapi-to-zod';
179
-
180
- export default defineConfig({
181
- specs: [
182
- {
183
- input: 'openapi.yaml',
184
- output: 'schemas.ts',
185
- mode: 'strict',
186
- prefix: 'Api',
187
- },
188
- ],
189
- });
190
- ```
191
-
192
- Then run:
193
- ```bash
194
- openapi-to-zod
195
- ```
196
-
197
168
  ### Configuration Options
198
169
 
199
170
 
@@ -216,9 +187,42 @@ openapi-to-zod
216
187
  | `schemaType` | `"all"` \| `"request"` \| `"response"` | Schema filtering |
217
188
  | `prefix` | `string` | Prefix for schema names |
218
189
  | `suffix` | `string` | Suffix for schema names |
190
+ | `stripSchemaPrefix` | `string \| RegExp` | Strip prefix from schema names before generating (e.g., `"Company.Models."` or `/^[A-Z]+\./`) |
219
191
  | `showStats` | `boolean` | Include generation statistics |
220
- | `request` | `object` | Request-specific options (mode, includeDescriptions, etc.) |
221
- | `response` | `object` | Response-specific options (mode, includeDescriptions, etc.) |
192
+ | `request` | `object` | Request-specific options (mode, includeDescriptions, useDescribe) |
193
+ | `response` | `object` | Response-specific options (mode, includeDescriptions, useDescribe) |
194
+ | `operationFilters` | `object` | Filter operations by tags, paths, methods, etc. (see below) |
195
+
196
+ #### Operation Filters
197
+
198
+ Filter which operations to include/exclude during schema generation. Useful for generating separate schemas for different API subsets.
199
+
200
+ | Filter | Type | Description |
201
+ |--------|------|-------------|
202
+ | `includeTags` | `string[]` | Include only operations with these tags |
203
+ | `excludeTags` | `string[]` | Exclude operations with these tags |
204
+ | `includePaths` | `string[]` | Include only these paths (supports glob patterns like `/users/**`) |
205
+ | `excludePaths` | `string[]` | Exclude these paths (supports glob patterns) |
206
+ | `includeMethods` | `string[]` | Include only these HTTP methods (`get`, `post`, etc.) |
207
+ | `excludeMethods` | `string[]` | Exclude these HTTP methods |
208
+ | `includeOperationIds` | `string[]` | Include only these operationIds (supports glob patterns) |
209
+ | `excludeOperationIds` | `string[]` | Exclude these operationIds (supports glob patterns) |
210
+ | `excludeDeprecated` | `boolean` | Exclude deprecated operations |
211
+
212
+ **Example:**
213
+ ```typescript
214
+ export default defineConfig({
215
+ specs: [{
216
+ input: 'openapi.yaml',
217
+ output: 'schemas.ts',
218
+ operationFilters: {
219
+ includeTags: ['public'], // Only public endpoints
220
+ excludeDeprecated: true, // Skip deprecated operations
221
+ excludePaths: ['/internal/**'] // Exclude internal paths
222
+ }
223
+ }]
224
+ });
225
+ ```
222
226
 
223
227
  ### Batch Execution
224
228
 
@@ -408,6 +412,141 @@ The generator supports all OpenAPI string formats with Zod v4:
408
412
 
409
413
  ## Advanced Features
410
414
 
415
+ ### Operation Filtering
416
+
417
+ Filter which operations are included in schema generation. This is useful when you want to generate schemas for only a subset of your API.
418
+
419
+ **Example 1: Filter by tags**
420
+ ```typescript
421
+ export default defineConfig({
422
+ specs: [{
423
+ input: 'openapi.yaml',
424
+ output: 'public-schemas.ts',
425
+ operationFilters: {
426
+ includeTags: ['public', 'users'] // Only include operations tagged with 'public' or 'users'
427
+ }
428
+ }]
429
+ });
430
+ ```
431
+
432
+ **Example 2: Filter by paths**
433
+ ```typescript
434
+ export default defineConfig({
435
+ specs: [{
436
+ input: 'openapi.yaml',
437
+ output: 'v1-schemas.ts',
438
+ operationFilters: {
439
+ includePaths: ['/api/v1/**'], // Only v1 endpoints
440
+ excludePaths: ['/api/v1/admin/**'] // But exclude admin endpoints
441
+ }
442
+ }]
443
+ });
444
+ ```
445
+
446
+ **Example 3: Exclude deprecated operations**
447
+ ```typescript
448
+ export default defineConfig({
449
+ specs: [{
450
+ input: 'openapi.yaml',
451
+ output: 'current-schemas.ts',
452
+ operationFilters: {
453
+ excludeDeprecated: true // Skip all deprecated operations
454
+ }
455
+ }]
456
+ });
457
+ ```
458
+
459
+ **Filtering Logic:**
460
+ 1. If no filters specified, all operations are included
461
+ 2. Empty arrays are treated as "no constraint"
462
+ 3. Include filters are applied first (allowlist)
463
+ 4. Exclude filters are applied second (blocklist)
464
+ 5. Exclude rules always win over include rules
465
+
466
+ **Statistics:** When using operation filters, generation statistics will show how many operations were filtered out.
467
+
468
+ ### Request/Response Schema Separation
469
+
470
+ Generate separate schemas for requests and responses by filtering `readOnly` and `writeOnly` properties.
471
+
472
+ **Example: Request schemas (exclude readOnly)**
473
+ ```typescript
474
+ export default defineConfig({
475
+ specs: [{
476
+ input: 'openapi.yaml',
477
+ output: 'request-schemas.ts',
478
+ schemaType: 'request' // Excludes readOnly properties like 'id', 'createdAt'
479
+ }]
480
+ });
481
+ ```
482
+
483
+ **Example: Response schemas (exclude writeOnly)**
484
+ ```typescript
485
+ export default defineConfig({
486
+ specs: [{
487
+ input: 'openapi.yaml',
488
+ output: 'response-schemas.ts',
489
+ schemaType: 'response' // Excludes writeOnly properties like 'password'
490
+ }]
491
+ });
492
+ ```
493
+
494
+ **Example: Context-specific validation**
495
+ ```typescript
496
+ export default defineConfig({
497
+ specs: [{
498
+ input: 'openapi.yaml',
499
+ output: 'schemas.ts',
500
+ request: {
501
+ mode: 'strict', // Strict validation for incoming data
502
+ includeDescriptions: false
503
+ },
504
+ response: {
505
+ mode: 'loose', // Flexible validation for API responses
506
+ includeDescriptions: true
507
+ }
508
+ }]
509
+ });
510
+ ```
511
+
512
+ **OpenAPI Spec:**
513
+ ```yaml
514
+ User:
515
+ type: object
516
+ properties:
517
+ id:
518
+ type: string
519
+ readOnly: true # Excluded in 'request' mode
520
+ email:
521
+ type: string
522
+ password:
523
+ type: string
524
+ writeOnly: true # Excluded in 'response' mode
525
+ createdAt:
526
+ type: string
527
+ format: date-time
528
+ readOnly: true # Excluded in 'request' mode
529
+ ```
530
+
531
+ **Generated Request Schema** (`schemaType: 'request'`):
532
+ ```typescript
533
+ export const userSchema = z.object({
534
+ email: z.string(),
535
+ password: z.string(), // writeOnly included
536
+ // id and createdAt excluded (readOnly)
537
+ });
538
+ ```
539
+
540
+ **Generated Response Schema** (`schemaType: 'response'`):
541
+ ```typescript
542
+ export const userSchema = z.object({
543
+ id: z.string(), // readOnly included
544
+ email: z.string(),
545
+ createdAt: z.string().datetime(), // readOnly included
546
+ // password excluded (writeOnly)
547
+ });
548
+ ```
549
+
411
550
  ### String Constraints
412
551
 
413
552
  - `minLength` and `maxLength` are automatically applied
@@ -441,18 +580,18 @@ Enums are generated as Zod enums with:
441
580
 
442
581
  Customize schema names with prefixes and suffixes:
443
582
 
444
- ```bash
445
- # Add API prefix
446
- openapi-to-zod -i openapi.yaml -o schemas.ts -p api
447
- # Output: apiUserSchema, apiProductSchema, etc.
448
-
449
- # Add DTO suffix
450
- openapi-to-zod -i openapi.yaml -o schemas.ts --suffix dto
451
- # Output: userDtoSchema, productDtoSchema, etc.
452
-
453
- # Combine both
454
- openapi-to-zod -i openapi.yaml -o schemas.ts -p api --suffix dto
455
- # Output: apiUserDtoSchema, apiProductDtoSchema, etc.
583
+ ```typescript
584
+ // In your config file
585
+ export default defineConfig({
586
+ specs: [
587
+ {
588
+ input: 'openapi.yaml',
589
+ output: 'schemas.ts',
590
+ prefix: 'api', // Output: apiUserSchema, apiProductSchema
591
+ suffix: 'dto', // Output: userDtoSchema, productDtoSchema
592
+ },
593
+ ],
594
+ });
456
595
  ```
457
596
 
458
597
  This is useful when:
@@ -460,9 +599,186 @@ This is useful when:
460
599
  - Following specific naming conventions (DTO, Model, Entity)
461
600
  - Avoiding naming conflicts with existing code
462
601
 
602
+ ### Schema Prefix Stripping
603
+
604
+ The `stripSchemaPrefix` option removes common prefixes from schema names in your OpenAPI spec before generating Zod schemas. This is particularly useful when your OpenAPI spec uses namespaced schema names (like .NET-generated specs with "Company.Models.User").
605
+
606
+ **OpenAPI Spec with Namespaced Schemas:**
607
+ ```yaml
608
+ components:
609
+ schemas:
610
+ Company.Models.User:
611
+ type: object
612
+ properties:
613
+ id:
614
+ type: string
615
+ name:
616
+ type: string
617
+ role:
618
+ $ref: '#/components/schemas/Company.Models.UserRole'
619
+ Company.Models.UserRole:
620
+ type: string
621
+ enum: [admin, user, guest]
622
+ Company.Models.Post:
623
+ type: object
624
+ properties:
625
+ id:
626
+ type: string
627
+ title:
628
+ type: string
629
+ author:
630
+ $ref: '#/components/schemas/Company.Models.User'
631
+ ```
632
+
633
+ **Without `stripSchemaPrefix`:**
634
+ ```typescript
635
+ export const companyModelsUserRoleSchema = z.enum(["admin", "user", "guest"]);
636
+
637
+ export const companyModelsUserSchema = z.object({
638
+ id: z.string(),
639
+ name: z.string(),
640
+ role: companyModelsUserRoleSchema // Long reference name
641
+ });
642
+
643
+ export const companyModelsPostSchema = z.object({
644
+ id: z.string(),
645
+ title: z.string(),
646
+ author: companyModelsUserSchema // Long reference name
647
+ });
648
+
649
+ export type CompanyModelsUserRole = z.infer<typeof companyModelsUserRoleSchema>;
650
+ export type CompanyModelsUser = z.infer<typeof companyModelsUserSchema>;
651
+ export type CompanyModelsPost = z.infer<typeof companyModelsPostSchema>;
652
+ ```
653
+
654
+ **With `stripSchemaPrefix: "Company.Models."`:**
655
+ ```typescript
656
+ export const userRoleSchema = z.enum(["admin", "user", "guest"]);
657
+
658
+ export const userSchema = z.object({
659
+ id: z.string(),
660
+ name: z.string(),
661
+ role: userRoleSchema // Clean reference
662
+ });
663
+
664
+ export const postSchema = z.object({
665
+ id: z.string(),
666
+ title: z.string(),
667
+ author: userSchema // Clean reference
668
+ });
669
+
670
+ export type UserRole = z.infer<typeof userRoleSchema>;
671
+ export type User = z.infer<typeof userSchema>;
672
+ export type Post = z.infer<typeof postSchema>;
673
+ ```
674
+
675
+ #### Usage
676
+
677
+ ```typescript
678
+ export default defineConfig({
679
+ specs: [{
680
+ input: 'openapi.yaml',
681
+ output: 'schemas.ts',
682
+ stripSchemaPrefix: 'Company.Models.' // Strip this exact prefix
683
+ }]
684
+ });
685
+ ```
686
+
687
+ #### Regex Patterns
688
+
689
+ Use regex patterns to strip dynamic prefixes:
690
+
691
+ ```typescript
692
+ export default defineConfig({
693
+ specs: [{
694
+ input: 'openapi.yaml',
695
+ output: 'schemas.ts',
696
+ // Strip any namespace prefix ending with a dot
697
+ stripSchemaPrefix: '^[A-Z][a-z]+\\.'
698
+ }]
699
+ });
700
+ ```
701
+
702
+ **Regex Auto-Detection:**
703
+
704
+ Regex patterns are auto-detected if they contain: `^`, `$`, `\\d`, `\\w`, `\\s`, `.*`, `.+`, `[]`, `()`
705
+
706
+ ```typescript
707
+ // These are all treated as regex patterns:
708
+ stripSchemaPrefix: '^Company\\.' // Starts with ^
709
+ stripSchemaPrefix: '[A-Z]+\\.' // Contains []
710
+ stripSchemaPrefix: '.*\\.Models\\.' // Contains .*
711
+
712
+ // This is a literal string:
713
+ stripSchemaPrefix: 'Company.Models.' // No regex markers
714
+ ```
715
+
716
+ For TypeScript configs, you can also use `RegExp` objects:
717
+
718
+ ```typescript
719
+ stripSchemaPrefix: /^[A-Z][a-z]+\./
720
+ ```
721
+
722
+ #### Common Patterns
723
+
724
+ **Pattern 1: .NET Namespaces**
725
+ ```typescript
726
+ {
727
+ stripSchemaPrefix: 'Company.Models.'
728
+ }
729
+ // Company.Models.User → User
730
+ // Company.Models.Post → Post
731
+ ```
732
+
733
+ **Pattern 2: Multiple Namespaces**
734
+ ```typescript
735
+ {
736
+ stripSchemaPrefix: '^[A-Za-z]+\\.Models\\.'
737
+ }
738
+ // MyApp.Models.User → User
739
+ // OtherApp.Models.User → User
740
+ ```
741
+
742
+ **Pattern 3: Version Prefixes**
743
+ ```typescript
744
+ {
745
+ stripSchemaPrefix: '^v\\d+\\.'
746
+ }
747
+ // v1.User → User
748
+ // v2.Product → Product
749
+ ```
750
+
751
+ #### Interaction with prefix/suffix Options
752
+
753
+ `stripSchemaPrefix` is applied **before** `prefix` and `suffix` options:
754
+
755
+ ```typescript
756
+ export default defineConfig({
757
+ specs: [{
758
+ input: 'openapi.yaml',
759
+ output: 'schemas.ts',
760
+ stripSchemaPrefix: 'Company.Models.', // Applied first
761
+ prefix: 'api', // Applied second
762
+ suffix: 'dto' // Applied third
763
+ }]
764
+ });
765
+ ```
766
+
767
+ **Result:**
768
+ - `Company.Models.User` → `User` → `apiUserDtoSchema`
769
+ - `Company.Models.Post` → `Post` → `apiPostDtoSchema`
770
+
771
+ #### Benefits
772
+
773
+ 1. **Cleaner Schema Names**: Generates `userSchema` instead of `companyModelsUserSchema`
774
+ 2. **Better Type Names**: Creates `User` type instead of `CompanyModelsUser`
775
+ 3. **Shorter References**: Simpler schema references in composed types
776
+ 4. **Better Code Completion**: Easier to find schemas in IDE autocomplete
777
+ 5. **Flexible Pattern Matching**: Use regex for dynamic prefixes
778
+
463
779
  ## Generation Statistics
464
780
 
465
- Statistics are **included by default** in generated files. Use `--no-stats` to disable:
781
+ Statistics are **included by default** in generated files. Use `showStats: false` to disable:
466
782
 
467
783
  ```typescript
468
784
  // Generation Statistics:
@@ -478,8 +794,6 @@ Helpful for:
478
794
  - Tracking changes over time
479
795
  - Debugging generation issues
480
796
 
481
- To disable: `openapi-to-zod -i openapi.yaml -o schemas.ts --no-stats`
482
-
483
797
  ## OpenAPI Features Supported
484
798
 
485
799
  ### Basic Types
@@ -823,6 +1137,89 @@ All errors include:
823
1137
  - Clear description of the problem
824
1138
  - Context about what was expected
825
1139
 
1140
+ ## Public Utility Exports
1141
+
1142
+ Starting from **v0.7.0**, this package exports several utilities that can be used by other packages (like `@cerios/openapi-to-zod-playwright`):
1143
+
1144
+ ### `LRUCache<K, V>`
1145
+
1146
+ A Least Recently Used (LRU) cache implementation for efficient caching.
1147
+
1148
+ ```typescript
1149
+ import { LRUCache } from '@cerios/openapi-to-zod';
1150
+
1151
+ const cache = new LRUCache<string, ParsedSpec>(50);
1152
+ cache.set('spec-key', parsedSpec);
1153
+ const spec = cache.get('spec-key');
1154
+ ```
1155
+
1156
+ ### `toPascalCase(str: string | number): string`
1157
+
1158
+ Converts strings to PascalCase, handling kebab-case, snake_case, and special characters.
1159
+
1160
+ ```typescript
1161
+ import { toPascalCase } from '@cerios/openapi-to-zod';
1162
+
1163
+ toPascalCase('my-api-client'); // => 'MyApiClient'
1164
+ toPascalCase('user_name'); // => 'UserName'
1165
+ ```
1166
+
1167
+ ### `escapeJSDoc(str: string): string`
1168
+
1169
+ Escapes JSDoc comment terminators to prevent injection.
1170
+
1171
+ ```typescript
1172
+ import { escapeJSDoc } from '@cerios/openapi-to-zod';
1173
+
1174
+ escapeJSDoc('Comment with */ terminator'); // => 'Comment with *\\/ terminator'
1175
+ ```
1176
+
1177
+ ### `executeBatch<T>()` and `Generator` Interface
1178
+
1179
+ Execute batch processing with custom generators.
1180
+
1181
+ ```typescript
1182
+ import { executeBatch, type Generator } from '@cerios/openapi-to-zod';
1183
+
1184
+ class MyGenerator implements Generator {
1185
+ generate(): void {
1186
+ // Your generation logic
1187
+ }
1188
+ }
1189
+
1190
+ await executeBatch(
1191
+ specs,
1192
+ 'sequential', // or 'parallel'
1193
+ spec => new MyGenerator(spec)
1194
+ );
1195
+ ```
1196
+
1197
+ ### Config Validation Utilities
1198
+
1199
+ Shared utilities for configuration file validation:
1200
+
1201
+ ```typescript
1202
+ import {
1203
+ createTypeScriptLoader,
1204
+ formatConfigValidationError,
1205
+ type RequestResponseOptions,
1206
+ type BaseOperationFilters
1207
+ } from '@cerios/openapi-to-zod';
1208
+
1209
+ // Create TypeScript config loader for cosmiconfig
1210
+ const loader = createTypeScriptLoader();
1211
+
1212
+ // Format Zod validation errors
1213
+ const errorMessage = formatConfigValidationError(
1214
+ zodError,
1215
+ filePath,
1216
+ configPath,
1217
+ ['Additional note 1', 'Additional note 2']
1218
+ );
1219
+ ```
1220
+
1221
+ These utilities are marked with `@shared` tags in the source code and are covered by comprehensive tests.
1222
+
826
1223
  ## API Reference
827
1224
 
828
1225
  ### `generateZodSchemas(options: OpenApiGeneratorOptions): void`