@cerios/openapi-to-zod 1.1.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 +84 -28
- package/dist/cli.js +97 -58
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +97 -58
- 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 +97 -58
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +97 -58
- package/dist/index.mjs.map +1 -1
- package/dist/internal.d.mts +12 -18
- package/dist/internal.d.ts +12 -18
- package/dist/internal.js +36 -49
- package/dist/internal.js.map +1 -1
- package/dist/internal.mjs +36 -49
- package/dist/internal.mjs.map +1 -1
- package/dist/{types-B7ePTDjr.d.mts → types-CI48CjiU.d.mts} +19 -8
- package/dist/{types-B7ePTDjr.d.ts → types-CI48CjiU.d.ts} +19 -8
- package/package.json +93 -102
package/README.md
CHANGED
|
@@ -187,7 +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
|
|
190
|
+
| `stripSchemaPrefix` | `string` | Strip prefix from schema names before generating using glob patterns (e.g., `"Company.Models."` or `"*.Models."`) |
|
|
191
191
|
| `showStats` | `boolean` | Include generation statistics |
|
|
192
192
|
| `request` | `object` | Request-specific options (mode, includeDescriptions, useDescribe) |
|
|
193
193
|
| `response` | `object` | Response-specific options (mode, includeDescriptions, useDescribe) |
|
|
@@ -571,10 +571,46 @@ OpenAPI's `nullable: true` is converted to `.nullable()`
|
|
|
571
571
|
|
|
572
572
|
### Enums
|
|
573
573
|
|
|
574
|
-
Enums are generated
|
|
575
|
-
|
|
576
|
-
-
|
|
577
|
-
-
|
|
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
|
+
```
|
|
578
614
|
|
|
579
615
|
## Schema Naming
|
|
580
616
|
|
|
@@ -684,39 +720,39 @@ export default defineConfig({
|
|
|
684
720
|
});
|
|
685
721
|
```
|
|
686
722
|
|
|
687
|
-
####
|
|
723
|
+
#### Glob Patterns
|
|
688
724
|
|
|
689
|
-
Use
|
|
725
|
+
Use glob patterns to strip dynamic prefixes:
|
|
690
726
|
|
|
691
727
|
```typescript
|
|
692
728
|
export default defineConfig({
|
|
693
729
|
specs: [{
|
|
694
730
|
input: 'openapi.yaml',
|
|
695
731
|
output: 'schemas.ts',
|
|
696
|
-
// Strip any namespace prefix
|
|
697
|
-
stripSchemaPrefix: '
|
|
732
|
+
// Strip any namespace prefix with wildcard
|
|
733
|
+
stripSchemaPrefix: '*.Models.'
|
|
698
734
|
}]
|
|
699
735
|
});
|
|
700
736
|
```
|
|
701
737
|
|
|
702
|
-
**
|
|
703
|
-
|
|
704
|
-
Regex patterns are auto-detected if they contain: `^`, `$`, `\\d`, `\\w`, `\\s`, `.*`, `.+`, `[]`, `()`
|
|
738
|
+
**Glob Pattern Syntax:**
|
|
705
739
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
stripSchemaPrefix: 'Company.Models.' // No regex markers
|
|
714
|
-
```
|
|
715
|
-
|
|
716
|
-
For TypeScript configs, you can also use `RegExp` objects:
|
|
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
|
|
717
747
|
|
|
718
748
|
```typescript
|
|
719
|
-
|
|
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
|
|
720
756
|
```
|
|
721
757
|
|
|
722
758
|
#### Common Patterns
|
|
@@ -730,24 +766,44 @@ stripSchemaPrefix: /^[A-Z][a-z]+\./
|
|
|
730
766
|
// Company.Models.Post → Post
|
|
731
767
|
```
|
|
732
768
|
|
|
733
|
-
**Pattern 2: Multiple Namespaces**
|
|
769
|
+
**Pattern 2: Multiple Namespaces with Wildcard**
|
|
734
770
|
```typescript
|
|
735
771
|
{
|
|
736
|
-
stripSchemaPrefix: '
|
|
772
|
+
stripSchemaPrefix: '*.Models.'
|
|
737
773
|
}
|
|
738
774
|
// MyApp.Models.User → User
|
|
739
775
|
// OtherApp.Models.User → User
|
|
776
|
+
// Company.Models.Post → Post
|
|
740
777
|
```
|
|
741
778
|
|
|
742
|
-
**Pattern 3:
|
|
779
|
+
**Pattern 3: Multiple Namespace Types**
|
|
743
780
|
```typescript
|
|
744
781
|
{
|
|
745
|
-
stripSchemaPrefix: '
|
|
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].'
|
|
746
792
|
}
|
|
747
793
|
// v1.User → User
|
|
748
794
|
// v2.Product → Product
|
|
749
795
|
```
|
|
750
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
|
+
|
|
751
807
|
#### Interaction with prefix/suffix Options
|
|
752
808
|
|
|
753
809
|
`stripSchemaPrefix` is applied **before** `prefix` and `suffix` options:
|
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
|
|
@@ -5192,8 +5192,26 @@ function resolveRef(ref) {
|
|
|
5192
5192
|
function generateEnum(name, values, options) {
|
|
5193
5193
|
const schemaName = `${toCamelCase(name, options)}Schema`;
|
|
5194
5194
|
const typeName = toPascalCase(name);
|
|
5195
|
-
const
|
|
5196
|
-
|
|
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}]);`;
|
|
5197
5215
|
const typeCode = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
|
|
5198
5216
|
return { schemaCode, typeCode };
|
|
5199
5217
|
}
|
|
@@ -5207,7 +5225,7 @@ function escapeDescription(str) {
|
|
|
5207
5225
|
return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
|
|
5208
5226
|
}
|
|
5209
5227
|
function escapePattern(str) {
|
|
5210
|
-
return str.replace(
|
|
5228
|
+
return str.replace(/\//g, "\\/");
|
|
5211
5229
|
}
|
|
5212
5230
|
function escapeJSDoc(str) {
|
|
5213
5231
|
return str.replace(/\*\//g, "*\\/");
|
|
@@ -5337,61 +5355,36 @@ var LRUCache = class {
|
|
|
5337
5355
|
|
|
5338
5356
|
// src/utils/pattern-utils.ts
|
|
5339
5357
|
init_cjs_shims();
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
if (/\\[dDwWsS]/.test(pattern)) {
|
|
5345
|
-
return true;
|
|
5346
|
-
}
|
|
5347
|
-
if (/\.\*|\.\+/.test(pattern)) {
|
|
5348
|
-
return true;
|
|
5349
|
-
}
|
|
5350
|
-
if (/[[\]()]/.test(pattern)) {
|
|
5351
|
-
return true;
|
|
5352
|
-
}
|
|
5353
|
-
if (/[^/][+?*]\{/.test(pattern)) {
|
|
5358
|
+
var import_minimatch = require("minimatch");
|
|
5359
|
+
function isValidGlobPattern(pattern) {
|
|
5360
|
+
try {
|
|
5361
|
+
new import_minimatch.minimatch.Minimatch(pattern);
|
|
5354
5362
|
return true;
|
|
5363
|
+
} catch {
|
|
5364
|
+
return false;
|
|
5355
5365
|
}
|
|
5356
|
-
return false;
|
|
5357
5366
|
}
|
|
5358
|
-
function
|
|
5359
|
-
|
|
5360
|
-
return pattern;
|
|
5361
|
-
}
|
|
5362
|
-
if (isRegexPattern(pattern)) {
|
|
5363
|
-
try {
|
|
5364
|
-
return new RegExp(pattern);
|
|
5365
|
-
} catch (error) {
|
|
5366
|
-
console.warn(`\u26A0\uFE0F Invalid regex pattern "${pattern}": ${error instanceof Error ? error.message : String(error)}`);
|
|
5367
|
-
return null;
|
|
5368
|
-
}
|
|
5369
|
-
}
|
|
5370
|
-
return null;
|
|
5367
|
+
function isGlobPattern(pattern) {
|
|
5368
|
+
return /[*?[\]{}!]/.test(pattern);
|
|
5371
5369
|
}
|
|
5372
5370
|
function stripPrefix(input, pattern, ensureLeadingChar) {
|
|
5373
5371
|
if (!pattern) {
|
|
5374
5372
|
return input;
|
|
5375
5373
|
}
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
return `${ensureLeadingChar}${stripped}`;
|
|
5387
|
-
}
|
|
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;
|
|
5388
5384
|
}
|
|
5389
|
-
return stripped;
|
|
5390
5385
|
}
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
if (input.startsWith(stringPattern)) {
|
|
5394
|
-
const stripped = input.substring(stringPattern.length);
|
|
5386
|
+
if (longestMatch > 0) {
|
|
5387
|
+
const stripped = input.substring(longestMatch);
|
|
5395
5388
|
if (ensureLeadingChar) {
|
|
5396
5389
|
if (stripped === "") {
|
|
5397
5390
|
return ensureLeadingChar;
|
|
@@ -5400,8 +5393,21 @@ function stripPrefix(input, pattern, ensureLeadingChar) {
|
|
|
5400
5393
|
return `${ensureLeadingChar}${stripped}`;
|
|
5401
5394
|
}
|
|
5402
5395
|
}
|
|
5403
|
-
return stripped;
|
|
5396
|
+
return stripped === "" && !ensureLeadingChar ? input : stripped;
|
|
5404
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;
|
|
5405
5411
|
}
|
|
5406
5412
|
return input;
|
|
5407
5413
|
}
|
|
@@ -6284,9 +6290,25 @@ var _PropertyGenerator = class _PropertyGenerator {
|
|
|
6284
6290
|
return wrapNullable(zodLiteral, nullable);
|
|
6285
6291
|
}
|
|
6286
6292
|
if (schema.enum) {
|
|
6287
|
-
const
|
|
6288
|
-
|
|
6289
|
-
|
|
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);
|
|
6290
6312
|
}
|
|
6291
6313
|
if (schema.allOf) {
|
|
6292
6314
|
let composition = generateAllOf(
|
|
@@ -6418,7 +6440,7 @@ var PropertyGenerator = _PropertyGenerator;
|
|
|
6418
6440
|
|
|
6419
6441
|
// src/utils/operation-filters.ts
|
|
6420
6442
|
init_cjs_shims();
|
|
6421
|
-
var
|
|
6443
|
+
var import_minimatch2 = require("minimatch");
|
|
6422
6444
|
function createFilterStatistics() {
|
|
6423
6445
|
return {
|
|
6424
6446
|
totalOperations: 0,
|
|
@@ -6437,7 +6459,7 @@ function matchesAnyPattern(value, patterns) {
|
|
|
6437
6459
|
if (!value) {
|
|
6438
6460
|
return false;
|
|
6439
6461
|
}
|
|
6440
|
-
return patterns.some((pattern) => (0,
|
|
6462
|
+
return patterns.some((pattern) => (0, import_minimatch2.minimatch)(value, pattern));
|
|
6441
6463
|
}
|
|
6442
6464
|
function containsAny(arr, values) {
|
|
6443
6465
|
if (!values || values.length === 0) {
|
|
@@ -6664,6 +6686,9 @@ var OpenApiGenerator = class {
|
|
|
6664
6686
|
throw new SpecValidationError("No schemas found in OpenAPI spec", { filePath: this.options.input });
|
|
6665
6687
|
}
|
|
6666
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
|
+
}
|
|
6667
6692
|
this.generateComponentSchema(name, schema);
|
|
6668
6693
|
}
|
|
6669
6694
|
this.generateQueryParameterSchemas();
|
|
@@ -7152,7 +7177,7 @@ ${propsCode}
|
|
|
7152
7177
|
const headerLower = headerName.toLowerCase();
|
|
7153
7178
|
return ignorePatterns.some((pattern) => {
|
|
7154
7179
|
const patternLower = pattern.toLowerCase();
|
|
7155
|
-
return (0,
|
|
7180
|
+
return (0, import_minimatch3.minimatch)(headerLower, patternLower);
|
|
7156
7181
|
});
|
|
7157
7182
|
}
|
|
7158
7183
|
/**
|
|
@@ -7240,8 +7265,22 @@ ${propsCode}
|
|
|
7240
7265
|
return `${schemaName}Schema`;
|
|
7241
7266
|
}
|
|
7242
7267
|
if (schema.enum) {
|
|
7243
|
-
const
|
|
7244
|
-
|
|
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}])`;
|
|
7245
7284
|
}
|
|
7246
7285
|
const type = schema.type;
|
|
7247
7286
|
if (type === "string") {
|