@technobuddha/library
Version:
A large library of useful functions
278 lines (251 loc) • 7.39 kB
text/typescript
/* eslint-disable unicorn/better-regex */
/* eslint-disable require-unicode-regexp */
/* eslint-disable no-control-regex */
import { re } from './re.ts';
//#region ipV4
/**
* Validate an IPv4 segment.
* @internal
*/
const IPV4_SEG = /(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])/;
/**
* validate an IPv4 address
* @group RegExp
* @category Constants
* @example
* ```typescript
* ipV4.test('192.168.1.1'); // true
* ipV4.test('255.255.255.255'); // true
* ipV4.test('256.0.0.1'); // false
* ipV4.test('abc.def.ghi.jkl'); // false
* ```
*/
export const ipV4 = re`^${IPV4_SEG}\.${IPV4_SEG}\.${IPV4_SEG}\.${IPV4_SEG}$`;
//#endregion
//#region ipV4Local
/**
* Validate a private network 10.x.x.x address.
* @internal
*/
const IPV4_NET10 = re`^0?10[.]${IPV4_SEG}`;
/**
* Validate a private network 172.16.x.x - 172.31.x.x address.
* @internal
*/
const IPV4_NET172 = /^172[.]0?(?:1[6-9]|2[0-9]|3[0-1])/;
/**
* Validate a private network 192.168.x.x address.
* @internal
*/
const IPV4_NET192 = /^192[.]168$/;
/**
* determine if Ipv4 address is local
* @group RegExp
* @category Constants
* @example
* ```typescript
* ipV4Local.test('192.168.1.1'); // true
* ipV4Local.test('10.0.0.1'); // true
* ipV4Local.test('172.16.0.1'); // true
* ipV4Local.test('8.8.8.8'); // false
* ipV4Local.test('256.0.0.1'); // false
* ```
*/
export const ipV4Local = re`^(?:${IPV4_NET10}|${IPV4_NET172}|${IPV4_NET192})[.]${IPV4_SEG}[.]${IPV4_SEG}$`;
//#endregion
//#region isoDate
//cspell:ignore ZONEPLUG, ZONEMINUS, ZONEPLUS, ZONEHOUR, ZONEMINUTE
/**
* Validate a year component in a date string.
* @internal
*/
const ISO_YEAR = /^[0-9]{4}$/;
/**
* Validate a month component in a date string.
* @internal
*/
const ISO_MONTH = /^(?:0[1-9]|1[0-2])$/;
/**
* Validate a day component in a date string.
* @internal
*/
const ISO_DAY = /^(?:3[0-1]|[1-2][0-9]|0[1-9])$/;
/**
* Validate an hour component in a time string.
* @internal
*/
const ISO_HOUR = /^(?:2[0-3]|[0-1][0-9])$/;
/**
* Validate a minute component in a time string.
* @internal
*/
const ISO_MINUTE = /^[0-5][0-9]$/;
/**
* Validate a second component in a time string.
* @internal
*/
const ISO_SECOND = ISO_MINUTE;
/**
* Validate a fractional second component in a time string.
* @internal
*/
const ISO_FRACTION = /^[0-9]+$/;
/**
* Validate a positive timezone offset hour.
* @internal
*/
const ISO_ZONEPLUS = /^[+](?:1[0-4]|0[0-9])$/;
/**
* Validate a negative timezone offset hour.
* @internal
*/
const ISO_ZONEMINUS = /^[-](?:1[0-2]|0[0-9])$/;
/**
* Validate a timezone offset hour.
* @internal
*/
const ISO_ZONEHOUR = re`^${ISO_ZONEPLUS}|${ISO_ZONEMINUS}$`;
/**
* Validate a timezone offset minute.
* @internal
*/
const ISO_ZONEMINUTE = /^[0-5][0-9]$/;
/**
* Validate a timezone offset minute.
* @internal
*/
const ISO_TIMEZONE = re`^(?:(?:${ISO_ZONEHOUR}(?::${ISO_ZONEMINUTE})?)|Z)$`;
/**
* Validate a ISO formatted date
* @group RegExp
* @category Constants
* @example
* ```typescript
* isoDate.test('2023-08-29T12:34:56Z'); // true
* isoDate.test('2023-08-29T12:34:56.789+02:00'); // true
* isoDate.test('2023-08-29T12:34'); // true
* isoDate.test('2023-08-29'); // false
* isoDate.test('not-a-date'); // false
* ```
*/
export const isoDate = re`^${ISO_YEAR}-${ISO_MONTH}-${ISO_DAY}T${ISO_HOUR}:${ISO_MINUTE}(?::${ISO_SECOND}(?:[.]${ISO_FRACTION})?)?${ISO_TIMEZONE}$`;
//#endregion
//#region numeric
/**
* Validate a valid number
* @group RegExp
* @category Constants
* @example
* ```typescript
* numeric.test('123'); // true
* numeric.test('-123.45'); // true
* numeric.test('1.23e4'); // true
* numeric.test('Infinity'); // true
* numeric.test('NaN'); // true
* numeric.test('abc'); // false
* numeric.test(''); // false
* ```
*/
export const numeric = /^((?:NaN|[+-]?(?:(?:\d+|\d*[.]\d+)(?:[Ee][+-]?\d+)?|[+-]?Infinity)))$/;
// TODO [>2.1]: enhance numeric regex to support hexadecimal and binary literals
// TODO [>2.1]: Add isNumeric function, but with a different name
//#endregion numeric
//#region domain
/**
* Regular expression to match a valid hostname label ending with a dot.
* @internal
*/
const DOMAIN_HOST = /^(?!-)[a-zA-Z0-9-]{1,63}(?<!-)[.]$/;
/**
* Regular expression to match a valid top-level domain (TLD).
* @internal
*/
const DOMAIN_TLD = /^[a-z]{2,}$/;
/**
* Regular expression for matching a domain name composed of a host and a top-level domain (TLD).
* @group RegExp
* @category Constants
* @example
* ```typescript
* domain.test('example.com'); // true
* domain.test('sub.example.co'); // true
* domain.test('invalid_domain'); // false
* ```
*/
export const domain = re`^${DOMAIN_HOST}+${DOMAIN_TLD}$`;
// TODO [>2.1]: enable punycode support
//#endregion
//#region email
// cspell:ignore EMAILGLYPH, EMAILQUOTE, EMAILESCAPE, EMAILADDRESS
/**
* Regular expression matching a single valid character for the local part of an email address.
* @internal
*/
const EMAIL_GLYPH = /[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]/;
/**
* Characters allowed within a quoted string in the local part of an email address.
* @internal
*/
const EMAIL_QUOTE = /[\u0001-\u0008\u000b\u000c\u000e-\u001f\u0021\u0023-\u005b\u005d-\u007f]/;
/**
* Matches a backslash followed by any ASCII character except line feed and carriage return.
* @internal
*/
const EMAIL_ESCAPE = /\\[\u0001-\u0009\u000b\u000c\u000e-\u007f]/;
/**
* Validate the local part of an email address.
* @internal
*/
const EMAIL_ADDRESS = re`(?:${EMAIL_GLYPH}+(?:[.]${EMAIL_GLYPH}+)*|"(?:${EMAIL_QUOTE}|${EMAIL_ESCAPE})*")`;
/**
* validate an valid email address
* @group RegExp
* @category Constants
* @example
* ```typescript
* email.test('user@example.com'); // true
* email.test('user@sub.example.co'); // true
* email.test('invalid@domain'); // false
* email.test('not-an-email'); // false
* ```
*/
export const email = re`${EMAIL_ADDRESS}@(?:\[${ipV4}\]|${domain})$`;
//#endregion
//#region trimEquivalent
/**
* Regular expression that matches any whitespace character, including standard spaces,
* non-breaking spaces (`\u00A0`), and zero-width no-break spaces (`\uFEFF`).
* Useful for trimming or identifying whitespace-equivalent characters in strings.
* @group RegExp
* @category Constants
*/
export const trimEquivalent = /[\s\uFEFF\u00A0]/u;
//#endregion
//#region ANSI Escape Sequences
// Valid string terminator sequences are BEL, ESC\, and 0x9c (String Terminator))
/**
* Matches ANSI terminator: Bell (BEL), String Terminator (ST), ESC\\
* @internal
*/
const ANSI_ST = re`\u0007|\u001b\u005c|\u009c`;
/**
* OSC sequences only: ESC ] ... ST (non-greedy until the first ST)
* @internal
*/
const ANSI_OSC = re`\u001B\][\s\S]*?${ANSI_ST}`;
/**
* CSI and related: ESC/C1, optional intermediates, optional params (supports ; and :) then final byte
* @internal
*/
const ANSI_CSI = re`[\u001B\u009B][\[\]()#;?]*(?:\d{1,4}(?:[;:]\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]`;
/**
* Regular expression that matches ANSI escape sequences, including OSC (Operating System Command)
* and CSI (Control Sequence Introducer) patterns. Useful for stripping or identifying ANSI codes
* in strings, such as those used for terminal text formatting.
*
* @see https://en.wikipedia.org/wiki/ANSI_escape_code
* @group RegExp
* @category Constants
*/
export const ansiEscapes = re('g')`${ANSI_OSC}|${ANSI_CSI}`;
//#endregion