UNPKG

@cardog/corgi

Version:

Fast, offline VIN decoding for Node.js, browsers, and Cloudflare Workers. Powered by the NHTSA VPIC database.

763 lines (749 loc) 20.8 kB
import { D1Database } from '@cloudflare/workers-types'; /** * Common interface for database operations across different environments */ interface DatabaseAdapter { /** * Execute a SQL query with parameters and return the results * * @param query - SQL query to execute * @param params - Optional array of parameters to bind to the query * @returns Array of query results */ exec(query: string, params?: any[]): Promise<QueryResult[]>; /** * Close the database connection */ close(): Promise<void>; } /** * Result from a database query */ interface QueryResult { /** * Array of column names in the result set */ columns: string[]; /** * Two-dimensional array of values: * - First dimension: rows * - Second dimension: column values for each row */ values: any[][]; } /** * Factory function to create the appropriate database adapter */ interface DatabaseAdapterFactory { /** * Create a database adapter for the given path or URL * * @param pathOrUrl - Path to SQLite file or URL for remote database * @returns Initialized database adapter */ createAdapter(pathOrUrl: string): Promise<DatabaseAdapter>; } /** * Error severity levels */ declare enum ErrorSeverity { WARNING = "warning", ERROR = "error", FATAL = "fatal" } /** * Error category types */ declare enum ErrorCategory { VALIDATION = "validation", STRUCTURE = "structure", LOOKUP = "lookup", PATTERN = "pattern", DATABASE = "database" } /** * Specific error codes with structured grouping */ declare enum ErrorCode { INVALID_LENGTH = "100", INVALID_CHARACTERS = "101", INVALID_CHECK_DIGIT = "200", INVALID_MODEL_YEAR = "201", INVALID_REGION = "202", WMI_NOT_FOUND = "300", MANUFACTURER_NOT_FOUND = "301", MAKE_NOT_FOUND = "302", NO_PATTERNS_FOUND = "400", LOW_CONFIDENCE_PATTERNS = "401", CONFLICTING_PATTERNS = "402", DATABASE_CONNECTION_ERROR = "500", QUERY_ERROR = "501", INVALID_RESULT = "502" } /** * Standardized vehicle body styles */ declare enum BodyStyle { SEDAN = "Sedan", COUPE = "Coupe", CONVERTIBLE = "Convertible", HATCHBACK = "Hatchback", SUV = "SUV", CROSSOVER = "Crossover", WAGON = "Wagon", VAN = "Van", MINIVAN = "Minivan", PICKUP = "Pickup", TRUCK = "Truck", TRACTOR = "Tractor", TRAILER = "Trailer", BUS = "Bus", MOTORCYCLE = "Motorcycle", OTHER = "Other" } /** * Core types for the VIN decoder library */ /** * Base error interface for all error types */ interface BaseError { code: ErrorCode; category: ErrorCategory; severity: ErrorSeverity; message: string; positions?: number[]; details?: string; } /** * Error for validation issues (check digit, etc.) */ interface ValidationError extends BaseError { category: ErrorCategory.VALIDATION; expected?: string; actual?: string; } /** * Error for VIN structure issues (length, characters) */ interface StructureError extends BaseError { category: ErrorCategory.STRUCTURE; positions?: number[]; } /** * Error for database lookup failures */ interface LookupError extends BaseError { category: ErrorCategory.LOOKUP; searchKey: string; searchType: string; } /** * Error for pattern matching issues */ interface PatternError extends BaseError { category: ErrorCategory.PATTERN; pattern?: string; confidence?: number; } /** * Error for database-related issues */ interface DatabaseError extends BaseError { category: ErrorCategory.DATABASE; query?: string; params?: unknown[]; } /** * Union type of all possible decoder errors */ type DecodeError = ValidationError | StructureError | LookupError | PatternError | DatabaseError; /** * Position information for a VIN pattern */ interface Position { start: number; length: number; value: string; } /** * Configuration options for VIN decoding */ interface DecodeOptions { /** Include raw database records in the response */ includeRawData?: boolean; /** Include detailed pattern matching information */ includePatternDetails?: boolean; /** Override the detected model year */ modelYear?: number; /** Minimum confidence threshold for pattern matches (default: 0.5) */ confidenceThreshold?: number; /** Include timing and debug information */ includeDiagnostics?: boolean; } /** * World Manufacturer Identifier result */ interface WMIResult { /** 3-character WMI code from the VIN */ code: string; /** Manufacturer name */ manufacturer: string; /** Manufacturing country */ country: string; /** Vehicle type */ vehicleType: string; /** Geographic region */ region: string; /** Vehicle make/brand */ make: string; } /** * Model year extraction result */ interface ModelYearResult { /** Determined model year */ year: number; /** Source of the year determination */ source: 'position' | 'override' | 'calculated'; /** Confidence in the year (0-1) */ confidence: number; } /** * Check digit validation result */ interface CheckDigitResult { /** Position in the VIN (typically 9) */ position: number; /** Actual check digit from the VIN */ actual: string; /** Expected check digit based on calculation */ expected?: string; /** Whether the check digit is valid */ isValid: boolean; } /** * Core vehicle information extracted from VIN patterns */ interface VehicleInfo { /** Vehicle manufacturer (e.g., "Hyundai") */ make: string; /** Vehicle model (e.g., "Kona") */ model: string; /** Model year (e.g., 2023) */ year: number; /** Vehicle series or sub-model */ series?: string; /** Trim level */ trim?: string; /** Body style (e.g., "SUV", "Sedan") */ bodyStyle?: string; /** Drive type (e.g., "AWD", "4x2") */ driveType?: string; /** Engine type */ engineType?: string; /** Primary fuel type */ fuelType?: string; /** Transmission type */ transmission?: string; /** Number of doors */ doors?: string; /** Gross Vehicle Weight Rating */ gvwr?: string; /** Vehicle manufacturer name */ manufacturer?: string; } /** * Manufacturing plant information */ interface PlantInfo { /** Manufacturing country */ country: string; /** Manufacturing city */ city?: string; /** Plant operator/manufacturer */ manufacturer?: string; /** Plant code (from VIN position 11) */ code: string; } /** * Engine specifications */ interface EngineInfo { /** Engine type */ type?: string; /** Engine model code */ model?: string; /** Number of cylinders */ cylinders?: string; /** Engine displacement in liters */ displacement?: string; /** Fuel type */ fuel?: string; /** Engine power (HP) */ power?: string; } /** * Pattern match result from database */ interface PatternMatch { /** Element name (e.g., "Model", "Body Class") */ element: string; /** Element code */ code: string; /** Attribute ID */ attributeId: string | number | null; /** Decoded value */ value: string | null; /** Confidence score (0-1) */ confidence: number; /** VIN positions covered by this pattern */ positions: number[]; /** Schema name */ schema: string; /** Additional metadata */ metadata?: { /** Lookup table name */ lookupTable?: string; /** Group name */ groupName?: string; /** Element weight for priority */ elementWeight?: number; /** Pattern type */ patternType?: 'VDS' | 'VIS'; /** Original pattern string */ rawPattern?: string; /** Match details */ matchDetails?: { exactMatches?: number; wildcardMatches?: number; totalPositions?: number; }; }; } /** * Decoded VIN components */ interface VINComponents { /** World Manufacturer Identifier information */ wmi?: WMIResult; /** Model year information */ modelYear?: ModelYearResult; /** Check digit validation */ checkDigit?: CheckDigitResult; /** Vehicle Descriptor Section patterns */ vds?: { raw: string; patterns: PatternMatch[]; }; /** Vehicle Identifier Section patterns */ vis?: { raw: string; patterns: PatternMatch[]; }; /** Core vehicle information */ vehicle?: VehicleInfo; /** Manufacturing plant information */ plant?: PlantInfo; /** Engine specifications */ engine?: EngineInfo; } /** * Diagnostic and timing information */ interface DiagnosticInfo { /** Total processing time in milliseconds */ processingTime: number; /** Overall confidence score */ confidence: number; /** Library version */ schemaVersion: string; /** Primary schema used for decoding */ matchedSchema?: string; /** Total number of patterns found */ totalPatterns?: number; /** Raw database records (if requested) */ rawRecords?: any[]; /** SQL query information (if diagnostics enabled) */ queries?: { sql: string; params: any[]; timing: number; }[]; } /** * Complete VIN decoding result */ interface DecodeResult { /** Input VIN */ vin: string; /** Whether the VIN is valid */ valid: boolean; /** Decoded components */ components: VINComponents; /** Any validation or decoding errors */ errors: DecodeError[]; /** Pattern matching details (if requested) */ patterns?: PatternMatch[]; /** Diagnostic information */ metadata?: DiagnosticInfo; } /** * Main VIN decoder class implementing the NHTSA VPIC decoding logic */ declare class VINDecoder { private db; private patternMatcher; /** * Create a new VIN decoder * * @param adapter - Database adapter for the current environment */ constructor(adapter: DatabaseAdapter); /** * Decode a VIN and return detailed vehicle information * * @param vin - The Vehicle Identification Number to decode * @param options - Optional configuration for the decoding process * @returns Decoded VIN information */ decode(vin: string, options?: DecodeOptions): Promise<DecodeResult>; /** * Find the primary schema from pattern matches * * @param patterns - Array of pattern matches * @returns Primary schema name or undefined */ private findPrimarySchema; /** * Map raw body style to standardized body style * * @param bodyStyle - Raw body style from database * @returns Standardized body style */ private coerceBodyStyle; /** * Extract vehicle information from pattern matches * * @param patterns - Array of pattern matches * @param wmiInfo - WMI information * @param modelYear - Model year information * @returns Vehicle information */ private extractVehicleInfo; /** * Extract plant information from pattern matches * * @param patterns - Array of pattern matches * @param vin - Complete VIN string * @returns Plant information or undefined */ private extractPlantInfo; /** * Extract engine information from pattern matches * * @param patterns - Array of pattern matches * @returns Engine information or undefined */ private extractEngineInfo; /** * Validate the structure of a VIN * * @param vin - VIN to validate * @returns Array of structure errors */ private validateStructure; /** * Extract the World Manufacturer Identifier from a VIN * * @param vin - Complete VIN string * @returns WMI code */ private extractWMI; /** * Determine model year from VIN * * @param vin - Complete VIN string * @returns Model year information or null */ private determineModelYear; /** * Validate the check digit in a VIN * * @param vin - Complete VIN string * @returns Check digit validation result */ private validateCheckDigit; /** * Close the database connection */ close(): Promise<void>; } /** * Interface for SQL.js static methods */ interface SQLJsStatic { Database: new (data: Uint8Array) => SQLJsDatabase; } /** * Interface for SQL.js database instance */ interface SQLJsDatabase { exec(sql: string, params?: any[]): SQLJsResult[]; close(): void; } /** * Interface for SQL.js query result */ interface SQLJsResult { columns: string[]; values: any[][]; } /** * Global window declarations for SQL.js */ declare global { interface Window { initSqlJs: () => Promise<SQLJsStatic>; SQL: SQLJsStatic; } } /** * Browser implementation of the DatabaseAdapter using SQL.js */ declare class BrowserDatabaseAdapter implements DatabaseAdapter { private db; private queryCount; /** * Create a new database adapter for browser environment * * @param db - SQL.js database instance */ constructor(db: SQLJsDatabase); /** * Execute a SQL query with parameters * * @param query - SQL query to execute * @param params - Parameters to bind to the query * @returns Query results */ exec(query: string, params?: any[]): Promise<QueryResult[]>; /** * Close the database connection */ close(): Promise<void>; } /** * Factory for creating browser database adapters */ declare class BrowserDatabaseAdapterFactory implements DatabaseAdapterFactory { /** * Create a new database adapter for the given URL * * @param pathOrUrl - URL to the SQLite database file * @returns Initialized database adapter */ createAdapter(pathOrUrl: string): Promise<DatabaseAdapter>; } /** * Node.js implementation of the DatabaseAdapter using better-sqlite3 */ declare class NodeDatabaseAdapter implements DatabaseAdapter { private db; private queryCount; /** * Create a new database adapter for Node.js environment * * @param dbPath - Path to the SQLite database file */ constructor(dbPath: string); /** * Execute a SQL query with parameters * * @param query - SQL query to execute * @param params - Parameters to bind to the query * @returns Query results */ exec(query: string, params?: any[]): Promise<QueryResult[]>; /** * Close the database connection */ close(): Promise<void>; } /** * Factory for creating Node.js database adapters */ declare class NodeDatabaseAdapterFactory implements DatabaseAdapterFactory { /** * Create a new database adapter for the given path * * @param pathOrUrl - Path to the SQLite database file * @returns Initialized database adapter */ createAdapter(pathOrUrl: string): Promise<DatabaseAdapter>; } declare class CloudflareD1Adapter implements DatabaseAdapter { private db; constructor(db: D1Database); exec(query: string, params?: any[]): Promise<QueryResult[]>; close(): Promise<void>; } declare function createD1Adapter(db: D1Database): DatabaseAdapter; /** * Gets the path to the database, handling decompression if needed * * @param options - Optional configuration * @returns Path to usable database file */ declare function getDatabasePath(options?: { forceFresh?: boolean; databasePath?: string; }): Promise<string>; interface Logger { info: (obj: unknown, message?: string) => void; error: (obj: unknown, message?: string) => void; warn: (obj: unknown, message?: string) => void; debug: (obj: unknown, message?: string) => void; trace: (obj: unknown, message?: string) => void; } declare function createLogger(component: string, meta?: Record<string, any>): Logger; /** * CORGI - Comprehensive Open Registry for Global Identification * A TypeScript library for decoding and validating Vehicle Identification Numbers (VINs) * * @packageDocumentation */ /** * Configuration options for creating a VIN decoder */ interface DecoderConfig { /** * Path to the VPIC database (optional - will use bundled database if not provided) */ databasePath?: string; /** * Force fresh database setup (ignore cache) */ forceFresh?: boolean; /** * Optional default decode options */ defaultOptions?: DecodeOptions; /** * Runtime environment (automatic detection if not specified) */ runtime?: 'node' | 'browser' | 'cloudflare'; } /** * Create a VIN decoder with the appropriate adapter for the current environment * * @param config - Decoder configuration (optional) * @returns VIN decoder instance * * @example * ```typescript * import { createDecoder } from '@crdg/corgi'; * * // Uses bundled database automatically * const decoder = await createDecoder(); * * // Or with explicit options * const customDecoder = await createDecoder({ * databasePath: '/path/to/vpic.db', * defaultOptions: { * includePatternDetails: true * } * }); * * const result = await decoder.decode('1HGCM82633A123456'); * ``` */ declare function createDecoder(config?: DecoderConfig): Promise<VINDecoderWrapper>; /** * Wrapper for VIN decoder with simplified API */ declare class VINDecoderWrapper { private decoder; private defaultOptions; /** * Create a new VIN decoder wrapper * * @param adapter - Database adapter * @param defaultOptions - Default decode options */ constructor(adapter: DatabaseAdapter, defaultOptions?: DecodeOptions); /** * Decode a VIN * * @param vin - The VIN to decode * @param options - Optional decode options * @returns Decoded VIN information */ decode(vin: string, options?: DecodeOptions): Promise<DecodeResult>; /** * Close the decoder and release resources */ close(): Promise<void>; } /** * Get or create the shared decoder instance with default configuration * * @param config - Optional configuration overrides * @returns Shared decoder instance * * @example * ```typescript * import { getDecoder } from '@crdg/corgi'; * * // Uses default shared instance * const decoder = await getDecoder(); * const result = await decoder.decode('1HGCM82633A123456'); * ``` */ declare function getDecoder(config?: DecoderConfig): Promise<VINDecoderWrapper>; /** * Convenient way to decode a VIN using the shared decoder instance * * @param vin - The VIN to decode * @param options - Optional decoding options * @returns The decode result * * @example * ```typescript * import { quickDecode } from '@crdg/corgi'; * * // Simple one-line decoding * const result = await quickDecode('1HGCM82633A123456'); * console.log(result.components.vehicle); * ``` */ declare function quickDecode(vin: string, options?: DecodeOptions): Promise<DecodeResult>; /** * Decode a VIN using a provided database adapter * This is a lower-level function for advanced uses * * @param vin The VIN to decode * @param adapter The database adapter to use * @param options Optional decoding options * @returns The decode result * * @example * ```typescript * import { decodeVIN, NodeDatabaseAdapterFactory } from '@crdg/corgi'; * * // Initialize the adapter * const factory = new NodeDatabaseAdapterFactory(); * const adapter = await factory.createAdapter('/path/to/vpic.db'); * * // Decode a VIN * const result = await decodeVIN('1HGCM82633A123456', adapter, { * includePatternDetails: true * }); * ``` */ declare function decodeVIN(vin: string, adapter: DatabaseAdapter, options?: DecodeOptions): Promise<DecodeResult>; declare function initD1Adapter(d1: any): void; declare global { var __D1_FACTORY: ((db: string) => Promise<DatabaseAdapter>) | undefined; } export { BodyStyle, BrowserDatabaseAdapter, BrowserDatabaseAdapterFactory, type CheckDigitResult, CloudflareD1Adapter, type DatabaseAdapter, type DatabaseAdapterFactory, type DatabaseError, type DecodeError, type DecodeOptions, type DecodeResult, type DecoderConfig, type DiagnosticInfo, type EngineInfo, ErrorCategory, ErrorCode, ErrorSeverity, type LookupError, type ModelYearResult, NodeDatabaseAdapter, NodeDatabaseAdapterFactory, type PatternError, type PatternMatch, type PlantInfo, type Position, type QueryResult, type StructureError, type VINComponents, VINDecoder, VINDecoderWrapper, type ValidationError, type VehicleInfo, type WMIResult, createD1Adapter, createDecoder, createLogger, decodeVIN, getDatabasePath, getDecoder, initD1Adapter, quickDecode };