UNPKG

regex-friendly

Version:

Readable regex transformations, validations, and utilities with both static and chainable API.

1,009 lines (1,000 loc) 40 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); /** 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); exports.advanced = advanced; exports.default = RegexFriendly; exports.rexFunctions = rexFunctions; exports.transformations = transformations; exports.validations = validations;