@cerios/openapi-to-zod 1.2.0 → 1.3.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 +87 -30
- package/dist/cli.js +306 -52
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +312 -52
- 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 +300 -51
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +300 -51
- package/dist/index.mjs.map +1 -1
- package/dist/internal.d.mts +93 -2
- package/dist/internal.d.ts +93 -2
- package/dist/internal.js +115 -4
- package/dist/internal.js.map +1 -1
- package/dist/internal.mjs +109 -4
- package/dist/internal.mjs.map +1 -1
- package/dist/{types--r0d47sd.d.mts → types-DZ4Bw-D5.d.mts} +86 -4
- package/dist/{types--r0d47sd.d.ts → types-DZ4Bw-D5.d.ts} +86 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ Transform OpenAPI YAML specifications into Zod v4 compliant schemas with full Ty
|
|
|
19
19
|
- 📊 **Statistics**: Optional generation statistics in output files
|
|
20
20
|
- ❗ **Better Errors**: Clear error messages with file paths and line numbers
|
|
21
21
|
- 🎭 **Tuple Validation**: OpenAPI 3.1 `prefixItems` support with `.tuple()` and `.rest()`
|
|
22
|
-
- 🔗 **Smart AllOf**: Uses `.
|
|
22
|
+
- 🔗 **Smart AllOf**: Uses `.extend()` for objects (Zod v4), `.and()` for primitives
|
|
23
23
|
- 🎯 **Literal Types**: `const` keyword support with `z.literal()`
|
|
24
24
|
- 🔢 **Exclusive Bounds**: `exclusiveMinimum`/`exclusiveMaximum` with `.gt()`/`.lt()`
|
|
25
25
|
- 🎨 **Unique Arrays**: `uniqueItems` validation with Set-based checking
|
|
@@ -181,7 +181,8 @@ Examples:
|
|
|
181
181
|
| `name` | `string` | Optional identifier for logging |
|
|
182
182
|
| `input` | `string` | Input OpenAPI YAML file path (required) |
|
|
183
183
|
| `output` | `string` | Output TypeScript file path (required) |
|
|
184
|
-
| `mode` | `"strict"` \| `"normal"` \| `"loose"` | Validation mode |
|
|
184
|
+
| `mode` | `"strict"` \| `"normal"` \| `"loose"` | Validation mode for top-level schemas (default: `"normal"`) |
|
|
185
|
+
| `emptyObjectBehavior` | `"strict"` \| `"loose"` \| `"record"` | How to handle empty objects (default: `"loose"`) |
|
|
185
186
|
| `includeDescriptions` | `boolean` | Include JSDoc comments |
|
|
186
187
|
| `useDescribe` | `boolean` | Add `.describe()` calls |
|
|
187
188
|
| `defaultNullable` | `boolean` | Treat properties as nullable by default when not explicitly specified (default: `false`) |
|
|
@@ -314,6 +315,43 @@ const userSchema = z.looseObject({
|
|
|
314
315
|
});
|
|
315
316
|
```
|
|
316
317
|
|
|
318
|
+
## Empty Object Behavior
|
|
319
|
+
|
|
320
|
+
When OpenAPI schemas define an object without any properties (e.g., `type: object` with no `properties`), the generator needs to decide how to represent it. The `emptyObjectBehavior` option controls this:
|
|
321
|
+
|
|
322
|
+
### Loose (default)
|
|
323
|
+
Uses `z.looseObject({})` which allows any additional properties:
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
// OpenAPI: { type: object }
|
|
327
|
+
const metadataSchema = z.looseObject({});
|
|
328
|
+
|
|
329
|
+
// Accepts: {}, { foo: "bar" }, { any: "properties" }
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Strict
|
|
333
|
+
Uses `z.strictObject({})` which rejects any properties:
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
// OpenAPI: { type: object }
|
|
337
|
+
const emptySchema = z.strictObject({});
|
|
338
|
+
|
|
339
|
+
// Accepts: {}
|
|
340
|
+
// Rejects: { foo: "bar" }
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Record
|
|
344
|
+
Uses `z.record(z.string(), z.unknown())` which treats it as an arbitrary key-value map:
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
// OpenAPI: { type: object }
|
|
348
|
+
const mapSchema = z.record(z.string(), z.unknown());
|
|
349
|
+
|
|
350
|
+
// Accepts: {}, { foo: "bar" }, { any: "properties" }
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
> **Note:** The `mode` option controls how top-level schema definitions are wrapped, while `emptyObjectBehavior` controls how nested empty objects (properties without defined structure) are generated. These are independent settings.
|
|
354
|
+
|
|
317
355
|
## Examples
|
|
318
356
|
|
|
319
357
|
### Input OpenAPI YAML
|
|
@@ -642,55 +680,74 @@ export default defineConfig({
|
|
|
642
680
|
});
|
|
643
681
|
```
|
|
644
682
|
|
|
683
|
+
**Important:** `defaultNullable` only applies to **primitive property values** within objects. It does NOT apply to:
|
|
684
|
+
|
|
685
|
+
- **Top-level schema definitions** - Schemas are not made nullable at the definition level
|
|
686
|
+
- **Schema references (`$ref`)** - References preserve the nullability of the target schema; add explicit `nullable: true` if needed
|
|
687
|
+
- **Enum values** - Enums define discrete values and are not nullable by default
|
|
688
|
+
- **Const/literal values** - Literals are exact values and are not nullable by default
|
|
689
|
+
|
|
645
690
|
**Behavior comparison:**
|
|
646
691
|
|
|
647
692
|
| Schema Property | `defaultNullable: false` (default) | `defaultNullable: true` |
|
|
648
693
|
|-----------------|-------------------------------------|-------------------------|
|
|
649
694
|
| `nullable: true` | `.nullable()` | `.nullable()` |
|
|
650
695
|
| `nullable: false` | No `.nullable()` | No `.nullable()` |
|
|
651
|
-
| No
|
|
696
|
+
| No annotation (primitive) | No `.nullable()` | `.nullable()` |
|
|
697
|
+
| No annotation (`$ref`) | No `.nullable()` | No `.nullable()` |
|
|
698
|
+
| No annotation (enum) | No `.nullable()` | No `.nullable()` |
|
|
699
|
+
| No annotation (const) | No `.nullable()` | No `.nullable()` |
|
|
652
700
|
|
|
653
701
|
**Example:**
|
|
654
702
|
|
|
655
703
|
```yaml
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
id:
|
|
660
|
-
type: integer
|
|
661
|
-
name:
|
|
662
|
-
type: string
|
|
663
|
-
email:
|
|
664
|
-
type: string
|
|
665
|
-
nullable: true
|
|
666
|
-
phone:
|
|
704
|
+
components:
|
|
705
|
+
schemas:
|
|
706
|
+
Status:
|
|
667
707
|
type: string
|
|
668
|
-
|
|
708
|
+
enum: [active, inactive]
|
|
709
|
+
User:
|
|
710
|
+
type: object
|
|
711
|
+
properties:
|
|
712
|
+
id:
|
|
713
|
+
type: integer
|
|
714
|
+
name:
|
|
715
|
+
type: string
|
|
716
|
+
status:
|
|
717
|
+
$ref: '#/components/schemas/Status'
|
|
718
|
+
nullableStatus:
|
|
719
|
+
allOf:
|
|
720
|
+
- $ref: '#/components/schemas/Status'
|
|
721
|
+
nullable: true
|
|
669
722
|
```
|
|
670
723
|
|
|
671
724
|
**With `defaultNullable: false` (default):**
|
|
672
725
|
```typescript
|
|
726
|
+
export const statusSchema = z.enum(["active", "inactive"]);
|
|
727
|
+
|
|
673
728
|
export const userSchema = z.object({
|
|
674
729
|
id: z.number().int(),
|
|
675
|
-
name: z.string(),
|
|
676
|
-
|
|
677
|
-
|
|
730
|
+
name: z.string(), // Not nullable (no annotation)
|
|
731
|
+
status: statusSchema, // Not nullable ($ref)
|
|
732
|
+
nullableStatus: statusSchema.nullable(), // Explicitly nullable
|
|
678
733
|
});
|
|
679
734
|
```
|
|
680
735
|
|
|
681
736
|
**With `defaultNullable: true`:**
|
|
682
737
|
```typescript
|
|
738
|
+
export const statusSchema = z.enum(["active", "inactive"]);
|
|
739
|
+
|
|
683
740
|
export const userSchema = z.object({
|
|
684
|
-
id: z.number().int().nullable(), // Nullable
|
|
685
|
-
name: z.string().nullable(), // Nullable
|
|
686
|
-
|
|
687
|
-
|
|
741
|
+
id: z.number().int().nullable(), // Nullable (primitive)
|
|
742
|
+
name: z.string().nullable(), // Nullable (primitive)
|
|
743
|
+
status: statusSchema, // NOT nullable ($ref - must be explicit)
|
|
744
|
+
nullableStatus: statusSchema.nullable(), // Explicitly nullable
|
|
688
745
|
});
|
|
689
746
|
```
|
|
690
747
|
|
|
691
748
|
### Schema Composition
|
|
692
749
|
|
|
693
|
-
- `allOf` → `.
|
|
750
|
+
- `allOf` → `.extend()` for objects (Zod v4), `.and()` for primitives
|
|
694
751
|
- `oneOf`, `anyOf` → `z.union()` or `z.discriminatedUnion()`
|
|
695
752
|
- `$ref` → Proper schema references
|
|
696
753
|
|
|
@@ -1140,11 +1197,11 @@ export const flexibleMetadataSchema = z
|
|
|
1140
1197
|
|
|
1141
1198
|
### Schema Composition
|
|
1142
1199
|
|
|
1143
|
-
#### AllOf - Smart
|
|
1200
|
+
#### AllOf - Smart Extending
|
|
1144
1201
|
|
|
1145
|
-
Uses `.
|
|
1202
|
+
Uses `.extend()` for objects (Zod v4 compliant - `.merge()` is deprecated), `.and()` for primitives:
|
|
1146
1203
|
|
|
1147
|
-
**Object
|
|
1204
|
+
**Object Extending:**
|
|
1148
1205
|
```yaml
|
|
1149
1206
|
User:
|
|
1150
1207
|
allOf:
|
|
@@ -1161,10 +1218,10 @@ User:
|
|
|
1161
1218
|
**Generated:**
|
|
1162
1219
|
```typescript
|
|
1163
1220
|
export const userSchema = baseEntitySchema
|
|
1164
|
-
.
|
|
1165
|
-
.
|
|
1221
|
+
.extend(timestampedSchema.shape)
|
|
1222
|
+
.extend(z.object({
|
|
1166
1223
|
username: z.string()
|
|
1167
|
-
}));
|
|
1224
|
+
}).shape);
|
|
1168
1225
|
```
|
|
1169
1226
|
|
|
1170
1227
|
#### OneOf / AnyOf
|
|
@@ -1286,7 +1343,7 @@ export const statusCodeSchema = z.enum(["200", "201", "400", "404", "500"]);
|
|
|
1286
1343
|
| const | ✅ | ✅ | `z.literal()` |
|
|
1287
1344
|
| nullable (property) | ✅ | ✅ | `.nullable()` |
|
|
1288
1345
|
| nullable (type array) | ❌ | ✅ | `.nullable()` |
|
|
1289
|
-
| allOf (objects) | ✅ | ✅ | `.
|
|
1346
|
+
| allOf (objects) | ✅ | ✅ | `.extend()` |
|
|
1290
1347
|
| allOf (primitives) | ✅ | ✅ | `.and()` |
|
|
1291
1348
|
| oneOf/anyOf | ✅ | ✅ | `z.union()` |
|
|
1292
1349
|
| discriminators | ✅ | ✅ | `z.discriminatedUnion()` |
|