proofix
v0.1.2
Published
Production-ready Zod validators for real-world data — names, passwords, IBANs, IPs and more
Maintainers
Readme
proofix
Production-ready Zod validators for real-world data
Why proofix?
You're already using Zod. Writing the same .refine() logic for emails, passwords, IBANs, and phone numbers across every project gets old fast. proofix is a growing, community-driven collection of 28 validators that slot directly into your schemas.
- 28 production-ready validators — emails, URLs, UUIDs, credit cards, IBANs, and more
- Fully typed — TypeScript-first with exported option interfaces
- Tree-shakeable — import only what you need
- Customizable — every validator accepts a
messageoption - Zero dependencies — only
zodas a peer dependency
Installation
npm install proofix zodpnpm add proofix zodyarn add proofix zodbun add proofix zod
zodis a peer dependency — you control the version (>= 3).
Quick Start
import { z } from 'zod';
import { email, password, creditCard, iban } from 'proofix';
const UserSchema = z.object({
email: email({ blockDisposable: true }),
password: password({ requireSpecial: true, minLength: 10 }),
});
const PaymentSchema = z.object({
cardNumber: creditCard(),
bankAccount: iban(),
});
type User = z.infer<typeof UserSchema>;
type Payment = z.infer<typeof PaymentSchema>;Every validator is a plain function returning a Zod schema. Compose, extend, and .pipe() them exactly like any native Zod type.
Tree-Shakeable Imports
Import from the root or go category-specific:
// Root import
import { email, password, uuid } from 'proofix';
// Category-specific imports
import { email, password, uuid } from 'proofix/string';
import { creditCard, iban, ssn } from 'proofix/document';
import { pastDate, futureDate, ageRange } from 'proofix/date';
import { currency, percentage, latitude } from 'proofix/number';
import { ipAddress, macAddress, domain } from 'proofix/network';Validators
String — proofix/string
| Validator | Description |
| ---------- | ------------------------------------------ |
| email | Email with disposable blocking |
| password | Configurable password strength |
| fullName | First and last name validation |
| username | Alphanumeric usernames |
| phone | International phone formats |
| slug | URL-friendly slugs |
| url | URL with protocol restrictions |
| uuid | UUID v1-v7 validation |
| hexColor | Hex color codes (#RGB, #RRGGBB, #RRGGBBAA) |
| jwt | JSON Web Token format |
| semver | Semantic versioning |
| cron | Cron expressions |
Document — proofix/document
| Validator | Description |
| ---------------- | --------------------------------- |
| iban | IBAN with checksum verification |
| creditCard | Credit card with Luhn check |
| passportNumber | Country-specific passport formats |
| serialNumber | Product serial numbers |
| vatNumber | EU VAT identification numbers |
| ssn | US Social Security Number |
| postalCode | Country-specific postal codes |
| mongoId | MongoDB ObjectId |
Date — proofix/date
| Validator | Description |
| ------------ | ---------------------- |
| pastDate | Date in the past |
| futureDate | Date in the future |
| ageRange | Age from date of birth |
Number — proofix/number
| Validator | Description |
| ------------ | ------------------------- |
| currency | Monetary amounts |
| percentage | Percentage values (0-100) |
| latitude | Latitude (-90 to 90) |
| longitude | Longitude (-180 to 180) |
| port | Network port numbers |
Network — proofix/network
| Validator | Description |
| ------------ | ----------------------- |
| ipAddress | IPv4 and IPv6 addresses |
| macAddress | MAC addresses |
| domain | Domain names |
Examples
email().parse('[email protected]'); // ok
email({ blockDisposable: true }).parse('[email protected]'); // throws
email({ allowedDomains: ['company.com'] }).parse('[email protected]'); // ok| Option | Type | Default | Description |
| ----------------- | ---------- | ------- | -------------------------- |
| blockDisposable | boolean | false | Block disposable providers |
| allowedDomains | string[] | — | Whitelist domains |
| blockedDomains | string[] | — | Blacklist domains |
| allowedTlds | string[] | — | Restrict TLDs |
| maxLength | number | 254 | Maximum length |
password
password().parse('Secret1'); // ok
password({ requireSpecial: true }).parse('Secret1!'); // ok
password({ minLength: 12 }).parse('Short1'); // throws| Option | Type | Default | Description |
| ------------------ | --------- | ------- | -------------------- |
| minLength | number | 8 | Minimum length |
| maxLength | number | 128 | Maximum length |
| requireUppercase | boolean | true | Require uppercase |
| requireLowercase | boolean | true | Require lowercase |
| requireNumbers | boolean | true | Require digit |
| requireSpecial | boolean | false | Require special char |
creditCard
creditCard().parse('4111111111111111'); // ok (Visa test)
creditCard().parse('4111 1111 1111 1111'); // ok
creditCard().parse('1234567890123456'); // throws (Luhn fails)iban
iban().parse('GB29 NWBK 6016 1331 9268 19'); // ok
iban().parse('DE89370400440532013000'); // ok
iban().parse('GB00INVALID'); // throwsuuid
uuid().parse('550e8400-e29b-41d4-a716-446655440000'); // ok
uuid({ version: 4 }).parse('550e8400-e29b-41d4-a716-446655440000'); // ok
uuid({ version: 4 }).parse('550e8400-e29b-11d4-a716-446655440000'); // throws (v1)| Option | Type | Default | Description |
| --------- | -------------------------------- | ------- | ------------ |
| version | 1 \| 3 \| 4 \| 5 \| 7 \| 'any' | 'any' | UUID version |
ipAddress
ipAddress().parse('192.168.1.1'); // ok (IPv4)
ipAddress().parse('::1'); // ok (IPv6)
ipAddress({ version: 4 }).parse('::1'); // throws| Option | Type | Default | Description |
| --------- | ------------------ | -------- | ----------- |
| version | 4 \| 6 \| 'both' | 'both' | IP version |
postalCode
postalCode({ country: 'US' }).parse('12345'); // ok
postalCode({ country: 'US' }).parse('12345-6789'); // ok (ZIP+4)
postalCode({ country: 'GB' }).parse('SW1A 1AA'); // ok
postalCode({ country: 'DE' }).parse('10115'); // okSupported: US CA GB DE FR IT ES NL BE AT CH PL PT SE NO DK FI AU NZ JP CN IN BR RU AZ TR
latitude / longitude
latitude().parse(40.7128); // ok (New York)
latitude().parse(91); // throws
longitude().parse(-74.006); // ok (New York)
longitude().parse(181); // throwsport
port().parse(8080); // ok
port({ range: 'registered' }).parse(3000); // ok
port({ excludeWellKnown: true }).parse(80); // throws| Option | Type | Default | Description |
| ------------------ | ---------------------------------------------------- | ------- | -------------- |
| range | 'any' \| 'privileged' \| 'registered' \| 'dynamic' | 'any' | Port range |
| excludeWellKnown | boolean | false | Exclude 0-1023 |
| excludeZero | boolean | true | Exclude port 0 |
Custom Error Messages
Every validator accepts a message option:
const schema = z.object({
email: email({ message: 'Invalid email address' }),
card: creditCard({ message: 'Card number is invalid' }),
});Composing with Zod
proofix validators are plain Zod schemas:
import { z } from 'zod';
import { password, email } from 'proofix';
const BrandedPassword = password().brand<'Password'>();
const OptionalEmail = email().optional();
const NormalizedEmail = email().transform((e) => e.toLowerCase());Works with react-hook-form, tRPC, Hono, Fastify, or any Zod-aware library.
Contributing
See CONTRIBUTING.md for the guide on adding validators.
License
MIT © Arif Hasanov
