vin-iso3779-validator
v1.1.0
Published
Tiny, typesafe ISO 3779 VIN validator with check digit
Downloads
2,539
Maintainers
Readme
vin-iso3779-validator
Tiny, typesafe VIN validator for ISO 3779 (length, charset, optional check digit enforcement).
No dependencies. ESM. Strict TypeScript.
Features
- ✅ ISO 3779 structural validation: Length (17 chars) and charset
- ✅ Optional strict check digit validation: Enable North American enforcement when needed
- ✅ Auto-normalization: Removes spaces/hyphens, converts to uppercase
- ✅ TypeScript branded types: Type-safe VIN handling with compile-time guarantees
- ✅ Multiple validation methods: Flexible API for different use cases
- ✅ Zero dependencies: Lightweight and secure
- ✅ ESM-first: Modern JavaScript module support
VIN Format Rules
- Length: Exactly 17 characters
- Characters: Uppercase A–Z (excluding I, O, Q) and digits 0–9
- Check digit: Can be computed for any VIN, but is only mandatory in some markets such as North America
- Standard: ISO 3779 (applies to vehicles made since 1981)
Install
npm i vin-iso3779-validator
# or
pnpm add vin-iso3779-validator
# or
yarn add vin-iso3779-validator
# or
bun add vin-iso3779-validatorAPI
validateVIN(input: string, options?: ValidateVINOptions): ValidationResult
Comprehensive validation with detailed error information.
import { validateVIN } from "vin-iso3779-validator";
const result = validateVIN("1HGCM82633A004352");
console.log(result);
// {
// isValid: true,
// vin: "1HGCM82633A004352",
// errors: [],
// checkDigit: "3",
// expectedCheckDigit: "3",
// checkDigitValid: true
// }
// North American strict mode example
const invalid = validateVIN("1HGCM82633A004351", { checkDigit: "required" });
console.log(invalid.errors); // ["INVALID_CHECK_DIGIT"]
// European/general ISO 3779 example
const euVin = validateVIN("VR3F45GFRKY097237");
console.log(euVin.isValid); // true
console.log(euVin.checkDigitValid); // falseisValidVIN(vin: string, options?: ValidateVINOptions): vin is VIN
Type guard for branded VIN type.
import { isValidVIN } from "vin-iso3779-validator";
if (isValidVIN("JHMCM56557C404453")) {
// TypeScript knows this is a valid VIN (branded type)
console.log("Valid VIN");
}
isValidVIN("VR3F45GFRKY097237"); // true
isValidVIN("VR3F45GFRKY097237", { checkDigit: "required" }); // falseassertVIN(input: string, options?: ValidateVINOptions): VIN
Throws error if invalid, returns branded VIN type if valid.
import { assertVIN } from "vin-iso3779-validator";
try {
const vin = assertVIN("1M8GDM9AXKP042788"); // returns branded VIN type
console.log("Valid VIN:", vin);
} catch (error) {
console.error("Invalid VIN:", error.message);
}
assertVIN("VR3F45GFRKY097237"); // ok
assertVIN("VR3F45GFRKY097237", { checkDigit: "required" }); // throwscomputeCheckDigit(vin: string): string
Calculate the expected check digit for a VIN.
import { computeCheckDigit } from "vin-iso3779-validator";
const checkDigit = computeCheckDigit("1HGCM82633A004352"); // "3"
const checkDigitX = computeCheckDigit("1M8GDM9AXKP042788"); // "X"normalizeVIN(input: string): string
Normalize VIN input (remove spaces/hyphens, uppercase).
import { normalizeVIN } from "vin-iso3779-validator";
const normalized = normalizeVIN(" 1hg-cm82633a004352 "); // "1HGCM82633A004352"Error Codes
The validateVIN function returns specific error codes:
INVALID_LENGTH: VIN is not exactly 17 charactersINVALID_CHARACTERS: Contains forbidden characters (I, O, Q, or non-alphanumeric)INVALID_CHECK_DIGIT: Check digit doesn't match calculated value when strict mode is enabled
Examples
import { validateVIN, isValidVIN, assertVIN, computeCheckDigit, normalizeVIN } from "vin-iso3779-validator";
// Basic validation
const result = validateVIN("1HGCM82633A004352");
console.log(result.isValid); // true
// Handle messy input
const messy = validateVIN(" 1hg-cm82633a004352 ");
console.log(messy.isValid); // true (auto-normalized)
// European/general ISO 3779 validation
const euVin = validateVIN("VR3F45GFRKY097237");
console.log(euVin.isValid); // true
// North American strict validation
const naVin = validateVIN("VR3F45GFRKY097237", { checkDigit: "required" });
console.log(naVin.isValid); // false
// Type-safe validation
if (isValidVIN("JHMCM56557C404453")) {
// vin is now typed as VIN (branded string)
}
// Exception-based validation
try {
const vin = assertVIN("1M8GDM9AXKP042788");
// vin is guaranteed to be valid
} catch (error) {
// Handle invalid VIN
}
// Check digit calculation
const expected = computeCheckDigit("1HGCM82633A004352"); // "3"
// Manual normalization
const clean = normalizeVIN("JHM CM8-2633-A004352"); // "JHMCM82633A004352"Testing
Run the test suite:
npm testRun tests in watch mode:
npm run test:watchThe test suite covers:
- ✅ Valid VIN validation with known test cases
- ✅ Input normalization (spaces, hyphens, case conversion)
- ✅ Invalid length detection
- ✅ Invalid character detection (I, O, Q, and other non-allowed characters)
- ✅ Optional strict check digit validation
- ✅ European/general ISO 3779 VIN acceptance without required check digit
- ✅ Error handling and branded type assertions
- ✅ Edge cases and boundary conditions
Requirements
- Node.js >= 18
- TypeScript >= 5.0 (for TypeScript projects)
License
MIT
Contributing
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass:
npm test - Submit a pull request
Related
- ISO 3779 Standard - Official VIN specification
- VIN Check Digit Algorithm - Wikipedia explanation
