@cerios/openapi-to-zod 1.0.0 → 1.1.1
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 +238 -4
- package/dist/cli.js +173 -35
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +180 -35
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +168 -26
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +168 -26
- package/dist/index.mjs.map +1 -1
- package/dist/internal.d.mts +53 -2
- package/dist/internal.d.ts +53 -2
- package/dist/internal.js +117 -2
- package/dist/internal.js.map +1 -1
- package/dist/internal.mjs +114 -2
- package/dist/internal.mjs.map +1 -1
- package/dist/{types-BjoP91vk.d.mts → types-CI48CjiU.d.mts} +43 -1
- package/dist/{types-BjoP91vk.d.ts → types-CI48CjiU.d.ts} +43 -1
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -187,6 +187,7 @@ Examples:
|
|
|
187
187
|
| `schemaType` | `"all"` \| `"request"` \| `"response"` | Schema filtering |
|
|
188
188
|
| `prefix` | `string` | Prefix for schema names |
|
|
189
189
|
| `suffix` | `string` | Suffix for schema names |
|
|
190
|
+
| `stripSchemaPrefix` | `string` | Strip prefix from schema names before generating using glob patterns (e.g., `"Company.Models."` or `"*.Models."`) |
|
|
190
191
|
| `showStats` | `boolean` | Include generation statistics |
|
|
191
192
|
| `request` | `object` | Request-specific options (mode, includeDescriptions, useDescribe) |
|
|
192
193
|
| `response` | `object` | Response-specific options (mode, includeDescriptions, useDescribe) |
|
|
@@ -570,10 +571,46 @@ OpenAPI's `nullable: true` is converted to `.nullable()`
|
|
|
570
571
|
|
|
571
572
|
### Enums
|
|
572
573
|
|
|
573
|
-
Enums are generated
|
|
574
|
-
|
|
575
|
-
-
|
|
576
|
-
-
|
|
574
|
+
Enums are generated based on their value types:
|
|
575
|
+
|
|
576
|
+
- **String enums**: `z.enum()` for type-safe string unions
|
|
577
|
+
- **Numeric enums**: `z.union([z.literal(n), ...])` for proper number types
|
|
578
|
+
- **Boolean enums**: `z.boolean()` for true/false values
|
|
579
|
+
- **Mixed enums**: `z.union([z.literal(...), ...])` for heterogeneous values
|
|
580
|
+
|
|
581
|
+
**Examples:**
|
|
582
|
+
|
|
583
|
+
```yaml
|
|
584
|
+
# String enum
|
|
585
|
+
Status:
|
|
586
|
+
type: string
|
|
587
|
+
enum: [active, inactive, pending]
|
|
588
|
+
|
|
589
|
+
# Integer enum
|
|
590
|
+
Priority:
|
|
591
|
+
type: integer
|
|
592
|
+
enum: [0, 1, 2, 3]
|
|
593
|
+
|
|
594
|
+
# Mixed enum
|
|
595
|
+
Value:
|
|
596
|
+
enum: [0, "none", 1, "some"]
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
**Generated schemas:**
|
|
600
|
+
|
|
601
|
+
```typescript
|
|
602
|
+
// String enum → z.enum()
|
|
603
|
+
export const statusSchema = z.enum(["active", "inactive", "pending"]);
|
|
604
|
+
export type Status = z.infer<typeof statusSchema>; // "active" | "inactive" | "pending"
|
|
605
|
+
|
|
606
|
+
// Integer enum → z.union with z.literal
|
|
607
|
+
export const prioritySchema = z.union([z.literal(0), z.literal(1), z.literal(2), z.literal(3)]);
|
|
608
|
+
export type Priority = z.infer<typeof prioritySchema>; // 0 | 1 | 2 | 3
|
|
609
|
+
|
|
610
|
+
// Mixed enum → z.union with z.literal
|
|
611
|
+
export const valueSchema = z.union([z.literal(0), z.literal("none"), z.literal(1), z.literal("some")]);
|
|
612
|
+
export type Value = z.infer<typeof valueSchema>; // 0 | "none" | 1 | "some"
|
|
613
|
+
```
|
|
577
614
|
|
|
578
615
|
## Schema Naming
|
|
579
616
|
|
|
@@ -598,6 +635,203 @@ This is useful when:
|
|
|
598
635
|
- Following specific naming conventions (DTO, Model, Entity)
|
|
599
636
|
- Avoiding naming conflicts with existing code
|
|
600
637
|
|
|
638
|
+
### Schema Prefix Stripping
|
|
639
|
+
|
|
640
|
+
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").
|
|
641
|
+
|
|
642
|
+
**OpenAPI Spec with Namespaced Schemas:**
|
|
643
|
+
```yaml
|
|
644
|
+
components:
|
|
645
|
+
schemas:
|
|
646
|
+
Company.Models.User:
|
|
647
|
+
type: object
|
|
648
|
+
properties:
|
|
649
|
+
id:
|
|
650
|
+
type: string
|
|
651
|
+
name:
|
|
652
|
+
type: string
|
|
653
|
+
role:
|
|
654
|
+
$ref: '#/components/schemas/Company.Models.UserRole'
|
|
655
|
+
Company.Models.UserRole:
|
|
656
|
+
type: string
|
|
657
|
+
enum: [admin, user, guest]
|
|
658
|
+
Company.Models.Post:
|
|
659
|
+
type: object
|
|
660
|
+
properties:
|
|
661
|
+
id:
|
|
662
|
+
type: string
|
|
663
|
+
title:
|
|
664
|
+
type: string
|
|
665
|
+
author:
|
|
666
|
+
$ref: '#/components/schemas/Company.Models.User'
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
**Without `stripSchemaPrefix`:**
|
|
670
|
+
```typescript
|
|
671
|
+
export const companyModelsUserRoleSchema = z.enum(["admin", "user", "guest"]);
|
|
672
|
+
|
|
673
|
+
export const companyModelsUserSchema = z.object({
|
|
674
|
+
id: z.string(),
|
|
675
|
+
name: z.string(),
|
|
676
|
+
role: companyModelsUserRoleSchema // Long reference name
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
export const companyModelsPostSchema = z.object({
|
|
680
|
+
id: z.string(),
|
|
681
|
+
title: z.string(),
|
|
682
|
+
author: companyModelsUserSchema // Long reference name
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
export type CompanyModelsUserRole = z.infer<typeof companyModelsUserRoleSchema>;
|
|
686
|
+
export type CompanyModelsUser = z.infer<typeof companyModelsUserSchema>;
|
|
687
|
+
export type CompanyModelsPost = z.infer<typeof companyModelsPostSchema>;
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
**With `stripSchemaPrefix: "Company.Models."`:**
|
|
691
|
+
```typescript
|
|
692
|
+
export const userRoleSchema = z.enum(["admin", "user", "guest"]);
|
|
693
|
+
|
|
694
|
+
export const userSchema = z.object({
|
|
695
|
+
id: z.string(),
|
|
696
|
+
name: z.string(),
|
|
697
|
+
role: userRoleSchema // Clean reference
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
export const postSchema = z.object({
|
|
701
|
+
id: z.string(),
|
|
702
|
+
title: z.string(),
|
|
703
|
+
author: userSchema // Clean reference
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
export type UserRole = z.infer<typeof userRoleSchema>;
|
|
707
|
+
export type User = z.infer<typeof userSchema>;
|
|
708
|
+
export type Post = z.infer<typeof postSchema>;
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
#### Usage
|
|
712
|
+
|
|
713
|
+
```typescript
|
|
714
|
+
export default defineConfig({
|
|
715
|
+
specs: [{
|
|
716
|
+
input: 'openapi.yaml',
|
|
717
|
+
output: 'schemas.ts',
|
|
718
|
+
stripSchemaPrefix: 'Company.Models.' // Strip this exact prefix
|
|
719
|
+
}]
|
|
720
|
+
});
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
#### Glob Patterns
|
|
724
|
+
|
|
725
|
+
Use glob patterns to strip dynamic prefixes:
|
|
726
|
+
|
|
727
|
+
```typescript
|
|
728
|
+
export default defineConfig({
|
|
729
|
+
specs: [{
|
|
730
|
+
input: 'openapi.yaml',
|
|
731
|
+
output: 'schemas.ts',
|
|
732
|
+
// Strip any namespace prefix with wildcard
|
|
733
|
+
stripSchemaPrefix: '*.Models.'
|
|
734
|
+
}]
|
|
735
|
+
});
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
**Glob Pattern Syntax:**
|
|
739
|
+
|
|
740
|
+
Glob patterns support powerful matching using [minimatch](https://github.com/isaacs/minimatch):
|
|
741
|
+
- `*` matches any characters within a single segment (stops at `.`)
|
|
742
|
+
- `**` matches any characters across multiple segments (crosses `.` boundaries)
|
|
743
|
+
- `?` matches a single character
|
|
744
|
+
- `[abc]` matches any character in the set
|
|
745
|
+
- `{a,b}` matches any of the alternatives
|
|
746
|
+
- `!(pattern)` matches anything except the pattern
|
|
747
|
+
|
|
748
|
+
```typescript
|
|
749
|
+
// Examples of glob patterns:
|
|
750
|
+
stripSchemaPrefix: '*.Models.' // Matches Company.Models., App.Models.
|
|
751
|
+
stripSchemaPrefix: '**.Models.' // Matches any depth: Company.Api.Models., App.V2.Models.
|
|
752
|
+
stripSchemaPrefix: 'Company.{Models,Services}.' // Matches Company.Models. or Company.Services.
|
|
753
|
+
stripSchemaPrefix: 'api_v[0-9]_' // Matches api_v1_, api_v2_, etc.
|
|
754
|
+
stripSchemaPrefix: 'v*.*.' // Matches v1.0., v2.1., etc.
|
|
755
|
+
stripSchemaPrefix: '!(Internal)*.' // Matches any prefix except those starting with Internal
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
#### Common Patterns
|
|
759
|
+
|
|
760
|
+
**Pattern 1: .NET Namespaces**
|
|
761
|
+
```typescript
|
|
762
|
+
{
|
|
763
|
+
stripSchemaPrefix: 'Company.Models.'
|
|
764
|
+
}
|
|
765
|
+
// Company.Models.User → User
|
|
766
|
+
// Company.Models.Post → Post
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
**Pattern 2: Multiple Namespaces with Wildcard**
|
|
770
|
+
```typescript
|
|
771
|
+
{
|
|
772
|
+
stripSchemaPrefix: '*.Models.'
|
|
773
|
+
}
|
|
774
|
+
// MyApp.Models.User → User
|
|
775
|
+
// OtherApp.Models.User → User
|
|
776
|
+
// Company.Models.Post → Post
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
**Pattern 3: Multiple Namespace Types**
|
|
780
|
+
```typescript
|
|
781
|
+
{
|
|
782
|
+
stripSchemaPrefix: '*.{Models,Services}.'
|
|
783
|
+
}
|
|
784
|
+
// App.Models.User → User
|
|
785
|
+
// App.Services.UserService → UserService
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
**Pattern 4: Version Prefixes with Character Class**
|
|
789
|
+
```typescript
|
|
790
|
+
{
|
|
791
|
+
stripSchemaPrefix: 'v[0-9].'
|
|
792
|
+
}
|
|
793
|
+
// v1.User → User
|
|
794
|
+
// v2.Product → Product
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
**Pattern 5: Versioned Prefixes with Wildcards**
|
|
798
|
+
```typescript
|
|
799
|
+
{
|
|
800
|
+
stripSchemaPrefix: 'api_v*_'
|
|
801
|
+
}
|
|
802
|
+
// api_v1_User → User
|
|
803
|
+
// api_v2_Product → Product
|
|
804
|
+
// api_v10_Comment → Comment
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
#### Interaction with prefix/suffix Options
|
|
808
|
+
|
|
809
|
+
`stripSchemaPrefix` is applied **before** `prefix` and `suffix` options:
|
|
810
|
+
|
|
811
|
+
```typescript
|
|
812
|
+
export default defineConfig({
|
|
813
|
+
specs: [{
|
|
814
|
+
input: 'openapi.yaml',
|
|
815
|
+
output: 'schemas.ts',
|
|
816
|
+
stripSchemaPrefix: 'Company.Models.', // Applied first
|
|
817
|
+
prefix: 'api', // Applied second
|
|
818
|
+
suffix: 'dto' // Applied third
|
|
819
|
+
}]
|
|
820
|
+
});
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
**Result:**
|
|
824
|
+
- `Company.Models.User` → `User` → `apiUserDtoSchema`
|
|
825
|
+
- `Company.Models.Post` → `Post` → `apiPostDtoSchema`
|
|
826
|
+
|
|
827
|
+
#### Benefits
|
|
828
|
+
|
|
829
|
+
1. **Cleaner Schema Names**: Generates `userSchema` instead of `companyModelsUserSchema`
|
|
830
|
+
2. **Better Type Names**: Creates `User` type instead of `CompanyModelsUser`
|
|
831
|
+
3. **Shorter References**: Simpler schema references in composed types
|
|
832
|
+
4. **Better Code Completion**: Easier to find schemas in IDE autocomplete
|
|
833
|
+
5. **Flexible Pattern Matching**: Use regex for dynamic prefixes
|
|
834
|
+
|
|
601
835
|
## Generation Statistics
|
|
602
836
|
|
|
603
837
|
Statistics are **included by default** in generated files. Use `showStats: false` to disable:
|
package/dist/cli.js
CHANGED
|
@@ -5129,7 +5129,7 @@ function getBatchExitCode(summary) {
|
|
|
5129
5129
|
init_cjs_shims();
|
|
5130
5130
|
var import_node_fs = require("fs");
|
|
5131
5131
|
var import_node_path = require("path");
|
|
5132
|
-
var
|
|
5132
|
+
var import_minimatch3 = require("minimatch");
|
|
5133
5133
|
var import_yaml = require("yaml");
|
|
5134
5134
|
|
|
5135
5135
|
// src/generators/enum-generator.ts
|
|
@@ -5137,8 +5137,20 @@ init_cjs_shims();
|
|
|
5137
5137
|
|
|
5138
5138
|
// src/utils/name-utils.ts
|
|
5139
5139
|
init_cjs_shims();
|
|
5140
|
+
function sanitizeIdentifier(str) {
|
|
5141
|
+
return str.replace(/[^a-zA-Z0-9._\-\s]+/g, "_");
|
|
5142
|
+
}
|
|
5140
5143
|
function toCamelCase(str, options) {
|
|
5141
|
-
|
|
5144
|
+
const sanitized = sanitizeIdentifier(str);
|
|
5145
|
+
const words = sanitized.split(/[.\-_\s]+/).filter((word) => word.length > 0);
|
|
5146
|
+
let name;
|
|
5147
|
+
if (words.length === 0) {
|
|
5148
|
+
name = str.charAt(0).toLowerCase() + str.slice(1);
|
|
5149
|
+
} else if (words.length === 1) {
|
|
5150
|
+
name = words[0].charAt(0).toLowerCase() + words[0].slice(1);
|
|
5151
|
+
} else {
|
|
5152
|
+
name = words[0].charAt(0).toLowerCase() + words[0].slice(1) + words.slice(1).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
5153
|
+
}
|
|
5142
5154
|
if (options == null ? void 0 : options.prefix) {
|
|
5143
5155
|
const prefix = options.prefix.toLowerCase();
|
|
5144
5156
|
name = prefix + name.charAt(0).toUpperCase() + name.slice(1);
|
|
@@ -5151,12 +5163,23 @@ function toCamelCase(str, options) {
|
|
|
5151
5163
|
}
|
|
5152
5164
|
function toPascalCase(str) {
|
|
5153
5165
|
const stringValue = String(str);
|
|
5154
|
-
|
|
5166
|
+
const isAlreadyValidCase = /^[a-zA-Z][a-zA-Z0-9]*$/.test(stringValue);
|
|
5167
|
+
if (isAlreadyValidCase) {
|
|
5168
|
+
return stringValue.charAt(0).toUpperCase() + stringValue.slice(1);
|
|
5169
|
+
}
|
|
5170
|
+
const sanitized = sanitizeIdentifier(stringValue);
|
|
5171
|
+
const words = sanitized.split(/[.\-_\s]+/).filter((word) => word.length > 0);
|
|
5172
|
+
let result;
|
|
5173
|
+
if (words.length === 0) {
|
|
5174
|
+
result = "Value";
|
|
5175
|
+
} else {
|
|
5176
|
+
result = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
5177
|
+
}
|
|
5155
5178
|
if (/^\d/.test(result)) {
|
|
5156
5179
|
result = `N${result}`;
|
|
5157
5180
|
}
|
|
5158
5181
|
if (!result || /^_+$/.test(result)) {
|
|
5159
|
-
|
|
5182
|
+
return "Value";
|
|
5160
5183
|
}
|
|
5161
5184
|
return result;
|
|
5162
5185
|
}
|
|
@@ -5168,9 +5191,28 @@ function resolveRef(ref) {
|
|
|
5168
5191
|
// src/generators/enum-generator.ts
|
|
5169
5192
|
function generateEnum(name, values, options) {
|
|
5170
5193
|
const schemaName = `${toCamelCase(name, options)}Schema`;
|
|
5171
|
-
const
|
|
5172
|
-
const
|
|
5173
|
-
|
|
5194
|
+
const typeName = toPascalCase(name);
|
|
5195
|
+
const allBooleans = values.every((v) => typeof v === "boolean");
|
|
5196
|
+
if (allBooleans) {
|
|
5197
|
+
const schemaCode2 = `export const ${schemaName} = z.boolean();`;
|
|
5198
|
+
const typeCode2 = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
|
|
5199
|
+
return { schemaCode: schemaCode2, typeCode: typeCode2 };
|
|
5200
|
+
}
|
|
5201
|
+
const allStrings = values.every((v) => typeof v === "string");
|
|
5202
|
+
if (allStrings) {
|
|
5203
|
+
const enumValues = values.map((v) => `"${v}"`).join(", ");
|
|
5204
|
+
const schemaCode2 = `export const ${schemaName} = z.enum([${enumValues}]);`;
|
|
5205
|
+
const typeCode2 = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
|
|
5206
|
+
return { schemaCode: schemaCode2, typeCode: typeCode2 };
|
|
5207
|
+
}
|
|
5208
|
+
const literalValues = values.map((v) => {
|
|
5209
|
+
if (typeof v === "string") {
|
|
5210
|
+
return `z.literal("${v}")`;
|
|
5211
|
+
}
|
|
5212
|
+
return `z.literal(${v})`;
|
|
5213
|
+
}).join(", ");
|
|
5214
|
+
const schemaCode = `export const ${schemaName} = z.union([${literalValues}]);`;
|
|
5215
|
+
const typeCode = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
|
|
5174
5216
|
return { schemaCode, typeCode };
|
|
5175
5217
|
}
|
|
5176
5218
|
|
|
@@ -5183,7 +5225,7 @@ function escapeDescription(str) {
|
|
|
5183
5225
|
return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
|
|
5184
5226
|
}
|
|
5185
5227
|
function escapePattern(str) {
|
|
5186
|
-
return str.replace(
|
|
5228
|
+
return str.replace(/\//g, "\\/");
|
|
5187
5229
|
}
|
|
5188
5230
|
function escapeJSDoc(str) {
|
|
5189
5231
|
return str.replace(/\*\//g, "*\\/");
|
|
@@ -5311,6 +5353,65 @@ var LRUCache = class {
|
|
|
5311
5353
|
}
|
|
5312
5354
|
};
|
|
5313
5355
|
|
|
5356
|
+
// src/utils/pattern-utils.ts
|
|
5357
|
+
init_cjs_shims();
|
|
5358
|
+
var import_minimatch = require("minimatch");
|
|
5359
|
+
function isValidGlobPattern(pattern) {
|
|
5360
|
+
try {
|
|
5361
|
+
new import_minimatch.minimatch.Minimatch(pattern);
|
|
5362
|
+
return true;
|
|
5363
|
+
} catch {
|
|
5364
|
+
return false;
|
|
5365
|
+
}
|
|
5366
|
+
}
|
|
5367
|
+
function isGlobPattern(pattern) {
|
|
5368
|
+
return /[*?[\]{}!]/.test(pattern);
|
|
5369
|
+
}
|
|
5370
|
+
function stripPrefix(input, pattern, ensureLeadingChar) {
|
|
5371
|
+
if (!pattern) {
|
|
5372
|
+
return input;
|
|
5373
|
+
}
|
|
5374
|
+
if (isGlobPattern(pattern) && !isValidGlobPattern(pattern)) {
|
|
5375
|
+
console.warn(`\u26A0\uFE0F Invalid glob pattern "${pattern}": Pattern is malformed`);
|
|
5376
|
+
return input;
|
|
5377
|
+
}
|
|
5378
|
+
if (isGlobPattern(pattern)) {
|
|
5379
|
+
let longestMatch = -1;
|
|
5380
|
+
for (let i = 1; i <= input.length; i++) {
|
|
5381
|
+
const testPrefix = input.substring(0, i);
|
|
5382
|
+
if ((0, import_minimatch.minimatch)(testPrefix, pattern)) {
|
|
5383
|
+
longestMatch = i;
|
|
5384
|
+
}
|
|
5385
|
+
}
|
|
5386
|
+
if (longestMatch > 0) {
|
|
5387
|
+
const stripped = input.substring(longestMatch);
|
|
5388
|
+
if (ensureLeadingChar) {
|
|
5389
|
+
if (stripped === "") {
|
|
5390
|
+
return ensureLeadingChar;
|
|
5391
|
+
}
|
|
5392
|
+
if (!stripped.startsWith(ensureLeadingChar)) {
|
|
5393
|
+
return `${ensureLeadingChar}${stripped}`;
|
|
5394
|
+
}
|
|
5395
|
+
}
|
|
5396
|
+
return stripped === "" && !ensureLeadingChar ? input : stripped;
|
|
5397
|
+
}
|
|
5398
|
+
return input;
|
|
5399
|
+
}
|
|
5400
|
+
if (input.startsWith(pattern)) {
|
|
5401
|
+
const stripped = input.substring(pattern.length);
|
|
5402
|
+
if (ensureLeadingChar) {
|
|
5403
|
+
if (stripped === "") {
|
|
5404
|
+
return ensureLeadingChar;
|
|
5405
|
+
}
|
|
5406
|
+
if (!stripped.startsWith(ensureLeadingChar)) {
|
|
5407
|
+
return `${ensureLeadingChar}${stripped}`;
|
|
5408
|
+
}
|
|
5409
|
+
}
|
|
5410
|
+
return stripped;
|
|
5411
|
+
}
|
|
5412
|
+
return input;
|
|
5413
|
+
}
|
|
5414
|
+
|
|
5314
5415
|
// src/validators/array-validator.ts
|
|
5315
5416
|
init_cjs_shims();
|
|
5316
5417
|
function generateArrayValidation(schema, context) {
|
|
@@ -6175,8 +6276,9 @@ var _PropertyGenerator = class _PropertyGenerator {
|
|
|
6175
6276
|
}
|
|
6176
6277
|
(_a = this.context.schemaDependencies.get(currentSchema)) == null ? void 0 : _a.add(refName);
|
|
6177
6278
|
}
|
|
6178
|
-
const
|
|
6179
|
-
|
|
6279
|
+
const strippedRefName = stripPrefix(resolvedRefName, this.context.stripSchemaPrefix);
|
|
6280
|
+
const schemaName = `${toCamelCase(strippedRefName, this.context.namingOptions)}Schema`;
|
|
6281
|
+
if (currentSchema && (refName === currentSchema || this.isCircularThroughAlias(currentSchema, refName))) {
|
|
6180
6282
|
const lazySchema = `z.lazy((): z.ZodTypeAny => ${schemaName})`;
|
|
6181
6283
|
return wrapNullable(lazySchema, nullable);
|
|
6182
6284
|
}
|
|
@@ -6188,9 +6290,25 @@ var _PropertyGenerator = class _PropertyGenerator {
|
|
|
6188
6290
|
return wrapNullable(zodLiteral, nullable);
|
|
6189
6291
|
}
|
|
6190
6292
|
if (schema.enum) {
|
|
6191
|
-
const
|
|
6192
|
-
|
|
6193
|
-
|
|
6293
|
+
const allBooleans = schema.enum.every((v) => typeof v === "boolean");
|
|
6294
|
+
if (allBooleans) {
|
|
6295
|
+
const zodBoolean = "z.boolean()";
|
|
6296
|
+
return wrapNullable(zodBoolean, nullable);
|
|
6297
|
+
}
|
|
6298
|
+
const allStrings = schema.enum.every((v) => typeof v === "string");
|
|
6299
|
+
if (allStrings) {
|
|
6300
|
+
const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
|
|
6301
|
+
const zodEnum = `z.enum([${enumValues}])`;
|
|
6302
|
+
return wrapNullable(zodEnum, nullable);
|
|
6303
|
+
}
|
|
6304
|
+
const literalValues = schema.enum.map((v) => {
|
|
6305
|
+
if (typeof v === "string") {
|
|
6306
|
+
return `z.literal("${v}")`;
|
|
6307
|
+
}
|
|
6308
|
+
return `z.literal(${v})`;
|
|
6309
|
+
}).join(", ");
|
|
6310
|
+
const zodUnion = `z.union([${literalValues}])`;
|
|
6311
|
+
return wrapNullable(zodUnion, nullable);
|
|
6194
6312
|
}
|
|
6195
6313
|
if (schema.allOf) {
|
|
6196
6314
|
let composition = generateAllOf(
|
|
@@ -6322,7 +6440,7 @@ var PropertyGenerator = _PropertyGenerator;
|
|
|
6322
6440
|
|
|
6323
6441
|
// src/utils/operation-filters.ts
|
|
6324
6442
|
init_cjs_shims();
|
|
6325
|
-
var
|
|
6443
|
+
var import_minimatch2 = require("minimatch");
|
|
6326
6444
|
function createFilterStatistics() {
|
|
6327
6445
|
return {
|
|
6328
6446
|
totalOperations: 0,
|
|
@@ -6341,7 +6459,7 @@ function matchesAnyPattern(value, patterns) {
|
|
|
6341
6459
|
if (!value) {
|
|
6342
6460
|
return false;
|
|
6343
6461
|
}
|
|
6344
|
-
return patterns.some((pattern) => (0,
|
|
6462
|
+
return patterns.some((pattern) => (0, import_minimatch2.minimatch)(value, pattern));
|
|
6345
6463
|
}
|
|
6346
6464
|
function containsAny(arr, values) {
|
|
6347
6465
|
if (!values || values.length === 0) {
|
|
@@ -6477,6 +6595,7 @@ var OpenApiGenerator = class {
|
|
|
6477
6595
|
schemaType: options.schemaType || "all",
|
|
6478
6596
|
prefix: options.prefix,
|
|
6479
6597
|
suffix: options.suffix,
|
|
6598
|
+
stripSchemaPrefix: options.stripSchemaPrefix,
|
|
6480
6599
|
showStats: (_c = options.showStats) != null ? _c : true,
|
|
6481
6600
|
request: options.request,
|
|
6482
6601
|
response: options.response,
|
|
@@ -6553,7 +6672,8 @@ var OpenApiGenerator = class {
|
|
|
6553
6672
|
namingOptions: {
|
|
6554
6673
|
prefix: this.options.prefix,
|
|
6555
6674
|
suffix: this.options.suffix
|
|
6556
|
-
}
|
|
6675
|
+
},
|
|
6676
|
+
stripSchemaPrefix: this.options.stripSchemaPrefix
|
|
6557
6677
|
});
|
|
6558
6678
|
}
|
|
6559
6679
|
/**
|
|
@@ -6566,6 +6686,9 @@ var OpenApiGenerator = class {
|
|
|
6566
6686
|
throw new SpecValidationError("No schemas found in OpenAPI spec", { filePath: this.options.input });
|
|
6567
6687
|
}
|
|
6568
6688
|
for (const [name, schema] of Object.entries(this.spec.components.schemas)) {
|
|
6689
|
+
if (this.options.operationFilters && this.schemaUsageMap.size > 0 && !this.schemaUsageMap.has(name)) {
|
|
6690
|
+
continue;
|
|
6691
|
+
}
|
|
6569
6692
|
this.generateComponentSchema(name, schema);
|
|
6570
6693
|
}
|
|
6571
6694
|
this.generateQueryParameterSchemas();
|
|
@@ -6587,9 +6710,11 @@ var OpenApiGenerator = class {
|
|
|
6587
6710
|
const typeCode = this.types.get(name);
|
|
6588
6711
|
if (schemaCode) {
|
|
6589
6712
|
output.push(schemaCode);
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
|
|
6713
|
+
const strippedName = stripPrefix(name, this.options.stripSchemaPrefix);
|
|
6714
|
+
const typeName = toPascalCase(strippedName);
|
|
6715
|
+
if (!schemaCode.includes(`export type ${typeName}`)) {
|
|
6716
|
+
const schemaName = `${toCamelCase(strippedName, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
|
|
6717
|
+
output.push(`export type ${typeName} = z.infer<typeof ${schemaName}>;`);
|
|
6593
6718
|
}
|
|
6594
6719
|
output.push("");
|
|
6595
6720
|
} else if (typeCode) {
|
|
@@ -6905,7 +7030,8 @@ var OpenApiGenerator = class {
|
|
|
6905
7030
|
const resolvedOptions = context === "response" ? this.responseOptions : this.requestOptions;
|
|
6906
7031
|
if (schema.enum) {
|
|
6907
7032
|
const jsdoc2 = generateJSDoc(schema, name, { includeDescriptions: resolvedOptions.includeDescriptions });
|
|
6908
|
-
const
|
|
7033
|
+
const strippedName2 = stripPrefix(name, this.options.stripSchemaPrefix);
|
|
7034
|
+
const { schemaCode, typeCode } = generateEnum(strippedName2, schema.enum, {
|
|
6909
7035
|
prefix: this.options.prefix,
|
|
6910
7036
|
suffix: this.options.suffix
|
|
6911
7037
|
});
|
|
@@ -6914,7 +7040,8 @@ ${typeCode}`;
|
|
|
6914
7040
|
this.schemas.set(name, enumSchemaCode);
|
|
6915
7041
|
return;
|
|
6916
7042
|
}
|
|
6917
|
-
const
|
|
7043
|
+
const strippedName = stripPrefix(name, this.options.stripSchemaPrefix);
|
|
7044
|
+
const schemaName = `${toCamelCase(strippedName, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
|
|
6918
7045
|
const jsdoc = generateJSDoc(schema, name, { includeDescriptions: resolvedOptions.includeDescriptions });
|
|
6919
7046
|
if (schema.allOf && schema.allOf.length === 1 && schema.allOf[0].$ref) {
|
|
6920
7047
|
const refName = resolveRef(schema.allOf[0].$ref);
|
|
@@ -6930,7 +7057,8 @@ ${typeCode}`;
|
|
|
6930
7057
|
namingOptions: {
|
|
6931
7058
|
prefix: this.options.prefix,
|
|
6932
7059
|
suffix: this.options.suffix
|
|
6933
|
-
}
|
|
7060
|
+
},
|
|
7061
|
+
stripSchemaPrefix: this.options.stripSchemaPrefix
|
|
6934
7062
|
});
|
|
6935
7063
|
const isAlias = !!(schema.$ref && !schema.properties && !schema.allOf && !schema.oneOf && !schema.anyOf);
|
|
6936
7064
|
const zodSchema = this.propertyGenerator.generatePropertySchema(schema, name, isAlias);
|
|
@@ -7049,7 +7177,7 @@ ${propsCode}
|
|
|
7049
7177
|
const headerLower = headerName.toLowerCase();
|
|
7050
7178
|
return ignorePatterns.some((pattern) => {
|
|
7051
7179
|
const patternLower = pattern.toLowerCase();
|
|
7052
|
-
return (0,
|
|
7180
|
+
return (0, import_minimatch3.minimatch)(headerLower, patternLower);
|
|
7053
7181
|
});
|
|
7054
7182
|
}
|
|
7055
7183
|
/**
|
|
@@ -7132,12 +7260,27 @@ ${propsCode}
|
|
|
7132
7260
|
generateQueryParamType(schema, param) {
|
|
7133
7261
|
if (schema.$ref) {
|
|
7134
7262
|
const refName = resolveRef(schema.$ref);
|
|
7135
|
-
const
|
|
7263
|
+
const strippedRefName = stripPrefix(refName, this.options.stripSchemaPrefix);
|
|
7264
|
+
const schemaName = toCamelCase(strippedRefName, { prefix: this.options.prefix, suffix: this.options.suffix });
|
|
7136
7265
|
return `${schemaName}Schema`;
|
|
7137
7266
|
}
|
|
7138
7267
|
if (schema.enum) {
|
|
7139
|
-
const
|
|
7140
|
-
|
|
7268
|
+
const allBooleans = schema.enum.every((v) => typeof v === "boolean");
|
|
7269
|
+
if (allBooleans) {
|
|
7270
|
+
return "z.boolean()";
|
|
7271
|
+
}
|
|
7272
|
+
const allStrings = schema.enum.every((v) => typeof v === "string");
|
|
7273
|
+
if (allStrings) {
|
|
7274
|
+
const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
|
|
7275
|
+
return `z.enum([${enumValues}])`;
|
|
7276
|
+
}
|
|
7277
|
+
const literalValues = schema.enum.map((v) => {
|
|
7278
|
+
if (typeof v === "string") {
|
|
7279
|
+
return `z.literal("${v}")`;
|
|
7280
|
+
}
|
|
7281
|
+
return `z.literal(${v})`;
|
|
7282
|
+
}).join(", ");
|
|
7283
|
+
return `z.union([${literalValues}])`;
|
|
7141
7284
|
}
|
|
7142
7285
|
const type = schema.type;
|
|
7143
7286
|
if (type === "string") {
|
|
@@ -7368,6 +7511,7 @@ var OpenApiGeneratorOptionsSchema = import_zod2.z.strictObject({
|
|
|
7368
7511
|
schemaType: import_zod2.z.enum(["all", "request", "response"]).optional(),
|
|
7369
7512
|
prefix: import_zod2.z.string().optional(),
|
|
7370
7513
|
suffix: import_zod2.z.string().optional(),
|
|
7514
|
+
stripSchemaPrefix: import_zod2.z.string().optional(),
|
|
7371
7515
|
showStats: import_zod2.z.boolean().optional(),
|
|
7372
7516
|
request: RequestResponseOptionsSchema.optional(),
|
|
7373
7517
|
response: RequestResponseOptionsSchema.optional(),
|
|
@@ -7384,6 +7528,7 @@ var ConfigFileSchema = import_zod2.z.strictObject({
|
|
|
7384
7528
|
schemaType: import_zod2.z.enum(["all", "request", "response"]).optional(),
|
|
7385
7529
|
prefix: import_zod2.z.string().optional(),
|
|
7386
7530
|
suffix: import_zod2.z.string().optional(),
|
|
7531
|
+
stripSchemaPrefix: import_zod2.z.string().optional(),
|
|
7387
7532
|
showStats: import_zod2.z.boolean().optional(),
|
|
7388
7533
|
request: RequestResponseOptionsSchema.optional(),
|
|
7389
7534
|
response: RequestResponseOptionsSchema.optional(),
|
|
@@ -7413,7 +7558,7 @@ async function loadConfig(configPath) {
|
|
|
7413
7558
|
}
|
|
7414
7559
|
if (!result || !result.config) {
|
|
7415
7560
|
throw new Error(
|
|
7416
|
-
configPath ? `Config file not found at: ${configPath}` : "No config file found. Searched for: openapi-to-zod.config.ts, openapi-to-zod.config.json, package.json (openapi-to-zod key)"
|
|
7561
|
+
configPath ? `Config file not found at: ${configPath}` : "No config file found. Searched for: openapi-to-zod.config.ts, openapi-to-zod.config.json, package.json (openapi-to-zod key)\nRun 'openapi-to-zod init' to create a new config file."
|
|
7417
7562
|
);
|
|
7418
7563
|
}
|
|
7419
7564
|
try {
|
|
@@ -7520,14 +7665,7 @@ function findSpecFiles() {
|
|
|
7520
7665
|
}
|
|
7521
7666
|
async function executeConfigMode(options) {
|
|
7522
7667
|
var _a, _b;
|
|
7523
|
-
|
|
7524
|
-
try {
|
|
7525
|
-
config = await loadConfig(options.config);
|
|
7526
|
-
} catch {
|
|
7527
|
-
throw new CliOptionsError("No config file found. Run 'openapi-to-zod init' to create one.", {
|
|
7528
|
-
configPath: options.config
|
|
7529
|
-
});
|
|
7530
|
-
}
|
|
7668
|
+
const config = await loadConfig(options.config);
|
|
7531
7669
|
const specs = mergeConfigWithDefaults(config);
|
|
7532
7670
|
const executionMode = config.executionMode || "parallel";
|
|
7533
7671
|
const batchSize = (_b = (_a = specs[0]) == null ? void 0 : _a.batchSize) != null ? _b : 10;
|