diff --git a/README.md b/README.md index e8b7aedd9..ef37f9707 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,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). +**isISMN(str)** | check if the string is a valid International Standard Music Number (ISMN). Supports both 13-digit and older 10-character ISMNs, allows spaces or hyphens, and checks the check digit. **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. diff --git a/src/index.js b/src/index.js index 5e2cd78a7..0dddff74b 100644 --- a/src/index.js +++ b/src/index.js @@ -79,6 +79,7 @@ import isIdentityCard from './lib/isIdentityCard'; import isEAN from './lib/isEAN'; import isISIN from './lib/isISIN'; import isISBN from './lib/isISBN'; +import isISMN from './lib/isISMN'; import isISSN from './lib/isISSN'; import isTaxID from './lib/isTaxID'; @@ -201,6 +202,7 @@ const validator = { isEAN, isISIN, isISBN, + isISMN, isISSN, isMobilePhone, isMobilePhoneLocales, diff --git a/src/lib/isISMN.js b/src/lib/isISMN.js new file mode 100644 index 000000000..5282bb7eb --- /dev/null +++ b/src/lib/isISMN.js @@ -0,0 +1,38 @@ +import assertString from './util/assertString'; + +export default function isISMN(str) { + assertString(str); + + // Check for spaces or hyphens at the beginning, end, or repeated together. + if (/^[ -]|[ -]$|[ -]{2}/.test(str)) { + return false; + } + + // Remove spaces and hyphens so we can check the number. + let ismn = str.replace(/[ -]/g, ''); + + // Change the older ISMN format to the 13-digit version. + if (/^M[0-9]{9}$/.test(ismn)) { + ismn = `9790${ismn.slice(1)}`; + } + + // Make sure it has 13 digits and starts with 9790. + if (!/^9790[0-9]{9}$/.test(ismn)) { + return false; + } + + // Go through the first 12 digits to calculate the check digit. + let sum = 0; + + for (let i = 0; i < 12; i++) { + const digit = Number(ismn[i]); + const weight = i % 2 === 0 ? 1 : 3; + sum += digit * weight; + } + + // Find what the last digit should be. + const checkDigit = (10 - (sum % 10)) % 10; + + // Check if the last digit matches what we calculated. + return checkDigit === Number(ismn[12]); +} diff --git a/test/validators.test.js b/test/validators.test.js index 98d2a12ff..1fb4e047a 100644 --- a/test/validators.test.js +++ b/test/validators.test.js @@ -7167,6 +7167,64 @@ describe('Validators', () => { }); }); + it('should validate ISMN numbers', () => { + test({ + validator: 'isISMN', + valid: [ + // Modern 13-digit ISMNs + '9790260000438', + '9790000000001', + '9790692006282', + '9790345246805', + + // ISMNs with spaces or hyphens + '979-0-2600-0043-8', + '979 0 2600 0043 8', + '979-0-69200-628-2', + + // Older ISMNs starting with M + 'M571100513', + 'M692006282', + 'M345246805', + 'M-345-24680-5', + 'M 345 24680 5', + ], + invalid: [ + // Numbers with the wrong check digit + '9790260000439', + '9790692006283', + 'M692006283', + + // Numbers that start incorrectly + '9780260000438', + '9791260000438', + 'A692006282', + + // Numbers that are too short or too long + '979026000043', + '97902600004388', + 'M69200628', + 'M6920062822', + + // Numbers with invalid characters + '9790A60000438', + 'M69200A282', + '9790.2600.0043.8', + '', + + // Incorrect spaces or hyphens + '-9790260000438', + '9790260000438-', + '979--0-2600-0043-8', + 'M--345-24680-5', + ], + error: [ + null, + 9790260000438, + ], + }); + }); + it('should validate ISSNs', () => { test({ validator: 'isISSN',