validation-box
Version:
The only validation library - with flexible regex - you need.
297 lines (293 loc) • 9.43 kB
JavaScript
;
// src/helpers/index.ts
var containsBannedWords = (value, bannedWords) => {
if (!bannedWords || bannedWords.length === 0) return false;
return bannedWords.some(
(word) => value.toLowerCase().includes(word.toLowerCase())
);
};
// src/validators/generics.ts
var validateUsername = (username, options = {}) => {
const errors = [];
const min = options.min ?? 3;
const max = options.max ?? 20;
const specialChars = options.allowSpecialChars ?? "_";
if (username.length < min) {
errors.push(options.messages?.min || `Username must be at least ${min} characters`);
}
if (username.length > max) {
errors.push(options.messages?.max || `Username must be at most ${max} characters`);
}
if (containsBannedWords(username, options.bannedWords)) {
errors.push(options.messages?.bannedWords || "Username contains banned words");
}
if (/^\d+$/.test(username)) {
errors.push(options.messages?.onlyNumbers || "Username cannot contain only numbers");
}
const regex = new RegExp(`^[a-zA-Z0-9${specialChars}]+$`);
if (!regex.test(username)) {
errors.push(options.messages?.invalidFormat || `Username can only contain letters, numbers and ${specialChars}`);
}
return {
valid: errors.length === 0,
errors: errors.length > 0 ? errors : void 0
};
};
var validateUser = (user, options = {}) => {
const errors = [];
const min = options.min ?? 3;
const max = options.max ?? 30;
const specialChars = options.allowSpecialChars ?? "''\\s";
if (user.length < min) {
errors.push(options.messages?.min || `Name must be at least ${min} characters`);
}
if (user.length > max) {
errors.push(options.messages?.max || `Name must be at most ${max} characters`);
}
if (containsBannedWords(user, options.bannedWords)) {
errors.push(options.messages?.bannedWords || "Name contains banned words");
}
if (/^\s*$/.test(user)) {
errors.push(options.messages?.emptySpace || "Name cannot be empty or contain only spaces");
}
const regex = new RegExp(`^[a-zA-Z\xC0-\xD6\xD8-\xF6\xF8-\xFF${specialChars}]+$`);
if (!regex.test(user)) {
errors.push(options.messages?.invalidFormat || `Name can only contain letters and ${specialChars}`);
}
return {
valid: errors.length === 0,
errors: errors.length > 0 ? errors : void 0
};
};
var validateEmail = (email, options = {}) => {
const errors = [];
const emailRegex = /^[a-zA-Z0-9._%+-]+@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$/;
const match = email.match(emailRegex);
if (!match) {
errors.push(options.messages?.invalidFormat || "Invalid email format");
} else if (options.allowedDomains && !options.allowedDomains.includes(match[1])) {
errors.push(options.messages?.allowedDomains || `Email domain must be one of: ${options.allowedDomains.join(", ")}`);
}
return {
valid: errors.length === 0,
errors: errors.length > 0 ? errors : void 0
};
};
var validatePassword = (password, options = {}) => {
const errors = [];
const min = options.min ?? 8;
const max = options.max ?? 100;
const specialChars = options.allowSpecialChars ?? "!@#$%^&*()_+";
if (password.length < min) {
errors.push(options.messages?.min || `Password must be at least ${min} characters`);
}
if (password.length > max) {
errors.push(options.messages?.max || `Password must be at most ${max} characters`);
}
if (containsBannedWords(password, options.bannedWords)) {
errors.push(options.messages?.bannedWords || "Password contains banned words");
}
const regex = new RegExp(
`^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d)(?=.*[${specialChars}])[A-Za-z\\d${specialChars}]+$`
);
if (!regex.test(password)) {
errors.push(options.messages?.invalidFormat || "Password must contain at least one uppercase letter, one lowercase letter, one number and one special character");
}
return {
valid: errors.length === 0,
errors: errors.length > 0 ? errors : void 0
};
};
var validateAge = (age, options = {}) => {
const errors = [];
const min = options.min ?? 18;
const max = options.max ?? 120;
if (!Number.isInteger(age)) {
errors.push(options.messages?.invalidFormat || "Age must be an integer");
}
if (age < min) {
errors.push(options.messages?.min || `Age must be at least ${min} years`);
}
if (age > max) {
errors.push(options.messages?.max || `Age must be at most ${max} years`);
}
return {
valid: errors.length === 0,
errors: errors.length > 0 ? errors : void 0
};
};
// src/schemas/index.ts
var vboxSchema = class _vboxSchema {
rules;
validateAll;
showErrors;
constructor(rules, options) {
this.rules = rules;
this.validateAll = options?.validateAll ?? false;
this.showErrors = options?.showErrors ?? true;
}
transformValue(value, transforms) {
if (!transforms) return value;
return transforms.reduce((acc, transform) => transform(acc), value);
}
validate(data) {
const validatedData = {};
const errors = {};
let isValid = true;
for (const field in this.rules) {
const rule = this.rules[field];
const value = data[field];
const options = rule.options || {};
if (options.required && (value === void 0 || value === null || value === "")) {
isValid = false;
const message = options.messages?.required || `${field} is required`;
errors[field] = [message];
if (!this.validateAll) break;
continue;
}
if (!options.required && (value === void 0 || value === null || value === "")) {
continue;
}
const transformedValue = this.transformValue(value, rule.transform);
const validation = rule.fn(transformedValue, options);
if (validation.valid) {
validatedData[field] = transformedValue;
} else {
isValid = false;
const errorMessages = validation.errors || [];
errors[field] = errorMessages;
if (!this.validateAll) break;
}
}
return {
success: isValid,
data: isValid ? validatedData : void 0,
errors: !isValid && this.showErrors ? errors : void 0
};
}
// Method to add custom validation rule
addRule(field, rule) {
this.rules[field] = rule;
return this;
}
// Method to extend schema with another schema
extend(schema) {
return new _vboxSchema({ ...this.rules, ...schema["rules"] });
}
// Method to resolve validation for form data
resolve(data, returnAllErrors = false) {
const result = this.validate(data);
return {
values: result.success ? data : {},
errors: result.errors ? Object.keys(result.errors).reduce((acc, key) => {
const errorMessages = result.errors?.[key] || [];
acc[key] = {
type: "manual",
messages: returnAllErrors ? errorMessages : [errorMessages[0]]
};
return acc;
}, {}) : {}
};
}
};
var validator = {
username: (options) => ({
fn: validateUsername,
options
}),
user: (options) => ({
fn: validateUser,
options
}),
email: (options) => ({
fn: validateEmail,
options
}),
password: (options) => ({
fn: validatePassword,
options
}),
age: (options) => ({
fn: validateAge,
options
})
};
// src/manual/schema.ts
var userSchema = new vboxSchema(
{
username: validator.username({
required: true,
min: 5,
messages: {
required: "Username is required",
min: "Username must be at least 5 characters"
}
}),
email: validator.email({
required: true,
allowedDomains: ["gmail.com"],
messages: {
required: "Email is required",
domain: "Only Gmail addresses are allowed"
}
})
}
// {
// validateAll: true,
// showErrors: true
// }
);
var testData = [
{
input: {
username: "admin",
email: "user@gmail.com",
password: "Secure@12345678901234"
}
// ❌ Username "admin" is banned
},
{
input: {
username: "test_123",
email: "example@outlook.com",
password: "Strong!P@ss4567890"
}
// ✅ All valid
},
{
input: {
username: "valid_user",
email: "test@hotmail.com",
password: "Valid@123"
}
// ❌ Invalid email domain
},
{
input: {
username: "ab",
email: "test@gmail.com",
password: "short"
}
// ❌ Username too short, weak password
}
];
console.log("\n\u{1F4CC} Running Schema Tests...\n");
var passedTests = 0;
var totalTests = testData.length;
testData.forEach(({ input }, index) => {
const result = userSchema.validate(input);
console.log(`\u{1F539} Test ${index + 1}:`);
console.log("\u{1F538} Input:", JSON.stringify(input, null, 2));
console.log("\u{1F538} Result:", JSON.stringify(result, null, 2));
const expectedSuccess = Object.values(result.errors || {}).length === 0;
const expected = expectedSuccess ? { success: true, data: input } : { success: false, errors: result.errors };
console.log("\u{1F538} Expected:", JSON.stringify(expected, null, 2));
const testPassed = JSON.stringify(result) === JSON.stringify(expected);
if (testPassed) passedTests++;
console.log(testPassed ? "\u2705 Test Passed!\n" : "\u274C Test Failed!\n");
});
console.log("\u{1F4CA} Test Summary");
console.log(`\u2705 Passed: ${passedTests}/${totalTests}`);
console.log(`\u274C Failed: ${totalTests - passedTests}/${totalTests}
`);
//# sourceMappingURL=schema.js.map