zee-light-validate
v1.0.1
Published
A lightweight TypeScript object validation library with zero dependencies
Maintainers
Readme
Light Validate
A comprehensive, lightweight validation library for TypeScript/JavaScript with zero dependencies. Tree-shakeable, type-safe, and designed for modern applications with support for file validation, cross-field validation, async validation, and internationalization.
Features
- 🪶 Lightweight: Zero dependencies, minimal bundle size
- 🌳 Tree-shakeable: Import only what you need
- 📘 TypeScript: Full TypeScript support with excellent IntelliSense
- 🔧 Flexible: Extensible with custom validators
- 🎯 Simple: Clean and intuitive API
- 📦 Universal: Works in Node.js (CommonJS) and modern JavaScript (ESM)
- 🌍 Internationalization: Multi-language error messages
- ⚡ Async Support: Database/API validation
- 🔗 Cross-field: Compare fields within schemas
- 📊 Schema Reusability: Create and reuse validation schemas
🔤 Advanced String Validations
- ✅ Email, URL, IP address validation
- ✅ UUID, Time (HH:mm) validation
- ✅ Slug, Credit Card (Luhn algorithm)
- ✅ IBAN, Postal Code (190+ countries)
- ✅ XSS/SQL injection protection
- ✅ Custom regex patterns
- ✅ Username, Domain, MAC address
- ✅ Hex colors, Base64, JWT tokens
- ✅ Country codes, API keys, OTP
- ✅ Hash validation, Currency codes
- ✅ Language codes, Timezones
- ✅ Latitude/Longitude, Port numbers
- ✅ Cron expressions, Environment variables
📁 File Validation
- ✅ File size limits (bytes, KB, MB)
- ✅ MIME type validation
- ✅ File extension validation
- ✅ Multiple file type categories
- ✅ Custom file validation rules
📦 Data Structure Validations
- ✅ Array unique items validation
- ✅ Array of objects validation
- ✅ Array sorted validation (ascending/descending)
- ✅ Array type consistency validation
- ✅ Array exact length validation
- ✅ Array contains/not contains validation
- ✅ Object required keys validation
- ✅ Object strict keys validation (no unexpected keys)
- ✅ Object deep nested validation (dot notation)
- ✅ Object array contains validation
- ✅ Object plain object validation (reject Date/RegExp)
- ✅ Date range validation (ISO/US/EU formats)
- ✅ Enum validation (string/number/mixed)
- ✅ Nested object validation
- ✅ Cross-field comparisons
- ✅ Schema composition and reusability
🚀 Advanced Features
- ✅ Async validation (DB/API checks)
- ✅ Cross-field validation (greater, lessThan, equalTo)
- ✅ Localization/i18n support
- ✅ Schema reusability and composition
- ✅ Custom validation functions
- ✅ Complex nested validations
Installation
npm install light-validateQuick Start
Basic Usage
import { string, number, boolean, object, array, schema } from "light-validate";
// Simple validation
const nameValidator = string().min(2).max(50).required();
const result = nameValidator.validate("Zohaib Irshad");
console.log(result); // { success: true }
// Object validation
const userSchema = object({
name: string().required().min(3),
email: string().email().required(),
age: number().min(18).max(120),
isActive: boolean(),
});
const user = {
name: "Zohaib Irshad",
email: "[email protected]",
age: 25,
isActive: true,
};
const validation = userSchema.validate(user);
console.log(validation); // { success: true }Advanced Schema Validation
import {
schema,
field,
greater,
equalTo,
string,
number,
} from "light-validate";
// Cross-field validation
const eventSchema = schema({
title: string().required(),
startDate: string().required(),
endDate: field(string().required(), greater("startDate")),
originalPrice: number().min(0).required(),
salePrice: field(number().min(0), lessThan("originalPrice")),
});
// Password confirmation
const passwordSchema = schema({
password: string().min(8).required(),
confirmPassword: field(string().required(), equalTo("password")),
});Async Validation
import { schema, string, asyncEmail, asyncUsername } from "light-validate";
// Database/API validation
const registrationSchema = schema({
username: string().username().required(),
email: string().email().required(),
})
.asyncValidate(
"email",
asyncEmail(async (email) => {
// Check if email exists in database
const exists = await checkEmailInDatabase(email);
return !exists; // Return true if unique
})
)
.asyncValidate(
"username",
asyncUsername(async (username) => {
// Check username availability
const available = await checkUsernameAvailability(username);
return available;
})
);
// Use async validation
const result = await registrationSchema.validateAsync({
username: "zohaib_irshad",
email: "[email protected]",
});Localization/i18n
import { schema, string } from "light-validate";
const userSchema = schema({
name: string().required(),
email: string().email().required(),
}).locale({
locale: "ur", // Urdu
messages: {
ur: {
required: "یہ فیلڈ ضروری ہے",
email: "درست ای میل ایڈریس درج کریں",
type: "یہ فیلڈ ایک آبجیکٹ ہونا چاہیے",
},
es: {
required: "Este campo es requerido",
email: "Ingrese una dirección de correo válida",
},
},
});Array of Objects Validation
import { array, string, number } from "light-validate";
// Array of user objects
const usersValidator = array()
.min(1, "At least one user required")
.max(10, "Maximum 10 users allowed")
.arrayOf({
name: string().required(),
email: string().email().required(),
age: number().min(0).max(120),
})
.unique("All users must be unique");
const users = [
{ name: "Zohaib Irshad", email: "[email protected]", age: 25 },
{ name: "John Doe", email: "[email protected]", age: 30 },
];
const result = usersValidator.validate(users);Schema Reusability
import { schema, string, number } from "light-validate";
// Reusable address schema
const addressSchema = schema({
street: string().required(),
city: string().required(),
zipCode: string().postalCode("US").required(),
country: string().countryCode().required(),
});
// User schema using address
const userSchema = schema({
name: string().required(),
email: string().email().required(),
address: addressSchema, // Nested schema
billingAddress: addressSchema, // Reuse same schema
});
// Company schema
const companySchema = schema({
name: string().required(),
email: string().email().required(),
employees: number().min(1).required(),
headquarters: addressSchema, // Reuse address schema
});Validation Types
String Validators
Basic String Validation
import { string } from "light-validate";
const validator = string()
.required("Name is required")
.min(2, "Minimum 2 characters")
.max(50, "Maximum 50 characters");
// Custom regex
const codeValidator = string().regex(/^[A-Z]{2,3}-\d{3,6}$/, "Invalid format");Email & Communication
// Email validation
const emailValidator = string().email("Invalid email address");
// URL validation
const urlValidator = string().url("Must be a valid URL");
// Domain validation (without protocol)
const domainValidator = string().domain("Invalid domain name");Geographic & Location
// IP address (IPv4 & IPv6)
const ipValidator = string().ip("Invalid IP address");
// Latitude/Longitude coordinates
const coordsValidator = string().latLng("Invalid coordinates (lat,lng)");
// Postal codes (190+ countries supported)
const zipValidator = string().postalCode("US", "Invalid US zip code");
const pkZipValidator = string().postalCode(
"PK",
"Invalid Pakistan postal code"
);
// Country codes (ISO 2 or 3 letter)
const countryValidator = string().countryCode("Invalid country code");
// Language codes (ISO 639)
const langValidator = string().languageCode("Invalid language code");
// Timezone validation
const timezoneValidator = string().timezone("Invalid timezone");Technical & Development
// Username validation (no spaces, only _ or . allowed)
const usernameValidator = string().username("Invalid username format");
// Environment variable names
const envVarValidator = string().envVar("Invalid environment variable");
// MAC address validation
const macValidator = string().mac("Invalid MAC address");
// Hexadecimal validation
const hexValidator = string().hex("Invalid hex value");
// Base64 validation
const base64Validator = string().base64("Invalid Base64 string");
// JWT token validation
const jwtValidator = string().jwt("Invalid JWT token");
// Port number validation (1-65535)
const portValidator = string().port("Invalid port number");
// Cron expression validation
const cronValidator = string().cron("Invalid cron expression");Identification & Security
// UUID validation
const uuidValidator = string().uuid("Invalid UUID format");
// API key validation (32+ characters)
const apiKeyValidator = string().apiKey("Invalid API key format");
// OTP validation (customizable length)
const otpValidator = string().otp(6, "Invalid 6-digit OTP");
// Hash validation (MD5, SHA1, SHA256, SHA512)
const hashValidator = string().hash("sha256", "Invalid SHA256 hash");
// Credit card validation (with Luhn algorithm)
const cardValidator = string().creditCard("Invalid credit card number");
// IBAN validation
const ibanValidator = string().iban("Invalid IBAN");Time & Format
// Time validation (HH:mm format)
const timeValidator = string().time("Invalid time format");
// Slug validation (URL-friendly strings)
const slugValidator = string().slug("Invalid slug format");
// Currency codes (ISO 4217)
const currencyValidator = string().currency("Invalid currency code");Security & Safety
// Safe string validation (XSS/SQL injection protection)
const safeValidator = string().safeString("Contains unsafe content");Number Validators
import { number } from "light-validate";
const ageValidator = number()
.required("Age is required")
.min(0, "Age cannot be negative")
.max(120, "Age cannot exceed 120");
const priceValidator = number()
.min(0, "Price must be positive")
.custom((val) =>
val % 0.01 === 0 ? null : "Price must have max 2 decimal places"
);Boolean Validators
import { boolean } from "light-validate";
const termsValidator = boolean()
.required("Must accept terms")
.custom((val) => (val === true ? null : "Terms must be accepted"));Array Validators
import { array, string, number } from "light-validate";
// Basic array validation
const tagsValidator = array()
.min(1, "At least one tag required")
.max(10, "Maximum 10 tags allowed")
.of(string().min(2))
.unique("Tags must be unique");
// Array of numbers
const scoresValidator = array()
.of(number().min(0).max(100))
.min(3, "Minimum 3 scores required");
// Array of objects
const productsValidator = array().arrayOf({
name: string().required(),
price: number().min(0).required(),
category: string().required(),
});File Validators
import { file } from "light-validate";
// Image validation
const avatarValidator = file()
.images("Only image files allowed")
.maxSizeMB(5, "File too large")
.required("Avatar is required");
// Document validation
const docValidator = file()
.documents("Only document files allowed")
.maxSizeKB(500, "Document too large");
// Custom file validation
const customValidator = file()
.mimeTypes(["application/pdf", "text/plain"])
.extensions([".pdf", ".txt"])
.maxSizeBytes(1024 * 1024); // 1MBEnum Validators
import { enum as enumValidator } from "light-validate";
// String enum
const statusValidator = enumValidator(["active", "inactive", "pending"]);
// Number enum
const priorityValidator = enumValidator([1, 2, 3, 4, 5]);
// Mixed enum
const mixedValidator = enumValidator(["low", "medium", "high", 1, 2, 3]);Date Range Validators
import { dateRange } from "light-validate";
// ISO date format (YYYY-MM-DD)
const isoRangeValidator = dateRange({
dateFormat: "ISO",
allowSameDate: false,
});
// US date format (MM/DD/YYYY)
const usRangeValidator = dateRange({
dateFormat: "US",
allowSameDate: true,
});
// EU date format (DD/MM/YYYY)
const euRangeValidator = dateRange({
dateFormat: "EU",
});
const result = isoRangeValidator.validate({
start: "2025-01-01",
end: "2025-12-31",
});name: "User Name", email: "[email protected]", age: 22, status: "active", });
console.log(result); // { success: true }
### Node.js/CommonJS
```javascript
const {
object,
string,
number,
enum: enumValidator,
} = require("light-validate");
const schema = object({
name: string().required().min(3),
email: string().email(),
age: number().min(18),
status: enumValidator(["active", "inactive"]).required(),
});
const result = schema.validate({
name: "User Name",
email: "[email protected]",
age: 22,
status: "active",
});
console.log(result);
// { success: true }API Reference
Basic Types
string()
Creates a string validator with comprehensive validation options.
import { string } from "light-validate";
const validator = string()
.required() // Field is required
.min(3) // Minimum length
.max(50) // Maximum length
.email() // Valid email format
.url() // Valid URL (http/https)
.ip() // Valid IP address (IPv4/IPv6)
.uuid() // Valid UUID v4 format
.time() // Valid time format (HH:mm)
.slug() // Valid slug (alphanumeric, dash, underscore)
.creditCard() // Valid credit card (Luhn algorithm)
.iban() // Valid IBAN format
.safeString() // XSS/SQL injection protection
.regex(/^[A-Z0-9]+$/) // Custom regex pattern
.postalCode("PK") // Postal code for specific country
.custom((value) => {
// Custom validation
return value.includes("@") ? null : "Must contain @";
});
// Advanced string validations examples:
// URL validation
string().url().validate("https://example.com"); // ✅
string().url().validate("invalid-url"); // ❌
// IP address validation
string().ip().validate("192.168.0.1"); // ✅ IPv4
string().ip().validate("2001:db8::1"); // ✅ IPv6
// UUID validation
string().uuid().validate("550e8400-e29b-41d4-a716-446655440000"); // ✅
// Time validation
string().time().validate("14:30"); // ✅
string().time().validate("25:30"); // ❌
// Slug validation
string().slug().validate("zohaib_irshad"); // ✅
string().slug().validate("invalid slug"); // ❌
// Credit card validation (Luhn algorithm)
string().creditCard().validate("4111111111111111"); // ✅ Visa test card
// IBAN validation
string().iban().validate("PK36SCBL0000001123456702"); // ✅
// Safe string (XSS/SQL injection protection)
string().safeString().validate("Safe user input"); // ✅
string().safeString().validate("<script>alert(1)</script>"); // ❌
// Custom regex
string()
.regex(/^[A-Z0-9]+$/)
.validate("ABC123"); // ✅
// Postal codes for different countries
string().postalCode("US").validate("12345"); // ✅ US ZIP
string().postalCode("PK").validate("54000"); // ✅ Pakistan
string().postalCode("UK").validate("SW1A 1AA"); // ✅ UK postcode
string().postalCode("CA").validate("K1A 0A6"); // ✅ Canadanumber()
Creates a number validator.
import { number } from "light-validate";
const validator = number()
.required() // Field is required
.min(0) // Minimum value
.max(100) // Maximum value
.custom((value) => {
// Custom validation
return value % 2 === 0 ? null : "Must be even";
});boolean()
Creates a boolean validator.
import { boolean } from "light-validate";
const validator = boolean()
.required() // Field is required
.custom((value) => {
// Custom validation
return value === true ? null : "Must be true";
});object(schema)
Creates an object validator with a schema.
import { object, string, number, array } from "light-validate";
const validator = object({
name: string().required(),
age: number().min(0),
})
.required() // Object itself is required
.custom((obj) => {
// Custom validation on entire object
return obj.name && obj.age ? null : "Invalid user";
});
// Advanced Object Validations
// Non-empty object validation
object({
name: string(),
email: string()
})
.notEmpty() // Object cannot be empty
.validate({ name: 'John' }); // ✅
// Required keys validation
object({
name: string(),
email: string(),
age: number()
})
.hasKeys(['name', 'email']) // These keys must exist
.validate({ name: 'John', email: '[email protected]', age: 30 }); // ✅
// Strict keys validation (no unexpected keys)
object({
name: string(),
age: number()
})
.strictKeys() // Only defined keys allowed
.validate({ name: 'John', age: 30 }); // ✅
.validate({ name: 'John', age: 30, email: 'extra' }); // ❌ (unexpected key)
// Deep nested validation using dot notation
object({
user: object({
profile: object({
name: string()
})
})
})
.deepKey('user.profile.name', string().min(2))
.validate({
user: {
profile: {
name: 'John'
}
}
}); // ✅
// Array contains validation (validate arrays inside objects)
object({
tags: array(),
categories: array()
})
.arrayContains('tags', 'javascript') // tags array must contain 'javascript'
.validate({
tags: ['javascript', 'typescript'],
categories: ['frontend']
}); // ✅
// Plain object validation (reject Date, RegExp, etc.)
object({
data: string()
})
.plainObject() // Must be a plain object, not Date/RegExp/etc
.validate({ data: 'value' }); // ✅
.validate(new Date()); // ❌ (not a plain object)
// Combined advanced validations
object({
name: string().required(),
email: string().email().required(),
age: number().min(18),
tags: array()
})
.notEmpty()
.hasKeys(['name', 'email'])
.strictKeys()
.plainObject()
.arrayContains('tags', 'verified')
.validate({
name: 'John',
email: '[email protected]',
age: 25,
tags: ['verified', 'user']
}); // ✅ (passes all validations)array()
Creates an array validator with advanced validation options.
import { array, string, number } from "light-validate";
const validator = array()
.required() // Array is required
.min(1) // Minimum number of items
.max(10) // Maximum number of items
.unique() // All items must be unique
.of(string()) // Validate each item as string
.custom((arr) => {
// Custom validation
return arr.length > 0 ? null : "Cannot be empty";
});
// Array validation examples:
// Basic array with length constraints
array().min(1).max(5).validate([1, 2, 3]); // ✅
// Unique items validation
array().unique().validate([1, 2, 3, 4]); // ✅
array().unique().validate([1, 2, 2, 3]); // ❌ (duplicate items)
// Array of validated items
array().of(string().email()).validate(["[email protected]", "[email protected]"]); // ✅
// Complex array validation
array()
.min(2, "At least 2 items required")
.max(5, "Maximum 5 items allowed")
.unique("All items must be unique")
.of(
string()
.min(3, "Each item must be at least 3 characters")
.slug("Each item must be a valid slug")
)
.validate(["user_1", "admin_2", "guest_3"]); // ✅
// Advanced Array Validations
// Sorted array validation
array()
.of(number())
.sorted() // Must be sorted in ascending order
.validate([1, 2, 3, 4]); // ✅
array()
.of(number())
.sorted("desc") // Must be sorted in descending order
.validate([4, 3, 2, 1]); // ✅
// Type consistency validation
array()
.ofType("string") // All items must be strings
.validate(["a", "b", "c"]); // ✅
array()
.ofType("number") // All items must be numbers
.validate([1, 2, 3]); // ✅
// Exact length validation
array()
.length(3) // Must have exactly 3 items
.validate([1, 2, 3]); // ✅
// Non-empty validation
array()
.notEmpty() // Cannot be empty
.validate([1]); // ✅
// Contains specific value
array()
.contains("required-item") // Must contain this value
.validate(["a", "required-item", "c"]); // ✅
// Does not contain specific value
array()
.notContains("forbidden") // Must not contain this value
.validate(["a", "b", "c"]); // ✅
// Combined advanced validations
array()
.of(number())
.notEmpty()
.length(3)
.sorted()
.contains(2)
.validate([1, 2, 3]); // ✅ (exactly 3 numbers, sorted, contains 2)enum(allowedValues)
Creates an enum validator for predefined values.
import { enum as enumValidator } from "light-validate";
// String enum validation
const statusValidator = enumValidator([
"active",
"inactive",
"pending",
] as const)
.required() // Enum value is required
.custom((value) => {
// Custom validation
return value === "inactive" ? "Inactive status not allowed" : null;
});
// Number enum validation
const priorityValidator = enumValidator([1, 2, 3, 4, 5] as const);
// TypeScript enum support
enum UserRole {
ADMIN = "admin",
USER = "user",
GUEST = "guest",
}
const roleValidator = enumValidator(Object.values(UserRole));
// Mixed enum (string + number)
const mixedValidator = enumValidator([
"low",
1,
"medium",
2,
"high",
3,
] as const);dateRange(options)
Creates a date range validator to ensure start date is before end date.
import { dateRange } from "light-validate";
// Basic date range validation (ISO format: YYYY-MM-DD)
const basicRange = dateRange().required().validate({
start: "2025-01-01",
end: "2025-12-31",
}); // ✅
// Date range with options
const flexibleRange = dateRange({
allowSameDate: true, // Allow start = end
dateFormat: "US", // MM/DD/YYYY format
});
// Different date formats supported:
const isoRange = dateRange({ dateFormat: "ISO" }); // YYYY-MM-DD
const usRange = dateRange({ dateFormat: "US" }); // MM/DD/YYYY
const euRange = dateRange({ dateFormat: "EU" }); // DD/MM/YYYY
// Usage examples:
dateRange().validate({
start: "2025-01-01",
end: "2025-12-31",
}); // ✅ Valid range
dateRange().validate({
start: "2025-12-31",
end: "2025-01-01",
}); // ❌ Start after end
dateRange({ allowSameDate: true }).validate({
start: "2025-06-15",
end: "2025-06-15",
}); // ✅ Same date allowed
// Custom validation on date ranges
dateRange()
.custom((range) => {
const start = new Date(range.start);
const end = new Date(range.end);
const diffDays = (end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24);
return diffDays <= 365 ? null : "Date range cannot exceed 1 year";
})
.validate({ start: "2025-01-01", end: "2025-12-31" }); // ✅file()
Creates a file validator for validating File objects or file-like data.
import { file } from "light-validate";
const validator = file()
.required() // File is required
.images() // Accept only image files
.maxSizeMB(5) // Maximum 5MB
.minSizeKB(10) // Minimum 10KB
.extensions(["jpg", "png"]) // Specific extensions
.types("IMAGE", "DOCUMENT") // File type categories
.mimeTypes(["image/jpeg", "image/png"]) // Specific MIME types
.custom((fileData) => {
// Custom validation (e.g., filename patterns)
return fileData.name.includes("profile") ? null : "Must be a profile image";
});
// File type categories available:
// IMAGE, AUDIO, VIDEO, DOCUMENT, TEXT, ARCHIVE, CODE
// File size methods:
file().maxSize(1024); // Maximum size in bytes
file().minSize(512); // Minimum size in bytes
file().maxSizeKB(500); // Maximum in kilobytes
file().minSizeKB(10); // Minimum in kilobytes
file().maxSizeMB(5); // Maximum in megabytes
file().minSizeMB(1); // Minimum in megabytes
// Multiple file type validation:
file().types("IMAGE", "DOCUMENT"); // Accept images and documents
file().extensions(["pdf", "jpg", "png"]); // Specific extensions
file().mimeTypes(["application/pdf", "image/jpeg"]); // MIME types
// Convenience methods for common file types:
file().images(); // Accept images only
file().audio(); // Accept audio files only
file().video(); // Accept video files only
file().documents(); // Accept documents only
file().textFiles(); // Accept text files only
file().archives(); // Accept archive files only
file().codeFiles(); // Accept code files only
// Note: Image dimension validation (width/height) can be implemented
// using custom validation with image processing librariesCustom Error Messages
All validators support custom error messages for internationalization and better UX:
import { string, number, object } from "light-validate";
// Individual custom messages
const nameValidator = string()
.required("نام ضروری ہے") // Name is required (Urdu)
.min(3, "کم از کم 3 حروف") // At least 3 characters (Urdu)
.email("صحیح ای میل درج کریں"); // Enter valid email (Urdu)
// Bulk custom messages using messages() method
const ageValidator = number()
.messages({
required: "Age is mandatory",
type: "Must be a valid number",
min: "Too young for this service",
max: "Age limit exceeded",
})
.required()
.min(18)
.max(65);
// Custom validation with custom error message
const passwordValidator = string().custom(
(password) =>
/^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])/.test(password) ? null : "Invalid",
"پاس ورڈ میں کم از کم ایک بڑا حرف، چھوٹا حرف اور نمبر ہونا چاہیے"
);Validation Methods
All validators support these methods:
.required()- Makes the field required (fails on null/undefined).custom(validator)- Adds a custom validation function
Type-specific methods:
- String:
.min(length),.max(length),.email(),.url(),.ip(),.uuid(),.time(),.slug(),.creditCard(),.iban(),.safeString(),.regex(pattern),.postalCode(country) - Number:
.min(value),.max(value) - Boolean: No additional methods beyond base methods
- Array:
.min(length),.max(length),.of(validator),.unique() - Enum: Validates against predefined allowed values
- File:
.maxSizeMB(),.extensions(),.mimeTypes(),.images(),.types(), etc. - DateRange: Date format options (
ISO,US,EU),.allowSameDateoption
Validation Result
All validators return a ValidationResult:
interface ValidationResult {
success: boolean;
errors?: string[];
}Example:
// Success
{ success: true }
// Failure
{
success: false,
errors: ["This field is required", "Must be at least 3 characters"]
}Examples
File Upload Validation
import { object, string, file, array } from "light-validate";
// Single file validation with comprehensive checks
const profileSchema = object({
name: string().required("نام ضروری ہے").min(2), // Urdu error message
avatar: file()
.required("تصویر اپ لوڈ کریں") // Upload image (Urdu)
.images() // Only image files
.maxSizeMB(5) // Maximum 5MB
.minSizeKB(10) // Minimum 10KB
.extensions(["jpg", "jpeg", "png"]) // Specific extensions
.mimeTypes(["image/jpeg", "image/png"]) // MIME type validation
.custom((fileData) => {
return fileData.name.includes("profile")
? null
: 'Filename must contain "profile"';
}),
});
// Multiple files validation with different types
const documentSchema = object({
title: string().required().min(5),
attachments: array()
.required()
.min(1, "At least one document required")
.max(5, "Maximum 5 documents allowed")
.of(
file()
.types("DOCUMENT", "TEXT") // PDF, DOC, CSV, etc.
.maxSizeMB(10) // Each file max 10MB
.mimeTypes([
"application/pdf",
"text/csv",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
])
),
// Mixed media files
mediaFiles: array()
.of(
file()
.types("IMAGE", "VIDEO", "AUDIO") // Multiple categories
.maxSizeMB(50) // Larger limit for media
.custom((fileData) => {
// Custom business rule
if (
fileData.size > 20 * 1024 * 1024 &&
fileData.name.includes("preview")
) {
return "Preview files should be under 20MB";
}
return null;
})
)
.max(10, "Maximum 10 media files"),
});
// Advanced file validation with size and type checks
const advancedFileSchema = object({
documents: array().of(
file()
.documents() // Document files only
.maxSizeMB(25)
.extensions(["pdf", "docx", "xlsx"])
),
images: array().of(
file()
.images() // Image files only
.maxSizeMB(5)
.minSizeKB(50) // Prevent tiny/corrupt images
.mimeTypes(["image/jpeg", "image/png", "image/webp"])
),
archives: array().of(
file()
.archives() // ZIP, RAR, etc.
.maxSizeMB(100)
.extensions(["zip", "rar", "7z"])
),
});
// Usage with File objects from input[type="file"]
const fileInput = document.getElementById("upload") as HTMLInputElement;
const files = fileInput.files;
if (files && files.length > 0) {
const result = profileSchema.validate({
name: "احمد علی", // Urdu name
avatar: files[0], // File object
});
console.log(result);
}Multi-language Error Messages
import { object, string, number } from "light-validate";
// English version
const englishSchema = object({
email: string()
.required("Email is required")
.email("Please enter a valid email address"),
age: number()
.required("Age is required")
.min(18, "You must be at least 18 years old"),
});
// Urdu version with same validation logic
const urduSchema = object({
email: string()
.required("ای میل ایڈریس ضروری ہے")
.email("صحیح ای میل ایڈریس درج کریں"),
age: number()
.required("عمر ضروری ہے")
.min(18, "آپ کی عمر کم از کم 18 سال ہونی چاہیے"),
});
// Test data
const userData = { email: "invalid", age: 16 };
console.log("English:", englishSchema.validate(userData));
// English: { success: false, errors: ['email: Please enter a valid email address', 'age: You must be at least 18 years old'] }
console.log("Urdu:", urduSchema.validate(userData));
// Urdu: { success: false, errors: ['email: صحیح ای میل ایڈریس درج کریں', 'age: آپ کی عمر کم از کم 18 سال ہونی چاہیے'] }Custom Business Rules
import { object, string, number, file } from "light-validate";
// E-commerce product validation with business rules
const productSchema = object({
title: string()
.required("Product name is required")
.min(5, "Product name too short")
.custom((title) => {
// Business rule: No spam words allowed
const spamWords = ["free", "urgent", "limited"];
const hasSpam = spamWords.some((word) =>
title.toLowerCase().includes(word)
);
return hasSpam ? "Product name contains prohibited words" : null;
}),
price: number()
.required("Price is required")
.min(1, "Price must be positive")
.custom((price) => {
// Business rule: Price should be reasonable
if (price > 10000 && price % 1000 === 0) {
return "Price seems too rounded - please set market rate";
}
return null;
}),
images: array()
.required("Product images required")
.min(1, "At least one image required")
.max(10, "Maximum 10 images allowed")
.of(
file()
.images()
.maxSizeMB(5)
.custom((fileData) => {
// Business rule: Images must follow naming convention
return fileData.name.includes("product_")
? null
: 'Image filename must start with "product_"';
})
),
});Advanced String & Array Validations
import { object, string, array, dateRange } from "light-validate";
// Comprehensive user profile validation
const userProfileSchema = object({
// Personal Information
username: string()
.required("صارف نام ضروری ہے") // Username required (Urdu)
.slug("صرف حروف، نمبرز، ڈیش اور انڈر سکور") // Only letters, numbers, dash, underscore (Urdu)
.min(3, "کم از کم 3 حروف")
.max(20, "زیادہ سے زیادہ 20 حروف"),
email: string().required("ای میل ضروری ہے").email("صحیح ای میل درج کریں"),
website: string()
.url("صحیح ویب سائٹ کا لنک درج کریں")
.custom((url) => {
// Business rule: Only allow HTTPS
return url.startsWith("https://") ? null : "صرف HTTPS لنکس کی اجازت ہے";
}),
phone: string().regex(
/^\+92[0-9]{10}$/,
"پاکستانی فون نمبر درج کریں (+92xxxxxxxxxx)"
),
// Security
ipAddress: string().ip("صحیح IP ایڈریس درج کریں"),
userId: string().uuid("صحیح UUID فارمیٹ درج کریں"),
// Financial
creditCard: string().creditCard("صحیح کریڈٹ کارڈ نمبر درج کریں"),
iban: string().iban("صحیح IBAN نمبر درج کریں"),
// Address
postalCode: string().postalCode("PK", "پاکستان کا صحیح پوسٹل کوڈ درج کریں"),
// Content Security
bio: string()
.safeString("غیر محفوظ مواد کی اجازت نہیں")
.max(500, "تعارف 500 حروف سے زیادہ نہیں ہو سکتا"),
// Time & Schedule
workingHours: object({
start: string().time("صحیح وقت فارمیٹ (HH:mm)"),
end: string().time("صحیح وقت فارمیٹ (HH:mm)"),
}),
// Project dates
projectDuration: dateRange({
dateFormat: "ISO",
allowSameDate: false,
})
.required("منصوبے کی تاریخ ضروری ہے")
.custom((range) => {
const start = new Date(range.start);
const end = new Date(range.end);
const diffMonths =
(end.getFullYear() - start.getFullYear()) * 12 +
(end.getMonth() - start.getMonth());
return diffMonths <= 24 ? null : "منصوبہ 2 سال سے زیادہ نہیں ہو سکتا";
}),
// Skills array with unique validation
skills: array()
.required("کم از کم ایک مہارت ضروری ہے")
.min(1, "کم از کم ایک مہارت")
.max(10, "زیادہ سے زیادہ 10 مہارتیں")
.unique("تمام مہارتیں منفرد ہونی چاہیے")
.of(
string()
.min(2, "مہارت کم از کم 2 حروف")
.slug("مہارت میں صرف حروف، نمبرز، ڈیش")
.custom((skill) => {
const forbiddenSkills = ["hacking", "spam", "fraud"];
return forbiddenSkills.includes(skill.toLowerCase())
? "یہ مہارت قابل قبول نہیں"
: null;
})
),
// Languages with proficiency
languages: array()
.min(1, "کم از کم ایک زبان")
.of(
object({
language: enumValidator([
"en",
"ur",
"ar",
"es",
"fr",
"de",
] as const).required("زبان کا انتخاب ضروری"),
proficiency: enumValidator([
"beginner",
"intermediate",
"advanced",
"native",
] as const).required("مہارت کی سطح"),
})
)
.unique("ہر زبان صرف ایک بار"),
});
// API validation example
const apiRequestSchema = object({
// Request validation
apiKey: string()
.required("API کلید ضروری")
.regex(/^[A-Za-z0-9]{32}$/, "API کلید 32 حروف کی ہونی چاہیے"),
callback: string()
.url("صحیح callback URL")
.custom((url) => {
// Security: Only allow HTTPS callbacks
return url.startsWith("https://") ? null : "Callback URL must use HTTPS";
}),
serverIp: string().required("سرور IP ضروری").ip("صحیح IP address"),
requestId: string().uuid("صحیح request UUID"),
// Data validation
payload: object({
data: string()
.safeString("غیر محفوظ ڈیٹا کی اجازت نہیں")
.max(10000, "ڈیٹا 10KB سے زیادہ نہیں"),
checksum: string().regex(/^[a-f0-9]{64}$/, "صحیح SHA-256 checksum"),
}),
});
// E-commerce validation
const productSchema = object({
sku: string()
.required("SKU ضروری")
.regex(/^[A-Z0-9-]{6,20}$/, "SKU: 6-20 حروف، بڑے حروف، نمبرز، ڈیش"),
pricing: object({
currency: enumValidator(["PKR", "USD", "EUR", "GBP"] as const).required(
"کرنسی کا انتخاب"
),
amount: number()
.min(0.01, "کم از کم 0.01")
.max(999999.99, "زیادہ سے زیادہ 999999.99"),
}),
availability: dateRange({
dateFormat: "ISO",
}).required("دستیابی کی تاریخ"),
tags: array()
.max(20, "زیادہ سے زیادہ 20 ٹیگز")
.unique("تمام ٹیگز منفرد")
.of(
string()
.slug("ٹیگ میں صرف حروف، نمبرز، ڈیش")
.min(2, "کم از کم 2 حروف")
.max(30, "زیادہ سے زیادہ 30 حروف")
),
});
// Usage examples
const profileResult = userProfileSchema.validate({
username: "zohaib_dev",
email: "[email protected]",
website: "https://zohaib.dev",
phone: "+923001234567",
skills: ["javascript", "typescript", "react", "node"],
languages: [
{ language: "en", proficiency: "native" },
{ language: "ur", proficiency: "native" },
],
projectDuration: {
start: "2025-01-01",
end: "2025-06-30",
},
});Enum Validation Examples
import { object, string, enum as enumValidator } from "light-validate";
// Basic enum validation
const orderSchema = object({
status: enumValidator([
"pending",
"processing",
"shipped",
"delivered",
] as const).required("Order status is required"),
priority: enumValidator([1, 2, 3, 4, 5] as const).required(
"Priority level is required"
),
});
// TypeScript enum support
enum PaymentMethod {
CREDIT_CARD = "credit_card",
DEBIT_CARD = "debit_card",
PAYPAL = "paypal",
BANK_TRANSFER = "bank_transfer",
}
enum SubscriptionTier {
FREE = 0,
BASIC = 1,
PREMIUM = 2,
ENTERPRISE = 3,
}
const subscriptionSchema = object({
paymentMethod: enumValidator(Object.values(PaymentMethod)).required(
"Payment method is required"
),
tier: enumValidator([
SubscriptionTier.FREE,
SubscriptionTier.BASIC,
SubscriptionTier.PREMIUM,
SubscriptionTier.ENTERPRISE,
])
.required("Subscription tier is required")
.custom((tier) => {
// Business rule: Free tier has limitations
if (tier === SubscriptionTier.FREE) {
return "Free tier signup is temporarily disabled";
}
return null;
}),
});
// Multi-language enum with custom messages
const languageSchema = object({
language: enumValidator(["en", "ur", "ar", "es", "fr"] as const)
.required("زبان کا انتخاب ضروری ہے") // Language selection required (Urdu)
.messages({
enum: "صرف مدد شدہ زبانیں منتخب کریں: انگریزی، اردو، عربی، ہسپانوی، فرانسیسی", // Only supported languages (Urdu)
}),
region: enumValidator(["US", "UK", "PK", "IN", "CA"] as const)
.required()
.custom((region) => {
return region === "PK"
? null
: "Service currently available in Pakistan only";
}),
});
// Usage examples
const result1 = orderSchema.validate({
status: "shipped",
priority: 3,
});
const result2 = subscriptionSchema.validate({
paymentMethod: PaymentMethod.CREDIT_CARD,
tier: SubscriptionTier.PREMIUM,
});
const result3 = languageSchema.validate({
language: "ur",
region: "PK",
});Advanced Array and Object Validations
The library provides sophisticated validation capabilities for complex data structures with chainable APIs.
Advanced Array Validations
import { array, string, number } from "light-validate";
// Sorted array validation
const sortedNumbers = array()
.of(number())
.sorted() // Ascending order
.notEmpty()
.validate([1, 2, 3, 4, 5]); // ✅
const sortedDescending = array()
.of(number())
.sorted("desc") // Descending order
.validate([5, 4, 3, 2, 1]); // ✅
// Type consistency validation
const stringArray = array()
.ofType("string") // All items must be strings
.notEmpty()
.validate(["apple", "banana", "cherry"]); // ✅
const numberArray = array()
.ofType("number") // All items must be numbers
.min(3) // At least 3 items
.max(10) // At most 10 items
.validate([1, 2, 3, 4, 5]); // ✅
// Exact length and content validation
const exactArray = array()
.length(3) // Exactly 3 items
.contains("required") // Must contain 'required'
.notContains("forbidden") // Must not contain 'forbidden'
.validate(["item1", "required", "item3"]); // ✅
// Complex array validation combining multiple rules
const advancedArray = array()
.of(string().min(2).max(20))
.notEmpty()
.length(5)
.sorted()
.ofType("string")
.contains("admin")
.unique()
.validate(["admin", "guest", "moderator", "user", "visitor"]); // ✅Advanced Object Validations
import { object, string, number, array } from "light-validate";
// Non-empty and required keys validation
const userProfile = object({
name: string().required(),
email: string().email(),
age: number(),
skills: array(),
})
.notEmpty() // Object cannot be empty
.hasKeys(["name", "email"]) // These keys must exist
.validate({
name: "John Doe",
email: "[email protected]",
age: 30,
skills: ["JavaScript", "TypeScript"],
}); // ✅
// Strict keys validation (no unexpected properties)
const strictUser = object({
username: string().required(),
password: string().required(),
})
.hasKeys(["username", "password"])
.strictKeys() // Only defined keys allowed
.validate({
username: "johndoe",
password: "secret123",
// email: '[email protected]' // ❌ Would fail - unexpected key
}); // ✅
// Deep nested property validation using dot notation
const nestedValidation = object({
user: object({
profile: object({
personal: object({
name: string(),
contact: object({
email: string(),
phone: string(),
}),
}),
}),
settings: object({
preferences: array(),
}),
}),
})
.deepKey("user.profile.personal.name", string().min(2).required())
.deepKey("user.profile.personal.contact.email", string().email())
.deepKey("user.settings.preferences", array().notEmpty())
.validate({
user: {
profile: {
personal: {
name: "John",
contact: {
email: "[email protected]",
phone: "+1234567890",
},
},
},
settings: {
preferences: ["dark-mode", "notifications"],
},
},
}); // ✅
// Array contains validation within objects
const projectValidation = object({
name: string().required(),
technologies: array(),
categories: array(),
team: array(),
})
.arrayContains("technologies", "javascript") // Must use JavaScript
.arrayContains("categories", "web") // Must be web category
.arrayContains("team", "developer") // Must have a developer
.validate({
name: "My Web App",
technologies: ["javascript", "typescript", "react"],
categories: ["web", "frontend"],
team: ["developer", "designer", "manager"],
}); // ✅
// Plain object validation (reject special objects)
const plainObjectValidation = object({
data: string(),
metadata: object({}),
})
.plainObject() // Must be plain object, not Date/RegExp/etc
.validate({
data: "sample",
metadata: { version: "1.0" },
}); // ✅
// .validate(new Date()); // ❌ Would fail - not a plain object
// Enterprise-level validation combining all features
const enterpriseUserSchema = object({
profile: object({
personalInfo: object({
firstName: string().required(),
lastName: string().required(),
email: string().email().required(),
}),
professionalInfo: object({
skills: array(),
experience: number(),
certifications: array(),
}),
}),
account: object({
permissions: array(),
roles: array(),
status: string(),
}),
preferences: object({
notifications: array(),
privacy: object({}),
}),
})
.notEmpty()
.hasKeys(["profile", "account"])
.strictKeys()
.plainObject()
.deepKey("profile.personalInfo.firstName", string().min(2))
.deepKey("profile.personalInfo.lastName", string().min(2))
.deepKey(
"profile.professionalInfo.skills",
array().notEmpty().ofType("string")
)
.deepKey("account.permissions", array().notEmpty())
.arrayContains("account.roles", "user")
.arrayContains("preferences.notifications", "email")
.validate({
profile: {
personalInfo: {
firstName: "John",
lastName: "Doe",
email: "[email protected]",
},
professionalInfo: {
skills: ["JavaScript", "TypeScript", "React"],
experience: 5,
certifications: ["AWS", "Google Cloud"],
},
},
account: {
permissions: ["read", "write", "delete"],
roles: ["user", "admin"],
status: "active",
},
preferences: {
notifications: ["email", "push", "sms"],
privacy: { shareProfile: false },
},
}); // ✅ Passes all validationsUser Registration Validation
import { object, string, number, boolean } from "light-validate";
const userSchema = object({
username: string().required().min(3).max(20),
email: string().required().email(),
password: string()
.required()
.min(8)
.custom((value) => {
if (!/[A-Z]/.test(value)) return "Must contain uppercase letter";
if (!/[a-z]/.test(value)) return "Must contain lowercase letter";
if (!/[0-9]/.test(value)) return "Must contain number";
return null;
}),
age: number().required().min(13),
acceptTerms: boolean()
.required()
.custom((value) => {
return value === true ? null : "Must accept terms";
}),
});
const result = userSchema.validate({
username: "john_doe",
email: "[email protected]",
password: "SecurePass123",
age: 25,
acceptTerms: true,
});Nested Object Validation
import { object, string, array } from "light-validate";
const companySchema = object({
name: string().required(),
employees: array()
.of(
object({
name: string().required(),
email: string().email(),
department: string().required(),
})
)
.min(1),
address: object({
street: string().required(),
city: string().required(),
zipCode: string().min(5).max(10),
}).required(),
});Tree-Shaking (Import Individual Validators)
// Import only what you need for smaller bundle size
import { string as createStringValidator } from "light-validate";
import { number as createNumberValidator } from "light-validate";
const nameValidator = createStringValidator().required().min(2);
const ageValidator = createNumberValidator().min(0).max(120);TypeScript Support
The library is written in TypeScript and provides excellent type inference:
import { object, string, number } from "light-validate";
// TypeScript will infer the correct types
const schema = object({
name: string().required(),
age: number(),
});
// result is typed as ValidationResult
const result = schema.validate(someData);
if (result.success) {
// No errors
console.log("Validation passed!");
} else {
// result.errors is string[]
console.log("Errors:", result.errors);
}Error Messages
The library provides clear, descriptive error messages:
const schema = object({
name: string().required().min(3),
age: number().min(18),
});
const result = schema.validate({ name: "Jo", age: 15 });
console.log(result.errors);
// [
// "name: Must be at least 3 characters long",
// "age: Must be at least 18"
// ]Bundle Size
Light Validate is designed to be minimal:
- Core library: ~3KB minified
- Tree-shakeable: Import only what you use
- Zero dependencies: No external libraries
Comparison with Other Libraries
| Feature | Light Validate | Yup | Joi | Zod | | -------------- | -------------- | ------- | ------- | ------ | | Bundle Size | ~3KB | ~65KB | ~145KB | ~30KB | | Dependencies | 0 | Several | Several | 0 | | Tree Shaking | ✅ | ❌ | ❌ | ✅ | | TypeScript | ✅ | ✅ | ❌ | ✅ | | Learning Curve | Easy | Medium | Medium | Medium |
License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
