regex-friendly
Version:
Readable regex transformations, validations, and utilities with both static and chainable API.
1,001 lines (994 loc) • 39.8 kB
JavaScript
/** Utilities to ensure RegExp from string safely */
const toRegExp = (pattern, flags = "g") => pattern instanceof RegExp ? pattern : new RegExp(pattern, flags);
/**
* String transformation helpers (all pure and chainable via index.ts)
*/
const transformations = {
/** Remove all whitespace
* @param str The input string
* @returns The modified string with all whitespace removed
* @example
* regexify.noSpace(" hello world "); // "helloworld"
* regexify(" hello world ").noSpace().result(); // "helloworld"
* @description
* This transformation removes all whitespace characters from the string.
* It uses a regex to match any whitespace and replaces it with an empty string.
*/
noSpace: (str) => str.replace(/\s+/g, ""),
/** Collapse runs of whitespace to a single space and trim
* @param str The input string
* @returns The modified string with consecutive spaces collapsed to one and trimmed
* @example
* regexify.collapseSpaces(" hello world "); // "hello world"
* regexify(" hello world ").collapseSpaces().result(); // "hello world"
* @description
* This transformation replaces multiple consecutive whitespace characters with a single space,
* and trims leading/trailing spaces. It uses a regex to match runs of whitespace.
*/
collapseSpaces: (str) => str.replace(/\s+/g, " ").trim(),
/** Trim both ends
* @param str The input string
* @returns The modified string with leading and trailing whitespace removed
* @example
* regexify.trim(" hello world "); // "hello world"
* regexify(" hello world ").trim().result(); // "hello world"
* @description
* This transformation removes whitespace from both ends of the string.
* It uses the native String.trim() method for efficiency.
*/
trim: (str) => str.trim(),
/** Trim left
* @param str The input string
* @returns The modified string with leading whitespace removed
* @example
* regexify.trimLeft(" hello world "); // "hello world "
* regexify(" hello world ").trimLeft().result(); // "hello world "
* @description
* This transformation removes whitespace from the start of the string.
* It uses a regex to match leading whitespace and replace it with an empty string.
*/
trimLeft: (str) => str.replace(/^\s+/, ""),
/** Trim right
* @param str The input string
* @returns The modified string with trailing whitespace removed
* @example
* regexify.trimRight(" hello world "); // " hello world"
* regexify(" hello world ").trimRight().result(); // " hello world"
* @description
* This transformation removes whitespace from the end of the string.
* It uses a regex to match trailing whitespace and replace it with an empty string.
*/
trimRight: (str) => str.replace(/\s+$/, ""),
/** Keep only digits
* @param str The input string
* @returns The modified string with only digits kept
* @example
* regexify.onlyNumbers("abc123xyz"); // "123"
* regexify("abc123xyz").onlyNumbers().result(); // "123"
* @description
* This transformation removes all non-digit characters from the string.
* It uses a regex to match any character that is not a digit and replaces it with an empty string.
*/
onlyNumbers: (str) => str.replace(/\D+/g, ""),
/** Remove digits
* @param str The input string
* @returns The modified string with all digits removed
* @example
* regexify.removeNumbers("abc123xyz"); // "abcxyz"
* regexify("abc123xyz").removeNumbers().result(); // "abcxyz"
* @description
* This transformation removes all digit characters from the string.
* It uses a regex to match any digit and replaces it with an empty string.
*/
removeNumbers: (str) => str.replace(/\d+/g, ""),
/** Keep only letters (ASCII)
* @param str The input string
* @returns The modified string with only letters kept
* @example
* regexify.onlyLetters("abc123XYZ!@#"); // "abcXYZ"
* regexify("abc123XYZ!@#").onlyLetters().result(); // "abcXYZ"
* @description
* This transformation removes all non-letter characters from the string.
* It uses a regex to match any character that is not a letter and replaces it with an empty string.
*/
onlyLetters: (str) => str.replace(/[^A-Za-z]+/g, ""),
/** Remove letters (ASCII)
* @param str The input string
* @returns The modified string with all letters removed
* @example
* regexify.removeLetters("abc123XYZ!@#"); // "123!@#"
* regexify("abc123XYZ!@#").removeLetters().result(); // "123!@#"
* @description
* This transformation removes all letter characters from the string.
* It uses a regex to match any letter and replaces it with an empty string.
*/
removeLetters: (str) => str.replace(/[A-Za-z]+/g, ""),
/** Keep only alphanumerics
* @param str The input string
* @returns The modified string with only alphanumeric characters kept
* @example
* regexify.onlyAlphanumerics("abc123!@#"); // "abc123"
* regexify("abc123!@#").onlyAlphanumerics().result(); // "abc123"
* @description
* This transformation removes all non-alphanumeric characters from the string.
* It uses a regex to match any character that is not a letter or digit and replaces it with an empty string.
*/
onlyAlphanumerics: (str) => str.replace(/[^A-Za-z0-9]+/g, ""),
/** Remove non-alphanumerics
* @param str The input string
* @returns The modified string with all non-alphanumeric characters removed
* @example
* regexify.removeNonAlphanumerics("abc123!@#"); // "abc123!@#"
* regexify("abc123!@#").removeNonAlphanumerics().result(); // "abc123!@#"
* @description
* This transformation removes all alphanumeric characters from the string.
* It uses a regex to match any letter or digit and replaces it with an empty string.
*/
removeNonAlphanumerics: (str) => str.replace(/[A-Za-z0-9]+/g, ""),
/** Remove special characters
* @param str The input string
* @returns The modified string with all special characters removed
* @example
* regexify.noSpecialChars("abc123!@#"); // "abc123"
* regexify("abc123!@#").noSpecialChars().result(); // "abc123"
* @description
* This transformation removes all characters that are not letters or digits.
* It uses a regex to match any character that is not alphanumeric and replaces it with an empty string.
*/
noSpecialChars: (str) => str.replace(/[^A-Za-z0-9]+/g, ""),
/** Replace all occurrences of pattern
* @param str The input string
* @param pattern The regex pattern to match
* @param replacement The string to replace matches with
* @example
* regexify.replaceAll("abc abc", "abc", "xyz"); // "xyz xyz"
* regexify("abc abc").replaceAll("abc", "xyz").result(); // "xyz xyz"
* @description
* This transformation replaces all occurrences of a pattern in the string.
* It uses a regex to find matches and replaces them with the specified replacement string.
*/
replaceAll: (str, pattern, replacement) => str.replace(toRegExp(pattern, pattern instanceof RegExp ? pattern.flags : "g"), replacement),
/** Replace first occurrence
* @param str The input string
* @param pattern The regex pattern to match
* @param replacement The string to replace the first match with
* @example
* regexify.replaceFirst("abc abc", "abc", "xyz"); // "xyz abc"
* regexify("abc abc").replaceFirst("abc", "xyz").result(); // "xyz abc"
* @description
* This transformation replaces the first occurrence of a pattern in the string.
* It uses a regex to find the first match and replaces it with the specified replacement string.
*/
replaceFirst: (str, pattern, replacement) => str.replace(toRegExp(pattern, pattern instanceof RegExp ? pattern.flags : ""), replacement),
/** Replace last occurrence by scanning from right
* @param str The input string
* @param pattern The regex pattern to match
* @param replacement The string to replace the last match with
* @example
* regexify.replaceLast("abc abc", "abc", "xyz"); // "abc xyz"
* regexify("abc abc").replaceLast("abc", "xyz").result(); // "abc xyz"
* @returns The modified string with the last occurrence replaced
* @description
* This transformation replaces the last occurrence of a pattern in the string.
*/
replaceLast: (str, pattern, replacement) => {
const r = toRegExp(pattern, pattern instanceof RegExp ? pattern.flags : "g");
let m;
let lastIndex = -1;
while ((m = r.exec(str)))
lastIndex = m.index;
return lastIndex === -1
? str
: str.slice(0, lastIndex) + str.slice(lastIndex).replace(r, replacement);
},
/** Extract first match for a pattern
* @param str The input string
* @param pattern The regex pattern to match
* @example
* regexify.extractFirst("abc123", /\d+/); // "123"
* regexify("abc123").extractFirst(/\d+/).result(); // "123"
* @returns The first match found in the string, or empty string if no match
* @description
* This transformation extracts the first occurrence of a pattern from the string.
* It uses a regex to find the first match and returns it.
* If no match is found, it returns an empty string.
*/
extractFirst: (str, pattern) => {
const m = RegExp(toRegExp(pattern, pattern instanceof RegExp ? pattern.flags : "")).exec(str);
return m ? m[0] : "";
},
/** Extract all matches for a pattern
* @param str The input string
* @param pattern The regex pattern to match
* @example
* regexify.extractAll("abc123abc", /\d+/g); // ["123"]
* regexify("abc123abc").extractAll(/\d+/g).result(); // ["123"]
* @returns An array of all matches found in the string
* @description
* This transformation extracts all occurrences of a pattern from the string.
* It uses a regex to find all matches and returns them as an array.
* If no matches are found, it returns an empty array.
*/
extractAll: (str, pattern) => {
const r = toRegExp(pattern, pattern instanceof RegExp ? pattern.flags : "g");
return Array.from(str.matchAll(r), (m) => m[0]);
},
/** Extract named groups from first match
* @param str The input string
* @param pattern The regex pattern with named groups
* @example
* regexify.extractGroups("abc", /(?<group>a)/); // { group: "a" }
* regexify("abc").extractGroups(/(?<group>a)/).result(); // { group: "a" }
* @returns An object with named groups or empty object if no match
* @description
* This transformation extracts named groups from the first match of a regex pattern.
* It uses the RegExp.exec() method to find the first match and returns an object with named groups.
* If no match is found, it returns an empty object.
*/
extractGroups: (str, pattern) => {
const m = RegExp(pattern).exec(str);
return (m && m.groups) || {};
},
/** Split by a regex (keeps it simple)
* @param str The input string
* @param pattern The regex pattern to split by
* @example
* regexify.splitBy("one,two,three", /,/); // ["one", "two", "three"]
* regexify("one,two,three").splitBy(/,/).result(); // ["one", "two", "three"]
* @returns An array of strings split by the pattern
* @description
* This transformation splits the string by a regex pattern.
* It uses the String.split() method with the provided regex to return an array of substrings.
* If no matches are found, it returns an array with the original string.
*/
splitBy: (str, pattern) => str.split(toRegExp(pattern)),
/** Get text between the first pair of delimiters
* @param str The input string
* @param start The starting delimiter (string or RegExp)
* @param end The ending delimiter (string or RegExp)
* @example
* regexify.between("start middle end", "start", "end"); // " middle "
* regexify("start middle end").between("start", "end").result(); // " middle "
* @returns The text found between the delimiters, or empty string if not found
* @description
* This transformation extracts the text between the first occurrence of two delimiters.
*/
between: (str, start, end) => {
const s = typeof start === "string"
? start.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
: start.source;
const e = typeof end === "string"
? end.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
: end.source;
const re = new RegExp(`${s}(.*?)${e}`);
const m = RegExp(re).exec(str);
return m ? m[1] : "";
},
/** Get all texts between delimiters (non-overlapping)
* @param str The input string
* @param start The starting delimiter (string or RegExp)
* @param end The ending delimiter (string or RegExp)
* @example
* regexify.betweenAll("start1 middle1 end1 start2 middle2 end2", "start", "end"); // ["1 middle1 ", "2 middle2 "]
* regexify("start1 middle1 end1 start2 middle2 end2").betweenAll("start", "end").result(); // ["1 middle1 ", "2 middle2 "]
* @returns An array of strings found between the delimiters
* @description
* This transformation extracts all occurrences of text between pairs of delimiters.
* It uses a regex to find all matches and returns them as an array.
*/
betweenAll: (str, start, end) => {
const s = typeof start === "string"
? start.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
: start.source;
const e = typeof end === "string"
? end.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
: end.source;
const re = new RegExp(`${s}(.*?)${e}`, "g");
return Array.from(str.matchAll(re), (m) => m[1]);
},
/** Remove HTML tags
* @param str The input string
* @returns The modified string with HTML tags removed
* @example
* regexify.stripHtml("<p>Hello</p>"); // "Hello"
* regexify("Hello").stripHtml().result(); // "Hello"
* @description
* This transformation removes HTML tags from the string.
* It uses a regex to match HTML tags and replaces them with an empty string.
* If no tags are found, the original string is returned unchanged.
*/
stripHtml: (str) => str.replace(/<[^>]+>/g, ""),
/** Squeeze consecutive spaces into one */
squeezeSpaces: (str) => str.replace(/ +/g, " "),
/** Mask everything except the last N chars
* @param str The input string
* @param visible The number of characters to keep visible at the end (default: 4)
* @param maskChar The character to use for masking (default: '*')
* @returns The modified string with all but the last N chars masked
* @example
* regexify.maskTail("SensitiveData", 4); // "********Data"
* regexify("SensitiveData").maskTail().result(); // "********Data"
* @description
* This transformation masks all characters in the string except for the last N characters.
* It uses a regex to match all but the last N characters and replaces them with the specified mask character.
* If the string is shorter than N, it returns the original string unchanged.
*/
maskTail: (str, visible = 4, maskChar = "*") => str.replace(new RegExp(`.(?=.{${visible}})`, "g"), maskChar),
/** Redact matches of a pattern
* @param str The input string
* @param pattern The regex pattern to match for redaction
* @param label The label to replace matches with (default: "[REDACTED]")
* @returns The modified string with matches replaced by the label
* @example
* regexify.redact("Sensitive data here", /\bdata\b/, "[REDACTED]"); // "Sensitive [REDACTED] here"
* regexify("Sensitive data here").redact(/\bdata\b/, "[REDACTED]").result(); // "Sensitive [REDACTED] here"
* @description
* This transformation replaces all occurrences of a pattern in the string with a label.
* It uses a regex to find matches and replaces them with the specified label.
* If no matches are found, the original string is returned unchanged.
*/
redact: (str, pattern, label = "[REDACTED]") => str.replace(toRegExp(pattern, "g"), label),
/** Pad numeric substrings to a fixed width: img2 -> img0002
* @param str The input string
* @param width The width to pad numbers to (default: 4)
* @returns The modified string with numeric substrings padded to the specified width
* @example
* regexify.padNumbers("img2"); // "img0002"
* regexify("img2").padNumbers().result(); // "img0002"
* @description
* This transformation pads numeric substrings in the string to a fixed width.
* It uses a regex to find all numeric substrings and pads them with leading zeros to the specified width.
* If no numeric substrings are found, the original string is returned unchanged.
*/
padNumbers: (str, width = 4) => str.replace(/\d+/g, (d) => d.padStart(width, "0")),
/** Convert to slug (kebab-case) via regex
* @param str The input string
* @returns The modified string converted to a slug format (kebab-case)
* @example
* regexify.toSlug("Hello World!"); // "hello-world"
* regexify("Hello World!").toSlug().result(); // "hello-world"
* @description
* This transformation converts the string to a slug format (kebab-case).
* It normalizes the string, removes accents, converts to lowercase, replaces non-alphanumeric characters with hyphens,
* and trims leading/trailing hyphens. This is useful for creating URL-friendly slugs.
*/
toSlug: (str) => str
.normalize("NFKD")
.replace(/[\u0300-\u036f]/g, "")
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/(?:^-+|-+$)/g, ""),
/** snake_case
* @param str The input string
* @returns The modified string converted to snake_case
* @example
* regexify.toSnakeCase("Hello World!"); // "hello_world"
* regexify("Hello World!").toSnakeCase().result(); // "hello_world"
* @description
* This transformation converts the string to snake_case.
* It replaces spaces and non-alphanumeric characters with underscores, converts to lowercase,
* and removes leading/trailing underscores. This is useful for creating database-friendly identifiers.
*/
toSnakeCase: (str) => str
.replace(/([a-z\d])([A-Z])/g, "$1_$2")
.replace(/[^A-Za-z0-9]+/g, "_")
.replace(/_{2,}/g, "_")
.toLowerCase()
.replace(/(^_+)|(_+$)/g, ""),
/** camelCase
* @param str The input string
* @returns The modified string converted to camelCase
* @example
* regexify.toCamelCase("Hello World!"); // "helloWorld"
* regexify("Hello World!").toCamelCase().result(); // "helloWorld"
* @description
* This transformation converts the string to camelCase.
* It first converts the string to a slug, then replaces hyphens with uppercase letters,
* effectively transforming it into camelCase. This is useful for creating JavaScript-friendly identifiers.
*/
toCamelCase: (str) => transformations
.toSlug(str)
.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase()),
/**
* Custom transformation: custom
* Applies a user-provided regex and replacement.
* @param input The input string
* @param pattern The regex pattern to apply
* @param replacement The replacement string or function
* @example
* regexify.custom("Hello123", /\d+/g, ""); // "Hello"
* regexify("Hello123").custom(/\d+/g, "").result(); // "Hello"
*/
custom: (input, pattern, replacement) => {
if (typeof replacement === "string") {
return input.replace(pattern, replacement);
}
return input.replace(pattern, replacement);
},
};
/**
* Credit card types with their validation patterns
*/
const CARD_TYPES = {
visa: {
name: "Visa",
pattern: /^4\d{12}(?:\d{3})?$/,
lengths: [13, 16, 19],
},
verve: {
name: "Verve",
pattern: /^(506(099|1\d{2})|650\d{3})\d{10,13}$/,
lengths: [16, 18, 19],
},
mastercard: {
name: "Mastercard",
pattern: /^(?:5[1-5]\d{14}|2[2-7]\d{14})$/,
lengths: [16],
},
amex: {
name: "American Express",
pattern: /^3[47]\d{13}$/,
lengths: [15],
},
discover: {
name: "Discover",
pattern: /^6(?:011|5\d{2})\d{12}$/,
lengths: [16],
},
dinersclub: {
name: "Diners Club",
pattern: /^3(?:0[0-5]|[68]\d)\d{11}$/,
lengths: [14],
},
jcb: {
name: "JCB",
pattern: /^(?:2131|1800|35\d{3})\d{11}$/,
lengths: [16],
},
};
/**
* Validates a credit card number using the Luhn algorithm
* @param cardNumber - Credit card number as string
* @returns true if valid according to Luhn algorithm
*/
function validateLuhn(cardNumber) {
// Remove only spaces and hyphens, keep other characters to detect invalid input
const digits = cardNumber.replace(/[\s-]/g, "");
// Check if contains only digits
if (!/^\d+$/.test(digits)) {
return false;
}
if (digits.length < 13 || digits.length > 19) {
return false;
}
let sum = 0;
let isEven = false;
// Process digits from right to left
for (let i = digits.length - 1; i >= 0; i--) {
let digit = parseInt(digits[i]);
if (isEven) {
digit *= 2;
if (digit > 9) {
digit -= 9;
}
}
sum += digit;
isEven = !isEven;
}
return sum % 10 === 0;
}
/**
* Identifies the credit card type based on the card number
* @param cardNumber - Credit card number as string
* @returns card type name or 'unknown'
*/
function identifyCardType(cardNumber) {
const cleanNumber = cardNumber.replace(/[\s-]/g, "");
// Return unknown if contains non-digits
if (!/^\d+$/.test(cleanNumber)) {
return "Unknown";
}
// Check against each card type
for (const [_, card] of Object.entries(CARD_TYPES)) {
if (card.pattern.test(cleanNumber)) {
return card.name;
}
}
return "Unknown";
}
function validateCreditCard(cardNumber) {
const errors = [];
// Check for invalid characters (allow only digits, spaces, and hyphens)
if (!/^[\d\s-]+$/.test(cardNumber)) {
errors.push("Contains invalid characters (only digits, spaces, and hyphens allowed)");
}
const cleanNumber = cardNumber.replace(/[\s-]/g, "");
// Basic format check
if (!cleanNumber || cleanNumber.length < 13 || cleanNumber.length > 19) {
errors.push("Invalid length: must be 13-19 digits");
}
if (!/^\d+$/.test(cleanNumber)) {
errors.push("Must contain only digits");
}
// Luhn algorithm validation
const luhnValid = validateLuhn(cardNumber);
if (!luhnValid) {
errors.push("Invalid card number (failed Luhn check)");
}
// Identify card type
const cardType = identifyCardType(cardNumber);
// Check if length matches card type
if (cardType !== "Unknown") {
const typeConfig = Object.values(CARD_TYPES).find((c) => c.name === cardType);
if (typeConfig && !typeConfig.lengths.includes(cleanNumber.length)) {
errors.push(`Invalid length for ${cardType}`);
}
}
return {
isValid: errors.length === 0,
cardType,
errors,
};
}
/** Validation helpers returning boolean */
const validations = {
/** Checks if string is a valid email
* @param str - Email string to validate
* @returns true if valid, false otherwise
* @example
* ```typescript
* isEmail("eze@gmail.com");
* // true
* * isEmail("invalid-email");
* // false
* ```
* This regex checks for a valid email format with local part and domain.
*/
isEmail: (str) => /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/i.test(str),
/** Checks if string is a valid URL
* @param str - URL string to validate
* @returns true if valid, false otherwise
* @example
* ```typescript
* isUrl("https://example.com");
* // true
* isUrl("ftp://example.com");
* // false
* isUrl("http://example.com/path?query=123");
* // true
* isUrl("invalid-url");
* // false
* ```
* This regex checks for a valid URL format starting with http or https.
*/
isUrl: (str) => /^https?:\/\/[^\s]+$/i.test(str),
/** Checks if string is a valid HTML tag
* @param str - HTML tag string to validate
* @returns true if valid, false otherwise
* @example
* ```typescript
* isHtmlTag("<div>");
* // true
* isHtmlTag("invalid-tag");
* // false
* ```
* This regex checks for a valid HTML tag format.
*/
isHtmlTag: (str) => /<[^>]+>/g.test(str),
/** Checks if string is a valid phone number
* @param str - Phone number string to validate
* @returns true if valid, false otherwise
* @example
* ```typescript
* isPhone("123-456-7890");
* // true
* isPhone("+1 (123) 456-7890");
* // true
* isPhone("invalid-phone");
* // false
*
* This regex allows optional leading +, digits, spaces, parentheses, and hyphens.
* Adjust regex for stricter validation if needed.
*/
isPhone: (str) => /^\+?[0-9\s().-]{7,20}$/.test(str),
/** Checks if string is a valid postal code
* @param str - Postal code string to validate
* @returns true if valid, false otherwise
* @example
* ```typescript
* isPostalCode("12345");
* // true
* isPostalCode("123-456");
* // true
* isPostalCode("invalid postal");
* // false
*
* This regex allows alphanumeric characters, spaces, and hyphens.
*/
isPostalCode: (str) => /^[A-Za-z0-9\s-]{3,10}$/.test(str),
/** Checks if string is a valid credit card number (simplified to length check)
* @param creditCard - Credit card number string to validate
* @returns true if valid, false otherwise
* @example
* ```typescript
* isCreditCard("4111111111111111");
* // true
* isCreditCard("1234567890123456");
* // false
*
* This regex checks for common credit card formats.
*/
isCreditCard: (creditCard) => {
return validateCreditCard(creditCard).isValid;
},
/** Checks if string is a valid hex color code
* @param str - Hex color code string to validate
* @returns true if valid, false otherwise
* @example
* ```typescript
* isHexColor("#ff5733");
* // true
* isHexColor("#123");
* // true
* isHexColor("invalid-color");
* // false
*
* This regex checks for 3 or 6 digit hex color codes with optional leading #.
*/
isHexColor: (str) => /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(str),
/** Checks if string is a valid IPv4 address
* @param str - IPv4 address string to validate
* @returns true if valid, false otherwise
* @example
* ```typescript
* isIPv4("invalid-ipv4");
* // false
* isIPv4("172.16.0.1");
* // true
* isIPv4("256.256.256.256");
* // false
*
* This regex checks for standard IPv4 format with 4 octets (0-255).
*/
isIPv4: (str) => {
const parts = str.split(".");
return (parts.length === 4 &&
parts.every((part) => {
const num = parseInt(part, 10);
return num >= 0 && num <= 255 && /^\d+$/.test(part);
}));
},
/** Checks if string is a valid IPv6 address
* @param str - IPv6 address string to validate
* @returns true if valid, false otherwise
* @example
* ```typescript
* isIPv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
* // true
* isIPv6("invalid-ipv6");
* // false
*
* This regex checks for standard IPv6 format with 8 groups of 1-4 hex digits.
*/
isIPv6: (str) => /^(?:[A-Fa-f0-9]{1,4}:){7}[A-Fa-f0-9]{1,4}$/.test(str),
/** Checks if string is a valid UUID v4
* @param str - UUID v4 string to validate
* @returns true if valid, false otherwise
* @example
* ```typescript
* isUUIDv4("123e4567-e89b-42d3-a456-426614174000");
* // true
* isUUIDv4("123e4567-e89b-12d3-a456-426614174000");
* // false
* isUUIDv4("invalid-uuid");
* // false
*
* This regex checks for standard UUID v4 format with 8-4-4-4-12 hex digits.
*/
isUUIDv4: (str) => /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(str),
/** Checks if string is alphanumeric (letters and digits only)
* @param str - String to validate
* @returns true if valid, false otherwise
* @example
* ```typescript
* isAlphanumeric("abc123");
* // true
* isAlphanumeric("abc 123");
* // false
*
* This regex checks for letters and digits only, no spaces or symbols.
*/
isAlphanumeric: (str) => /^[A-Za-z0-9]+$/.test(str),
/** Checks if string is a valid username
* @param str - Username string to validate
* @returns true if valid, false otherwise
* @example
* ```typescript
* isUsername("user123");
* // true
* isUsername("user name");
* // false
*
* This regex checks for usernames with 3-32 characters, allowing letters, digits, and underscores.
*/
isUsername: (str) => /^\w{3,32}$/.test(str),
/** Checks if string is a valid slug
* @param str - Slug string to validate
* @returns true if valid, false otherwise
* @example
* ```typescript
* isSlug("my-awesome-slug");
* // true
* isSlug("My Awesome Slug");
* // false
*
* This regex checks for slugs with lowercase letters, digits, and hyphens.
*/
isSlug: (str) => /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(str),
/** Checks if string is a valid date in ISO format (YYYY-MM-DD)
* @param str - Date string to validate
* @returns true if valid, false otherwise
* @example
* ```typescript
* isDateISO("2023-10-01");
* // true
* isDateISO("01-10-2023");
* // false
*
* This regex checks for dates in the format YYYY-MM-DD.
*/
isDateISO: (str) => /^\d{4}-\d{2}-\d{2}$/.test(str),
/** Checks if string is a valid time in 24-hour format (HH:MM or HH:MM:SS)
* @param str - Time string to validate
* @returns true if valid, false otherwise
* @example
* ```typescript
* isTime24h("14:30");
* // true
* isTime24h("25:00");
* // false
*
* This regex checks for times in the format HH:MM or HH:MM:SS, allowing 00-23 for hours and 00-59 for minutes/seconds.
*/
isTime24h: (str) => /^(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d)?$/.test(str),
/** Checks if string is a valid date and time in ISO format (YYYY-MM-DDTHH:MM:SSZ)
* @param str - DateTime string to validate
* @returns true if valid, false otherwise
* @example
* ```typescript
* isDateTimeISO("2023-10-01T14:30:00Z");
* // true
* isDateTimeISO("2023-10-01 14:30:00");
* // false
*
* This regex checks for date and time in ISO format with optional timezone offset.
*/
isDateTimeISO: (str) => /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:Z|[+-]\d{2}:\d{2})$/.test(str),
/** Checks if string is a valid JSON
* @param str - JSON string to validate
* @returns true if valid, false otherwise
* @example
* ```typescript
* isJson('{"key": "value"}');
* // true
* isJson('{"key": value}');
* // false
*
* This function attempts to parse the string as JSON and returns true if successful.
*/
isJson: (str) => {
try {
JSON.parse(str);
return true;
}
catch {
return false;
}
},
/** Checks if string is a valid base64 encoded string
* @param str - Base64 string to validate
* @returns true if valid, false otherwise
* @example
* ```typescript
* isBase64("SGVsbG8gV29ybGQ=");
* // true
* isBase64("Invalid Base64");
* // false
*
* This function checks if the string can be encoded and decoded as base64.
*/
isBase64: (str) => {
try {
return btoa(atob(str)) === str;
}
catch {
return false;
}
},
/** Checks if string contains uppercase letters
* @param str - String to check
* @returns true if contains uppercase letters, false otherwise
* @example
* ```typescript
* hasUppercase("Hello World");
* // true
* hasUppercase("hello world");
* // false
*
* This regex checks for at least one uppercase letter in the string.
*/
hasUppercase: (str) => /[A-Z]/.test(str),
/** Checks if string contains lowercase letters
* @param str - String to check
* @returns true if contains lowercase letters, false otherwise
* @example
* ```typescript
* hasLowercase("Hello World");
* // true
* hasLowercase("HELLO WORLD");
* // false
*
* This regex checks for at least one lowercase letter in the string.
*/
hasLowercase: (str) => /[a-z]/.test(str),
/** Checks if string contains digits
* @param str - String to check
* @returns true if contains digits, false otherwise
* @example
* ```typescript
* hasDigit("Hello123");
* // true
* hasDigit("Hello World");
* // false
*
* This regex checks for at least one digit in the string.
*/
hasDigit: (str) => /\d/.test(str),
/** Checks if string contains special characters
* @param str - String to check
* @returns true if contains special characters, false otherwise
* @example
* ```typescript
* hasSymbol("Hello@World");
* // true
* hasSymbol("Hello World");
* // false
*
* This regex checks for at least one character that is not a letter, digit, or space.
*/
hasSymbol: (str) => /[^A-Za-z0-9\s]/.test(str),
/** Checks if string is a strong password
* @param str - Password string to validate
* @returns true if valid, false otherwise
* @example
* ```typescript
* isStrongPassword("StrongP@ssw0rd");
* // true
* isStrongPassword("weakpassword");
* // false
*
* This regex checks for at least 8 characters with at least one lowercase, one uppercase, one digit, and one special character.
*/
isStrongPassword: (str) => /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z0-9]).{8,}$/.test(str),
/** Checks if string contains a specific word
* @param str - String to check
* @param word - Word to search for
* @returns true if contains the word, false otherwise
* @example
* ```typescript
* hasWord("Hello World", "World");
* // true
* hasWord("Hello World", "Universe");
* // false
*
* This regex checks for the exact word with word boundaries.
*/
hasWordBoundary: (str, word) => new RegExp(`\\b${word}\\b`).test(str),
};
/** Advanced pattern helpers and small builders. */
const advanced = {
/** Positive lookahead fragment */
lookahead: (pattern) => `(?=${pattern})`,
/** Negative lookahead fragment */
negativeLookahead: (pattern) => `(?!${pattern})`,
/** Positive lookbehind fragment */
lookbehind: (pattern) => `(?<=${pattern})`,
/** Negative lookbehind fragment */
negativeLookbehind: (pattern) => `(?<!${pattern})`,
/** Wrap as a non-capturing group */
nonCapturing: (pattern) => `(?:${pattern})`,
/** Named capturing group */
namedGroup: (name, pattern) => `(?<${name}>${pattern})`,
/** Either/or */
or: (...patterns) => `(?:${patterns.join("|")})`,
/** Character class any-of */
anyOf: (chars) => `[${chars}]`,
/** Character class none-of */
noneOf: (chars) => `[^${chars}]`,
/** Repeat {n} or {min,max} */
repeat: (pattern, min, max, lazy = false) => advanced.nonCapturing(pattern) +
"{" +
(max == null ? min : min + "," + max) +
"}" +
(lazy ? "?" : ""),
/** Optional */
optional: (pattern, lazy = false) => `${advanced.nonCapturing(pattern)}?${lazy ? "?" : ""}`,
/** One or more */
oneOrMore: (pattern, lazy = false) => `${advanced.nonCapturing(pattern)}+${lazy ? "?" : ""}`,
/** Zero or more */
zeroOrMore: (pattern, lazy = false) => `${advanced.nonCapturing(pattern)}*${lazy ? "?" : ""}`,
/** Anchors */
startsWith: (pattern) => new RegExp(`^${pattern}`),
endsWith: (pattern) => new RegExp(`${pattern}$`),
/** Word boundary wrappers */
word: (pattern) => new RegExp(`\\b(?:${pattern})\\b`, "g"),
notWordBoundary: () => /\B/g,
/** Build a RegExp with flags from a source string */
build: (source, flags = "g") => new RegExp(source, flags),
};
/** Regex function helpers */
const rexFunctions = {
/**
* Formats a credit card number with spaces for display
* @param cardNumber - Credit card number as string
* @returns formatted card number
* @example
* formatCardNumber("4532015112830366"); // "4532 0151 1283 0366"
*/
formatCardNumber: (cardNumber) => {
const cleanNumber = cardNumber.replace(/\D/g, "");
const cardType = identifyCardType(cleanNumber);
// Different formatting for different card types
if (cardType === "American Express") {
return cleanNumber.replace(/(\d{4})(\d{6})(\d{5})/, "$1 $2 $3");
}
else if (cardType === "Diners Club") {
return cleanNumber.replace(/(\d{4})(\d{6})(\d{4})/, "$1 $2 $3");
}
else {
// Default 4-digit grouping for Visa, Mastercard, etc.
return cleanNumber.replace(/(\d{4})(?=\d)/g, "$1 ");
}
},
/**
* Validates a credit card number and returns detailed results
* @param cardNumber - Credit card number as string
* @returns validation result object
* @example
* validateCreditCard("4111111111111111");
* // {
* // isValid: true,
* // cardType: "Visa",
* // errors: []
* // }
* validateCreditCard("1234567890123456");
* // {
* // isValid: false,
* // cardType: "Unknown",
* // errors: ["Invalid card number (failed Luhn check)"]
* // }
*/
validateCreditCard: (cardNumber) => {
return validateCreditCard(cardNumber);
},
};
function regexify(input) {
let value = input || "";
const chain = {};
for (const key of Object.keys(transformations)) {
chain[key] = (...args) => {
value = transformations[key](value, ...args);
return chain;
};
}
for (const key of Object.keys(validations)) {
chain[key] = (...args) => {
return validations[key](value, ...args);
};
}
chain.result = () => value;
return chain;
}
// ---------- Exports ----------
const RegexFriendly = Object.assign(regexify, transformations, validations, advanced, rexFunctions);
export { advanced, RegexFriendly as default, rexFunctions, transformations, validations };