@schemashift/zod-valibot
v0.14.0
Published
Zod to Valibot transformer for SchemaShift — converts fluent chain API to pipe-based API
Maintainers
Readme
@schemashift/zod-valibot
Zod ↔ Valibot transformer for SchemaShift. Supports both forward (Zod → Valibot) and backward (Valibot → Zod) migrations.
Tier: Pro
Installation
npm install @schemashift/zod-valibotUsage
import { createZodToValibotHandler, createValibotToZodHandler } from '@schemashift/zod-valibot';
import { TransformEngine } from '@schemashift/core';
const engine = new TransformEngine();
engine.registerHandler('zod', 'valibot', createZodToValibotHandler());
engine.registerHandler('valibot', 'zod', createValibotToZodHandler()); // Backward migration (Pro+)Backward Migration: Valibot → Zod
Text-based transformer that converts Valibot schemas to Zod equivalents:
| Valibot | Zod |
|---------|-----|
| v.object({...}) | z.object({...}) |
| v.string() | z.string() |
| v.number() | z.number() |
| v.boolean() | z.boolean() |
| v.date() | z.date() |
| v.array(s) | z.array(s) |
| v.picklist([...]) | z.enum([...]) |
| v.union([...]) | z.union([...]) |
| v.literal(val) | z.literal(val) |
| v.optional(s) | s.optional() |
| v.nullable(s) | s.nullable() |
| v.nullish(s) | s.nullish() |
| v.pipe(s, ...) | Unwrapped with validations chained |
| v.check(fn, msg) | .refine(fn, msg) |
| v.transform(fn) | .transform(fn) |
| v.brand(name) | .brand(name) |
| v.partial(s) | s.partial() |
| v.pick(s, [...]) | s.pick({...}) |
| v.omit(s, [...]) | s.omit({...}) |
Tier: Pro+
API Model Difference
Zod uses a fluent method chain API:
z.string().email().min(5).max(100)Valibot uses a pipe-based API where validations are arguments to v.pipe():
v.pipe(v.string(), v.email(), v.minLength(5), v.maxLength(100))Modifiers like .optional() and .nullable() become wrapping functions:
// Zod
z.string().optional()
// Valibot
v.optional(v.string())Transformation Mappings
Imports
| Zod | Valibot |
|-----|---------|
| import { z } from 'zod' | import * as v from 'valibot' |
| import * as z from 'zod' | import * as v from 'valibot' |
Factory Methods
| Zod | Valibot |
|-----|---------|
| z.string() | v.string() |
| z.number() | v.number() |
| z.boolean() | v.boolean() |
| z.date() | v.date() |
| z.bigint() | v.bigint() |
| z.symbol() | v.symbol() |
| z.undefined() | v.undefined_() |
| z.null() | v.null_() |
| z.void() | v.void_() |
| z.any() | v.any() |
| z.unknown() | v.unknown() |
| z.never() | v.never() |
| z.nan() | v.nan() |
| z.literal(val) | v.literal(val) |
| z.enum([...]) | v.picklist([...]) |
| z.nativeEnum(E) | v.enum_(E) |
| z.array(s) | v.array(s) |
| z.object({...}) | v.object({...}) |
| z.record(k, v) | v.record(k, v) |
| z.tuple([...]) | v.tuple([...]) |
| z.union([...]) | v.union([...]) |
| z.discriminatedUnion(d, [...]) | v.variant(d, [...]) |
| z.intersection(a, b) | v.intersect(a, b) |
| z.lazy(() => s) | v.lazy(() => s) |
| z.promise(s) | v.promise(s) |
| z.instanceof(C) | v.instance(C) |
String Validations (Pipe)
| Zod | Valibot |
|-----|---------|
| .email() | v.email() |
| .url() | v.url() |
| .uuid() | v.uuid() |
| .regex(r) | v.regex(r) |
| .ip() | v.ip() |
| .min(n) | v.minLength(n) |
| .max(n) | v.maxLength(n) |
| .length(n) | v.length(n) |
| .trim() | v.trim() |
| .toLowerCase() | v.toLowerCase() |
| .toUpperCase() | v.toUpperCase() |
| .includes(s) | v.includes(s) |
| .startsWith(s) | v.startsWith(s) |
| .endsWith(s) | v.endsWith(s) |
| .datetime() | v.isoDateTime() |
| .date() | v.isoDate() |
| .time() | v.isoTime() |
| .emoji() | v.emoji() |
Number Validations (Pipe)
| Zod | Valibot |
|-----|---------|
| .min(n) | v.minValue(n) |
| .max(n) | v.maxValue(n) |
| .gt(n) | v.minValue(n + 1) |
| .lt(n) | v.maxValue(n - 1) |
| .gte(n) | v.minValue(n) |
| .lte(n) | v.maxValue(n) |
| .int() | v.integer() |
| .positive() | v.minValue(1) |
| .negative() | v.maxValue(-1) |
| .nonnegative() | v.minValue(0) |
| .nonpositive() | v.maxValue(0) |
| .multipleOf(n) | v.multipleOf(n) |
| .finite() | v.finite() |
| .safe() | v.safeInteger() |
Array Validations
| Zod | Valibot |
|-----|---------|
| .min(n) | v.minLength(n) |
| .max(n) | v.maxLength(n) |
| .nonempty() | v.nonEmpty() |
Modifiers (Wrapping)
| Zod | Valibot |
|-----|---------|
| .optional() | v.optional(schema) |
| .nullable() | v.nullable(schema) |
| .nullish() | v.nullish(schema) |
| .default(val) | v.optional(schema, val) |
| .catch(val) | v.fallback(schema, val) |
| .readonly() | v.readonly(schema) |
| .transform(fn) | v.pipe(schema, v.transform(fn)) |
| .refine(fn, msg) | v.pipe(schema, v.check(fn, msg)) |
| .brand(name) | v.pipe(schema, v.brand(name)) |
| .describe(msg) | (removed — Valibot has no equivalent) |
Object Methods (Wrapping)
| Zod | Valibot |
|-----|---------|
| .partial() | v.partial(schema) |
| .required() | v.required(schema) |
| .pick({...}) | v.pick(schema, {...}) |
| .omit({...}) | v.omit(schema, {...}) |
| .extend(other) | v.merge([schema, other]) |
| .merge(other) | v.merge([schema, other]) |
| .strict() | v.strict(schema) |
| .passthrough() | (no-op — Valibot objects are loose by default) |
| .strip() | (no-op — Valibot strips unknown by default with v.object) |
Type Helpers
| Zod | Valibot |
|-----|---------|
| z.infer<typeof s> | v.InferOutput<typeof s> |
| z.input<typeof s> | v.InferInput<typeof s> |
| z.output<typeof s> | v.InferOutput<typeof s> |
Patterns Requiring Manual Review
.superRefine()
.superRefine() is converted with a warning since Valibot's custom validation has a different API:
// Zod
z.object({...}).superRefine((data, ctx) => {
ctx.addIssue({ code: 'custom', message: '...' });
});
// Valibot (needs manual conversion)
v.pipe(v.object({...}), /* TODO: superRefine -> custom validation */ v.check(...)).catchall()
Zod's .catchall() for extra properties requires manual conversion to Valibot's v.record() or custom validation.
.keyof()
Zod's .keyof() requires manual conversion to v.picklist(Object.keys(...)).
Related Packages
- schemashift-cli — CLI tool for running migrations
- @schemashift/core — Core analysis engine
- @schemashift/yup-zod — Chain: Yup → Zod → Valibot
License
MIT
