@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.
104 lines (102 loc) • 29.8 kB
JavaScript
'use strict';var commander=require('commander'),se=require('better-sqlite3'),zlib=require('zlib'),fs=require('fs'),Ee=require('path'),os=require('os'),promises=require('stream/promises'),url=require('url'),process$1=require('process');var _documentCurrentScript=typeof document!=='undefined'?document.currentScript:null;function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var se__default=/*#__PURE__*/_interopDefault(se);var Ee__default=/*#__PURE__*/_interopDefault(Ee);var ne={level:"info",enabled:true,name:"app"},Q={fatal:0,error:1,warn:2,info:3,debug:4,trace:5},F=class l{constructor(e={}){this.globalLevel="info";this.options={...ne,...e},this.globalLevel=this.options.level||"info";}shouldLog(e){return this.options.enabled?Q[e]<=Q[this.globalLevel]:false}formatMessage(e,r="",t="",a){let n=new Date().toISOString(),s=r?`[${r}]`:"";if(a&&typeof a=="object")try{let o=JSON.stringify(a);return `${n} ${e.toUpperCase()} ${s} ${t} ${o}`}catch{return `${n} ${e.toUpperCase()} ${s} ${t} [Object cannot be stringified]`}return `${n} ${e.toUpperCase()} ${s} ${t} ${a!==void 0?a:""}`}trace(e,r=""){this.shouldLog("trace")&&console.debug(this.formatMessage("trace",this.options.name,r,e));}debug(e,r=""){this.shouldLog("debug")&&console.debug(this.formatMessage("debug",this.options.name,r,e));}info(e,r=""){this.shouldLog("info")&&console.log(this.formatMessage("info",this.options.name,r,e));}warn(e,r=""){this.shouldLog("warn")&&console.warn(this.formatMessage("warn",this.options.name,r,e));}error(e,r=""){this.shouldLog("error")&&console.error(this.formatMessage("error",this.options.name,r,e));}fatal(e,r=""){this.shouldLog("fatal")&&console.error(this.formatMessage("fatal",this.options.name,r,e));}child(e,r={}){let t=new l({...this.options,name:e}),a={trace:t.trace.bind(t),debug:t.debug.bind(t),info:t.info.bind(t),warn:t.warn.bind(t),error:t.error.bind(t)};return Object.keys(a).forEach(n=>{let s=n;t[s]=(o,i="")=>{a[s](typeof o=="object"&&o!==null?{...r,...o}:{...r,value:o},i);};}),t}setLevel(e){this.globalLevel=e;}},R=new F;function I(l,e={}){return R.child(l,e)}var C=class{constructor(e){this.queryCache=new Map;this.adapter=e;}async get(e,r=[]){var t,a;try{let n=`${e}:${JSON.stringify(r)}`;if(this.queryCache.has(n))return this.queryCache.get(n);let s=await this.adapter.exec(e,r);if(((a=(t=s[0])==null?void 0:t.values)==null?void 0:a.length)>0){let o={};return s[0].columns.forEach((i,d)=>{o[i]=s[0].values[0][d];}),this.queryCache.set(n,o),o}return this.queryCache.set(n,null),null}catch(n){throw R.error({error:n,sql:e,params:r},"Database get error"),n}}async query(e,r=[]){var t,a;try{let n=`query:${e}:${JSON.stringify(r)}`;if(this.queryCache.has(n))return this.queryCache.get(n);let s=await this.adapter.exec(e,r);if(((a=(t=s[0])==null?void 0:t.values)==null?void 0:a.length)>0){let i=s[0].values.map(d=>{let u={};return s[0].columns.forEach((m,b)=>{u[m]=d[b];}),u});return this.queryCache.set(n,i),i}let o=[];return this.queryCache.set(n,o),o}catch(n){throw R.error({error:n,sql:e,params:r},"Database query error"),n}}clearCache(){this.queryCache.clear();}async close(){await this.adapter.close();}async getWMI(e){return this.get(`
WITH RECURSIVE
WmiMakes AS (
SELECT
w.Id as WmiId,
w.Wmi as code,
m.Name as manufacturer,
ma.Name as make,
c.Name as country,
vt.Name as vehicleType,
CASE
WHEN c.Name IN ('UNITED STATES', 'CANADA', 'MEXICO') THEN 'NORTH AMERICA'
WHEN c.Name IN ('JAPAN', 'KOREA', 'CHINA', 'TAIWAN') THEN 'ASIA'
WHEN c.Name IN ('GERMANY', 'UNITED KINGDOM', 'ITALY', 'FRANCE', 'SWEDEN') THEN 'EUROPE'
ELSE 'OTHER'
END as region,
ROW_NUMBER() OVER (PARTITION BY w.Wmi ORDER BY
CASE
-- Prioritize RAM for specific WMIs
WHEN w.Wmi IN ('1C6', '2C6', '3C6') AND ma.Name = 'RAM' THEN 1
-- Then prioritize by creation date
ELSE 2
END,
w.CreatedOn DESC
) as rn
FROM Wmi w
LEFT JOIN Manufacturer m ON w.ManufacturerId = m.Id
LEFT JOIN Wmi_Make wm ON w.Id = wm.WmiId
LEFT JOIN Make ma ON wm.MakeId = ma.Id
LEFT JOIN Country c ON w.CountryId = c.Id
LEFT JOIN VehicleType vt ON w.VehicleTypeId = vt.Id
WHERE w.Wmi = ?
)
SELECT
code,
manufacturer,
make,
country,
vehicleType,
region
FROM WmiMakes
WHERE rn = 1
`,[e])}async getValidSchemas(e,r){return this.query(`
SELECT DISTINCT vs.Id as SchemaId, vs.Name as SchemaName
FROM Wmi w
JOIN Wmi_VinSchema wvs ON w.Id = wvs.WmiId
JOIN VinSchema vs ON wvs.VinSchemaId = vs.Id
WHERE w.Wmi = ?
AND ? >= wvs.YearFrom
AND (wvs.YearTo IS NULL OR ? <= wvs.YearTo)
`,[e,r,r])}async getPatterns(e){if(e.length===0)return [];let r=`
WITH ValidSchemas AS (
SELECT vs.Id, vs.Name
FROM VinSchema vs
WHERE vs.Id IN (${e.join(",")})
)
SELECT DISTINCT
p.Keys as Pattern,
e.Id as ElementId,
e.Name as ElementName,
e.Code as ElementCode,
e.GroupName,
e.Description,
e.LookupTable,
p.AttributeId,
vs.Name as SchemaName,
wvs.YearFrom,
wvs.YearTo,
e.weight as ElementWeight
FROM Pattern p
JOIN Element e ON p.ElementId = e.Id
JOIN ValidSchemas vs ON p.VinSchemaId = vs.Id
JOIN Wmi_VinSchema wvs ON p.VinSchemaId = wvs.VinSchemaId
WHERE p.VinSchemaId IN (${e.join(",")})
UNION ALL
SELECT
p.Keys as Pattern,
(SELECT Id FROM Element WHERE Name = 'Make' LIMIT 1) as ElementId,
'Make' as ElementName,
'MK' as ElementCode,
'Vehicle' as GroupName,
NULL as Description,
NULL as LookupTable,
m.Name as AttributeId,
vs.Name as SchemaName,
wvs.YearFrom,
wvs.YearTo,
(SELECT weight FROM Element WHERE Name = 'Make' LIMIT 1) as ElementWeight
FROM Pattern p
JOIN Element e ON p.ElementId = e.Id
JOIN ValidSchemas vs ON p.VinSchemaId = vs.Id
JOIN Wmi_VinSchema wvs ON p.VinSchemaId = wvs.VinSchemaId
JOIN Make_Model mm ON mm.ModelId = CAST(p.AttributeId AS INTEGER)
JOIN Make m ON m.Id = mm.MakeId
WHERE e.Name = 'Model'
AND p.VinSchemaId IN (${e.join(",")})
`;return this.query(r,[])}async lookupValues(e,r){if(!e||r.length===0)return new Map;try{let t=r.map(()=>"?").join(","),a=`
SELECT CAST(Id AS TEXT) as Id, Name
FROM ${e}
WHERE CAST(Id AS TEXT) IN (${t})
`,n=await this.query(a,[...r]),s=new Map;for(let o of n)s.set(o.Id,o.Name);return s}catch(t){return R.warn({error:t,tableName:e,ids:r},"Lookup table query failed"),new Map}}};var $=I("PatternMatcher"),V=class{constructor(e){this.db=new C(e);}parsePositions(e){let r=[],t=0;for(;t<e.length;){let a=e[t],n=1;for(;t+n<e.length&&e[t+n]===a;)n++;r.push({start:t,length:n,value:a}),t+=n;}return r}isCharInRange(e,r){if(!r.startsWith("[")||!r.endsWith("]"))return e===r||r==="*";let t=r.slice(1,-1),a=0;for(;a<t.length;)if(a+2<t.length&&t[a+1]==="-"){let n=t[a].charCodeAt(0),s=t[a+2].charCodeAt(0),o=e.charCodeAt(0);if(o>=n&&o<=s)return true;a+=3;}else {if(e===t[a])return true;a++;}return false}matchesPattern(e,r){if(!e||!r)return false;let[t,...a]=r.split("|");if(a.length>0&&t.length===5){let n=a[0],s=e[0],o=n[1];return o==="*"||s===o}return this.matchesSimplePattern(e,t)}matchesSimplePattern(e,r){let t=0,a=0;for(;t<r.length&&a<e.length;){let n=r[t],s=e[a];if(n==="["){let o=r.indexOf("]",t);if(o===-1)return false;let i=r.substring(t,o+1);if(!this.isCharInRange(s,i))return false;t=o+1,a++;continue}if(n==="*"){if(t===r.length-1)return true;t++,a++;continue}if(s!==n)return false;t++,a++;}return t>=r.length||t===r.length-1&&r[t]==="*"}calculateConfidence(e,r){if(!e||!r)return 0;let[t,...a]=e.split("|");if(a.length>0&&t.length===5){let b=r,h=a[0][1];return h==="*"?.8:h===b?1:0}if(!this.matchesPattern(r,t))return 0;let n=0,s=0,o=0,i=0,d=0,u=0;for(;d<t.length&&u<r.length;){let b=t[d],f=r[u];if(b==="["){let h=t.indexOf("]",d);if(h===-1)break;t.substring(d,h+1).slice(1,-1).includes("-")?s+=.7:s+=.8,i++,d=h+1,u++;}else b==="*"?(o++,i++,d++,u++):(b===f&&n++,i++,d++,u++);}let m=(n*1+s+o*.5)/i;return Math.min(1,Math.max(0,m))}transformPatternMatch(e){let r=e.positions||[];return {element:e.elementName,code:e.elementCode,attributeId:e.attributeId,value:e.value,confidence:e.confidence,positions:r,schema:e.schemaName,metadata:{lookupTable:e.lookupTable,groupName:e.groupName,elementWeight:e.elementWeight,patternType:e.patternType,rawPattern:e.pattern}}}async getPatternMatches(e,r,t,a){let s=(await this.getRawPatternMatches(e,r,t,a)).filter(d=>d.elementName.toLowerCase().includes("plant")?d.confidence>.3:d.confidence>.5).map(d=>this.transformPatternMatch(d)),o={};s.forEach(d=>{let u=d.element;o[u]||(o[u]=[]),o[u].push(d);});let i=[];for(let[d,u]of Object.entries(o)){let m=u.sort((h,c)=>{var p,v;let g=((p=h.metadata)==null?void 0:p.elementWeight)??0,E=((v=c.metadata)==null?void 0:v.elementWeight)??0;return g!==E?E-g:c.confidence-h.confidence}),b=new Set,f=m.filter(h=>{let c=JSON.stringify({value:h.value,positions:h.positions.join(","),schema:h.schema});return b.has(c)?false:(b.add(c),true)});i=i.concat(f);}return i}async getRawPatternMatches(e,r,t,a){try{let n=await this.db.getValidSchemas(e,r);if(n.length===0)return $.debug({wmi:e,modelYear:r},"No valid schemas found"),[];let s=n.map(c=>c.SchemaId),o=await this.db.getPatterns(s),i=["DriveType","EngineModel","EngineConfiguration","FuelType","ChargerLevel","Conversion","BodyStyle","Transmission","Make","Model","Series","Trim","Turbo","Country","DestinationMarket","Plant","DaytimeRunningLight"],d=o.filter(c=>!(c.LookupTable&&(!i.includes(c.LookupTable)||c.LookupTable.includes("vNCSA")))),u={},m=[];for(let c of d)c.LookupTable?(u[c.LookupTable]||(u[c.LookupTable]=[]),u[c.LookupTable].push(c)):(c.ResolvedValue=c.AttributeId,m.push(c));for(let[c,g]of Object.entries(u)){let E=[...new Set(g.map(p=>String(p.AttributeId)))];if(E.length!==0)try{let p=await this.db.lookupValues(c,E);for(let v of g){let O=String(v.AttributeId);v.ResolvedValue=p.get(O)||v.AttributeId;}}catch(p){$.warn({error:p,tableName:c},"Lookup table resolution failed");for(let v of g)v.ResolvedValue=v.AttributeId;}}let b=[...m,...Object.values(u).flat()];b.sort((c,g)=>c.ElementWeight!==g.ElementWeight?g.ElementWeight-c.ElementWeight:c.Pattern.localeCompare(g.Pattern));let f=b.filter(c=>c.ElementName==="Model").map(c=>({...c,confidence:this.calculateConfidence(c.Pattern,t+a)})).sort((c,g)=>g.confidence-c.confidence),h=f.length>0?f[0].SchemaName:null;return b.map(c=>{var J;let g=c.Pattern,E=g.includes("|"),p=E?this.calculateConfidence(g,a[1]):this.calculateConfidence(g,t+a),v=p;c.ElementName.toLowerCase().includes("plant")&&(h?v=c.SchemaName===h?p:0:v=p*.5);let O=[],q=g.split("|")[0],re=E?9:3;for(let M=0;M<q.length;M++)q[M]!=="|"&&O.push(re+M);return {pattern:c.Pattern,elementId:c.ElementId,elementName:c.ElementName,element:c.ElementName,elementCode:c.ElementCode,groupName:c.GroupName,description:((J=c.Description)==null?void 0:J.toString())??null,lookupTable:c.LookupTable,attributeId:c.ResolvedValue?String(c.ResolvedValue):null,value:c.ResolvedValue?String(c.ResolvedValue):null,schemaName:c.SchemaName,yearFrom:c.YearFrom,yearTo:c.YearTo,confidence:v,keys:c.Pattern,elementWeight:c.ElementWeight,patternType:E?"VIS":"VDS",positions:O}})}catch(n){throw $.error({error:n,wmi:e,modelYear:r},"Error getting pattern matches"),n}}};var k={"Sedan/Saloon":"Sedan",Sedan:"Sedan","4-Door Sedan":"Sedan","2-Door Sedan":"Sedan","4-Door Saloon":"Sedan",Coupe:"Coupe","2-Door Coupe":"Coupe",Convertible:"Convertible","2-Door Convertible":"Convertible","4-Door Convertible":"Convertible",Hatchback:"Hatchback","3-Door Hatchback":"Hatchback","5-Door Hatchback":"Hatchback","Station Wagon":"Wagon",Wagon:"Wagon","Sport Utility Vehicle (SUV)/Multi-Purpose Vehicle (MPV)":"SUV","Sport Utility Vehicle (SUV)":"SUV",SUV:"SUV","Crossover Utility Vehicle (CUV)":"SUV",Crossover:"SUV",Van:"Van","Cargo Van":"Van",Minivan:"Minivan","Passenger Van":"Van",Pickup:"Pickup","Pickup Truck":"Pickup",Truck:"Truck","Standard Pickup Truck":"Pickup","Extended Cab Pickup":"Pickup","Crew Cab Pickup":"Pickup",Bus:"Bus","School Bus":"Bus",Motorcycle:"Motorcycle","Incomplete Vehicle":"Other",Other:"Other"};var ae=I("VINDecoder");var x=class{constructor(e){this.db=new C(e),this.patternMatcher=new V(e);}async decode(e,r={}){let t=performance.now?performance.now():Date.now(),a=e.toUpperCase().trim(),n={vin:a,valid:false,components:{},errors:[],metadata:{processingTime:0,confidence:0,schemaVersion:"1.0"}};r.includeDiagnostics&&(n.metadata.queries=[]),r.includeRawData&&(n.metadata.rawRecords=[]);try{let s=this.validateStructure(a);if(s.length>0)return n.errors=s,n.metadata.processingTime=Date.now()-t,n;let o=this.validateCheckDigit(a);n.components.checkDigit=o,o.isValid||n.errors.push({code:"200",category:"validation",severity:"warning",message:"Invalid check digit",positions:[8],expected:o.expected,actual:o.actual});let i=r.modelYear?{year:r.modelYear,source:"override",confidence:1}:this.determineModelYear(a);if(!i)return n.errors.push({code:"201",category:"validation",severity:"error",message:"Could not determine model year",positions:[9]}),n.metadata.processingTime=Date.now()-t,n;n.components.modelYear=i;let d=this.extractWMI(a),u=await this.db.getWMI(d);if(!u)return n.errors.push({code:"300",category:"lookup",severity:"error",message:"WMI not found in database",searchKey:d,searchType:"WMI"}),n.metadata.processingTime=Date.now()-t,n;n.components.wmi=u;try{let m=a.substring(3,9),b=a.substring(9,17),f=await this.patternMatcher.getPatternMatches(d,i.year,m,b);if(f.length>0){let h=f.filter(E=>{var p;return ((p=E.metadata)==null?void 0:p.patternType)==="VDS"}),c=f.filter(E=>{var p;return ((p=E.metadata)==null?void 0:p.patternType)==="VIS"});h.length>0&&(n.components.vds={raw:m,patterns:h}),c.length>0&&(n.components.vis={raw:b,patterns:c}),n.components.vehicle=this.extractVehicleInfo(f,u,i),n.components.plant=this.extractPlantInfo(f,a),n.components.engine=this.extractEngineInfo(f),r.includePatternDetails&&(n.patterns=f);let g=f.reduce((E,p)=>E+p.confidence,0)/f.length;n.metadata.confidence=g,n.metadata.matchedSchema=this.findPrimarySchema(f),n.metadata.totalPatterns=f.length,g<(r.confidenceThreshold||.5)&&n.errors.push({code:"401",category:"pattern",severity:"warning",message:"Low confidence in pattern matches",confidence:g});}else return n.errors.push({code:"400",category:"pattern",severity:"error",message:"No matching patterns found"}),n.metadata.processingTime=Date.now()-t,n}catch(m){return n.errors.push({code:"501",category:"database",severity:"error",message:"Error matching patterns",details:m instanceof Error?m.message:"Unknown error"}),n.metadata.processingTime=Date.now()-t,n}n.valid=n.errors.every(m=>m.severity==="warning")||n.errors.length===0;}catch(s){ae.error({vin:e,error:s},"Decoder error"),n.errors.push({code:"501",category:"database",severity:"error",message:"Unexpected error during decoding",details:s instanceof Error?s.message:"Unknown error"});}return n.metadata.processingTime=performance.now?performance.now()-t:Date.now()-t,n}findPrimarySchema(e){var t;return (t=e.filter(a=>a.element==="Model").sort((a,n)=>n.confidence-a.confidence)[0])==null?void 0:t.schema}coerceBodyStyle(e){if(e in k)return k[e];for(let[t,a]of Object.entries(k))if(e.toLowerCase().includes(t.toLowerCase())||t.toLowerCase().includes(e.toLowerCase()))return a;let r=e.toLowerCase();return r.includes("pickup")||r.includes("truck")?"Pickup":"Other"}extractVehicleInfo(e,r,t){let a={make:r.make||"",model:"",year:t.year,manufacturer:r.manufacturer},n=e.filter(s=>s.element==="Model"&&s.value).sort((s,o)=>{var u,m;let i=((u=s.metadata)==null?void 0:u.elementWeight)??0,d=((m=o.metadata)==null?void 0:m.elementWeight)??0;return i!==d?d-i:o.confidence-s.confidence});n.length>0&&(a.model=n[0].value);for(let s of e)if(s.value)switch(s.element){case "Make":a.make=s.value;break;case "Series":a.series=s.value;break;case "Trim":case "Trim Level":a.trim=s.value;break;case "Body Class":case "Body Style":a.bodyStyle=this.coerceBodyStyle(s.value);break;case "Drive Type":a.driveType=s.value;break;case "Fuel Type - Primary":a.fuelType=s.value;break;case "Fuel Type - Secondary":a.fuelType="Hybrid";break;case "Transmission":a.transmission=s.value;break;case "Doors":a.doors=s.value;break}return a}extractPlantInfo(e,r){let t,a,n;for(let o of e){if(!o.value)continue;let i=o.element.toLowerCase();i==="plant country"?t=o.value:i==="plant city"?a=o.value:i==="plant company name"&&(n=o.value);}let s=r[10];if(t)return {country:t,city:a,manufacturer:n,code:s}}extractEngineInfo(e){let r={},t=false;for(let a of e)if(a.value)switch(a.element){case "Engine Model":r.model=a.value,t=true;break;case "Engine Number of Cylinders":case "Cylinders":r.cylinders=a.value,t=true;break;case "Displacement (L)":r.displacement=a.value,t=true;break;case "Engine Brake (hp) From":case "Engine Power (KW)":r.power=a.value,t=true;break;case "Fuel Type - Primary":case "Fuel Type":r.fuel=a.value,t=true;break}return t?r:void 0}validateStructure(e){let r=[];if(e.length!==17)return r.push({code:"100",category:"structure",severity:"error",message:"Invalid VIN length"}),r;let t=[...e].reduce((a,n,s)=>(s===8?/[0-9X]/.test(n)||a.push({char:n,pos:s+1}):s===9?/[0-9A-HJ-NPR-Z]/.test(n)||a.push({char:n,pos:s+1}):/[0-9A-HJ-NPR-Z]/.test(n)||a.push({char:n,pos:s+1}),a),[]);return t.length>0&&r.push({code:"101",category:"structure",severity:"error",message:`Invalid characters: ${t.map(a=>`${a.char} at position ${a.pos}`).join(", ")}`,positions:t.map(a=>a.pos)}),r}extractWMI(e){let r=e.substring(0,3);return r[2]==="9"&&e.length>=14?r+e.substring(11,14):r}determineModelYear(e){let r=e[9].toUpperCase(),t=new Map;for(let o=0;o<8;o++)t.set(String.fromCharCode(65+o),1980+o);for(let o=0;o<5;o++)t.set(String.fromCharCode(74+o),1988+o);for(let o=0;o<9;o++){let i=String.fromCharCode(80+o);i!=="Q"&&i!=="U"&&t.set(i,1993+o);}for(let o=0;o<8;o++)t.set(String.fromCharCode(65+o),2010+o);for(let o=0;o<5;o++)t.set(String.fromCharCode(74+o),2018+o);for(let o=0;o<9;o++){let i=String.fromCharCode(80+o);i==="P"?t.set(i,2023):i==="R"?t.set(i,2024):i==="S"?t.set(i,2025):i==="T"?t.set(i,2026):i==="V"?t.set(i,2027):i==="W"?t.set(i,2028):i==="X"?t.set(i,2029):i==="Y"&&t.set(i,2030);}for(let o=1;o<=9;o++)t.set(String(o),2030+o);let a=t.get(r);if(!a)return null;let n=a,s=new Date().getFullYear()+1;return n>s&&(n-=30),{year:n,source:"position",confidence:1}}validateCheckDigit(e){let r=[8,7,6,5,4,3,2,10,0,9,8,7,6,5,4,3,2],t=i=>{let d=i.toUpperCase();if(/[0-9]/.test(d))return parseInt(d,10);if(/[A-Z]/.test(d))switch(d){case "A":return 1;case "B":return 2;case "C":return 3;case "D":return 4;case "E":return 5;case "F":return 6;case "G":return 7;case "H":return 8;case "J":return 1;case "K":return 2;case "L":return 3;case "M":return 4;case "N":return 5;case "P":return 7;case "R":return 9;case "S":return 2;case "T":return 3;case "U":return 4;case "V":return 5;case "W":return 6;case "X":return 7;case "Y":return 8;case "Z":return 9;default:return 0}return 0},n=[...e].reduce((i,d,u)=>i+t(d)*r[u],0)%11,s=n===10?"X":n.toString(),o=e[8].toUpperCase();return {position:9,actual:o,expected:s,isValid:o===s}}async close(){await this.db.close();}};var N=I("BrowserDatabaseAdapter");function oe(l){return l==null?"NULL":typeof l=="number"?l.toString():typeof l=="boolean"?l?"1":"0":`'${l.toString().replace(/'/g,"''")}'`}var W=class{constructor(e){this.queryCount=0;this.db=e,N.debug("Browser database adapter initialized");}async exec(e,r=[]){var a,n;this.queryCount++;let t=this.queryCount;try{N.debug({queryId:t,query:e,paramCount:r.length},"Executing browser query");let s=Date.now(),o=r.reduce((u,m,b)=>u.replace("?",oe(m)),e),i=this.db.exec(o),d=Date.now()-s;return !i||i.length===0?(N.debug({queryId:t,executionTime:d},"Query returned no results"),[{columns:[],values:[]}]):(N.debug({queryId:t,executionTime:d,resultCount:i.length,rowCount:((n=(a=i[0])==null?void 0:a.values)==null?void 0:n.length)||0},"Query completed"),i.map(u=>({columns:u.columns,values:u.values})))}catch(s){throw N.error({queryId:t,query:e,error:s},"Browser database query error"),s}}async close(){N.debug("Closing browser database connection"),this.db.close();}},U=class{async createAdapter(e){N.debug({pathOrUrl:e},"Creating browser database adapter");try{if(!window.SQL){N.debug("Loading SQL.js");let n=await window.initSqlJs({locateFile:s=>`/${s}`});window.SQL=n;}N.debug({pathOrUrl:e},"Fetching database");let r=await fetch(e);if(r&&"ok"in r&&!r.ok)throw new Error(`Failed to load database: ${r.statusText}`);let t;try{t=await r.arrayBuffer();}catch{N.debug("Using empty array buffer for tests"),t=new ArrayBuffer(8);}N.debug({size:t.byteLength/1024/1024},"Database loaded");let a=new window.SQL.Database(new Uint8Array(t));return new W(a)}catch(r){throw N.error({pathOrUrl:e,error:r},"Failed to create browser database adapter"),r}}};var T=I("NodeDatabaseAdapter"),B=class{constructor(e){this.queryCount=0;T.debug({dbPath:e},"Opening database"),this.db=new se__default.default(e,{readonly:true,fileMustExist:true,timeout:5e3}),this.db.pragma("mmap_size=268435456"),this.db.pragma("cache_size=-2000"),this.db.pragma("temp_store=MEMORY");}async exec(e,r=[]){this.queryCount++;let t=this.queryCount;try{T.debug({queryId:t,query:e,params:r},"Executing query");let a=Date.now(),s=this.db.prepare(e).all(...r),o=Date.now()-a;if(!s||s.length===0)return T.debug({queryId:t,executionTime:o},"Query returned no results"),[{columns:[],values:[]}];let i=Object.keys(s[0]),d=s.map(u=>i.map(m=>u[m]));return T.debug({queryId:t,executionTime:o,rowCount:s.length},"Query completed"),[{columns:i,values:d}]}catch(a){throw T.error({queryId:t,query:e,params:r,error:a},"Database query error"),a}}async close(){T.debug("Closing database connection"),this.db.close();}},_=class{async createAdapter(e){if(e.startsWith("libsql:")||e.startsWith("http"))throw new Error("Remote database connections are not supported in the CLI. Use a local SQLite file instead.");return new B(e)}};var y=I("DbUtils");function ge(){try{if(typeof (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href))=="string")return Ee.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href))))}catch{if(typeof __dirname=="string")return __dirname}return process.cwd()}var H=ge(),A=Ee.join(os.homedir(),".corgi-cache"),D=Ee.join(A,"vpic.lite.db"),pe=H,w=Ee.join(H,"..","..","dist","db","vpic.lite.db.gz"),K=[Ee.join(H,"..","..","db","vpic.lite.db")];async function Z(l={}){if(l.databasePath)return y.debug({path:l.databasePath},"Using explicitly provided database path"),l.databasePath;try{if(y.debug({CACHE_DIR:A,CACHE_DB_PATH:D,PACKAGE_DIR:pe,UNCOMPRESSED_DB_PATHS:K,COMPRESSED_DB_PATH:w},"Database paths being checked"),!l.forceFresh&&fs.existsSync(D))return y.debug({path:D},"Using cached database"),D;fs.existsSync(A)||(y.debug({dir:A},"Creating cache directory"),fs.mkdirSync(A,{recursive:!0})),y.debug("Checking for uncompressed database files...");for(let r of K)if(y.debug({dbPath:r,exists:fs.existsSync(r)},"Checking path"),fs.existsSync(r))return y.debug({from:r,to:D},"Copying uncompressed database to cache"),await G(r,D),D;if(y.debug({cwd:process.cwd()},"Current working directory"),y.debug({files:fs.readdirSync(process.cwd())},"Current directory files"),y.debug({path:w,exists:fs.existsSync(w)},"Checking compressed database"),fs.existsSync(w))return y.debug({from:w,to:D},"Decompressing database to cache"),await fe(w,D),D;let e=Ee.join(process.cwd(),"db","vpic.lite.db");if(y.debug({path:e,exists:fs.existsSync(e)},"Checking database in current directory"),fs.existsSync(e))return y.debug({from:e,to:D},"Copying database from current directory"),await G(e,D),D;throw y.error("No database files found at any of the expected locations"),new Error("Database file not found. Please specify a database path explicitly when creating the decoder.")}catch(e){throw y.error({error:e},"Failed to prepare database"),new Error(`Failed to prepare database: ${e.message}`)}}async function fe(l,e){let r=zlib.createGunzip(),t=fs.createReadStream(l),a=fs.createWriteStream(e);try{await promises.pipeline(t,r,a),y.debug("Database decompression complete");}catch(n){throw y.error({error:n},"Database decompression failed"),n}}async function G(l,e){let r=fs.createReadStream(l),t=fs.createWriteStream(e);try{await promises.pipeline(r,t),y.debug("File copy complete");}catch(a){throw y.error({error:a},"File copy failed"),a}}var he=I("index");async function ee(l={}){let{databasePath:e,forceFresh:r=false,defaultOptions:t={},runtime:a=ye()}=l,n=await Z({databasePath:e,forceFresh:r});he.debug({runtime:a,databasePath:n},"Creating VIN decoder");let s;if(a==="browser")s=await new U().createAdapter(n);else if(a==="cloudflare"){if(!globalThis.__D1_FACTORY)throw new Error("D1 adapter not initialized. Call initD1Adapter before creating a decoder.");s=await globalThis.__D1_FACTORY(n);}else s=await new _().createAdapter(n);return new Y(s,t)}var Y=class{constructor(e,r={}){this.decoder=new x(e),this.defaultOptions=r;}decode(e,r){let t={...this.defaultOptions,...r};return this.decoder.decode(e,t)}async close(){await this.decoder.close();}};function ye(){return typeof globalThis.__D1_FACTORY<"u"?"cloudflare":typeof window<"u"&&typeof window.document<"u"?"browser":"node"}var te=I("cli"),P=new commander.Command;P.name("corgi").description("CORGI - Comprehensive Open Registry for Global Identification").version(process$1.version);P.command("decode <vin>").description("Decode a Vehicle Identification Number (VIN)").option("-d, --database <path>","Path to the VPIC database file","./db/vpic.lite.db").option("-p, --patterns","Include pattern matching details").option("-r, --raw","Include raw database records").option("-f, --format <format>","Output format (json, pretty)","pretty").option("-y, --year <year>","Override model year detection",De).option("-v, --verbose","Enable verbose logging").action(async(l,e)=>{process.env.LOG_LEVEL=e.verbose?"debug":"info";try{l=l.trim().toUpperCase(),/^[A-HJ-NPR-Z0-9]{17}$/.test(l)||(console.error("Error: VIN must be 17 characters (letters A-Z except I,O,Q and numbers 0-9)"),process.exit(1));let r=Ee__default.default.resolve(e.database),t={includePatternDetails:e.patterns,includeRawData:e.raw,includeDiagnostics:e.verbose};e.year&&(t.modelYear=e.year),te.debug({vin:l,dbPath:r,options:t},"Decoding VIN");let a=await ee({databasePath:r,defaultOptions:t}),n=await a.decode(l);await a.close(),e.format==="json"?console.log(JSON.stringify(n,null,2)):ve(n),process.exit(n.valid?0:1);}catch(r){te.error({error:r},"Failed to decode VIN"),e.verbose?console.error(r):console.error(`Error: ${r instanceof Error?r.message:"Unknown error"}`),process.exit(1);}});P.action(()=>{P.help();});function ve(l){let{vin:e,valid:r,components:t,errors:a}=l;console.log(`VIN: ${e}`),console.log(`Valid: ${r?"Yes":"No"}`),console.log(),t.vehicle&&(console.log("Vehicle Information:"),console.log(` Make: ${t.vehicle.make||"Unknown"}`),console.log(` Model: ${t.vehicle.model||"Unknown"}`),console.log(` Year: ${t.vehicle.year||"Unknown"}`),t.vehicle.trim&&console.log(` Trim: ${t.vehicle.trim}`),t.vehicle.series&&console.log(` Series: ${t.vehicle.series}`),t.vehicle.bodyStyle&&console.log(` Body Style: ${t.vehicle.bodyStyle}`),t.vehicle.driveType&&console.log(` Drive Type: ${t.vehicle.driveType}`),t.vehicle.transmission&&console.log(` Transmission: ${t.vehicle.transmission}`),t.vehicle.fuelType&&console.log(` Fuel Type: ${t.vehicle.fuelType}`),console.log()),t.engine&&(console.log("Engine Information:"),t.engine.model&&console.log(` Model: ${t.engine.model}`),t.engine.displacement&&console.log(` Displacement: ${t.engine.displacement}L`),t.engine.cylinders&&console.log(` Cylinders: ${t.engine.cylinders}`),t.engine.fuel&&console.log(` Fuel: ${t.engine.fuel}`),t.engine.power&&console.log(` Power: ${t.engine.power}`),console.log()),t.plant&&(console.log("Manufacturing Information:"),console.log(` Country: ${t.plant.country}`),t.plant.city&&console.log(` City: ${t.plant.city}`),t.plant.manufacturer&&console.log(` Plant: ${t.plant.manufacturer}`),console.log()),a.length>0&&(console.log("Errors:"),a.forEach(n=>{console.log(` [${n.severity.toUpperCase()}] ${n.message}`);}),console.log()),l.metadata&&(console.log("Metadata:"),console.log(` Confidence: ${(l.metadata.confidence*100).toFixed(1)}%`),console.log(` Processing Time: ${l.metadata.processingTime}ms`),l.metadata.matchedSchema&&console.log(` Schema: ${l.metadata.matchedSchema}`),console.log()),l.patterns&&l.patterns.length>0&&(console.log("Pattern Details:"),console.log("==============="),l.patterns.sort((n,s)=>s.confidence-n.confidence).forEach(n=>{console.log(`${n.element}: ${n.value} (${(n.confidence*100).toFixed(1)}%)`);}),console.log());}function De(l){let e=parseInt(l,10);if(isNaN(e)||e<1900||e>2100)throw new Error("Year must be a number between 1900 and 2100");return e}P.parse();process.argv.slice(2).length||P.outputHelp();