@ericblade/barcode-validator
Advanced tools
Comparing version 1.0.0 to 2.4.0
128
index.js
@@ -0,1 +1,5 @@ | ||
// Useful information: GTIN13 and EAN are basically the same thing. | ||
// TODO: should also be functions to convert between UPC and EAN. | ||
// TODO: need to also return 978 and 979 versions of 290 and 291 series ISBN barcodes. | ||
/* eslint-disable eqeqeq */ | ||
@@ -8,2 +12,16 @@ const validationRegex = /^(\d{8,14}|\d{9}[xX])$/; | ||
// generates a checksum calculator. The various checksums we support are all generated in similar | ||
// fashion, just using some slightly different parameters. In UPC, odd numbers are multiplied by 3, | ||
// in GTIN and ISBN, the even numbers are multiplied by 3. The difference between ISBN and GTIN checksums | ||
// is that GTIN checksums are calculated from last digit to first, instead of first to last. | ||
// Therefore, we have 3 function parameters: evenMult, oddMult, and shouldReverse. The formulae are | ||
// otherwise identical between UPC, GTIN, and ISBN. This may also be true for other types of barcodes, | ||
// but I have not studied those, as these are the most commonly used ones in the United States, where I am | ||
// located. -Eric | ||
// The returned function takes a string containing numbers from 0-9, and calculates a checksum value. | ||
// Note that it does NOT validate that the length of the string is valid -- it returns a checksum based | ||
// on the entire string given to it, it needs to be up to other functions to determine if the length | ||
// is valid or not. | ||
// Note also that ISBN10, which contains "X" as a valid character, has a completely different checksum | ||
// routine, below. | ||
const generateChecksumCalculator = ({ evenMult, oddMult, shouldReverse }) => { | ||
@@ -30,2 +48,3 @@ return (str) => { | ||
// create the UPC, ISBN13, and GTIN checksum generators. | ||
// ([ a, b, c, d, e, f, g, h, i, j, k ] * [ 3 1 3 1 3 1 3 1 3 1 3 1 ]) % 10 | ||
@@ -38,2 +57,3 @@ const getUpcChecksum = generateChecksumCalculator({ evenMult: 1, oddMult: 3, shouldReverse: false }); | ||
// all of the supported barcode types have their checksum digit as the last digit of the string. | ||
const extractCheckDigit = (code) => { | ||
@@ -77,43 +97,87 @@ const str = code.toString().toUpperCase(); | ||
// https://stackoverflow.com/questions/2123131/determine-if-10-digit-string-is-valid-amazon-asin | ||
// effectively tells us "does this string start with a "B" and is 10 alpha-numeric digits. | ||
// Unfortunately, 10 digit strings such as BOOTSTRAPS look like a perfectly valid ASIN, and you wouldn't | ||
// know the difference without asking Amazon if it's real. | ||
const validAsin = (asin) => { | ||
return /^(B[\dA-Z]{9}|\d{9}(X|\d))$/.test(asin); | ||
} | ||
function getTypeOfBarcode(code) { | ||
switch(code.length) { | ||
case 10: { | ||
if (code.startsWith('B')) { | ||
return 'asin'; | ||
} | ||
return 'isbn10'; | ||
} | ||
case 12: return 'upc'; | ||
case 13: { | ||
if(/^(978|979|290|291)[0-9].*/.test(code)) { | ||
return 'isbn13'; | ||
} | ||
return 'gtin'; | ||
} | ||
default: return 'gtin'; | ||
} | ||
} | ||
const validatorMap = { | ||
gtin: validGtinChecksum, | ||
upc: validUpcChecksum, | ||
isbn10: validIsbn10Checksum, | ||
isbn13: validIsbn13Checksum, | ||
asin: validAsin, | ||
}; | ||
// Given a barcode and an optional type, will attempt to correct an invalid code in some possible ways, | ||
// and return if the code was valid. If a type is NOT provided, type will be inferred (see getTypeOfBarcode()) | ||
// and returned. If you DO specify a type, *only* validation on that type will be performed. | ||
// Therefore, if your application *requires* a ISBN, pass the type. If your application doesn't care what | ||
// kind, or wants to know what kind it is, then don't pass the type. | ||
// From the return value, you probably want to make use of the modifiedCode value, as that will contain | ||
// the barcode that was actually validated -- ancient UPC codes pre-12 digit usage will be modified, | ||
// if passed a "used" book code of 290 or 291, modifiedCode will contain the 978/979 bookland version. | ||
const validate = (code, type) => { | ||
// 1980s era UPC codes were apparently 11 digits, not using a checksum. So, if we get 11, I | ||
// guess there's not much we can do? | ||
// TODO: we might want to re-architect this so it can pass the corrected UPC back to the caller | ||
// if (code.length === 11) code = `${code}${getUpcChecksum(code)}`; | ||
// if (code.length === 11) code = `0${code}`; | ||
if (code.length === 11) code = `11 digit upc not valid`; | ||
// guess there's not much we can do besides pass it with a 0 prefixing it and hope it works. | ||
const modifiedCode = code.length === 11 ? `0${code}` : code; | ||
if (!type) { | ||
type = (function determineType(t) { | ||
switch(t.length) { | ||
case 10: return 'isbn10'; | ||
case 12: return 'upc'; | ||
case 13: { | ||
if (code.startsWith('978') || code.startsWith('979') || code.startsWith('290') || code.startsWith('291')) { | ||
return 'isbn13'; | ||
} | ||
return 'gtin'; | ||
} | ||
default: return 'gtin'; | ||
} | ||
})(code); | ||
type = getTypeOfBarcode(modifiedCode); | ||
} | ||
// console.warn('* validating ', code, type); | ||
if (validationRegex.exec(code) === null) { | ||
return false; | ||
// console.warn('* validating ', code, modifiedCode, type); | ||
if (type === 'isbn13') { | ||
if (modifiedCode.startsWith('290')) { | ||
const [ junk1, junk2, junk3, ...rest ] = modifiedCode; | ||
const baseCode = rest.join(''); | ||
modifiedCode = `978${baseCode}${getIsbn13Checksum(`978${baseCode}`)}`; | ||
} else if (modifiedCode.startsWith('291')) { | ||
const [ junk1, junk2, junk3, ...rest ] = modifiedCode; | ||
const baseCode = rest.join(''); | ||
modifiedCode = `979${baseCode}${getIsbn13Checksum(`979${baseCode}`)}` | ||
} | ||
} | ||
if (type === 'gtin') { | ||
return validGtinChecksum(code); | ||
let valid = false; | ||
if (type !== 'asin' && !validationRegex.test(modifiedCode)) { | ||
type = 'unknown'; | ||
} else { | ||
valid = validatorMap[type](modifiedCode); | ||
} | ||
if (type === 'upc') { | ||
return validUpcChecksum(code); | ||
return { | ||
code, | ||
type, | ||
valid, | ||
modifiedCode, | ||
} | ||
if (type === 'isbn10') { | ||
return validIsbn10Checksum(code); | ||
} | ||
if (type === 'isbn13') { | ||
return validIsbn13Checksum(code); | ||
} | ||
} | ||
// console.warn(validate('092091900451')); | ||
// console.warn(validate('092091900451')); // valid UPC | ||
// console.warn(validate('92091900451')); // valid old UPC | ||
// console.warn(validate('0083717201410')); // should be a valid EAN / GTIN13 | ||
// console.warn(validate('00837172014104')); // should be a valid GTIN14 !! | ||
// console.warn(validate('BOOTSTRA P')); // should be an invalid ASIN | ||
// console.warn(getUpcChecksum('02035616631')); | ||
// console.warn(validate('Test random text')); | ||
module.exports = validate; |
{ | ||
"name": "@ericblade/barcode-validator", | ||
"version": "1.0.0", | ||
"version": "2.4.0", | ||
"description": "Node Javascript Barcode Validation for ISBN10, ISBN13, UPC, GTIN", | ||
"main": "index.js", | ||
"main": "./index.js", | ||
"types": "./index.d.ts", | ||
"scripts": { | ||
@@ -7,0 +8,0 @@ "test": "echo \"Error: no test specified\" && exit 1" |
# barcode-validator | ||
Simple Javascript nodejs barcode validation for ISBN10, ISBN13, UPC, GTIN | ||
Usage: | ||
```` | ||
> const barcodeValidator = require('@ericblade/barcode-validator'); | ||
undefined | ||
> barcodeValidator('092091900451'); | ||
true | ||
> barcodeValidator('092091900452'); | ||
false | ||
> | ||
```` | ||
Javascript nodejs barcode validation for ISBN10, ISBN13, UPC, GTIN |
9686
6
171
3