diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index bf7c05a0e..ba6041d49 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -5,8 +5,11 @@ feat(validatorName): brief title of what has been done
+
+
## Checklist
- [ ] PR contains only changes related; no stray files, etc.
- [ ] README updated (where applicable)
- [ ] Tests written (where applicable)
+- [ ] References provided in PR (where applicable)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index edd1960f4..8d27cf979 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,145 @@
+# 13.11.0
+
+### New Features / Validators
+
+- [#2144](https://github.com/validatorjs/validator.js/pull/2144) `isFreightContainerID`: for shipping containers IDs @songyuew
+- [#2188](https://github.com/validatorjs/validator.js/pull/2188) `isMailtoURI` @uksarkar
+
+### Fixes, New Locales and Enhancements
+
+- [#2025](https://github.com/validatorjs/validator.js/pull/2025) `isIBAN` add `MA` locale @lroudge
+- [#2117](https://github.com/validatorjs/validator.js/pull/2117) `isCreditCard` refactor @pano9000
+- [#2189](https://github.com/validatorjs/validator.js/pull/2189) `isLocale` add support for more language tags @kwahome
+- [#2203](https://github.com/validatorjs/validator.js/pull/2203) `isVAT` for `CU` @jimmyorpheus
+- [#2217](https://github.com/validatorjs/validator.js/pull/2217) `isJWT` @Prathamesh061
+- [#2222](https://github.com/validatorjs/validator.js/pull/2222) `IsFQDN` test enhancements @aalekhpatel07
+- [#2226](https://github.com/validatorjs/validator.js/pull/2226) `isAlpha`, `isAlphanumeric` for `kk-KZ` @BekStar7
+- [#2229](https://github.com/validatorjs/validator.js/pull/2229) `isEmail` support `allow_underscores` @guspower
+- [#2231](https://github.com/validatorjs/validator.js/pull/2231) `isDate` enhance Date declaration compatibility across multiple environments @CiprianS
+- [#2235](https://github.com/validatorjs/validator.js/pull/2235) `isIBAN` add white and blacklist options to the isIBAN validator @edilson
+- [#2237](https://github.com/validatorjs/validator.js/pull/2237) `isEmail` do not allow non-breaking space in user part @jeremy21212121
+- `isMobilePhone`:
+ - [#2175](https://github.com/validatorjs/validator.js/pull/2175) `so-SO` @ohersi
+ - [#2176](https://github.com/validatorjs/validator.js/pull/2176) `fr-CF` @cheboi
+ - [#2197](https://github.com/validatorjs/validator.js/pull/2197) `es-CU` @klaframboise
+ - [#2202](https://github.com/validatorjs/validator.js/pull/2202) `pl-PL` @czerwony03
+ - [#2209](https://github.com/validatorjs/validator.js/pull/2209) `fr-WF` @aidos42
+ - [#2246](https://github.com/validatorjs/validator.js/pull/2246) `ar-SD` @Hussienma
+
+
+
+# 13.9.0
+
+### New Features / Validators
+
+- [#1892](https://github.com/validatorjs/validator.js/pull/1892) `isISO6391`: add ISO 639-1 validator @braaar
+- [#1974](https://github.com/validatorjs/validator.js/pull/1974) `isLuhnNumber` @ST-DDT
+
+### Fixes and Enhancements
+
+- [#1865](https://github.com/validatorjs/validator.js/pull/1865) `isMACAddress`: add EUI-validation @WikiRik @tux-tn
+- [#1888](https://github.com/validatorjs/validator.js/pull/1888) `isBase32`: add option for Crockford's base32 alternative @BigOsvaap
+- [#1916](https://github.com/validatorjs/validator.js/pull/1916) `isDataURI`: fix mediaType format @temoffey
+- [#1920](https://github.com/validatorjs/validator.js/pull/1920) `isEmail`: add `host_whitelist` option @poor-coder
+- [#1939](https://github.com/validatorjs/validator.js/pull/1939) `isFQDN`: fix `allow_numeric_tld` option @BigOsvaap
+- [#1962](https://github.com/validatorjs/validator.js/pull/1962) `isIP`: refactor @UnKnoWn-Consortium
+- [#1967](https://github.com/validatorjs/validator.js/pull/1967) `isLength` @ikkyu-3
+- [#1992](https://github.com/validatorjs/validator.js/pull/1992) `isMagnetURI` @Rhilip @tux-tn
+- [#1995](https://github.com/validatorjs/validator.js/pull/1995) `isURL`: fix check for host @mortbauer
+- [#2008](https://github.com/validatorjs/validator.js/pull/2008) `isCreditCard` @brianwhaley
+- [#2075](https://github.com/validatorjs/validator.js/pull/2075) `isAfter`: allow usage of option object @WikiRik
+- [#2114](https://github.com/validatorjs/validator.js/pull/2114) `isRgbColor` @pano9000
+- [#2122](https://github.com/validatorjs/validator.js/pull/2122) `isDataURI`: fix MIME types with underscores @pano9000
+- [#2148](https://github.com/validatorjs/validator.js/pull/2148) `isStrongPassword` @sandmule
+- [#2157](https://github.com/validatorjs/validator.js/pull/2157) `isISBN`: allow usage of option object @WikiRik
+- [#2170](https://github.com/validatorjs/validator.js/pull/2170) `isEmail`: fix `ignore_max_length` for FQDN @sakhmedbayev
+- [#2020](https://github.com/validatorjs/validator.js/pull/2170) `isFloat`: fix comma(,) passing as float @frederike-ramin
+
+- Documentation fixes:
+ - [#1860](https://github.com/validatorjs/validator.js/pull/1860) @leonardovillela
+ - [#1861](https://github.com/validatorjs/validator.js/pull/1860) @tux-tn
+ - [#1957](https://github.com/validatorjs/validator.js/pull/1957) @tfilo
+ - [#2010](https://github.com/validatorjs/validator.js/pull/2010) @marcelozarate
+ - [#2107](https://github.com/validatorjs/validator.js/pull/2107) @pano9000
+ - [#2160](https://github.com/validatorjs/validator.js/pull/2160) @WikiRik
+
+- Code Refactors:
+ - [#1942](https://github.com/validatorjs/validator.js/pull/1942) @CommanderRoot
+ - [#1975](https://github.com/validatorjs/validator.js/pull/1975) @fedeci
+ - [#2137](https://github.com/validatorjs/validator.js/pull/2137) [#2132](https://github.com/validatorjs/validator.js/pull/2132) @pano9000
+
+### New and Improved Locales
+
+- `isAlpha`, `isAlphanumeric`:
+ - [#1678](https://github.com/validatorjs/validator.js/pull/1678) `bn-BD` @rak810
+ - [#1996](https://github.com/validatorjs/validator.js/pull/1996) `si-LK` @melkorCBA
+ - [#2014](https://github.com/validatorjs/validator.js/pull/2014) `ja-JP` @starcharles
+ - [#1995](https://github.com/validatorjs/validator.js/pull/1995) `ko-KR` @Dongkyuuuu
+
+- `isBIC`:
+ - [#2046](https://github.com/validatorjs/validator.js/pull/2046) `XK` @import-brain
+
+- `isIdentityCard`:
+ - [#2142](https://github.com/validatorjs/validator.js/pull/2142) `hk-HK` @Dongkyuuuu
+
+- `isMobilePhone`:
+ - [#1813](https://github.com/validatorjs/validator.js/pull/1813) `my-MM`, @ferdousulhaque
+ - [#1868](https://github.com/validatorjs/validator.js/pull/1868) `de-DE`, @thomaschaaf
+ - [#1896](https://github.com/validatorjs/validator.js/pull/1896) `en-LS`, @DevilsAutumn
+ - [#1897](https://github.com/validatorjs/validator.js/pull/1897) `el-CY`, @ikerasiotis
+ - [#1909](https://github.com/validatorjs/validator.js/pull/1909) `es-NI`, @ajGingrich
+ - [#1910](https://github.com/validatorjs/validator.js/pull/1910) `az-AZ`, @shaanaliyev
+ - [#1922](https://github.com/validatorjs/validator.js/pull/1922) `ir-IR`, @ArashST79
+ - [#1924](https://github.com/validatorjs/validator.js/pull/1924) `ky-KG`, @arsalanfiroozi
+ - [#1925](https://github.com/validatorjs/validator.js/pull/1925) `ar-YE`, `ar-EH`, `fa-AF`, @Mustafiz04
+ - [#1932](https://github.com/validatorjs/validator.js/pull/1932) `ro-MD`, @mik7up
+ - [#1940](https://github.com/validatorjs/validator.js/pull/1940) `ar-YE`, `en-BS`, @savannahvaith
+ - [#1952](https://github.com/validatorjs/validator.js/pull/1952) `ka-GE`, @avkvak
+ - [#1964](https://github.com/validatorjs/validator.js/pull/1964) [#1951](https://github.com/validatorjs/validator.js/pull/1951) `pt-BR`, @jhcaiafa @matheusnascgomes
+ - [#1983](https://github.com/validatorjs/validator.js/pull/1983) `es-HN`, @ademyan05
+ - [#1985](https://github.com/validatorjs/validator.js/pull/1985) `nl-AW`, @adida948
+ - [#1986](https://github.com/validatorjs/validator.js/pull/1986) `en-JM`, @ademyan05
+ - [#1993](https://github.com/validatorjs/validator.js/pull/1993) `mn-MN`, @rksp25
+ - [#1997](https://github.com/validatorjs/validator.js/pull/1997) `fr-BJ`, @rkuma552 @rksp25
+ - [#2001](https://github.com/validatorjs/validator.js/pull/2001) `mg-MG`, @ShivangiRai1310
+ - [#2002](https://github.com/validatorjs/validator.js/pull/2002) `en-PG`, @kai2128
+ - [#2004](https://github.com/validatorjs/validator.js/pull/2004) `en-AG`, @jiaweilow
+ - [#2007](https://github.com/validatorjs/validator.js/pull/2007) `en-AI`, @elaine1129
+ - [#2011](https://github.com/validatorjs/validator.js/pull/2011) `en-KN`, @Eelyneee
+ - [#2041](https://github.com/validatorjs/validator.js/pull/2041) `fr-CD`, @coolbeatz71
+ - [#2084](https://github.com/validatorjs/validator.js/pull/2084) `en-SS`, @cheboi
+ - [#2109](https://github.com/validatorjs/validator.js/pull/2109) `dv-MV`, @pano9000
+ - [#2129](https://github.com/validatorjs/validator.js/pull/2129) `en-HN`, @WikiRik
+ - [#2148](https://github.com/validatorjs/validator.js/pull/2148) `ar-KW`, @Yazan-KE @WikiRik
+ - [#2112](https://github.com/validatorjs/validator.js/pull/2112) `el-GR`, @pano9000
+ - [#2116](https://github.com/validatorjs/validator.js/pull/2116) `en-BM`, @pano9000
+ - [#2155](https://github.com/validatorjs/validator.js/pull/2155) `ms-MY`, @pano9000
+ - [#2156](https://github.com/validatorjs/validator.js/pull/2156) `ro-RO`, @pano9000
+
+- `isLicensePlate`:
+ - [#1665](https://github.com/validatorjs/validator.js/pull/1665) `sv-SE`, @elmaxe
+ - [#1895](https://github.com/validatorjs/validator.js/pull/1895) `hu-HU`, @szabolcstarnai
+ - [#1944](https://github.com/validatorjs/validator.js/pull/1944) `en-NI`, @NishantJS
+ - [#1945](https://github.com/validatorjs/validator.js/pull/1945) `de-DE`, @bennetfabian
+ - [#1945](https://github.com/validatorjs/validator.js/pull/1945) `de-DE`, @bennetfabian
+ - [#2103](https://github.com/validatorjs/validator.js/pull/2103) `es-AR`, @alvarocastro
+
+- `isPassportNumber`:
+ - [#1515](https://github.com/validatorjs/validator.js/pull/1515) `JM`,`KZ`,`LI`,`NZ` @JuanFML
+ - [#1814](https://github.com/validatorjs/validator.js/pull/1814) `TH` @TonPC64 @braaar
+ - [#2061](https://github.com/validatorjs/validator.js/pull/2061) `AZ` @djeks922
+ - [#2073](https://github.com/validatorjs/validator.js/pull/2073) `PH`,`PK` @digambar-t7
+
+- `isPostalCode`:
+ - [#1951](https://github.com/validatorjs/validator.js/pull/1951) `BA`, @matheusnascgomes
+ - [#2134](https://github.com/validatorjs/validator.js/pull/2134) `BY`, @pano9000
+ - [#2136](https://github.com/validatorjs/validator.js/pull/2136) `IR`, @pano9000
+
+
+- `isTaxID`:
+ - [#1867](https://github.com/validatorjs/validator.js/pull/1867) `en-CA`, @boonya
+ - [#1989](https://github.com/validatorjs/validator.js/pull/1989) `'AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'EL', 'HU', 'IE', 'LV', 'LT', 'LU', 'MT', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE', 'AL', 'MK', 'AU', 'BY', 'CA', 'IS', 'IN', 'ID', 'IL', 'KZ', 'NZ', 'NG', 'NO', 'PH', 'RU', 'SM', 'SA', 'RS', 'CH', 'TR', 'UA', 'UZ', 'AR', 'BO', 'BR', 'CL', 'CO', 'CR', 'EC', 'SV', 'GT', 'HN', 'MX', 'NI', 'PA', 'PY', 'PE', 'DO', 'UY', 'VE'` @Dev1lDragon
+
## 13.7.0
### New Features
diff --git a/README.md b/README.md
index de00deb72..e00b7cfac 100644
--- a/README.md
+++ b/README.md
@@ -91,8 +91,8 @@ Validator | Description
**contains(str, seed [, options])** | check if the string contains the seed.
`options` is an object that defaults to `{ ignoreCase: false, minOccurrences: 1 }`.
Options:
`ignoreCase`: Ignore case when doing comparison, default false.
`minOccurences`: Minimum number of occurrences for the seed in the string. Defaults to 1.
**equals(str, comparison)** | check if the string matches the comparison.
**isAfter(str [, options])** | check if the string is a date that is after the specified date.
`options` is an object that defaults to `{ comparisonDate: Date().toString() }`.
**Options:**
`comparisonDate`: Date to compare to. Defaults to `Date().toString()` (now).
-**isAlpha(str [, locale, options])** | check if the string contains only letters (a-zA-Z).
`locale` is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'bg-BG', 'bn', 'cs-CZ', 'da-DK', 'de-DE', 'el-GR', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'es-ES', 'fa-IR', 'fi-FI', 'fr-CA', 'fr-FR', 'he', 'hi-IN', 'hu-HU', 'it-IT', 'ko-KR', 'ja-JP', 'ku-IQ', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'si-LK', 'sl-SI', 'sk-SK', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'tr-TR', 'uk-UA']` and defaults to `en-US`. Locale list is `validator.isAlphaLocales`. `options` is an optional object that can be supplied with the following key(s): `ignore` which can either be a String or RegExp of characters to be ignored e.g. " -" will ignore spaces and -'s.
-**isAlphanumeric(str [, locale, options])** | check if the string contains only letters and numbers (a-zA-Z0-9).
`locale` is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'bn', 'bg-BG', 'cs-CZ', 'da-DK', 'de-DE', 'el-GR', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'es-ES', 'fa-IR', 'fi-FI', 'fr-CA', 'fr-FR', 'he', 'hi-IN', 'hu-HU', 'it-IT', 'ko-KR', 'ja-JP','ku-IQ', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'si-LK', 'sl-SI', 'sk-SK', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'tr-TR', 'uk-UA']`) and defaults to `en-US`. Locale list is `validator.isAlphanumericLocales`. `options` is an optional object that can be supplied with the following key(s): `ignore` which can either be a String or RegExp of characters to be ignored e.g. " -" will ignore spaces and -'s.
+**isAlpha(str [, locale, options])** | check if the string contains only letters (a-zA-Z).
`locale` is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'bg-BG', 'bn', 'cs-CZ', 'da-DK', 'de-DE', 'el-GR', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'es-ES', 'fa-IR', 'fi-FI', 'fr-CA', 'fr-FR', 'he', 'hi-IN', 'hu-HU', 'it-IT', 'kk-KZ', 'ko-KR', 'ja-JP', 'ku-IQ', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'si-LK', 'sl-SI', 'sk-SK', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'th-TH', 'tr-TR', 'uk-UA']` and defaults to `en-US`. Locale list is `validator.isAlphaLocales`. `options` is an optional object that can be supplied with the following key(s): `ignore` which can either be a String or RegExp of characters to be ignored e.g. " -" will ignore spaces and -'s.
+**isAlphanumeric(str [, locale, options])** | check if the string contains only letters and numbers (a-zA-Z0-9).
`locale` is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'bn', 'bg-BG', 'cs-CZ', 'da-DK', 'de-DE', 'el-GR', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'es-ES', 'fa-IR', 'fi-FI', 'fr-CA', 'fr-FR', 'he', 'hi-IN', 'hu-HU', 'it-IT', 'kk-KZ', 'ko-KR', 'ja-JP','ku-IQ', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'si-LK', 'sl-SI', 'sk-SK', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'th-TH', 'tr-TR', 'uk-UA']`) and defaults to `en-US`. Locale list is `validator.isAlphanumericLocales`. `options` is an optional object that can be supplied with the following key(s): `ignore` which can either be a String or RegExp of characters to be ignored e.g. " -" will ignore spaces and -'s.
**isAscii(str)** | check if the string contains ASCII chars only.
**isBase32(str [, options])** | check if the string is base32 encoded. `options` is optional and defaults to `{ crockford: false }`.
When `crockford` is true it tests the given base32 encoded string using [Crockford's base32 alternative][Crockford Base32].
**isBase58(str)** | check if the string is base58 encoded.
@@ -109,18 +109,19 @@ Validator | Description
**isDecimal(str [, options])** | check if the string represents a decimal number, such as 0.1, .3, 1.1, 1.00003, 4.0, etc.
`options` is an object which defaults to `{force_decimal: false, decimal_digits: '1,', locale: 'en-US'}`.
`locale` determines the decimal separator and is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'bg-BG', 'cs-CZ', 'da-DK', 'de-DE', 'el-GR', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'es-ES', 'fa', 'fa-AF', 'fa-IR', 'fr-FR', 'fr-CA', 'hu-HU', 'id-ID', 'it-IT', 'ku-IQ', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pl-Pl', 'pt-BR', 'pt-PT', 'ru-RU', 'sl-SI', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'tr-TR', 'uk-UA', 'vi-VN']`.
**Note:** `decimal_digits` is given as a range like '1,3', a specific value like '3' or min like '1,'.
**isDivisibleBy(str, number)** | check if the string is a number that is divisible by another.
**isEAN(str)** | check if the string is an [EAN (European Article Number)][European Article Number].
-**isEmail(str [, options])** | check if the string is an email.
`options` is an object which defaults to `{ allow_display_name: false, require_display_name: false, allow_utf8_local_part: true, require_tld: true, allow_ip_domain: false, domain_specific_validation: false, blacklisted_chars: '', host_blacklist: [] }`. If `allow_display_name` is set to true, the validator will also match `Display Name `. If `require_display_name` is set to true, the validator will reject strings without the format `Display Name `. If `allow_utf8_local_part` is set to false, the validator will not allow any non-English UTF8 character in email address' local part. If `require_tld` is set to false, email addresses without a TLD in their domain will also be matched. If `ignore_max_length` is set to true, the validator will not check for the standard max length of an email. If `allow_ip_domain` is set to true, the validator will allow IP addresses in the host part. If `domain_specific_validation` is true, some additional validation will be enabled, e.g. disallowing certain syntactically valid email addresses that are rejected by Gmail. If `blacklisted_chars` receives a string, then the validator will reject emails that include any of the characters in the string, in the name part. If `host_blacklist` is set to an array of strings and the part of the email after the `@` symbol matches one of the strings defined in it, the validation fails. If `host_whitelist` is set to an array of strings and the part of the email after the `@` symbol matches none of the strings defined in it, the validation fails.
+**isEmail(str [, options])** | check if the string is an email.
`options` is an object which defaults to `{ allow_display_name: false, require_display_name: false, allow_utf8_local_part: true, require_tld: true, allow_ip_domain: false, allow_underscores: false, domain_specific_validation: false, blacklisted_chars: '', host_blacklist: [] }`. If `allow_display_name` is set to true, the validator will also match `Display Name `. If `require_display_name` is set to true, the validator will reject strings without the format `Display Name `. If `allow_utf8_local_part` is set to false, the validator will not allow any non-English UTF8 character in email address' local part. If `require_tld` is set to false, email addresses without a TLD in their domain will also be matched. If `ignore_max_length` is set to true, the validator will not check for the standard max length of an email. If `allow_ip_domain` is set to true, the validator will allow IP addresses in the host part. If `domain_specific_validation` is true, some additional validation will be enabled, e.g. disallowing certain syntactically valid email addresses that are rejected by Gmail. If `blacklisted_chars` receives a string, then the validator will reject emails that include any of the characters in the string, in the name part. If `host_blacklist` is set to an array of strings and the part of the email after the `@` symbol matches one of the strings defined in it, the validation fails. If `host_whitelist` is set to an array of strings and the part of the email after the `@` symbol matches none of the strings defined in it, the validation fails.
**isEmpty(str [, options])** | check if the string has a length of zero.
`options` is an object which defaults to `{ ignore_whitespace: false }`.
**isEthereumAddress(str)** | check if the string is an [Ethereum][Ethereum] address. Does not validate address checksums.
**isFloat(str [, options])** | check if the string is a float.
`options` is an object which can contain the keys `min`, `max`, `gt`, and/or `lt` to validate the float is within boundaries (e.g. `{ min: 7.22, max: 9.55 }`) it also has `locale` as an option.
`min` and `max` are equivalent to 'greater or equal' and 'less or equal', respectively while `gt` and `lt` are their strict counterparts.
`locale` determines the decimal separator and is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'bg-BG', 'cs-CZ', 'da-DK', 'de-DE', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'es-ES', 'fr-CA', 'fr-FR', 'hu-HU', 'it-IT', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'sl-SI', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'tr-TR', 'uk-UA']`. Locale list is `validator.isFloatLocales`.
**isFQDN(str [, options])** | check if the string is a fully qualified domain name (e.g. domain.com).
`options` is an object which defaults to `{ require_tld: true, allow_underscores: false, allow_trailing_dot: false, allow_numeric_tld: false, allow_wildcard: false, ignore_max_length: false }`. If `allow_wildcard` is set to true, the validator will allow domain starting with `*.` (e.g. `*.example.com` or `*.shop.example.com`).
+**isFreightContainerID(str)** | alias for `isISO6346`, check if the string is a valid [ISO 6346](https://en.wikipedia.org/wiki/ISO_6346) shipping container identification.
**isFullWidth(str)** | check if the string contains any full-width chars.
**isHalfWidth(str)** | check if the string contains any half-width chars.
**isHash(str, algorithm)** | check if the string is a hash of type algorithm.
Algorithm is one of `['crc32', 'crc32b', 'md4', 'md5', 'ripemd128', 'ripemd160', 'sha1', 'sha256', 'sha384', 'sha512', 'tiger128', 'tiger160', 'tiger192']`.
**isHexadecimal(str)** | check if the string is a hexadecimal number.
**isHexColor(str)** | check if the string is a hexadecimal color.
**isHSL(str)** | check if the string is an HSL (hue, saturation, lightness, optional alpha) color based on [CSS Colors Level 4 specification][CSS Colors Level 4 Specification].
Comma-separated format supported. Space-separated format supported with the exception of a few edge cases (ex: `hsl(200grad+.1%62%/1)`).
-**isIBAN(str)** | check if the string is an IBAN (International Bank Account Number).
+**isIBAN(str, [, options])** | check if the string is an IBAN (International Bank Account Number).
`options` is an object which accepts two attributes: `whitelist`: where you can restrict IBAN codes you want to receive data from and `blacklist`: where you can remove some of the countries from the current list. For both you can use an array with the following values `['AD','AE','AL','AT','AZ','BA','BE','BG','BH','BR','BY','CH','CR','CY','CZ','DE','DK','DO','EE','EG','ES','FI','FO','FR','GB','GE','GI','GL','GR','GT','HR','HU','IE','IL','IQ','IR','IS','IT','JO','KW','KZ','LB','LC','LI','LT','LU','LV','MC','MD','ME','MK','MR','MT','MU','MZ','NL','NO','PK','PL','PS','PT','QA','RO','RS','SA','SC','SE','SI','SK','SM','SV','TL','TN','TR','UA','VA','VG','XK']`.
**isIdentityCard(str [, locale])** | check if the string is a valid identity card code.
`locale` is one of `['LK', 'PL', 'ES', 'FI', 'IN', 'IT', 'IR', 'MZ', 'NO', 'TH', 'zh-TW', 'he-IL', 'ar-LY', 'ar-TN', 'zh-CN', 'zh-HK']` OR `'any'`. If 'any' is used, function will check if any of the locales match.
Defaults to 'any'.
**isIMEI(str [, options]))** | check if the string is a valid [IMEI number][IMEI]. IMEI should be of format `###############` or `##-######-######-#`.
`options` is an object which can contain the keys `allow_hyphens`. Defaults to first format. If `allow_hyphens` is set to true, the validator will validate the second format.
**isIn(str, values)** | check if the string is in an array of allowed values.
@@ -129,6 +130,7 @@ Validator | Description
**isIPRange(str [, version])** | check if the string is an IP Range (version 4 or 6).
**isISBN(str [, options])** | check if the string is an [ISBN][ISBN].
`options` is an object that has no default.
**Options:**
`version`: ISBN version to compare to. Accepted values are '10' and '13'. If none provided, both will be tested.
**isISIN(str)** | check if the string is an [ISIN][ISIN] (stock/security identifier).
+**isISO6346(str)** | check if the string is a valid [ISO 6346](https://en.wikipedia.org/wiki/ISO_6346) shipping container identification.
**isISO6391(str)** | check if the string is a valid [ISO 639-1][ISO 639-1] language code.
**isISO8601(str [, options])** | check if the string is a valid [ISO 8601][ISO 8601] date.
`options` is an object which defaults to `{ strict: false, strictSeparator: false }`. If `strict` is true, date strings with invalid dates like `2009-02-29` will be invalid. If `strictSeparator` is true, date strings with date and time separated by anything other than a T will be invalid.
**isISO31661Alpha2(str)** | check if the string is a valid [ISO 3166-1 alpha-2][ISO 3166-1 alpha-2] officially assigned country code.
@@ -143,17 +145,18 @@ Validator | Description
**isLicensePlate(str, locale)** | check if the string matches the format of a country's license plate.
`locale` is one of `['cs-CZ', 'de-DE', 'de-LI', 'en-IN', 'es-AR', 'hu-HU', 'pt-BR', 'pt-PT', 'sq-AL', 'sv-SE']` or `'any'`.
**isLocale(str)** | check if the string is a locale.
**isLowercase(str)** | check if the string is lowercase.
-**isLuhnValid(str)** | check if the string passes the [Luhn check][Luhn Check].
+**isLuhnNumber(str)** | check if the string passes the [Luhn algorithm check](https://en.wikipedia.org/wiki/Luhn_algorithm).
**isMACAddress(str [, options])** | check if the string is a MAC address.
`options` is an object which defaults to `{ no_separators: false }`. If `no_separators` is true, the validator will allow MAC addresses without separators. Also, it allows the use of hyphens, spaces or dots e.g. '01 02 03 04 05 ab', '01-02-03-04-05-ab' or '0102.0304.05ab'. The options also allow a `eui` property to specify if it needs to be validated against EUI-48 or EUI-64. The accepted values of `eui` are: 48, 64.
**isMagnetURI(str)** | check if the string is a [Magnet URI format][Magnet URI Format].
+**isMailtoURI(str, [, options])** | check if the string is a [Magnet URI format][Mailto URI Format].
`options` is an object of validating emails inside the URI (check `isEmail`s options for details).
**isMD5(str)** | check if the string is a MD5 hash.
Please note that you can also use the `isHash(str, 'md5')` function. Keep in mind that MD5 has some collision weaknesses compared to other algorithms (e.g., SHA).
**isMimeType(str)** | check if the string matches to a valid [MIME type][MIME Type] format.
-**isMobilePhone(str [, locale [, options]])** | check if the string is a mobile phone number,
`locale` is either an array of locales (e.g. `['sk-SK', 'sr-RS']`) OR one of `['am-Am', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-EH', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-PS', 'ar-SA', 'ar-SY', 'ar-TN', 'ar-YE', 'az-AZ', 'az-LB', 'az-LY', 'be-BY', 'bg-BG', 'bn-BD', 'bs-BA', 'ca-AD', 'cs-CZ', 'da-DK', 'de-AT', 'de-CH', 'de-DE', 'de-LU', 'dv-MV', 'dz-BT', 'el-CY', 'el-GR', 'en-AG', 'en-AI', 'en-AU', 'en-BM', 'en-BS', 'en-BW', 'en-CA', 'en-GB', 'en-GG', 'en-GH', 'en-GY', 'en-HK', 'en-IE', 'en-IN', 'en-JM', 'en-KE', 'en-KI', 'en-KN', 'en-LS', 'en-MO', 'en-MT', 'en-MU', 'en-NG', 'en-NZ', 'en-PG', 'en-PH', 'en-PK', 'en-RW', 'en-SG', 'en-SL', 'en-SS', 'en-TZ', 'en-UG', 'en-US', 'en-ZA', 'en-ZM', 'en-ZW', 'es-AR', 'es-BO', 'es-CL', 'es-CO', 'es-CR', 'es-CU', 'es-DO', 'es-EC', 'es-ES', 'es-HN', 'es-MX', 'es-NI', 'es-PA', 'es-PE', 'es-PY', 'es-SV', 'es-UY', 'es-VE', 'et-EE', 'fa-AF', 'fa-IR', 'fi-FI', 'fj-FJ', 'fo-FO', 'fr-BE', 'fr-BF', 'fr-BJ', 'fr-CD', 'fr-FR', 'fr-GF', 'fr-GP', 'fr-MQ', 'fr-PF', 'fr-RE', 'ga-IE', 'he-IL', 'hu-HU', 'id-ID', 'ir-IR', 'it-IT', 'it-SM', 'ja-JP', 'ka-GE', 'kk-KZ', 'kl-GL', 'ko-KR', 'ky-KG', 'lt-LT', 'mg-MG', 'mn-MN', 'ms-MY', 'my-MM', 'mz-MZ', 'nb-NO', 'ne-NP', 'nl-AW', 'nl-BE', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-AO', 'pt-BR', 'pt-PT', 'ro-Md', 'ro-RO', 'ru-RU', 'si-LK', 'sk-SK', 'sl-SI', 'sq-AL', 'sr-RS', 'sv-SE', 'tg-TJ', 'th-TH', 'tk-TM', 'tr-TR', 'uk-UA', 'uz-UZ', 'vi-VN', 'zh-CN', 'zh-HK', 'zh-MO', 'zh-TW']` OR defaults to `'any'`. If 'any' or a falsey value is used, function will check if any of the locales match).
`options` is an optional object that can be supplied with the following keys: `strictMode`, if this is set to `true`, the mobile phone number must be supplied with the country code and therefore must start with `+`. Locale list is `validator.isMobilePhoneLocales`.
+**isMobilePhone(str [, locale [, options]])** | check if the string is a mobile phone number,
`locale` is either an array of locales (e.g. `['sk-SK', 'sr-RS']`) OR one of `['am-Am', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-EH', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-PS', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'az-AZ', 'az-LB', 'az-LY', 'be-BY', 'bg-BG', 'bn-BD', 'bs-BA', 'ca-AD', 'cs-CZ', 'da-DK', 'de-AT', 'de-CH', 'de-DE', 'de-LU', 'dv-MV', 'dz-BT', 'el-CY', 'el-GR', 'en-AG', 'en-AI', 'en-AU', 'en-BM', 'en-BS', 'en-BW', 'en-CA', 'en-GB', 'en-GG', 'en-GH', 'en-GY', 'en-HK', 'en-IE', 'en-IN', 'en-JM', 'en-KE', 'en-KI', 'en-KN', 'en-LS', 'en-MO', 'en-MT', 'en-MU', 'en-MW', 'en-NG', 'en-NZ', 'en-PG', 'en-PH', 'en-PK', 'en-RW', 'en-SG', 'en-SL', 'en-SS', 'en-TZ', 'en-UG', 'en-US', 'en-ZA', 'en-ZM', 'en-ZW', 'es-AR', 'es-BO', 'es-CL', 'es-CO', 'es-CR', 'es-CU', 'es-DO', 'es-EC', 'es-ES', 'es-HN', 'es-MX', 'es-NI', 'es-PA', 'es-PE', 'es-PY', 'es-SV', 'es-UY', 'es-VE', 'et-EE', 'fa-AF', 'fa-IR', 'fi-FI', 'fj-FJ', 'fo-FO', 'fr-BE', 'fr-BF', 'fr-BJ', 'fr-CD', 'fr-CF', 'fr-FR', 'fr-GF', 'fr-GP', 'fr-MQ', 'fr-PF', 'fr-RE', 'fr-WF', 'ga-IE', 'he-IL', 'hu-HU', 'id-ID', 'ir-IR', 'it-IT', 'it-SM', 'ja-JP', 'ka-GE', 'kk-KZ', 'kl-GL', 'ko-KR', 'ky-KG', 'lt-LT', 'mg-MG', 'mn-MN', 'ms-MY', 'my-MM', 'mz-MZ', 'nb-NO', 'ne-NP', 'nl-AW', 'nl-BE', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-AO', 'pt-BR', 'pt-PT', 'ro-Md', 'ro-RO', 'ru-RU', 'si-LK', 'sk-SK', 'sl-SI', 'so-SO', 'sq-AL', 'sr-RS', 'sv-SE', 'tg-TJ', 'th-TH', 'tk-TM', 'tr-TR', 'uk-UA', 'uz-UZ', 'vi-VN', 'zh-CN', 'zh-HK', 'zh-MO', 'zh-TW']` OR defaults to `'any'`. If 'any' or a falsey value is used, function will check if any of the locales match).
`options` is an optional object that can be supplied with the following keys: `strictMode`, if this is set to `true`, the mobile phone number must be supplied with the country code and therefore must start with `+`. Locale list is `validator.isMobilePhoneLocales`.
**isMongoId(str)** | check if the string is a valid hex-encoded representation of a [MongoDB ObjectId][mongoid].
**isMultibyte(str)** | check if the string contains one or more multibyte chars.
**isNumeric(str [, options])** | check if the string contains only numbers.
`options` is an object which defaults to `{ no_symbols: false }` it also has `locale` as an option. If `no_symbols` is true, the validator will reject numeric strings that feature a symbol (e.g. `+`, `-`, or `.`).
`locale` determines the decimal separator and is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'bg-BG', 'cs-CZ', 'da-DK', 'de-DE', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'es-ES', 'fr-FR', 'fr-CA', 'hu-HU', 'it-IT', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'sl-SI', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'tr-TR', 'uk-UA']`.
**isOctal(str)** | check if the string is a valid octal number.
-**isPassportNumber(str, countryCode)** | check if the string is a valid passport number.
`countryCode` is one of `['AM', 'AR', 'AT', 'AU', 'AZ', 'BE', 'BG', 'BY', 'BR', 'CA', 'CH', 'CN', 'CY', 'CZ', 'DE', 'DK', 'DZ', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HU', 'IE', 'IN', 'IR', 'ID', 'IS', 'IT', 'JM', 'JP', 'KR', 'KZ', 'LI', 'LT', 'LU', 'LV', 'LY', 'MT', 'MX', 'MY', 'MZ', 'NL', 'NZ', 'PH', 'PK', 'PL', 'PT', 'RO', 'RU', 'SE', 'SL', 'SK', 'TH', 'TR', 'UA', 'US']`.
+**isPassportNumber(str, countryCode)** | check if the string is a valid passport number.
`countryCode` is one of `['AM', 'AR', 'AT', 'AU', 'AZ', 'BE', 'BG', 'BY', 'BR', 'CA', 'CH', 'CN', 'CY', 'CZ', 'DE', 'DK', 'DZ', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HU', 'IE', 'IN', 'IR', 'ID', 'IS', 'IT', 'JM', 'JP', 'KR', 'KZ', 'LI', 'LT', 'LU', 'LV', 'LY', 'MT', 'MX', 'MY', 'MZ', 'NL', 'NZ', 'PH', 'PK', 'PL', 'PT', 'RO', 'RU', 'SE', 'SL', 'SK', 'TH', 'TR', 'UA', 'US', 'ZA']`.
**isPort(str)** | check if the string is a valid port number.
**isPostalCode(str, locale)** | check if the string is a postal code.
`locale` is one of `['AD', 'AT', 'AU', 'AZ', 'BA', 'BE', 'BG', 'BR', 'BY', 'CA', 'CH', 'CN', 'CZ', 'DE', 'DK', 'DO', 'DZ', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HT', 'HU', 'ID', 'IE', 'IL', 'IN', 'IR', 'IS', 'IT', 'JP', 'KE', 'KR', 'LI', 'LK', 'LT', 'LU', 'LV', 'MG', 'MT', 'MX', 'MY', 'NL', 'NO', 'NP', 'NZ', 'PL', 'PR', 'PT', 'RO', 'RU', 'SA', 'SE', 'SG', 'SI', 'SK', 'TH', 'TN', 'TW', 'UA', 'US', 'ZA', 'ZM']` OR `'any'`. If 'any' is used, function will check if any of the locales match. Locale list is `validator.isPostalCodeLocales`.
**isRFC3339(str)** | check if the string is a valid [RFC 3339][RFC 3339] date.
@@ -164,7 +167,7 @@ Validator | Description
**isSlug(str)** | check if the string is of type slug.
**isStrongPassword(str [, options])** | check if the string can be considered a strong password or not. Allows for custom requirements or scoring rules. If `returnScore` is true, then the function returns an integer score for the password rather than a boolean.
Default options:
`{ minLength: 8, minLowercase: 1, minUppercase: 1, minNumbers: 1, minSymbols: 1, returnScore: false, pointsPerUnique: 1, pointsPerRepeat: 0.5, pointsForContainingLower: 10, pointsForContainingUpper: 10, pointsForContainingNumber: 10, pointsForContainingSymbol: 10 }`
**isTime(str [, options])** | check if the string is a valid time e.g. [`23:01:59`, new Date().toLocaleTimeString()].
`options` is an object which can contain the keys `hourFormat` or `mode`.
`hourFormat` is a key and defaults to `'hour24'`.
`mode` is a key and defaults to `'default'`.
`hourFomat` can contain the values `'hour12'` or `'hour24'`, `'hour24'` will validate hours in 24 format and `'hour12'` will validate hours in 12 format.
`mode` can contain the values `'default'` or `'withSeconds'`, `'default'` will validate `HH:MM` format, `'withSeconds'` will validate the `HH:MM:SS` format.
-**isTaxID(str, locale)** | check if the string is a valid Tax Identification Number. Default locale is `en-US`.
More info about exact TIN support can be found in `src/lib/isTaxID.js`.
Supported locales: `[ 'bg-BG', 'cs-CZ', 'de-AT', 'de-DE', 'dk-DK', 'el-CY', 'el-GR', 'en-CA', 'en-GB', 'en-IE', 'en-US', 'es-ES', 'et-EE', 'fi-FI', 'fr-BE', 'fr-CA', 'fr-FR', 'fr-LU', 'hr-HR', 'hu-HU', 'it-IT', 'lb-LU', 'lt-LT', 'lv-LV', 'mt-MT', 'nl-BE', 'nl-NL', 'pl-PL', 'pt-BR', 'pt-PT', 'ro-RO', 'sk-SK', 'sl-SI', 'sv-SE' ]`.
+**isTaxID(str, locale)** | check if the string is a valid Tax Identification Number. Default locale is `en-US`.
More info about exact TIN support can be found in `src/lib/isTaxID.js`.
Supported locales: `[ 'bg-BG', 'cs-CZ', 'de-AT', 'de-DE', 'dk-DK', 'el-CY', 'el-GR', 'en-CA', 'en-GB', 'en-IE', 'en-US', 'es-AR', 'es-ES', 'et-EE', 'fi-FI', 'fr-BE', 'fr-CA', 'fr-FR', 'fr-LU', 'hr-HR', 'hu-HU', 'it-IT', 'lb-LU', 'lt-LT', 'lv-LV', 'mt-MT', 'nl-BE', 'nl-NL', 'pl-PL', 'pt-BR', 'pt-PT', 'ro-RO', 'sk-SK', 'sl-SI', 'sv-SE' ]`.
**isURL(str [, options])** | check if the string is a URL.
`options` is an object which defaults to `{ protocols: ['http','https','ftp'], require_tld: true, require_protocol: false, require_host: true, require_port: false, require_valid_protocol: true, allow_underscores: false, host_whitelist: false, host_blacklist: false, allow_trailing_dot: false, allow_protocol_relative_urls: false, allow_fragments: true, allow_query_components: true, disallow_auth: false, validate_length: true }`.
`require_protocol` - if set to true isURL will return false if protocol is not present in the URL.
`require_valid_protocol` - isURL will check if the URL's protocol is present in the protocols option.
`protocols` - valid protocols can be modified with this option.
`require_host` - if set to false isURL will not check if host is present in the URL.
`require_port` - if set to true isURL will check if port is present in the URL.
`allow_protocol_relative_urls` - if set to true protocol relative URLs will be allowed.
`allow_fragments` - if set to false isURL will return false if fragments are present.
`allow_query_components` - if set to false isURL will return false if query components are present.
`validate_length` - if set to false isURL will skip string length validation (2083 characters is IE max URL length).
**isUUID(str [, version])** | check if the string is a UUID (version 1, 2, 3, 4 or 5).
**isVariableWidth(str)** | check if the string contains a mixture of full and half-width chars.
@@ -299,7 +302,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
[ISSN]: https://en.wikipedia.org/wiki/International_Standard_Serial_Number
[Luhn Check]: https://en.wikipedia.org/wiki/Luhn_algorithm
[Magnet URI Format]: https://en.wikipedia.org/wiki/Magnet_URI_scheme
+[Mailto URI Format]: https://en.wikipedia.org/wiki/Mailto
[MIME Type]: https://en.wikipedia.org/wiki/Media_type
[mongoid]: http://docs.mongodb.org/manual/reference/object-id/
[RFC 3339]: https://tools.ietf.org/html/rfc3339
-[VAT Number]: https://en.wikipedia.org/wiki/VAT_identification_number
\ No newline at end of file
+[VAT Number]: https://en.wikipedia.org/wiki/VAT_identification_number
diff --git a/package.json b/package.json
index 13768746f..153e5c1a4 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "validator",
"description": "String validation and sanitization",
- "version": "13.7.0",
+ "version": "13.11.0",
"sideEffects": false,
"homepage": "https://github.com/validatorjs/validator.js",
"files": [
@@ -51,6 +51,7 @@
"rimraf": "^3.0.0",
"rollup": "^0.47.0",
"rollup-plugin-babel": "^4.0.1",
+ "timezone-mock": "^1.3.6",
"uglify-js": "^3.0.19"
},
"scripts": {
diff --git a/src/index.js b/src/index.js
index f45eead05..bef4cfff4 100644
--- a/src/index.js
+++ b/src/index.js
@@ -70,7 +70,7 @@ import isBefore from './lib/isBefore';
import isIn from './lib/isIn';
-import isLuhnValid from './lib/isLuhnValid';
+import isLuhnNumber from './lib/isLuhnNumber';
import isCreditCard from './lib/isCreditCard';
import isIdentityCard from './lib/isIdentityCard';
@@ -88,6 +88,7 @@ import isCurrency from './lib/isCurrency';
import isBtcAddress from './lib/isBtcAddress';
+import { isISO6346, isFreightContainerID } from './lib/isISO6346';
import isISO6391 from './lib/isISO6391';
import isISO8601 from './lib/isISO8601';
import isRFC3339 from './lib/isRFC3339';
@@ -100,6 +101,7 @@ import isBase58 from './lib/isBase58';
import isBase64 from './lib/isBase64';
import isDataURI from './lib/isDataURI';
import isMagnetURI from './lib/isMagnetURI';
+import isMailtoURI from './lib/isMailtoURI';
import isMimeType from './lib/isMimeType';
@@ -124,7 +126,7 @@ import isStrongPassword from './lib/isStrongPassword';
import isVAT from './lib/isVAT';
-const version = '13.7.0';
+const version = '13.11.0';
const validator = {
version,
@@ -185,7 +187,7 @@ const validator = {
isAfter,
isBefore,
isIn,
- isLuhnValid,
+ isLuhnNumber,
isCreditCard,
isIdentityCard,
isEAN,
@@ -199,6 +201,8 @@ const validator = {
isEthereumAddress,
isCurrency,
isBtcAddress,
+ isISO6346,
+ isFreightContainerID,
isISO6391,
isISO8601,
isRFC3339,
@@ -210,6 +214,7 @@ const validator = {
isBase64,
isDataURI,
isMagnetURI,
+ isMailtoURI,
isMimeType,
isLatLong,
ltrim,
diff --git a/src/lib/alpha.js b/src/lib/alpha.js
index 0535e50d1..d540ed1cf 100644
--- a/src/lib/alpha.js
+++ b/src/lib/alpha.js
@@ -19,6 +19,7 @@ export const alpha = {
'pl-PL': /^[A-ZĄĆĘŚŁŃÓŻŹ]+$/i,
'pt-PT': /^[A-ZÃÁÀÂÄÇÉÊËÍÏÕÓÔÖÚÜ]+$/i,
'ru-RU': /^[А-ЯЁ]+$/i,
+ 'kk-KZ': /^[А-ЯЁ\u04D8\u04B0\u0406\u04A2\u0492\u04AE\u049A\u04E8\u04BA]+$/i,
'sl-SI': /^[A-ZČĆĐŠŽ]+$/i,
'sk-SK': /^[A-ZÁČĎÉÍŇÓŠŤÚÝŽĹŔĽÄÔ]+$/i,
'sr-RS@latin': /^[A-ZČĆŽŠĐ]+$/i,
@@ -58,6 +59,7 @@ export const alphanumeric = {
'pl-PL': /^[0-9A-ZĄĆĘŚŁŃÓŻŹ]+$/i,
'pt-PT': /^[0-9A-ZÃÁÀÂÄÇÉÊËÍÏÕÓÔÖÚÜ]+$/i,
'ru-RU': /^[0-9А-ЯЁ]+$/i,
+ 'kk-KZ': /^[0-9А-ЯЁ\u04D8\u04B0\u0406\u04A2\u0492\u04AE\u049A\u04E8\u04BA]+$/i,
'sl-SI': /^[0-9A-ZČĆĐŠŽ]+$/i,
'sk-SK': /^[0-9A-ZÁČĎÉÍŇÓŠŤÚÝŽĹŔĽÄÔ]+$/i,
'sr-RS@latin': /^[0-9A-ZČĆŽŠĐ]+$/i,
@@ -125,7 +127,7 @@ export const dotDecimal = ['ar-EG', 'ar-LB', 'ar-LY'];
export const commaDecimal = [
'bg-BG', 'cs-CZ', 'da-DK', 'de-DE', 'el-GR', 'en-ZM', 'es-ES', 'fr-CA', 'fr-FR',
'id-ID', 'it-IT', 'ku-IQ', 'hi-IN', 'hu-HU', 'nb-NO', 'nn-NO', 'nl-NL', 'pl-PL', 'pt-PT',
- 'ru-RU', 'si-LK', 'sl-SI', 'sr-RS@latin', 'sr-RS', 'sv-SE', 'tr-TR', 'uk-UA', 'vi-VN',
+ 'ru-RU', 'kk-KZ', 'si-LK', 'sl-SI', 'sr-RS@latin', 'sr-RS', 'sv-SE', 'tr-TR', 'uk-UA', 'vi-VN',
];
for (let i = 0; i < dotDecimal.length; i++) {
diff --git a/src/lib/isCreditCard.js b/src/lib/isCreditCard.js
index 0a874c9cf..938679d39 100644
--- a/src/lib/isCreditCard.js
+++ b/src/lib/isCreditCard.js
@@ -1,5 +1,5 @@
import assertString from './util/assertString';
-import isLuhnValid from './isLuhnValid';
+import isLuhnValid from './isLuhnNumber';
const cards = {
amex: /^3[47][0-9]{13}$/,
@@ -10,9 +10,17 @@ const cards = {
unionpay: /^(6[27][0-9]{14}|^(81[0-9]{14,17}))$/,
visa: /^(?:4[0-9]{12})(?:[0-9]{3,6})?$/,
};
-/* eslint-disable max-len */
-const allCards = /^(?:4[0-9]{12}(?:[0-9]{3,6})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12,15}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11}|6[27][0-9]{14}|^(81[0-9]{14,17}))$/;
-/* eslint-enable max-len */
+
+const allCards = (() => {
+ const tmpCardsArray = [];
+ for (const cardProvider in cards) {
+ // istanbul ignore else
+ if (cards.hasOwnProperty(cardProvider)) {
+ tmpCardsArray.push(cards[cardProvider]);
+ }
+ }
+ return tmpCardsArray;
+})();
export default function isCreditCard(card, options = {}) {
assertString(card);
@@ -26,7 +34,7 @@ export default function isCreditCard(card, options = {}) {
} else if (provider && !(provider.toLowerCase() in cards)) {
/* specific provider not in the list */
throw new Error(`${provider} is not a valid credit card provider.`);
- } else if (!(allCards.test(sanitized))) {
+ } else if (!allCards.some(cardProvider => cardProvider.test(sanitized))) {
// no specific provider
return false;
}
diff --git a/src/lib/isDate.js b/src/lib/isDate.js
index 8f2d51f3d..4fde3686b 100644
--- a/src/lib/isDate.js
+++ b/src/lib/isDate.js
@@ -48,7 +48,37 @@ export default function isDate(input, options) {
dateObj[formatWord.charAt(0)] = dateWord;
}
- return new Date(`${dateObj.m}/${dateObj.d}/${dateObj.y}`).getDate() === +dateObj.d;
+ let fullYear = dateObj.y;
+
+ if (dateObj.y.length === 2) {
+ const parsedYear = parseInt(dateObj.y, 10);
+
+ if (isNaN(parsedYear)) {
+ return false;
+ }
+
+ const currentYearLastTwoDigits = new Date().getFullYear() % 100;
+
+ if (parsedYear < currentYearLastTwoDigits) {
+ fullYear = `20${dateObj.y}`;
+ } else {
+ fullYear = `19${dateObj.y}`;
+ }
+ }
+
+ let month = dateObj.m;
+
+ if (dateObj.m.length === 1) {
+ month = `0${dateObj.m}`;
+ }
+
+ let day = dateObj.d;
+
+ if (dateObj.d.length === 1) {
+ day = `0${dateObj.d}`;
+ }
+
+ return new Date(`${fullYear}-${month}-${day}T00:00:00.000Z`).getUTCDate() === +dateObj.d;
}
if (!options.strictMode) {
diff --git a/src/lib/isEmail.js b/src/lib/isEmail.js
index d1c35bd46..12938765e 100644
--- a/src/lib/isEmail.js
+++ b/src/lib/isEmail.js
@@ -1,12 +1,13 @@
import assertString from './util/assertString';
-import merge from './util/merge';
import isByteLength from './isByteLength';
import isFQDN from './isFQDN';
import isIP from './isIP';
+import merge from './util/merge';
const default_email_options = {
allow_display_name: false,
+ allow_underscores: false,
require_display_name: false,
allow_utf8_local_part: true,
require_tld: true,
@@ -22,7 +23,7 @@ const splitNameAddress = /^([^\x00-\x1F\x7F-\x9F\cX]+) !(countryCode in ibanRegexThroughCountryCode));
+
+ if (countryCodeArrayFilteredWithObjectIbanCode.length > 0) {
+ return false;
+ }
+
+ return true;
+}
+
/**
* Check whether string has correct universal IBAN format
* The IBAN consists of up to 34 alphanumeric characters, as follows:
@@ -95,14 +115,37 @@ const ibanRegexThroughCountryCode = {
* NOTE: Permitted IBAN characters are: digits [0-9] and the 26 latin alphabetic [A-Z]
*
* @param {string} str - string under validation
+ * @param {object} options - object to pass the countries to be either whitelisted or blacklisted
* @return {boolean}
*/
-function hasValidIbanFormat(str) {
+function hasValidIbanFormat(str, options) {
// Strip white spaces and hyphens
const strippedStr = str.replace(/[\s\-]+/gi, '').toUpperCase();
const isoCountryCode = strippedStr.slice(0, 2).toUpperCase();
- return (isoCountryCode in ibanRegexThroughCountryCode) &&
+ const isoCountryCodeInIbanRegexCodeObject = isoCountryCode in ibanRegexThroughCountryCode;
+
+ if (options.whitelist) {
+ if (!hasOnlyValidCountryCodes(options.whitelist)) {
+ return false;
+ }
+
+ const isoCountryCodeInWhiteList = options.whitelist.includes(isoCountryCode);
+
+ if (!isoCountryCodeInWhiteList) {
+ return false;
+ }
+ }
+
+ if (options.blacklist) {
+ const isoCountryCodeInBlackList = options.blacklist.includes(isoCountryCode);
+
+ if (isoCountryCodeInBlackList) {
+ return false;
+ }
+ }
+
+ return (isoCountryCodeInIbanRegexCodeObject) &&
ibanRegexThroughCountryCode[isoCountryCode].test(strippedStr);
}
@@ -130,10 +173,10 @@ function hasValidIbanChecksum(str) {
return remainder === 1;
}
-export default function isIBAN(str) {
+export default function isIBAN(str, options = {}) {
assertString(str);
- return hasValidIbanFormat(str) && hasValidIbanChecksum(str);
+ return hasValidIbanFormat(str, options) && hasValidIbanChecksum(str);
}
export const locales = Object.keys(ibanRegexThroughCountryCode);
diff --git a/src/lib/isISO6346.js b/src/lib/isISO6346.js
new file mode 100644
index 000000000..0cb657e7c
--- /dev/null
+++ b/src/lib/isISO6346.js
@@ -0,0 +1,37 @@
+import assertString from './util/assertString';
+
+// https://en.wikipedia.org/wiki/ISO_6346
+// according to ISO6346 standard, checksum digit is mandatory for freight container but recommended
+// for other container types (J and Z)
+const isISO6346Str = /^[A-Z]{3}(U[0-9]{7})|([J,Z][0-9]{6,7})$/;
+const isDigit = /^[0-9]$/;
+
+export function isISO6346(str) {
+ assertString(str);
+
+ str = str.toUpperCase();
+
+ if (!isISO6346Str.test(str)) return false;
+
+ if (str.length === 11) {
+ let sum = 0;
+ for (let i = 0; i < str.length - 1; i++) {
+ if (!isDigit.test(str[i])) {
+ let convertedCode;
+ const letterCode = str.charCodeAt(i) - 55;
+ if (letterCode < 11) convertedCode = letterCode;
+ else if (letterCode >= 11 && letterCode <= 20) convertedCode = 12 + (letterCode % 11);
+ else if (letterCode >= 21 && letterCode <= 30) convertedCode = 23 + (letterCode % 21);
+ else convertedCode = 34 + (letterCode % 31);
+ sum += convertedCode * (2 ** i);
+ } else sum += str[i] * (2 ** i);
+ }
+
+ const checkSumDigit = sum % 11;
+ return Number(str[str.length - 1]) === checkSumDigit;
+ }
+
+ return true;
+}
+
+export const isFreightContainerID = isISO6346;
diff --git a/src/lib/isJWT.js b/src/lib/isJWT.js
index 1a8896f98..1d0ade5ee 100644
--- a/src/lib/isJWT.js
+++ b/src/lib/isJWT.js
@@ -7,7 +7,7 @@ export default function isJWT(str) {
const dotSplit = str.split('.');
const len = dotSplit.length;
- if (len > 3 || len < 2) {
+ if (len !== 3) {
return false;
}
diff --git a/src/lib/isLocale.js b/src/lib/isLocale.js
index cacac8aec..ec84c8fce 100644
--- a/src/lib/isLocale.js
+++ b/src/lib/isLocale.js
@@ -1,11 +1,111 @@
import assertString from './util/assertString';
-const localeReg = /^[A-Za-z]{2,4}([_-]([A-Za-z]{4}|[\d]{3}))?([_-]([A-Za-z]{2}|[\d]{3}))?$/;
+/*
+ = 3ALPHA ; selected ISO 639 codes
+ *2("-" 3ALPHA) ; permanently reserved
+ */
+const extlang = '([A-Za-z]{3}(-[A-Za-z]{3}){0,2})';
+
+/*
+ = 2*3ALPHA ; shortest ISO 639 code
+ ["-" extlang] ; sometimes followed by
+ ; extended language subtags
+ / 4ALPHA ; or reserved for future use
+ / 5*8ALPHA ; or registered language subtag
+ */
+const language = `(([a-zA-Z]{2,3}(-${extlang})?)|([a-zA-Z]{5,8}))`;
+
+/*
+ = 4ALPHA ; ISO 15924 code
+ */
+const script = '([A-Za-z]{4})';
+
+/*
+ = 2ALPHA ; ISO 3166-1 code
+ / 3DIGIT ; UN M.49 code
+ */
+const region = '([A-Za-z]{2}|\\d{3})';
+
+/*
+ = 5*8alphanum ; registered variants
+ / (DIGIT 3alphanum)
+ */
+const variant = '([A-Za-z0-9]{5,8}|(\\d[A-Z-a-z0-9]{3}))';
+
+/*
+ = DIGIT ; 0 - 9
+ / %x41-57 ; A - W
+ / %x59-5A ; Y - Z
+ / %x61-77 ; a - w
+ / %x79-7A ; y - z
+ */
+const singleton = '(\\d|[A-W]|[Y-Z]|[a-w]|[y-z])';
+
+/*
+ = singleton 1*("-" (2*8alphanum))
+ ; Single alphanumerics
+ ; "x" reserved for private use
+ */
+const extension = `(${singleton}(-[A-Za-z0-9]{2,8})+)`;
+
+/*
+ = "x" 1*("-" (1*8alphanum))
+ */
+const privateuse = '(x(-[A-Za-z0-9]{1,8})+)';
+
+// irregular tags do not match the 'langtag' production and would not
+// otherwise be considered 'well-formed'. These tags are all valid, but
+// most are deprecated in favor of more modern subtags or subtag combination
+
+const irregular = '((en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|' +
+ '(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|' +
+ '(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE))';
+
+// regular tags match the 'langtag' production, but their subtags are not
+// extended language or variant subtags: their meaning is defined by
+// their registration and all of these are deprecated in favor of a more
+// modern subtag or sequence of subtags
+
+const regular = '((art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|' +
+ '(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang))';
+
+/*
+ = irregular ; non-redundant tags registered
+ / regular ; during the RFC 3066 era
+
+ */
+const grandfathered = `(${irregular}|${regular})`;
+
+/*
+ RFC 5646 defines delimitation of subtags via a hyphen:
+
+ "Subtag" refers to a specific section of a tag, delimited by a
+ hyphen, such as the subtags 'zh', 'Hant', and 'CN' in the tag "zh-
+ Hant-CN". Examples of subtags in this document are enclosed in
+ single quotes ('Hant')
+
+ However, we need to add "_" to maintain the existing behaviour.
+ */
+const delimiter = '(-|_)';
+
+/*
+ = language
+ ["-" script]
+ ["-" region]
+ *("-" variant)
+ *("-" extension)
+ ["-" privateuse]
+ */
+const langtag = `${language}(${delimiter}${script})?(${delimiter}${region})?(${delimiter}${variant})*(${delimiter}${extension})*(${delimiter}${privateuse})?`;
+
+/*
+ Regex implementation based on BCP RFC 5646
+ Tags for Identifying Languages
+ https://www.rfc-editor.org/rfc/rfc5646.html
+ */
+const languageTagRegex = new RegExp(`(^${privateuse}$)|(^${grandfathered}$)|(^${langtag}$)`);
export default function isLocale(str) {
assertString(str);
- if (str === 'en_US_POSIX' || str === 'ca_ES_VALENCIA') {
- return true;
- }
- return localeReg.test(str);
+ return languageTagRegex.test(str);
}
diff --git a/src/lib/isLuhnValid.js b/src/lib/isLuhnNumber.js
similarity index 93%
rename from src/lib/isLuhnValid.js
rename to src/lib/isLuhnNumber.js
index da205271f..95a066115 100644
--- a/src/lib/isLuhnValid.js
+++ b/src/lib/isLuhnNumber.js
@@ -1,6 +1,6 @@
import assertString from './util/assertString';
-export default function isLuhnValid(str) {
+export default function isLuhnNumber(str) {
assertString(str);
const sanitized = str.replace(/[- ]+/g, '');
let sum = 0;
diff --git a/src/lib/isMailtoURI.js b/src/lib/isMailtoURI.js
new file mode 100644
index 000000000..0dd95b6a9
--- /dev/null
+++ b/src/lib/isMailtoURI.js
@@ -0,0 +1,67 @@
+import trim from './trim';
+import isEmail from './isEmail';
+import assertString from './util/assertString';
+
+function parseMailtoQueryString(queryString) {
+ const allowedParams = new Set(['subject', 'body', 'cc', 'bcc']),
+ query = { cc: '', bcc: '' };
+ let isParseFailed = false;
+
+ const queryParams = queryString.split('&');
+
+ if (queryParams.length > 4) {
+ return false;
+ }
+
+ for (const q of queryParams) {
+ const [key, value] = q.split('=');
+
+ // checked for invalid and duplicated query params
+ if (key && !allowedParams.has(key)) {
+ isParseFailed = true;
+ break;
+ }
+
+ if (value && (key === 'cc' || key === 'bcc')) {
+ query[key] = value;
+ }
+
+ if (key) {
+ allowedParams.delete(key);
+ }
+ }
+
+ return isParseFailed ? false : query;
+}
+
+export default function isMailtoURI(url, options) {
+ assertString(url);
+
+ if (url.indexOf('mailto:') !== 0) {
+ return false;
+ }
+
+ const [to = '', queryString = ''] = url.replace('mailto:', '').split('?');
+
+ if (!to && !queryString) {
+ return true;
+ }
+
+ const query = parseMailtoQueryString(queryString);
+
+ if (!query) {
+ return false;
+ }
+
+ return `${to},${query.cc},${query.bcc}`
+ .split(',')
+ .every((email) => {
+ email = trim(email, ' ');
+
+ if (email) {
+ return isEmail(email, options);
+ }
+
+ return true;
+ });
+}
diff --git a/src/lib/isMobilePhone.js b/src/lib/isMobilePhone.js
index 5c37d42bb..7c9b420e9 100644
--- a/src/lib/isMobilePhone.js
+++ b/src/lib/isMobilePhone.js
@@ -16,6 +16,7 @@ const phones = {
'ar-OM': /^((\+|00)968)?(9[1-9])\d{6}$/,
'ar-PS': /^(\+?970|0)5[6|9](\d{7})$/,
'ar-SA': /^(!?(\+?966)|0)?5\d{8}$/,
+ 'ar-SD': /^((\+?249)|0)?(9[012369]|1[012])\d{7}$/,
'ar-SY': /^(!?(\+?963)|0)?9\d{8}$/,
'ar-TN': /^(\+?216)?[2459]\d{7}$/,
'az-AZ': /^(\+994|0)(10|5[015]|7[07]|99)\d{7}$/,
@@ -48,12 +49,14 @@ const phones = {
'en-IN': /^(\+?91|0)?[6789]\d{9}$/,
'en-JM': /^(\+?876)?\d{7}$/,
'en-KE': /^(\+?254|0)(7|1)\d{8}$/,
+ 'fr-CF': /^(\+?236| ?)(70|75|77|72|21|22)\d{6}$/,
'en-SS': /^(\+?211|0)(9[1257])\d{7}$/,
'en-KI': /^((\+686|686)?)?( )?((6|7)(2|3|8)[0-9]{6})$/,
'en-KN': /^(?:\+1|1)869(?:46\d|48[89]|55[6-8]|66\d|76[02-7])\d{4}$/,
'en-LS': /^(\+?266)(22|28|57|58|59|27|52)\d{6}$/,
'en-MT': /^(\+?356|0)?(99|79|77|21|27|22|25)[0-9]{6}$/,
'en-MU': /^(\+?230|0)?\d{8}$/,
+ 'en-MW': /^(\+?265|0)(((77|88|31|99|98|21)\d{7})|(((111)|1)\d{6})|(32000\d{4}))$/,
'en-NA': /^(\+?264|0)(6|8)\d{7}$/,
'en-NG': /^(\+?234|0)?[789]\d{9}$/,
'en-NZ': /^(\+?64|0)[28]\d{7,9}$/,
@@ -75,7 +78,7 @@ const phones = {
'es-CO': /^(\+?57)?3(0(0|1|2|4|5)|1\d|2[0-4]|5(0|1))\d{7}$/,
'es-CL': /^(\+?56|0)[2-9]\d{1}\d{7}$/,
'es-CR': /^(\+506)?[2-8]\d{7}$/,
- 'es-CU': /^(\+53|0053)?5\d{7}/,
+ 'es-CU': /^(\+53|0053)?5\d{7}$/,
'es-DO': /^(\+?1)?8[024]9\d{7}$/,
'es-HN': /^(\+?504)?[9|8|3|2]\d{7}$/,
'es-EC': /^(\+?593|0)([2-7]|9[2-9])\d{7}$/,
@@ -103,6 +106,7 @@ const phones = {
'fr-MQ': /^(\+?596|0|00596)[67]\d{8}$/,
'fr-PF': /^(\+?689)?8[789]\d{6}$/,
'fr-RE': /^(\+?262|0|00262)[67]\d{8}$/,
+ 'fr-WF': /^(\+681)?\d{6}$/,
'he-IL': /^(\+972|0)([23489]|5[012345689]|77)[1-9]\d{6}$/,
'hu-HU': /^(\+?36|06)(20|30|31|50|70)\d{7}$/,
'id-ID': /^(\+?62|0)8(1[123456789]|2[1238]|3[1238]|5[12356789]|7[78]|9[56789]|8[123456789])([\s?|\d]{5,11})$/,
@@ -128,7 +132,7 @@ const phones = {
'nl-NL': /^(((\+|00)?31\(0\))|((\+|00)?31)|0)6{1}\d{8}$/,
'nl-AW': /^(\+)?297(56|59|64|73|74|99)\d{5}$/,
'nn-NO': /^(\+?47)?[49]\d{7}$/,
- 'pl-PL': /^(\+?48)? ?[5-8]\d ?\d{3} ?\d{2} ?\d{2}$/,
+ 'pl-PL': /^(\+?48)? ?([5-8]\d|45) ?\d{3} ?\d{2} ?\d{2}$/,
'pt-BR': /^((\+?55\ ?[1-9]{2}\ ?)|(\+?55\ ?\([1-9]{2}\)\ ?)|(0[1-9]{2}\ ?)|(\([1-9]{2}\)\ ?)|([1-9]{2}\ ?))((\d{4}\-?\d{4})|(9[1-9]{1}\d{3}\-?\d{4}))$/,
'pt-PT': /^(\+?351)?9[1236]\d{7}$/,
'pt-AO': /^(\+244)\d{9}$/,
@@ -138,6 +142,7 @@ const phones = {
'si-LK': /^(?:0|94|\+94)?(7(0|1|2|4|5|6|7|8)( |-)?)\d{7}$/,
'sl-SI': /^(\+386\s?|0)(\d{1}\s?\d{3}\s?\d{2}\s?\d{2}|\d{2}\s?\d{3}\s?\d{3})$/,
'sk-SK': /^(\+?421)? ?[1-9][0-9]{2} ?[0-9]{3} ?[0-9]{3}$/,
+ 'so-SO': /^(\+?252|0)((6[0-9])\d{7}|(7[1-9])\d{7})$/,
'sq-AL': /^(\+355|0)6[789]\d{6}$/,
'sr-RS': /^(\+3816|06)[- \d]{5,9}$/,
'sv-SE': /^(\+?46|0)[\s\-]?7[\s\-]?[02369]([\s\-]?\d){7}$/,
diff --git a/src/lib/isPassportNumber.js b/src/lib/isPassportNumber.js
index 11d01e8d1..4763d6566 100644
--- a/src/lib/isPassportNumber.js
+++ b/src/lib/isPassportNumber.js
@@ -66,6 +66,7 @@ const passportRegexByCountryCode = {
TR: /^[A-Z]\d{8}$/, // TURKEY
UA: /^[A-Z]{2}\d{6}$/, // UKRAINE
US: /^\d{9}$/, // UNITED STATES
+ ZA: /^[TAMD]\d{8}$/, // SOUTH AFRICA
};
/**
diff --git a/src/lib/isTaxID.js b/src/lib/isTaxID.js
index 933783f44..b82156ed1 100644
--- a/src/lib/isTaxID.js
+++ b/src/lib/isTaxID.js
@@ -376,6 +376,30 @@ function enUsCheck(tin) {
return enUsGetPrefixes().indexOf(tin.slice(0, 2)) !== -1;
}
+/*
+ * es-AR validation function
+ * Clave Única de Identificación Tributaria (CUIT/CUIL)
+ * Sourced from:
+ * - https://servicioscf.afip.gob.ar/publico/abc/ABCpaso2.aspx?id_nivel1=3036&id_nivel2=3040&p=Conceptos%20b%C3%A1sicos
+ * - https://es.wikipedia.org/wiki/Clave_%C3%9Anica_de_Identificaci%C3%B3n_Tributaria
+ */
+
+function esArCheck(tin) {
+ let accum = 0;
+ let digits = tin.split('');
+ let digit = parseInt(digits.pop(), 10);
+ for (let i = 0; i < digits.length; i++) {
+ accum += digits[9 - i] * (2 + (i % 6));
+ }
+ let verif = 11 - (accum % 11);
+ if (verif === 11) {
+ verif = 0;
+ } else if (verif === 10) {
+ verif = 9;
+ }
+ return digit === verif;
+}
+
/*
* es-ES validation function
* (Documento Nacional de Identidad (DNI)
@@ -1137,6 +1161,7 @@ const taxIdFormat = {
'en-GB': /^\d{10}$|^(?!GB|NK|TN|ZZ)(?![DFIQUV])[A-Z](?![DFIQUVO])[A-Z]\d{6}[ABCD ]$/i,
'en-IE': /^\d{7}[A-W][A-IW]{0,1}$/i,
'en-US': /^\d{2}[- ]{0,1}\d{7}$/,
+ 'es-AR': /(20|23|24|27|30|33|34)[0-9]{8}[0-9]/,
'es-ES': /^(\d{0,8}|[XYZKLM]\d{7})[A-HJ-NP-TV-Z]$/i,
'et-EE': /^[1-6]\d{6}(00[1-9]|0[1-9][0-9]|[1-6][0-9]{2}|70[0-9]|710)\d$/,
'fi-FI': /^\d{6}[-+A]\d{3}[0-9A-FHJ-NPR-Y]$/i,
@@ -1175,6 +1200,7 @@ const taxIdCheck = {
'en-CA': isCanadianSIN,
'en-IE': enIeCheck,
'en-US': enUsCheck,
+ 'es-AR': esArCheck,
'es-ES': esEsCheck,
'et-EE': etEeCheck,
'fi-FI': fiFiCheck,
diff --git a/src/lib/isVAT.js b/src/lib/isVAT.js
index 95593569d..ece7d8560 100644
--- a/src/lib/isVAT.js
+++ b/src/lib/isVAT.js
@@ -1,6 +1,21 @@
import assertString from './util/assertString';
import * as algorithms from './util/algorithms';
+const CH = (str) => {
+ // @see {@link https://www.ech.ch/de/ech/ech-0097/5.2.0}
+ const hasValidCheckNumber = (digits) => {
+ const lastDigit = digits.pop(); // used as check number
+ const weights = [5, 4, 3, 2, 7, 6, 5, 4];
+ const calculatedCheckNumber = (11 - (digits.reduce((acc, el, idx) =>
+ acc + (el * weights[idx]), 0) % 11)) % 11;
+
+ return lastDigit === calculatedCheckNumber;
+ };
+
+ // @see {@link https://www.estv.admin.ch/estv/de/home/mehrwertsteuer/uid/mwst-uid-nummer.html}
+ return /^(CHE[- ]?)?(\d{9}|(\d{3}\.\d{3}\.\d{3})|(\d{3} \d{3} \d{3})) ?(TVA|MWST|IVA)?$/.test(str) && hasValidCheckNumber((str.match(/\d/g).map(el => +el)));
+};
+
const PT = (str) => {
const match = str.match(/^(PT)?(\d{9})$/);
if (!match) {
@@ -69,7 +84,7 @@ export const vatMatchers = {
SM: str => /^(SM)?\d{5}$/.test(str),
SA: str => /^(SA)?\d{15}$/.test(str),
RS: str => /^(RS)?\d{9}$/.test(str),
- CH: str => /^(CH)?(\d{6}|\d{9}|(\d{3}.\d{3})|(\d{3}.\d{3}.\d{3}))(TVA|MWST|IVA)$/.test(str),
+ CH,
TR: str => /^(TR)?\d{10}$/.test(str),
UA: str => /^(UA)?\d{12}$/.test(str),
GB: str => /^GB((\d{3} \d{4} ([0-8][0-9]|9[0-6]))|(\d{9} \d{3})|(((GD[0-4])|(HA[5-9]))[0-9]{2}))$/.test(str),
diff --git a/test/validators.test.js b/test/validators.test.js
index 6c0fed5f0..b181b7df8 100644
--- a/test/validators.test.js
+++ b/test/validators.test.js
@@ -1,5 +1,6 @@
import assert from 'assert';
import fs from 'fs';
+import timezone_mock from 'timezone-mock';
import { format } from 'util';
import vm from 'vm';
import validator from '../src/index';
@@ -36,6 +37,7 @@ describe('Validators', () => {
'invalid.com',
'@invalid.com',
'foo@bar.com.',
+ 'foo@_bar.com',
'somename@gmail.com',
'foo@bar.co.uk.',
'z@co.c',
@@ -66,6 +68,9 @@ describe('Validators', () => {
'"wrong()[]",:;<>@@gmail.com',
'username@domain.com�',
'username@domain.com©',
+ 'nbsp test@test.com',
+ 'nbsp_test@te st.com',
+ 'nbsp_test@test.co m',
],
});
});
@@ -89,6 +94,16 @@ describe('Validators', () => {
});
});
+ it('should validate email addresses with underscores in the domain', () => {
+ test({
+ validator: 'isEmail',
+ args: [{ allow_underscores: true }],
+ valid: [
+ 'foobar@my_sarisari_store.typepad.com',
+ ],
+ invalid: [],
+ });
+ });
it('should validate email addresses without UTF8 characters in local part', () => {
test({
@@ -117,6 +132,7 @@ describe('Validators', () => {
'hans.m端ller@test.com',
'z@co.c',
'tüst@invalid.com',
+ 'nbsp test@test.com',
],
});
});
@@ -1604,6 +1620,27 @@ describe('Validators', () => {
});
});
+ it('should validate kazakh alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['kk-KZ'],
+ valid: [
+ 'Сәлем',
+ 'қанағаттандырылмағандықтарыңыздан',
+ 'Кешіріңіз',
+ 'Өкінішке',
+ 'Қайталаңызшы',
+ 'ағылшынша',
+ 'түсінбедім',
+ ],
+ invalid: [
+ 'Кешіріңіз1',
+ ' Кет бар ',
+ 'مرحبا العا',
+ ],
+ });
+ });
+
it('should validate Vietnamese alpha strings', () => {
test({
validator: 'isAlpha',
@@ -2433,6 +2470,27 @@ describe('Validators', () => {
});
});
+ it('should validate kazakh alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['kk-KZ'],
+ valid: [
+ 'Сәлем777',
+ '123Бәсе',
+ 'солай',
+ 'Жиенсу',
+ '90тоқсан',
+ 'жалғыз',
+ '570бердім',
+ ],
+ invalid: [
+ ' кешіріңіз ',
+ 'abcағылшынша',
+ 'мүмкін!!',
+ ],
+ });
+ });
+
it('should validate kurdish alphanumeric strings', () => {
test({
validator: 'isAlphanumeric',
@@ -4674,10 +4732,11 @@ describe('Validators', () => {
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb3JlbSI6Imlwc3VtIn0.ymiJSsMJXR6tMSr8G9usjQ15_8hKPDv_CArLhxw28MI',
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkb2xvciI6InNpdCIsImFtZXQiOlsibG9yZW0iLCJpcHN1bSJdfQ.rRpe04zbWbbJjwM43VnHzAboDzszJtGrNsUxaqQ-GQ8',
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqb2huIjp7ImFnZSI6MjUsImhlaWdodCI6MTg1fSwiamFrZSI6eyJhZ2UiOjMwLCJoZWlnaHQiOjI3MH19.YRLPARDmhGMC3BBk_OhtwwK21PIkVCqQe8ncIRPKo-E',
- 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ', // No signature
],
invalid: [
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9',
+ 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NSIsIm5hbWUiOiJKb2huIERvZSIsImlhdCI6MTUxNjIzOTAyMn0',
+ 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NSIsIm5hbWUiOiJKb2huIERvZSIsImlhdCI6MTYxNjY1Mzg3Mn0.eyJpc3MiOiJodHRwczovL2V4YW1wbGUuY29tIiwiaWF0IjoxNjE2NjUzODcyLCJleHAiOjE2MTY2NTM4ODJ9.a1jLRQkO5TV5y5ERcaPAiM9Xm2gBdRjKrrCpHkGr_8M',
'$Zs.ewu.su84',
'ks64$S/9.dy$§kz.3sd73b',
],
@@ -4816,16 +4875,51 @@ describe('Validators', () => {
'uz_Latn_UZ',
'en',
'gsw',
+ 'en-US',
'es_ES',
+ 'es-419',
'sw_KE',
'am_ET',
+ 'zh-CHS',
'ca_ES_VALENCIA',
'en_US_POSIX',
+ 'hak-CN',
+ 'zh-Hant',
+ 'zh-Hans',
+ 'sr-Cyrl',
+ 'sr-Latn',
+ 'zh-cmn-Hans-CN',
+ 'cmn-Hans-CN',
+ 'zh-yue-HK',
+ 'yue-HK',
+ 'zh-Hans-CN',
+ 'sr-Latn-RS',
+ 'sl-rozaj',
+ 'sl-rozaj-biske',
+ 'sl-nedis',
+ 'de-CH-1901',
+ 'sl-IT-nedis',
+ 'hy-Latn-IT-arevela',
+ 'i-enochian',
+ 'en-scotland-fonipa',
+ 'sl-IT-rozaj-biske-1994',
+ 'de-CH-x-phonebk',
+ 'az-Arab-x-AZE-derbend',
+ 'x-whatever',
+ 'qaa-Qaaa-QM-x-southern',
+ 'de-Qaaa',
+ 'sr-Latn-QM',
+ 'sr-Qaaa-RS',
+ 'en-US-u-islamcal',
+ 'zh-CN-a-myext-x-private',
+ 'en-a-myext-b-another',
],
invalid: [
'lo_POP',
'12',
'12_DD',
+ 'de-419-DE',
+ 'a-DE',
],
});
});
@@ -5164,8 +5258,51 @@ describe('Validators', () => {
'LB92000700000000123123456123',
'IR200170000000339545727003',
'MZ97123412341234123412341',
+ 'MA64011519000001205000534921',
+ ],
+ invalid: [
+ 'XX22YYY1234567890123',
+ 'FR14 2004 1010 0505 0001 3',
+ 'FR7630006000011234567890189@',
+ 'FR7630006000011234567890189😅',
+ 'FR763000600001123456!!🤨7890189@',
+ ],
+ });
+ test({
+ validator: 'isIBAN',
+ args: [{ whitelist: ['DK', 'GB'] }],
+ valid: [
+ 'DK5000400440116243',
+ 'GB29NWBK60161331926819',
+ ],
+ invalid: [
+ 'BE71 0961 2345 6769',
+ 'FR76 3000 6000 0112 3456 7890 189',
+ 'DE91 1000 0000 0123 4567 89',
+ 'GR96 0810 0010 0000 0123 4567 890',
+ 'RO09 BCYP 0000 0012 3456 7890',
+ 'SA44 2000 0001 2345 6789 1234',
+ 'ES79 2100 0813 6101 2345 6789',
+ 'XX22YYY1234567890123',
+ 'FR14 2004 1010 0505 0001 3',
+ 'FR7630006000011234567890189@',
+ 'FR7630006000011234567890189😅',
+ 'FR763000600001123456!!🤨7890189@',
],
+ });
+ test({
+ validator: 'isIBAN',
+ args: [{ whitelist: ['XX', 'AA'] }],
invalid: [
+ 'DK5000400440116243',
+ 'GB29NWBK60161331926819',
+ 'BE71 0961 2345 6769',
+ 'FR76 3000 6000 0112 3456 7890 189',
+ 'DE91 1000 0000 0123 4567 89',
+ 'GR96 0810 0010 0000 0123 4567 890',
+ 'RO09 BCYP 0000 0012 3456 7890',
+ 'SA44 2000 0001 2345 6789 1234',
+ 'ES79 2100 0813 6101 2345 6789',
'XX22YYY1234567890123',
'FR14 2004 1010 0505 0001 3',
'FR7630006000011234567890189@',
@@ -5173,6 +5310,41 @@ describe('Validators', () => {
'FR763000600001123456!!🤨7890189@',
],
});
+ test({
+ validator: 'isIBAN',
+ args: [{ blacklist: ['IT'] }],
+ valid: [
+ 'SC52BAHL01031234567890123456USD',
+ 'LC14BOSL123456789012345678901234',
+ 'MT31MALT01100000000000000000123',
+ 'SV43ACAT00000000000000123123',
+ 'EG800002000156789012345180002',
+ 'BE71 0961 2345 6769',
+ 'FR76 3000 6000 0112 3456 7890 189',
+ 'DE91 1000 0000 0123 4567 89',
+ 'GR96 0810 0010 0000 0123 4567 890',
+ 'RO09 BCYP 0000 0012 3456 7890',
+ 'SA44 2000 0001 2345 6789 1234',
+ 'ES79 2100 0813 6101 2345 6789',
+ 'CH56 0483 5012 3456 7800 9',
+ 'GB98 MIDL 0700 9312 3456 78',
+ 'IL170108000000012612345',
+ 'JO71CBJO0000000000001234567890',
+ 'TR320010009999901234567890',
+ 'BR1500000000000010932840814P2',
+ 'LB92000700000000123123456123',
+ 'IR200170000000339545727003',
+ 'MZ97123412341234123412341',
+ ],
+ invalid: [
+ 'XX22YYY1234567890123',
+ 'FR14 2004 1010 0505 0001 3',
+ 'FR7630006000011234567890189@',
+ 'FR7630006000011234567890189😅',
+ 'FR763000600001123456!!🤨7890189@',
+ 'IT60X0542811101000000123456',
+ ],
+ });
});
it('should validate BIC codes', () => {
@@ -5215,7 +5387,7 @@ describe('Validators', () => {
it('should validate luhn numbers', () => {
test({
- validator: 'isLuhnValid',
+ validator: 'isLuhnNumber',
valid: [
'0',
'5421',
@@ -6777,6 +6949,24 @@ describe('Validators', () => {
'0114152198',
],
},
+ {
+ locale: 'ar-SD',
+ valid: [
+ '0128652312',
+ '+249919425113',
+ '249123212345',
+ '0993212345',
+ ],
+ invalid: [
+ '12345',
+ '',
+ '+249972662622',
+ '+24946266262',
+ '+24933221097',
+ '0614152198',
+ '096554',
+ ],
+ },
{
locale: 'ar-TN',
valid: [
@@ -7404,6 +7594,31 @@ describe('Validators', () => {
'+254800723845',
],
},
+ {
+ locale: 'fr-CF',
+ valid: [
+ '+23670850000',
+ '+23675038756',
+ '+23677859002',
+ '+23672854202',
+ '+23621854052',
+ '+23622854072',
+ '72234650',
+ '70045902',
+ '77934567',
+ '21456794',
+ '22452389',
+ ],
+ invalid: [
+ '+23689032',
+ '123456789',
+ '+236723845987',
+ '022452389',
+ '+236772345678',
+ '+236700456794',
+
+ ],
+ },
{
locale: 'en-KI',
valid: [
@@ -7501,6 +7716,25 @@ describe('Validators', () => {
'+255800723845',
],
},
+ {
+ locale: 'en-MW',
+ valid: [
+ '+265994563785',
+ '+265111785436',
+ '+265318596857',
+ '0320008744',
+ '01256258',
+ '0882541896',
+ '+265984563214',
+ ],
+ invalid: [
+ '58563',
+ '+2658256258',
+ '0896328741',
+ '0708574896',
+ '+26570857489635',
+ ],
+ },
{
locale: 'es-PE',
valid: [
@@ -7739,6 +7973,32 @@ describe('Validators', () => {
'6898912345',
],
},
+ {
+ locale: 'fr-WF',
+ valid: [
+ '+681408500',
+ '+681499387',
+ '+681728590',
+ '+681808542',
+ '+681828540',
+ '+681832014',
+ '408500',
+ '499387',
+ '728590',
+ '808542',
+ '828540',
+ '832014',
+ ],
+ invalid: [
+ '+68189032',
+ '123456789',
+ '+681723845987',
+ '022452389',
+ '+681772345678',
+ '+681700456794',
+
+ ],
+ },
{
locale: 'ka-GE',
valid: [
@@ -8271,6 +8531,7 @@ describe('Validators', () => {
'',
'abc',
'+535123457',
+ '56043029304',
],
},
{
@@ -8531,6 +8792,7 @@ describe('Validators', () => {
'+48 56 6572724',
'+48 67 621 5461',
'48 67 621 5461',
+ '+48 45 621 5461',
],
invalid: [
'+48 67 621 5461',
@@ -8541,6 +8803,7 @@ describe('Validators', () => {
'1800-88-8687',
'+6019-5830837',
'357562855',
+ '+48 44 621 5461',
],
},
{
@@ -9321,6 +9584,27 @@ describe('Validators', () => {
'NotANumber',
],
},
+ {
+ locale: 'so-SO',
+ valid: [
+ '+252601234567',
+ '+252650101010',
+ '+252794567120',
+ '252650647388',
+ '252751234567',
+ '0601234567',
+ '0609876543',
+ ],
+ invalid: [
+ '',
+ 'not a number',
+ '+2526012345678',
+ '25260123456',
+ '+252705555555',
+ '+0601234567',
+ '06945454545',
+ ],
+ },
{
locale: 'sq-AL',
valid: [
@@ -11946,6 +12230,58 @@ describe('Validators', () => {
});
});
+
+ it('should validate ISO6346 shipping containerID', () => {
+ test({
+ validator: 'isISO6346',
+ valid: [
+ 'HLXU2008419',
+ 'TGHU7599330',
+ 'ECMU4657496',
+ 'MEDU6246078',
+ 'YMLU2809976',
+ 'MRKU0046221',
+ 'EMCU3811879',
+ 'OOLU8643084',
+ 'HJCU1922713',
+ 'QJRZ123456',
+ ],
+ invalid: [
+ 'OOLU1922713',
+ 'HJCU1922413',
+ 'FCUI985619',
+ 'ECMJ4657496',
+ 'TBJA7176445',
+ 'AFFU5962593',
+ ],
+ });
+ });
+ it('should validate ISO6346 shipping containerID', () => {
+ test({
+ validator: 'isFreightContainerID',
+ valid: [
+ 'HLXU2008419',
+ 'TGHU7599330',
+ 'ECMU4657496',
+ 'MEDU6246078',
+ 'YMLU2809976',
+ 'MRKU0046221',
+ 'EMCU3811879',
+ 'OOLU8643084',
+ 'HJCU1922713',
+ 'QJRZ123456',
+ ],
+ invalid: [
+ 'OOLU1922713',
+ 'HJCU1922413',
+ 'FCUI985619',
+ 'ECMJ4657496',
+ 'TBJA7176445',
+ 'AFFU5962593',
+ ],
+ });
+ });
+
// EU-UK valid numbers sourced from https://ec.europa.eu/taxation_customs/tin/specs/FS-TIN%20Algorithms-Public.docx or constructed by @tplessas.
it('should validate taxID', () => {
test({
@@ -12161,6 +12497,27 @@ describe('Validators', () => {
'28-1234567',
'96-1234567'],
});
+ test({
+ validator: 'isTaxID',
+ args: ['es-AR'],
+ valid: [
+ '20271633638',
+ '23274986069',
+ '27333234519',
+ '30678561165',
+ '33693450239',
+ '30534868460',
+ '23111111129',
+ '34557619099'],
+ invalid: [
+ '20-27163363-8',
+ '20.27163363.8',
+ '33693450231',
+ '69345023',
+ '693450233123123',
+ '3369ew50231',
+ '34557619095'],
+ });
test({
validator: 'isTaxID',
args: ['es-ES'],
@@ -12643,6 +13000,7 @@ describe('Validators', () => {
'15/7/2002',
'15-7-2002',
'15/07-02',
+ '30/04/--',
],
});
test({
@@ -12657,6 +13015,7 @@ describe('Validators', () => {
'15/7/02',
'15-7-02',
'5/7-02',
+ '3/4/aa',
],
});
test({
@@ -12737,6 +13096,17 @@ describe('Validators', () => {
'02.29.2020.20',
],
});
+ // emulating Pacific time zone offset & time
+ // which could potentially result in UTC conversion issues
+ timezone_mock.register('US/Pacific');
+ test({
+ validator: 'isDate',
+ valid: [
+ new Date(2016, 2, 29),
+ '2017-08-04',
+ ],
+ });
+ timezone_mock.unregister();
});
it('should validate time', () => {
test({
@@ -13767,18 +14137,30 @@ describe('Validators', () => {
validator: 'isVAT',
args: ['CH'],
valid: [
- 'CH123456TVA',
- '123456TVA',
- 'CH123456789MWST',
- '123456789MWST',
- 'CH123.456IVA',
- '123.456IVA',
- 'CH123.456.789TVA',
- '123.456.789TVA',
- ],
- invalid: [
- 'CH 123456',
- '12345',
+ // strictly valid
+ 'CHE-116.281.710 MWST',
+ 'CHE-116.281.710 IVA',
+ 'CHE-116.281.710 TVA',
+ // loosely valid presentation variants
+ 'CHE 116 281 710 IVA', // all separators are spaces
+ 'CHE-191.398.369MWST', // no space before suffix
+ 'CHE-116281710 MWST', // no number separators
+ 'CHE-116281710MWST', // no number separators and no space before suffix
+ 'CHE105854263MWST', // no separators
+ 'CHE-116.285.524', // no suffix (vat abbreviation)
+ 'CHE116281710', // no suffix and separators
+ '116.281.710 TVA', // no prefix (CHE, ISO-3166-1 Alpha-3)
+ '116281710MWST', // no prefix and separators
+ '100.218.485', // no prefix and suffix
+ '123456788', // no prefix, separators and suffix
+ ],
+ invalid: [
+ 'CH-116.281.710 MWST', // invalid prefix (should be CHE)
+ 'CHE-116.281 MWST', // invalid number of digits (should be 9)
+ 'CHE-123.456.789 MWST', // invalid last digit (should match the calculated check-number 8)
+ 'CHE-123.356.780 MWST', // invalid check-number (there are no swiss UIDs with the calculated check number 10)
+ 'CH-116.281.710 VAT', // invalid suffix (should be MWST, IVA or TVA)
+ 'CHE-116/281/710 IVA', // invalid number separators (should be all dots or all spaces)
],
});
test({
@@ -14082,4 +14464,55 @@ describe('Validators', () => {
],
});
});
+ it('should validate mailto URI', () => {
+ test({
+ validator: 'isMailtoURI',
+ valid: [
+ 'mailto:?subject=something&cc=valid@mail.com',
+ 'mailto:?subject=something&cc=valid@mail.com,another@mail.com,',
+ 'mailto:?subject=something&bcc=valid@mail.com',
+ 'mailto:?subject=something&bcc=valid@mail.com,another@mail.com',
+ 'mailto:?bcc=valid@mail.com,another@mail.com',
+ 'mailto:?cc=valid@mail.com,another@mail.com',
+ 'mailto:?cc=valid@mail.com',
+ 'mailto:?bcc=valid@mail.com',
+ 'mailto:?subject=something&body=something else',
+ 'mailto:?subject=something&body=something else&cc=hello@mail.com,another@mail.com',
+ 'mailto:?subject=something&body=something else&bcc=hello@mail.com,another@mail.com',
+ 'mailto:?subject=something&body=something else&cc=something@mail.com&bcc=hello@mail.com,another@mail.com',
+ 'mailto:hello@mail.com',
+ 'mailto:info@mail.com?',
+ 'mailto:hey@mail.com?subject=something',
+ 'mailto:info@mail.com?subject=something&cc=valid@mail.com',
+ 'mailto:info@mail.com?subject=something&cc=valid@mail.com,another@mail.com,',
+ 'mailto:info@mail.com?subject=something&bcc=valid@mail.com',
+ 'mailto:info@mail.com?subject=something&bcc=valid@mail.com,another@mail.com',
+ 'mailto:info@mail.com?bcc=valid@mail.com,another@mail.com',
+ 'mailto:info@mail.com?cc=valid@mail.com,another@mail.com',
+ 'mailto:info@mail.com?cc=valid@mail.com',
+ 'mailto:info@mail.com?bcc=valid@mail.com&',
+ 'mailto:info@mail.com?subject=something&body=something else',
+ 'mailto:info@mail.com?subject=something&body=something else&cc=hello@mail.com,another@mail.com',
+ 'mailto:info@mail.com?subject=something&body=something else&bcc=hello@mail.com,another@mail.com',
+ 'mailto:info@mail.com?subject=something&body=something else&cc=something@mail.com&bcc=hello@mail.com,another@mail.com',
+ 'mailto:',
+ ],
+ invalid: [
+ '',
+ 'somthing',
+ 'valid@gmail.com',
+ 'mailto:?subject=okay&subject=444',
+ 'mailto:?subject=something&wrong=888',
+ 'mailto:somename@gmail.com',
+ 'mailto:hello@world.com?cc=somename@gmail.com',
+ 'mailto:hello@world.com?bcc=somename@gmail.com',
+ 'mailto:hello@world.com?bcc=somename@gmail.com&bcc',
+ 'mailto:valid@gmail.com?subject=anything&body=nothing&cc=&bcc=&key=',
+ 'mailto:hello@world.com?cc=somename',
+ 'mailto:somename',
+ 'mailto:info@mail.com?subject=something&body=something else&cc=something@mail.com&bcc=hello@mail.com,another@mail.com&',
+ 'mailto:?subject=something&body=something else&cc=something@mail.com&bcc=hello@mail.com,another@mail.com&',
+ ],
+ });
+ });
});
diff --git a/test/validators/isFQDN.test.js b/test/validators/isFQDN.test.js
new file mode 100644
index 000000000..134bab005
--- /dev/null
+++ b/test/validators/isFQDN.test.js
@@ -0,0 +1,26 @@
+import test from '../testFunctions';
+
+describe('isFQDN', () => {
+ it('should validate domain names.', () => {
+ test({
+ validator: 'isFQDN',
+ args: [],
+ valid: [
+ 'google.com',
+ ],
+ invalid: [
+ 'google.l33t',
+ ],
+ });
+ test({
+ validator: 'isFQDN',
+ args: [{ allow_numeric_tld: true }],
+ valid: [
+ 'google.com',
+ 'google.l33t',
+ ],
+ invalid: [
+ ],
+ });
+ });
+});