vld-ts 0.4.0

Generate TypeScript Zod schemas from vld schemas and types
Documentation
[![Crates.io](https://img.shields.io/crates/v/vld-ts?style=for-the-badge)](https://crates.io/crates/vld-ts)
[![docs.rs](https://img.shields.io/docsrs/vld-ts?style=for-the-badge)](https://docs.rs/vld-ts)
[![License](https://img.shields.io/badge/license-MIT-green?style=for-the-badge)](https://github.com/s00d/vld/blob/main/LICENSE)
[![Platform](https://img.shields.io/badge/platform-macOS%20%7C%20Windows%20%7C%20Linux-blue?style=for-the-badge)](https://github.com/s00d/vld)
[![GitHub issues](https://img.shields.io/badge/github-issues-orange?style=for-the-badge)](https://github.com/s00d/vld/issues)
[![GitHub stars](https://img.shields.io/badge/github-stars-yellow?style=for-the-badge)](https://github.com/s00d/vld/stargazers)

# vld-ts

Generate TypeScript [Zod](https://zod.dev/) and [Valibot](https://valibot.dev/) schemas directly from [vld](https://crates.io/crates/vld) schemas and types.

## Overview

`vld-ts` is **vld-first**: use your `vld` schemas/types as the source of truth and generate frontend Zod/Valibot from them.

## Installation

```toml
[dependencies]
vld-ts = "0.4"
```

## Quick start

```rust
use vld_ts::{
    impl_to_openapi, impl_to_valibot, impl_to_zod, openapi_refs, to_openapi, to_valibot, to_zod,
    ToOpenApi, ToRefs, ToValibot, ToZod,
};

vld::schema! {
    pub struct Address {
        pub city: String => vld::string().min(1),
    }
}

vld::schema! {
    pub struct Order {
        pub shipping: Address => vld::nested!(Address),
    }
}

impl_to_zod!(Order);
impl_to_valibot!(Order);
impl_to_openapi!(Order);
impl_to_openapi!(Address);

// From schema instance
let zod = to_zod(&vld::string().min(2).email());
assert!(zod.contains(".email()"));

// From vld type (single schema expression, no imports/exports)
let order_zod = Order::to_zod();
assert!(order_zod.starts_with("z.object("));
assert!(order_zod.contains("shipping: z.lazy(() => AddressSchema)"));
assert!(!order_zod.contains("import { z } from \"zod\""));


// Valibot from schema instance
let single_valibot = to_valibot(&vld::string().min(2).email());
assert!(single_valibot.contains("v.string()"));
assert!(single_valibot.contains("v.email()"));

// Valibot from vld type (single schema expression, no imports/exports)
let order_valibot = Order::to_valibot();
assert!(order_valibot.starts_with("v.object"));
assert!(order_valibot.contains("shipping: v.lazy(() => AddressSchema)"));
assert!(!order_valibot.contains("import * as v from \"valibot\""));


// OpenAPI schema object from schema instance
let email_schema = to_openapi(&vld::string().email());
assert_eq!(email_schema["type"], "string");

// OpenAPI schema object from vld type
let openapi_schema = Address::to_openapi();
assert_eq!(openapi_schema["type"], "object");
let refs = openapi_refs(&Order::to_openapi());
assert!(refs.contains(&"#/components/schemas/Address".to_string()));
let refs2 = Order::to_refs();
assert!(refs2.contains(&"#/components/schemas/Address".to_string()));
```

### Generated output

```typescript
// Auto-generated by vld-ts — do not edit manually
import { z } from "zod";

export const UserSchema = z.object({
  name: z.string().min(2).max(50),
  email: z.string().email(),
  age: z.number().int().min(0).optional()
});
export type User = z.infer<typeof UserSchema>;

export const EmailSchema = z.string().email();
export type Email = z.infer<typeof EmailSchema>;
```

```typescript
// Auto-generated by vld-ts — do not edit manually
import * as v from "valibot";

export const UserSchema = v.objectWithRest({
  name: v.pipe(v.string(), v.minLength(2), v.maxLength(50)),
  email: v.pipe(v.string(), v.email())
}, v.unknown());
export type User = v.InferOutput<typeof UserSchema>;
```

## Supported output features

| JSON Schema                  | Zod output                     |
|------------------------------|--------------------------------|
| `type: "string"`             | `z.string()`                   |
| `type: "number"`             | `z.number()`                   |
| `type: "integer"`            | `z.number().int()`             |
| `type: "boolean"`            | `z.boolean()`                  |
| `type: "null"`               | `z.null()`                     |
| `type: "array"`              | `z.array(...)`                 |
| `type: "object"`             | `z.object({...})`              |
| `oneOf`                      | `z.union([...])`               |
| `allOf`                      | `z.intersection(...)`          |
| `anyOf`                      | `z.union([...])`               |
| `enum`                       | `z.literal(...)` / `z.union()` |
| `format: "email"`            | `.email()`                     |
| `format: "uuid"`             | `.uuid()`                      |
| `format: "date"/"date-time"` | `.date()` / `.datetime()`      |
| `minLength/maxLength`        | `.min()/.max()`                |
| `minimum/maximum`            | `.min()/.max()`                |
| `pattern`                    | `.regex()`                     |
| `description`                | `.describe()`                  |
| nullable (`oneOf` + null)    | `.nullable()`                  |
| `$ref` to named schemas      | `z.lazy(() => NameSchema)`     |

## Examples

```bash
cargo run -p vld-ts --example generate_zod
```

## License

MIT