@cardog/corgi
Version:
A TypeScript library for decoding and validating Vehicle Identification Numbers (VINs) using a customized VPIC (Vehicle Product Information Catalog) database. Supports Node.js, browser, and Cloudflare environments with fully offline VIN decoding.
330 lines (261 loc) • 11.3 kB
Markdown
# Corgi VIN Decoder
A TypeScript library for decoding and validating Vehicle Identification Numbers (VINs) using a customized VPIC (Vehicle Product Information Catalog) database.
## Features
- Fully local VIN validation and decoding
- Comprehensive vehicle information extraction
- Plant and manufacturing information
- Engine specifications
- Pattern-based decoding with confidence scores
- Support for Node.js, browser, and Cloudflare environments
- TypeScript-first with complete type definitions
- Command-line interface for quick VIN lookups
## Installation
```bash
npm install /corgi
```
## Offline Database and How It Works
Corgi is designed for fully offline VIN decoding. It achieves this by bundling a customized, compressed SQLite database (`vpic.lite.db.gz`, approximately 40MB) derived from the NHTSA VPIC dataset.
### Node.js Environment
- When you call `await createDecoder()` without a `databasePath` option, Corgi automatically locates the bundled `vpic.lite.db.gz`.
- On the first run, this gzipped database is decompressed into a local cache directory at `~/.corgi-cache/vpic.lite.db`.
- Subsequent calls will use this cached, uncompressed database for faster initialization.
- If you need to force a re-decompress (e.g., if the cache is corrupted or after a package update that changes the bundled DB), you can use the `forceFresh: true` option: `await createDecoder({ forceFresh: true });`
- If you prefer to manage your own uncompressed SQLite database file, you can provide its path using the `databasePath` option: `await createDecoder({ databasePath: "/path/to/your/vpic.lite.db" });`
### Browser Environment
- In the browser, you **must** provide a `databasePath` option, which should be a URL pointing to where you are hosting the database file.
- **Recommended Method:** Host the compressed `vpic.lite.db.gz` file (found in the `dist/db/` directory of the installed `/corgi` package or from the [GitHub repository](https://github.com/cardog-ai/corgi)). Configure your web server to serve this `.gz` file with the `Content-Encoding: gzip` HTTP header. The browser will then handle decompression automatically.
```typescript
// Browser (uses sql.js, server handles gzip decompression)
const browserDecoder = await createDecoder({
databasePath: "https://your-cdn.com/assets/vpic.lite.db.gz", // Path to your gzipped DB
runtime: "browser",
});
```
- **Alternative Method:** Host an uncompressed `vpic.lite.db` file. You would need to decompress the `vpic.lite.db.gz` file yourself first.
```typescript
// Browser (uses sql.js, serving an uncompressed DB)
const browserDecoder = await createDecoder({
databasePath: "/assets/vpic.lite.db", // Path to your uncompressed DB
runtime: "browser",
});
```
To get an uncompressed database, you can:
1. Find `vpic.lite.db.gz` in `node_modules/@cardog/corgi/dist/db/`.
2. Manually decompress it using a tool like `gunzip`.
3. Place the resulting `vpic.lite.db` in your web server's public assets directory.
### Cloudflare D1 Environment
- For Cloudflare Workers using D1, the database is managed by D1. Initialize the D1 adapter using `initD1Adapter(env.D1_DATABASE)`. The `databasePath` in `createDecoder` is then a placeholder and not used to load a file.
```typescript
import { createDecoder, initD1Adapter } from "@cardog/corgi";
// In your worker setup (e.g., `fetch` handler or module scope)
// initD1Adapter(env.YOUR_D1_BINDING); // Replace env.YOUR_D1_BINDING with your actual D1 binding
// Then, when you need a decoder:
const d1Decoder = await createDecoder({
databasePath: "D1", // Path is ignored for D1 but still a required parameter
runtime: "cloudflare",
});
```
## Quick Start
```typescript
import { createDecoder } from "@cardog/corgi";
// Create a decoder (it will automatically find and use the bundled database)
const decoder = await createDecoder();
// Decode a VIN
const result = await decoder.decode("KM8K2CAB4PU001140");
console.log(result.components.vehicle);
// {
// make: 'Hyundai',
// model: 'Kona',
// year: 2023,
// series: 'SE',
// bodyStyle: 'SUV',
// driveType: '4WD/4-Wheel Drive/4x4',
// fuelType: 'Gasoline',
// doors: '5'
// }
// Don't forget to close when done
await decoder.close();
```
## Usage
### Environment-aware Decoder
The library automatically detects and configures itself for Node.js, browser, or Cloudflare environments:
```typescript
import { createDecoder } from "@cardog/corgi";
// Node.js (uses better-sqlite3)
// The library automatically finds the bundled database, decompresses it to a cache
// on first run (~/.corgi-cache/vpic.lite.db), and uses the cache thereafter.
const nodeDecoder = await createDecoder();
// Or, if you manage your own uncompressed database file:
// const nodeDecoder = await createDecoder({ databasePath: "/path/to/your/vpic.lite.db" });
// Browser (uses sql.js)
// See "Offline Database and How It Works" -> "Browser Environment" for details
// on how to host and provide the databasePath.
const browserDecoder = await createDecoder({
databasePath: "https://your-cdn.com/assets/vpic.lite.db.gz", // Or /path/to/uncompressed.db
runtime: "browser",
});
// Cloudflare (uses D1)
// See "Offline Database and How It Works" -> "Cloudflare D1 Environment" for details.
import { initD1Adapter } from "@cardog/corgi";
// Init D1 adapter once (e.g., in your worker setup)
// initD1Adapter(env.YOUR_D1_BINDING); // Replace with your D1 binding
// Then create decoder
const d1Decoder = await createDecoder({
databasePath: "D1", // Path is ignored for D1 but still a required parameter
runtime: "cloudflare",
});
```
### Configuration Options
```typescript
// Example for Node.js - will use cached DB if databasePath is omitted
const decoder = await createDecoder({
// databasePath: "./db/vpic.lite.db", // Optional: omit to use auto-caching
defaultOptions: {
includePatternDetails: true, // Include pattern matching details
includeRawData: false, // Include raw database records
confidenceThreshold: 0.5, // Custom confidence threshold
includeDiagnostics: true, // Include timing and debug info
},
});
// Override options for specific decodes
const result = await decoder.decode("KM8K2CAB4PU001140", {
modelYear: 2024, // Override model year detection
});
```
### Response Structure
```typescript
{
vin: string; // Input VIN
valid: boolean; // Overall validation status
components: {
wmi?: { // World Manufacturer Identifier info
code: string;
manufacturer: string;
make: string;
country: string;
vehicleType: string;
region: string;
};
modelYear?: { // Model year info
year: number;
source: "position" | "override" | "calculated";
confidence: number;
};
checkDigit?: { // Check digit validation
position: number;
actual: string;
expected?: string;
isValid: boolean;
};
vehicle?: { // Core vehicle info
make: string;
model: string;
year: number;
series?: string;
trim?: string;
bodyStyle?: string;
driveType?: string;
fuelType?: string;
doors?: string;
};
plant?: { // Manufacturing plant info
country: string;
city?: string;
manufacturer?: string;
code: string;
};
engine?: { // Engine specifications
model?: string;
cylinders?: string;
displacement?: string;
fuel?: string;
power?: string;
};
};
errors: DecodeError[]; // Any validation or decode errors
metadata?: { // Diagnostic metadata
processingTime: number;
confidence: number;
schemaVersion: string;
matchedSchema?: string;
};
patterns?: PatternMatch[]; // Pattern matching details (if requested)
}
```
### Error Handling
```typescript
import { ErrorCode, ErrorCategory, ErrorSeverity } from "@cardog/corgi";
try {
const result = await decoder.decode("INVALID_VIN");
if (!result.valid) {
for (const error of result.errors) {
console.log(`Error: ${error.message}`);
console.log(`Category: ${error.category}`);
console.log(`Severity: ${error.severity}`);
// Check for specific error types
if (error.code === ErrorCode.INVALID_CHECK_DIGIT) {
console.log(`Expected: ${error.expected}, Actual: ${error.actual}`);
}
}
}
} catch (error) {
console.error("Decoder error:", error);
}
```
## Command Line Interface
The library includes a CLI for quick VIN lookups:
```bash
# Basic usage
npx corgi decode 1HGCM82633A123456
# Specify database path
npx corgi decode 1HGCM82633A123456 --database ./db/vpic.lite.db
# Include pattern details
npx corgi decode 1HGCM82633A123456 --patterns
# Override model year
npx corgi decode 1HGCM82633A123456 --year 2022
# JSON output
npx corgi decode 1HGCM82633A123456 --format json
# Help
npx corgi --help
```
The CLI also benefits from the automatic database caching. If you don't provide a `--database` path, it will use the bundled database and cache it in `~/.corgi-cache/` just like the Node.js library usage.
## Advanced Features
### Body Style Normalization
The library automatically normalizes database body class values to consistent body styles:
```typescript
import { BodyStyle } from "@cardog/corgi";
// Standard body style enum
console.log(BodyStyle.SUV); // "SUV"
console.log(BodyStyle.SEDAN); // "Sedan"
console.log(BodyStyle.PICKUP); // "Pickup"
// Raw database values are mapped to standard styles
// "Sport Utility Vehicle (SUV)/Multi-Purpose Vehicle (MPV)" -> "SUV"
// "Sedan/Saloon" -> "Sedan"
// "Crew Cab Pickup" -> "Pickup"
```
### Confidence Scores
Each pattern match includes a confidence score:
```typescript
const result = await decoder.decode("KM8K2CAB4PU001140", {
includePatternDetails: true,
});
// Overall confidence
console.log(`Overall: ${result.metadata?.confidence}`);
// Individual pattern confidences
for (const pattern of result.patterns || []) {
console.log(`${pattern.element}: ${pattern.value} (${pattern.confidence})`);
}
```
## Contributing
Contributions are welcome! If you're looking to improve Corgi or add new features, here's a brief overview of how to get started:
- **Database:**
- The source SQLite database (`db/vpic.db` - not included in repo, generated from NHTSA data) is processed by `db/optimize-db.sh` to create `db/vpic.lite.db`. This script slims down the database by removing unused tables and data.
- The `scripts/prepare-db.js` script then compresses `db/vpic.lite.db` into `dist/db/vpic.lite.db.gz`, which is the file bundled with the npm package.
- **Development:**
- After cloning the repository, install dependencies using your preferred package manager (e.g., `npm install` or `pnpm install`).
- The library is written in TypeScript and uses `tsup` for building.
- **Tests:**
- Run tests using `npm test` or `pnpm test`. Tests are written with `vitest`. Ensure any changes pass existing tests and add new tests for new functionality.
Please open an issue to discuss significant changes before submitting a pull request.
## License
ISC