diff --git a/README.md b/README.md
index c991f9cbd..cc9a97882 100644
--- a/README.md
+++ b/README.md
@@ -111,16 +111,16 @@ Validator | Description
**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', 'eo', '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 }`.
`require_tld` - If set to false the validator will not check if the domain includes a TLD.
`allow_underscores` - if set to true, the validator will allow underscores in the domain.
`allow_trailing_dot` - if set to true, the validator will allow the domain to end with a `.` character.
`allow_numeric_tld` - if set to true, the validator will allow the TLD of the domain to be made up solely of numbers.
`allow_wildcard` - if set to true, the validator will allow domains starting with `*.` (e.g. `*.example.com` or `*.shop.example.com`).
`ignore_max_length` - if set to true, the validator will not check for the standard max length of a domain.
-**isFreightContainerID(str)** | alias for `isISO6346`, check if the string is a valid [ISO 6346](https://en.wikipedia.org/wiki/ISO_6346) shipping container identification.
+**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 [, options])** | check if the string is a hexadecimal color.
`options` is an object that defaults to `{ require_hashtag: false }`.
Options:
`require_hashtag`: Enforce # prefix, default false.
**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, [, 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']`.
+**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', 'PK']` 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.
+**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.
**isInt(str [, options])** | check if the string is an integer.
`options` is an object which can contain the keys `min` and/or `max` to check the integer is within boundaries (e.g. `{ min: 10, max: 99 }`). `options` can also contain the key `allow_leading_zeroes`, which when set to false will disallow integer values with leading zeroes (e.g. `{ allow_leading_zeroes: false }`). Finally, `options` can contain the keys `gt` and/or `lt` which will enforce integers being greater than or less than, respectively, the value provided (e.g. `{gt: 1, lt: 4}` for a number between 1 and 4).
**isIP(str [, options])** | check if the string is an IP address (version 4 or 6).
`options` is an object that defaults to `{ version: '' }`.
**Options:**
`version`: defines which IP version to compare to. Accepted values: `4`, `6`, `'4'`, `'6'`.
@@ -130,18 +130,18 @@ Validator | Description
**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.
-**isISO15924(str)** | check if the string is a valid [ISO 15924][ISO 15924] officially assigned script code.
-**isISO31661Alpha2(str)** | check if the string is a valid [ISO 3166-1 alpha-2][ISO 3166-1 alpha-2] officially assigned country code.
-**isISO31661Alpha3(str)** | check if the string is a valid [ISO 3166-1 alpha-3][ISO 3166-1 alpha-3] officially assigned country code.
+**isISO15924(str)** | check if the string is a valid [ISO 15924][ISO 15924] officially assigned script code.
+**isISO31661Alpha2(str [, options])** | check if the string is a valid [ISO 3166-1 alpha-2][ISO 3166-1 alpha-2] officially assigned country code.
`options` is an object which can contain the key `userAssignedCodes`: an array of custom codes that are not officially assigned (e.g. `['XK']`).
+**isISO31661Alpha3(str [, options])** | check if the string is a valid [ISO 3166-1 alpha-3][ISO 3166-1 alpha-3] officially assigned country code.
`options` is an object which can contain the key `userAssignedCodes`: an array of custom codes that are not officially assigned (e.g. `['XXK']`).
**isISO31661Numeric(str)** | check if the string is a valid [ISO 3166-1 numeric][ISO 3166-1 numeric] officially assigned country code.
**isISO4217(str)** | check if the string is a valid [ISO 4217][ISO 4217] officially assigned currency code.
**isISRC(str)** | check if the string is an [ISRC][ISRC].
**isISSN(str [, options])** | check if the string is an [ISSN][ISSN].
`options` is an object which defaults to `{ case_sensitive: false, require_hyphen: false }`. If `case_sensitive` is true, ISSNs with a lowercase `'x'` as the check digit are rejected.
**isJSON(str [, options])** | check if the string is valid JSON (note: uses JSON.parse).
`options` is an object which defaults to `{ allow_primitives: false }`. If `allow_primitives` is true, the primitives 'true', 'false' and 'null' are accepted as valid JSON values.
-**isJWT(str)** | check if the string is valid JWT token.
-**isLatLong(str [, options])** | check if the string is a valid latitude-longitude coordinate in the format `lat,long` or `lat, long`.
`options` is an object that defaults to `{ checkDMS: false }`. Pass `checkDMS` as `true` to validate DMS(degrees, minutes, and seconds) latitude-longitude format.
-**isLength(str [, options])** | check if the string's length falls in a range and equal to any of the integers of the `discreteLengths` array if provided.
`options` is an object which defaults to `{ min: 0, max: undefined, discreteLengths: undefined }`. Note: this function takes into account surrogate pairs.
-**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', 'en-SG', 'en-PK', 'es-AR', 'hu-HU', 'pt-BR', 'pt-PT', 'sq-AL', 'sv-SE']` or `'any'`.
+**isJWT(str)** | check if the string is valid JWT token.
+**isLatLong(str [, options])** | check if the string is a valid latitude-longitude coordinate in the format `lat,long` or `lat, long`.
`options` is an object that defaults to `{ checkDMS: false }`. Pass `checkDMS` as `true` to validate DMS(degrees, minutes, and seconds) latitude-longitude format.
+**isLength(str [, options])** | check if the string's length falls in a range and equal to any of the integers of the `discreteLengths` array if provided.
`options` is an object which defaults to `{ min: 0, max: undefined, discreteLengths: undefined }`. Note: this function takes into account surrogate pairs.
+**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', 'en-SG', 'en-PK', '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.
**isLuhnNumber(str)** | check if the string passes the [Luhn algorithm check](https://en.wikipedia.org/wiki/Luhn_algorithm).
diff --git a/src/lib/isISO31661Alpha2.js b/src/lib/isISO31661Alpha2.js
index e67bb1e15..8757025eb 100644
--- a/src/lib/isISO31661Alpha2.js
+++ b/src/lib/isISO31661Alpha2.js
@@ -29,8 +29,24 @@ const validISO31661Alpha2CountriesCodes = new Set([
'ZA', 'ZM', 'ZW',
]);
-export default function isISO31661Alpha2(str) {
+const alpha2CountryCode = /^[a-zA-Z]{2}$/;
+
+export default function isISO31661Alpha2(str, options = {}) {
assertString(str);
+
+ const { userAssignedCodes } = options;
+ const validUserAssignedCodes = (userAssignedCodes || [])
+ .reduce((accumulator, userAssignedCode) => {
+ if (alpha2CountryCode.test(userAssignedCode)) {
+ accumulator.push(userAssignedCode.toUpperCase());
+ }
+ return accumulator;
+ }, []);
+
+ if (validUserAssignedCodes.includes(str.toUpperCase())) {
+ return true;
+ }
+
return validISO31661Alpha2CountriesCodes.has(str.toUpperCase());
}
diff --git a/src/lib/isISO31661Alpha3.js b/src/lib/isISO31661Alpha3.js
index 34e552cdd..29f88c673 100644
--- a/src/lib/isISO31661Alpha3.js
+++ b/src/lib/isISO31661Alpha3.js
@@ -20,7 +20,23 @@ const validISO31661Alpha3CountriesCodes = new Set([
'VEN', 'VNM', 'VGB', 'VIR', 'WLF', 'ESH', 'YEM', 'ZMB', 'ZWE',
]);
-export default function isISO31661Alpha3(str) {
+const alpha3CountryCode = /^[a-zA-Z]{3}$/;
+
+export default function isISO31661Alpha3(str, options = {}) {
assertString(str);
+
+ const { userAssignedCodes } = options;
+ const validUserAssignedCodes = (userAssignedCodes || [])
+ .reduce((accumulator, userAssignedCode) => {
+ if (alpha3CountryCode.test(userAssignedCode)) {
+ accumulator.push(userAssignedCode.toUpperCase());
+ }
+ return accumulator;
+ }, []);
+
+ if (validUserAssignedCodes.includes(str.toUpperCase())) {
+ return true;
+ }
+
return validISO31661Alpha3CountriesCodes.has(str.toUpperCase());
}
diff --git a/test/validators.test.js b/test/validators.test.js
index 9bd00d6ec..60ffa9c81 100644
--- a/test/validators.test.js
+++ b/test/validators.test.js
@@ -12471,61 +12471,6 @@ describe('Validators', () => {
});
});
- it('should validate ISO 3166-1 alpha 2 country codes', () => {
- // from https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
- test({
- validator: 'isISO31661Alpha2',
- valid: [
- 'FR',
- 'fR',
- 'GB',
- 'PT',
- 'CM',
- 'JP',
- 'PM',
- 'ZW',
- 'MM',
- 'cc',
- 'GG',
- ],
- invalid: [
- '',
- 'FRA',
- 'AA',
- 'PI',
- 'RP',
- 'WV',
- 'WL',
- 'UK',
- 'ZZ',
- ],
- });
- });
-
- it('should validate ISO 3166-1 alpha 3 country codes', () => {
- // from https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3
- test({
- validator: 'isISO31661Alpha3',
- valid: [
- 'ABW',
- 'HND',
- 'KHM',
- 'RWA',
- ],
- invalid: [
- '',
- 'FR',
- 'fR',
- 'GB',
- 'PT',
- 'CM',
- 'JP',
- 'PM',
- 'ZW',
- ],
- });
- });
-
it('should validate ISO 3166-1 numeric country codes', () => {
// from https://en.wikipedia.org/wiki/ISO_3166-1_numeric
test({
diff --git a/test/validators/isISO31661Alpha2.test.js b/test/validators/isISO31661Alpha2.test.js
new file mode 100644
index 000000000..d559d1408
--- /dev/null
+++ b/test/validators/isISO31661Alpha2.test.js
@@ -0,0 +1,70 @@
+import test from '../testFunctions';
+
+describe('isISO31661Alpha2', () => {
+ it('should validate ISO 3166-1 alpha 2 country codes', () => {
+ // from https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
+ test({
+ validator: 'isISO31661Alpha2',
+ valid: [
+ 'FR',
+ 'fR',
+ 'GB',
+ 'PT',
+ 'CM',
+ 'JP',
+ 'PM',
+ 'ZW',
+ 'MM',
+ 'cc',
+ 'GG',
+ ],
+ invalid: [
+ '',
+ 'FRA',
+ 'AA',
+ 'PI',
+ 'RP',
+ 'WV',
+ 'WL',
+ 'UK',
+ 'ZZ',
+ ],
+ });
+ });
+
+ it('should validate user assigned alpha 2 code if provided', () => {
+ test({
+ validator: 'isISO31661Alpha2',
+ args: [{ userAssignedCodes: ['XK', 'xl', '', 'x'] }],
+ valid: ['BE', 'FR', 'GB', 'US', 'XK', 'XL'],
+ invalid: ['XA', 'XB'],
+ });
+ });
+
+ it('should not validate user assigned alpha 2 not valid code if provided', () => {
+ test({
+ validator: 'isISO31661Alpha2',
+ args: [{ userAssignedCodes: ['', 'x', 'XXX'] }],
+ valid: ['FR'],
+ invalid: ['XXX', 'X'],
+ });
+ });
+
+ it('should still validate ISO 3166-1 alpha 2 country codes if the options object is empty', () => {
+ test({
+ validator: 'isISO31661Alpha2',
+ args: [{}],
+ valid: ['FR', 'US'],
+ invalid: ['XK'],
+ });
+ });
+
+ it('should still validate ISO 3166-1 alpha 2 country codes if the userAssignedCodes option is empty', () => {
+ test({
+ validator: 'isISO31661Alpha2',
+ args: [{ userAssignedCodes: [] }],
+ valid: ['FR', 'US'],
+ invalid: ['XK'],
+ });
+ });
+});
diff --git a/test/validators/isISO31661Alpha3.test.js b/test/validators/isISO31661Alpha3.test.js
new file mode 100644
index 000000000..f3993da84
--- /dev/null
+++ b/test/validators/isISO31661Alpha3.test.js
@@ -0,0 +1,64 @@
+import test from '../testFunctions';
+
+describe('isISO31661Alpha3', () => {
+ it('should validate ISO 3166-1 alpha 3 country codes', () => {
+ // from https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3
+ test({
+ validator: 'isISO31661Alpha3',
+ valid: [
+ 'ABW',
+ 'HND',
+ 'KHM',
+ 'RWA',
+ ],
+ invalid: [
+ '',
+ 'FR',
+ 'fR',
+ 'GB',
+ 'PT',
+ 'CM',
+ 'JP',
+ 'PM',
+ 'ZW',
+ 'XXK',
+ ],
+ });
+ });
+
+ it('should validate user assigned ISO 3166-1 alpha 3 code if provided', () => {
+ test({
+ validator: 'isISO31661Alpha3',
+ args: [{ userAssignedCodes: ['XXK', 'xxl'] }],
+ valid: ['BEL', 'FRA', 'GBR', 'USA', 'XXK', 'XXL'],
+ invalid: ['XXA', 'GGG'],
+ });
+ });
+
+ it('should not validate user assigned ISO 3166-1 alpha 3 not valid code if provided', () => {
+ test({
+ validator: 'isISO31661Alpha3',
+ args: [{ userAssignedCodes: ['', 'x', 'XX', 'XXXX'] }],
+ valid: ['FRA'],
+ invalid: ['XX', 'XXXX', 'X'],
+ });
+ });
+
+ it('should still validate ISO 3166-1 alpha 3 country codes if the options object is empty', () => {
+ test({
+ validator: 'isISO31661Alpha3',
+ args: [{}],
+ valid: ['FRA', 'USA'],
+ invalid: ['XXK'],
+ });
+ });
+
+ it('should still validate ISO 3166-1 alpha 3 country codes if the userAssignedCodes option is empty', () => {
+ test({
+ validator: 'isISO31661Alpha3',
+ args: [{ userAssignedCodes: [] }],
+ valid: ['FRA', 'USA'],
+ invalid: ['XXK'],
+ });
+ });
+});