regex-fa
Version:
Professional JavaScript utility to generate flexible and accurate regex patterns for data validation
184 lines (139 loc) • 5.26 kB
JavaScript
/**
* Validate value based on Iranian formats like nationalId and postCode.
*
* @param {Object} options
* @param {"nationalId"|"postCode"|"mobile"|"cardNumber"|"sheba"|"bankNumber"|"email"|"date"|"time"|"ipv4"|"ipv6"} options.type - Type of value to validate.
* @param {string|number} options.value - The value to validate.
* @returns {boolean}
*/
export default function regexValue({ type, value }) {
if (value == null) return false;
let str = String(value).trim();
switch (type) {
case 'nationalId': {
if (!/^\d+$/.test(str)) return false;
if (str.length < 8 || str.length > 10) return false;
const padded = str.padStart(10, '0');
if (/^(\d)\1{9}$/.test(padded)) return false;
const digits = Array.from(padded, Number);
const checkDigit = digits[9];
let sum = 0;
for (let i = 0; i < 9; i++) {
sum += digits[i] * (10 - i);
}
const mod = sum % 11;
const expected = (mod < 2 ? mod : 11 - mod);
return expected === checkDigit;
}
case 'postCode': {
const clean = str.replace('-', '');
if (!/^\d{10}$/.test(clean)) return false;
if (/^(\d)\1{9}$/.test(clean)) return false;
const pattern = /^(?!.*(\d)\1{3})[13-9]{4}[1346-9][0-9]{5}$/;
return pattern.test(clean);
}
case 'mobile': {
str = str.replace(/^(\+98|0098)/, '0');
return /^09\d{9}$/.test(str);
}
case 'cardNumber': {
const clean = str.replace(/[\s-]/g, '');
if (!/^\d{16}$/.test(clean)) return false;
if (/^(\d)\1{15}$/.test(clean)) return false;
const digits = clean.split('').map(Number);
let sum = 0;
for (let i = 0; i < 16; i++) {
let digit = digits[i];
if (i % 2 === 0) {
digit *= 2;
if (digit > 9) digit -= 9;
}
sum += digit;
}
return sum % 10 === 0;
}
case 'sheba': {
const clean = str.toUpperCase().replace(/\s|-/g, '');
if (!/^IR\d{22}$/.test(clean)) return false;
const rearranged = clean.slice(4) + clean.slice(0, 4);
const converted = rearranged.replace(/[A-Z]/g, ch => (ch.charCodeAt(0) - 55));
let remainder = converted;
let block = '';
while (remainder.length > 2) {
block = remainder.slice(0, 9); // حداکثر 9 رقم
remainder = (parseInt(block, 10) % 97).toString() + remainder.slice(block.length);
}
const isValid = parseInt(remainder, 10) % 97 === 1;
return isValid;
}
case 'bankNumber': {
const clean = str.replace(/\D/g, '');
if (clean.length < 6 || clean.length > 20) return false;
if (!/^\d+$/.test(clean)) return false;
if (/^(\d)\1+$/.test(clean)) return false;
return true;
}
case 'email': {
const clean = String(str).trim().toLowerCase();
if (!clean.includes('@')) return false;
if (clean.length < 6 || clean.length > 320) return false;
if (!/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(clean)) return false;
return true;
}
case 'date': {
const persianNums = '۰۱۲۳۴۵۶۷۸۹';
const englishNums = '0123456789';
let clean = String(str).trim().replace(/[۰-۹]/g, d => englishNums[persianNums.indexOf(d)]);
clean = clean.replace(/[-.]/g, '/');
const regex = /^(13|14)\d{2}\/(0[1-9]|1[0-2])\/(0[1-9]|[12]\d|3[01])$/;
return regex.test(clean);
}
case 'time': {
const persianNums = '۰۱۲۳۴۵۶۷۸۹';
const englishNums = '0123456789';
let clean = String(str).trim().replace(/[۰-۹]/g, d => englishNums[persianNums.indexOf(d)]);
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?(Z)?)?$/.test(clean)) {
const date = new Date(clean);
return !isNaN(date.getTime());
}
const match = clean.match(/^(\d{1,2}):(\d{1,2})$/);
if (!match) return false;
const hour = Number(match[1]);
const minute = Number(match[2]);
if (hour < 0 || hour > 24) return false;
if (minute < 0 || minute > 59) return false;
if (hour === 24 && minute !== 0) return false;
return true;
}
case 'ipv4': {
const clean = String(str).trim();
if (!/^\d{1,3}(\.\d{1,3}){3}$/.test(clean)) return false;
const parts = clean.split('.');
for (const part of parts) {
if (part.length > 1 && part.startsWith('0')) return false;
const num = Number(part);
if (num < 0 || num > 255 || isNaN(num)) return false;
}
return true;
}
case 'ipv6': {
const clean = String(str).trim().toLowerCase();
if (!/^[0-9a-f:]+$/.test(clean)) return false;
if ((clean.match(/::/g) || []).length > 1) return false;
const parts = clean.split(':');
if (clean.includes('::')) {
if (parts.length > 8) return false;
} else {
if (parts.length !== 8) return false;
}
for (const part of parts) {
if (part.length === 0) continue;
if (part.length > 4) return false;
if (!/^[0-9a-f]{1,4}$/.test(part)) return false;
}
return true;
}
default:
throw new Error(`Invalid validation type: ${type}`);
}
}