@catbee/utils
Version:
A modular, production-grade utility toolkit for Node.js and TypeScript, designed for robust, scalable applications (including Express-based services). All utilities are tree-shakable and can be imported independently.
313 lines • 10.2 kB
JavaScript
;
/**
* Collection of common validation helpers for strings, numbers, email, UUID, etc.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.isEmail = isEmail;
exports.isUUID = isUUID;
exports.isURL = isURL;
exports.isPhone = isPhone;
exports.isAlphanumeric = isAlphanumeric;
exports.isNumeric = isNumeric;
exports.isHexColor = isHexColor;
exports.isISODate = isISODate;
exports.isLengthBetween = isLengthBetween;
exports.isNumberBetween = isNumberBetween;
exports.isAlpha = isAlpha;
exports.isStrongPassword = isStrongPassword;
exports.isIPv4 = isIPv4;
exports.isIPv6 = isIPv6;
exports.isCreditCard = isCreditCard;
exports.isValidJSON = isValidJSON;
exports.isArray = isArray;
exports.isBase64 = isBase64;
exports.hasRequiredProps = hasRequiredProps;
exports.isDateInRange = isDateInRange;
exports.matchesPattern = matchesPattern;
exports.validateAll = validateAll;
/**
* Checks if a string is a valid email address.
*
* @param {string} str - The input string.
* @returns {boolean} True if valid email, else false.
*/
function isEmail(str) {
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(str))
return false;
// Additional checks
const [local, domain] = str.split("@");
if (!local || !domain)
return false;
// Disallow consecutive dots in local or domain part
if (local.includes("..") || domain.includes(".."))
return false;
return true;
}
/**
* Checks if a string is a valid UUID (versions 1-5).
*
* @param {string} str - The input string.
* @returns {boolean} True if valid UUID, else false.
*/
function isUUID(str) {
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(str);
}
/**
* Checks if a string is a valid URL.
*
* @param {string} str - The input string.
* @returns {boolean} True if valid URL, else false.
*/
function isURL(str) {
try {
new URL(str);
return true;
}
catch (_a) {
return false;
}
}
/**
* Checks if a string is a valid international phone number (E.164 or common patterns).
*
* @param {string} str - The input string.
* @returns {boolean} True if looks like a phone number.
*/
function isPhone(str) {
if (typeof str !== "string")
return false;
// Strip non-digit characters to count total digits
const digitsOnly = str.replace(/\D/g, "");
if (digitsOnly.length < 6 || digitsOnly.length > 15)
return false;
// Accept typical phone characters: +, digits, space, -, (, )
return /^[+]?[\d\s().-]+$/.test(str);
}
/**
* Checks if a string is strictly alphanumeric (letters/numbers only).
*
* @param {string} str - The input string.
* @returns {boolean} True if alphanumeric.
*/
function isAlphanumeric(str) {
return /^[a-z0-9]+$/i.test(str);
}
/**
* Checks if a string or number can be safely parsed to a number.
*
* @param {string | number} value - The value to check.
* @returns {boolean} True if the value is numeric.
*/
function isNumeric(value) {
if (typeof value === "string" && value.trim() === "")
return false;
const num = typeof value === "number" ? value : Number(value);
return typeof num === "number" && isFinite(num);
}
/**
* Checks if a string is a valid hex color code (e.g. #FFF or #FFFFFF).
*
* @param {string} str - Input string.
* @returns {boolean}
*/
function isHexColor(str) {
return /^#([a-f0-9]{6}|[a-f0-9]{3})$/i.test(str);
}
/**
* Checks if a string is a valid date string.
*
* @param {string} str - Input string.
* @returns {boolean}
*/
function isISODate(str) {
const isoRegex = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})?)?$/;
if (!isoRegex.test(str))
return false;
const date = new Date(str);
return (!isNaN(date.getTime()) && date.toISOString().startsWith(str.slice(0, 10)));
}
/**
* Checks if a string matches a specified length range.
*
* @param {string} str - The input string.
* @param {number} min - Minimum length (inclusive).
* @param {number} max - Maximum length (inclusive).
* @returns {boolean} True if string length is within range.
*/
function isLengthBetween(str, min, max) {
return str.length >= min && str.length <= max;
}
/**
* Validates that a number is between specified min and max values (inclusive).
*
* @param {number} value - The number to validate.
* @param {number} min - Minimum value.
* @param {number} max - Maximum value.
* @returns {boolean} True if number is within range.
*/
function isNumberBetween(value, min, max) {
return value >= min && value <= max;
}
/**
* Checks if a string contains only alphabetic characters.
*
* @param {string} str - The input string.
* @returns {boolean} True if alphabetic only.
*/
function isAlpha(str) {
return /^[a-zA-Z]+$/.test(str);
}
/**
* Validates that a string contains at least one uppercase letter,
* one lowercase letter, one number, and one special character.
*
* @param {string} str - The input string.
* @returns {boolean} True if string meets password complexity requirements.
*/
function isStrongPassword(str) {
if (str.length < 8)
return false;
const hasUpperCase = /[A-Z]/.test(str);
const hasLowerCase = /[a-z]/.test(str);
const hasNumbers = /[0-9]/.test(str);
const hasSpecialChar = /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>?]/.test(str);
return hasUpperCase && hasLowerCase && hasNumbers && hasSpecialChar;
}
/**
* Checks if a string is a valid IPv4 address.
*
* @param {string} str - The input string.
* @returns {boolean} True if valid IPv4 address.
*/
function isIPv4(str) {
const parts = str.split(".");
if (parts.length !== 4)
return false;
return parts.every((part) => {
const num = parseInt(part, 10);
return part === num.toString() && num >= 0 && num <= 255;
});
}
/**
* Checks if a string is a valid IPv6 address.
*
* @param {string} str - The input string.
* @returns {boolean} True if valid IPv6 address.
*/
function isIPv6(str) {
// Simple but effective check for common IPv6 formats
return /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]+|::(ffff(:0{1,4})?:)?((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9]))$/.test(str);
}
/**
* Validates a credit card number using the Luhn algorithm.
*
* @param {string} str - The credit card number string.
* @returns {boolean} True if valid credit card number.
*/
function isCreditCard(str) {
// Remove non-digit characters
const cardNumber = str.replace(/\D/g, "");
if (cardNumber.length < 13 || cardNumber.length > 19)
return false;
// Luhn algorithm implementation
let sum = 0;
let shouldDouble = false;
for (let i = cardNumber.length - 1; i >= 0; i--) {
let digit = parseInt(cardNumber.charAt(i), 10);
if (shouldDouble) {
digit *= 2;
if (digit > 9)
digit -= 9;
}
sum += digit;
shouldDouble = !shouldDouble;
}
return sum % 10 === 0;
}
/**
* Checks if a string is a valid JSON.
*
* @param {string} str - The input string.
* @returns {boolean} True if valid JSON.
*/
function isValidJSON(str) {
try {
JSON.parse(str);
return true;
}
catch (_a) {
return false;
}
}
/**
* Type guard that checks if a value is an array.
*
* @template T Optional expected element type
* @param {unknown} value - The value to check.
* @param {(item: unknown) => boolean} [itemGuard] - Optional function to validate each item.
* @returns {boolean} True if value is an array (with optional item validation).
*/
function isArray(value, itemGuard) {
if (!Array.isArray(value))
return false;
return !itemGuard || value.every((item) => itemGuard(item));
}
/**
* Checks if a string is a valid base64 encoded string.
*
* @param {string} str - The input string.
* @returns {boolean} True if valid base64.
*/
function isBase64(str) {
return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(str);
}
/**
* Validates that an object has all required properties.
*
* @param {object} obj - The object to validate.
* @param {string[]} requiredProps - Array of required property names.
* @returns {boolean} True if all required properties exist.
*/
function hasRequiredProps(obj, requiredProps) {
return requiredProps.every((prop) => Object.prototype.hasOwnProperty.call(obj, prop) &&
obj[prop] !== undefined &&
obj[prop] !== null);
}
/**
* Validates a date is within a specified range.
*
* @param {Date} date - The date to validate.
* @param {Date} [minDate] - Optional minimum date (inclusive).
* @param {Date} [maxDate] - Optional maximum date (inclusive).
* @returns {boolean} True if date is within range.
*/
function isDateInRange(date, minDate, maxDate) {
if (!(date instanceof Date) || isNaN(date.getTime()))
return false;
if (minDate && date < minDate)
return false;
if (maxDate && date > maxDate)
return false;
return true;
}
/**
* Validates a string matches a specific regular expression pattern.
*
* @param {string} str - The input string.
* @param {RegExp} pattern - Regular expression to test against.
* @returns {boolean} True if string matches pattern.
*/
function matchesPattern(str, pattern) {
return pattern.test(str);
}
/**
* Validates data against multiple constraints.
*
* @param {unknown} value - The value to validate.
* @param {Array<(value: unknown) => boolean>} validators - Array of validation functions.
* @returns {boolean} True if value passes all validations.
*/
function validateAll(value, validators) {
return validators.every((validator) => validator(value));
}
//# sourceMappingURL=validate.utils.js.map