@cardog/corgi
Version:
⚡ The fastest and most lightweight open-source VIN decoding library. Fully offline, TypeScript-first, with comprehensive NHTSA VPIC database integration for Node.js, browsers, and Cloudflare Workers.
97 lines (95 loc) • 29.5 kB
JavaScript
'use strict';var commander=require('commander'),re=require('better-sqlite3'),zlib=require('zlib'),fs=require('fs'),path=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 re__default=/*#__PURE__*/_interopDefault(re);var X={level:"info",enabled:true,name:"app"},q={fatal:0,error:1,warn:2,info:3,debug:4,trace:5},_=class l{constructor(t={}){this.globalLevel="info";this.options={...X,...t},this.globalLevel=this.options.level||"info";}shouldLog(t){return this.options.enabled?q[t]<=q[this.globalLevel]:false}formatMessage(t,r="",e="",a){let n=new Date().toISOString(),s=r?`[${r}]`:"";if(a&&typeof a=="object")try{let o=JSON.stringify(a);return `${n} ${t.toUpperCase()} ${s} ${e} ${o}`}catch{return `${n} ${t.toUpperCase()} ${s} ${e} [Object cannot be stringified]`}return `${n} ${t.toUpperCase()} ${s} ${e} ${a!==void 0?a:""}`}trace(t,r=""){this.shouldLog("trace")&&console.debug(this.formatMessage("trace",this.options.name,r,t));}debug(t,r=""){this.shouldLog("debug")&&console.debug(this.formatMessage("debug",this.options.name,r,t));}info(t,r=""){this.shouldLog("info")&&console.log(this.formatMessage("info",this.options.name,r,t));}warn(t,r=""){this.shouldLog("warn")&&console.warn(this.formatMessage("warn",this.options.name,r,t));}error(t,r=""){this.shouldLog("error")&&console.error(this.formatMessage("error",this.options.name,r,t));}fatal(t,r=""){this.shouldLog("fatal")&&console.error(this.formatMessage("fatal",this.options.name,r,t));}child(t,r={}){let e=new l({...this.options,name:t}),a={trace:e.trace.bind(e),debug:e.debug.bind(e),info:e.info.bind(e),warn:e.warn.bind(e),error:e.error.bind(e)};return Object.keys(a).forEach(n=>{let s=n;e[s]=(o,c="")=>{a[s](typeof o=="object"&&o!==null?{...r,...o}:{...r,value:o},c);};}),e}setLevel(t){this.globalLevel=t;}},P=new _;function E(l,t={}){return P.child(l,t)}var C=class{constructor(t){this.queryCache=new Map;this.adapter=t;}async get(t,r=[]){var e,a;try{let n=`${t}:${JSON.stringify(r)}`;if(this.queryCache.has(n))return this.queryCache.get(n);let s=await this.adapter.exec(t,r);if(((a=(e=s[0])==null?void 0:e.values)==null?void 0:a.length)>0){let o={};return s[0].columns.forEach((c,d)=>{o[c]=s[0].values[0][d];}),this.queryCache.set(n,o),o}return this.queryCache.set(n,null),null}catch(n){throw P.error({error:n,sql:t,params:r},"Database get error"),n}}async query(t,r=[]){var e,a;try{let n=`query:${t}:${JSON.stringify(r)}`;if(this.queryCache.has(n))return this.queryCache.get(n);let s=await this.adapter.exec(t,r);if(((a=(e=s[0])==null?void 0:e.values)==null?void 0:a.length)>0){let c=s[0].values.map(d=>{let u={};return s[0].columns.forEach((g,y)=>{u[g]=d[y];}),u});return this.queryCache.set(n,c),c}let o=[];return this.queryCache.set(n,o),o}catch(n){throw P.error({error:n,sql:t,params:r},"Database query error"),n}}clearCache(){this.queryCache.clear();}async close(){await this.adapter.close();}async getWMI(t){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 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
`,[t])}async getValidSchemas(t,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)
`,[t,r,r])}async getPatterns(t){if(t.length===0)return [];let r=`
WITH ValidSchemas AS (
SELECT vs.Id, vs.Name
FROM VinSchema vs
WHERE vs.Id IN (${t.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 (${t.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 (${t.join(",")})
`;return this.query(r,[])}async lookupValues(t,r){if(!t||r.length===0)return new Map;try{let e=r.map(()=>"?").join(","),a=`
SELECT CAST(Id AS TEXT) as Id, Name
FROM ${t}
WHERE CAST(Id AS TEXT) IN (${e})
`,n=await this.query(a,[...r]),s=new Map;for(let o of n)s.set(o.Id,o.Name);return s}catch(e){return P.warn({error:e,tableName:t,ids:r},"Lookup table query failed"),new Map}}};var F=E("PatternMatcher"),Z=["DriveType","EngineModel","EngineConfiguration","FuelType","Transmission","BodyStyle","GrossVehicleWeightRating","GrossVehicleWeightRatingTo","GrossVehicleWeightRatingFrom","ChargerLevel","ElectrificationLevel","EVDriveUnit","BatteryType","Make","Model","Series","Trim","Turbo","DaytimeRunningLight","Plant","Country","DaytimeRunningLight","DestinationMarket","Conversion"],O=class{constructor(t){this.db=new C(t);}parsePositions(t){let r=[],e=0;for(;e<t.length;){let a=t[e],n=1;for(;e+n<t.length&&t[e+n]===a;)n++;r.push({start:e,length:n,value:a}),e+=n;}return r}isCharInRange(t,r){if(!r.startsWith("[")||!r.endsWith("]"))return t===r||r==="*";let e=r.slice(1,-1),a=0;for(;a<e.length;)if(a+2<e.length&&e[a+1]==="-"){let n=e[a].charCodeAt(0),s=e[a+2].charCodeAt(0),o=t.charCodeAt(0);if(o>=n&&o<=s)return true;a+=3;}else {if(t===e[a])return true;a++;}return false}matchesPattern(t,r){if(!t||!r)return false;let[e,...a]=r.split("|");if(a.length>0&&e.length===5){let n=a[0],s=t[0],o=n[1];return o==="*"||s===o}return this.matchesSimplePattern(t,e)}matchesSimplePattern(t,r){let e=0,a=0;for(;e<r.length&&a<t.length;){let n=r[e],s=t[a];if(n==="["){let o=r.indexOf("]",e);if(o===-1)return false;let c=r.substring(e,o+1);if(!this.isCharInRange(s,c))return false;e=o+1,a++;continue}if(n==="*"){if(e===r.length-1)return true;e++,a++;continue}if(s!==n)return false;e++,a++;}return e>=r.length||e===r.length-1&&r[e]==="*"}calculateConfidence(t,r){if(!t||!r)return 0;let[e,...a]=t.split("|");if(a.length>0&&e.length===5){let y=r,i=a[0][1];return i==="*"?.8:i===y?1:0}if(!this.matchesPattern(r,e))return 0;let n=0,s=0,o=0,c=0,d=0,u=0;for(;d<e.length&&u<r.length;){let y=e[d],f=r[u];if(y==="["){let i=e.indexOf("]",d);if(i===-1)break;e.substring(d,i+1).slice(1,-1).includes("-")?s+=.7:s+=.8,c++,d=i+1,u++;}else y==="*"?(o++,c++,d++,u++):(y===f&&n++,c++,d++,u++);}let g=(n*1+s+o*.5)/c;return Math.min(1,Math.max(0,g))}transformPatternMatch(t){let r=t.positions||[];return {element:t.elementName,code:t.elementCode,attributeId:t.attributeId,value:t.value,confidence:t.confidence,positions:r,schema:t.schemaName,metadata:{lookupTable:t.lookupTable,groupName:t.groupName,elementWeight:t.elementWeight,patternType:t.patternType,rawPattern:t.pattern}}}async getPatternMatches(t,r,e,a){let s=(await this.getRawPatternMatches(t,r,e,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 c=[];for(let[d,u]of Object.entries(o)){let g=u.sort((i,m)=>{var p,S;let b=((p=i.metadata)==null?void 0:p.elementWeight)??0,h=((S=m.metadata)==null?void 0:S.elementWeight)??0;return b!==h?h-b:m.confidence-i.confidence}),y=new Set,f=g.filter(i=>{let m=JSON.stringify({value:i.value,positions:i.positions.join(","),schema:i.schema});return y.has(m)?false:(y.add(m),true)});c=c.concat(f);}return c}async getRawPatternMatches(t,r,e,a){try{let n=await this.db.getValidSchemas(t,r);if(n.length===0)return F.debug({wmi:t,modelYear:r},"No valid schemas found"),[];let s=n.map(i=>i.SchemaId),c=(await this.db.getPatterns(s)).filter(i=>!(i.LookupTable&&(!Z.includes(i.LookupTable)||i.LookupTable.includes("vNCSA")))),d={},u=[];for(let i of c)i.LookupTable?(d[i.LookupTable]||(d[i.LookupTable]=[]),d[i.LookupTable].push(i)):(i.ResolvedValue=i.AttributeId,u.push(i));for(let[i,m]of Object.entries(d)){let b=[...new Set(m.map(h=>String(h.AttributeId)))];if(b.length!==0)try{let h=await this.db.lookupValues(i,b);for(let p of m){let S=String(p.AttributeId);p.ResolvedValue=h.get(S)||p.AttributeId;}}catch(h){F.warn({error:h,tableName:i},"Lookup table resolution failed");for(let p of m)p.ResolvedValue=p.AttributeId;}}let g=[...u,...Object.values(d).flat()];g.sort((i,m)=>i.ElementWeight!==m.ElementWeight?m.ElementWeight-i.ElementWeight:i.Pattern.localeCompare(m.Pattern));let y=g.filter(i=>i.ElementName==="Model").map(i=>({...i,confidence:this.calculateConfidence(i.Pattern,e+a)})).sort((i,m)=>m.confidence-i.confidence),f=y.length>0?y[0].SchemaName:null;return g.map(i=>{var H;let m=i.Pattern,b=m.includes("|"),h=b?this.calculateConfidence(m,a[1]):this.calculateConfidence(m,e+a),p=h;i.ElementName.toLowerCase().includes("plant")&&(f?p=i.SchemaName===f?h:0:p=h*.5);let S=[],Y=m.split("|")[0],z=b?9:3;for(let L=0;L<Y.length;L++)Y[L]!=="|"&&S.push(z+L);return {pattern:i.Pattern,elementId:i.ElementId,elementName:i.ElementName,element:i.ElementName,elementCode:i.ElementCode,groupName:i.GroupName,description:((H=i.Description)==null?void 0:H.toString())??null,lookupTable:i.LookupTable,attributeId:i.ResolvedValue?String(i.ResolvedValue):null,value:i.ResolvedValue?String(i.ResolvedValue):null,schemaName:i.SchemaName,yearFrom:i.YearFrom,yearTo:i.YearTo,confidence:p,keys:i.Pattern,elementWeight:i.ElementWeight,patternType:b?"VIS":"VDS",positions:S}})}catch(n){throw F.error({error:n,wmi:t,modelYear:r},"Error getting pattern matches"),n}}};var M={"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",Trailer:"Trailer",Tractor:"Tractor",Bus:"Bus","School Bus":"Bus",Motorcycle:"Motorcycle","Incomplete Vehicle":"Other",Other:"Other"};var ee=E("VINDecoder");var V=class{constructor(t){this.db=new C(t),this.patternMatcher=new O(t);}async decode(t,r={}){let e=performance.now?performance.now():Date.now(),a=t.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()-e,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 c=r.modelYear?{year:r.modelYear,source:"override",confidence:1}:this.determineModelYear(a);if(!c)return n.errors.push({code:"201",category:"validation",severity:"error",message:"Could not determine model year",positions:[9]}),n.metadata.processingTime=Date.now()-e,n;n.components.modelYear=c;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()-e,n;n.components.wmi=u;try{let g=a.substring(3,9),y=a.substring(9,17),f=await this.patternMatcher.getPatternMatches(d,c.year,g,y);if(f.length>0){let i=f.filter(h=>{var p;return ((p=h.metadata)==null?void 0:p.patternType)==="VDS"}),m=f.filter(h=>{var p;return ((p=h.metadata)==null?void 0:p.patternType)==="VIS"});i.length>0&&(n.components.vds={raw:g,patterns:i}),m.length>0&&(n.components.vis={raw:y,patterns:m}),n.components.vehicle=this.extractVehicleInfo(f,u,c),n.components.plant=this.extractPlantInfo(f,a),n.components.engine=this.extractEngineInfo(f),r.includePatternDetails&&(n.patterns=f);let b=f.reduce((h,p)=>h+p.confidence,0)/f.length;n.metadata.confidence=b,n.metadata.matchedSchema=this.findPrimarySchema(f),n.metadata.totalPatterns=f.length,b<(r.confidenceThreshold||.5)&&n.errors.push({code:"401",category:"pattern",severity:"warning",message:"Low confidence in pattern matches",confidence:b});}else return n.errors.push({code:"400",category:"pattern",severity:"error",message:"No matching patterns found"}),n.metadata.processingTime=Date.now()-e,n}catch(g){return n.errors.push({code:"501",category:"database",severity:"error",message:"Error matching patterns",details:g instanceof Error?g.message:"Unknown error"}),n.metadata.processingTime=Date.now()-e,n}n.valid=n.errors.every(g=>g.severity==="warning")||n.errors.length===0;}catch(s){ee.error({vin:t,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()-e:Date.now()-e,n}findPrimarySchema(t){var e;return (e=t.filter(a=>a.element==="Model").sort((a,n)=>n.confidence-a.confidence)[0])==null?void 0:e.schema}coerceBodyStyle(t){if(t in M)return M[t];console.log("bodyStyle",t);for(let[e,a]of Object.entries(M))if(t.toLowerCase().includes(e.toLowerCase())||e.toLowerCase().includes(t.toLowerCase()))return a;let r=t.toLowerCase();return r.includes("pickup")||r.includes("truck")?"Pickup":"Other"}extractVehicleInfo(t,r,e){let a={make:r.make||"",model:"",year:e.year,manufacturer:r.manufacturer},n=t.filter(s=>s.element==="Model"&&s.value).sort((s,o)=>{var u,g;let c=((u=s.metadata)==null?void 0:u.elementWeight)??0,d=((g=o.metadata)==null?void 0:g.elementWeight)??0;return c!==d?d-c:o.confidence-s.confidence});n.length>0&&(a.model=n[0].value);for(let s of t)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(t,r){let e,a,n;for(let o of t){if(!o.value)continue;let c=o.element.toLowerCase();c==="plant country"?e=o.value:c==="plant city"?a=o.value:c==="plant company name"&&(n=o.value);}let s=r[10];if(e)return {country:e,city:a,manufacturer:n,code:s}}extractEngineInfo(t){let r={},e=false;for(let a of t)if(a.value)switch(a.element){case "Engine Model":r.model=a.value,e=true;break;case "Engine Number of Cylinders":case "Cylinders":r.cylinders=a.value,e=true;break;case "Displacement (L)":r.displacement=a.value,e=true;break;case "Engine Brake (hp) From":case "Engine Power (KW)":r.power=a.value,e=true;break;case "Fuel Type - Primary":case "Fuel Type":r.fuel=a.value,e=true;break}return e?r:void 0}validateStructure(t){let r=[];if(t.length!==17)return r.push({code:"100",category:"structure",severity:"error",message:"Invalid VIN length"}),r;let e=[...t].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 e.length>0&&r.push({code:"101",category:"structure",severity:"error",message:`Invalid characters: ${e.map(a=>`${a.char} at position ${a.pos}`).join(", ")}`,positions:e.map(a=>a.pos)}),r}extractWMI(t){let r=t.substring(0,3);return r[2]==="9"&&t.length>=14?r+t.substring(11,14):r}determineModelYear(t){let r=t[9].toUpperCase(),e=new Map;for(let o=0;o<8;o++)e.set(String.fromCharCode(65+o),1980+o);for(let o=0;o<5;o++)e.set(String.fromCharCode(74+o),1988+o);for(let o=0;o<9;o++){let c=String.fromCharCode(80+o);c!=="Q"&&c!=="U"&&e.set(c,1993+o);}for(let o=0;o<8;o++)e.set(String.fromCharCode(65+o),2010+o);for(let o=0;o<5;o++)e.set(String.fromCharCode(74+o),2018+o);for(let o=0;o<9;o++){let c=String.fromCharCode(80+o);c==="P"?e.set(c,2023):c==="R"?e.set(c,2024):c==="S"?e.set(c,2025):c==="T"?e.set(c,2026):c==="V"?e.set(c,2027):c==="W"?e.set(c,2028):c==="X"?e.set(c,2029):c==="Y"&&e.set(c,2030);}for(let o=1;o<=9;o++)e.set(String(o),2030+o);let a=e.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(t){let r=[8,7,6,5,4,3,2,10,0,9,8,7,6,5,4,3,2],e=c=>{let d=c.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=[...t].reduce((c,d,u)=>c+e(d)*r[u],0)%11,s=n===10?"X":n.toString(),o=t[8].toUpperCase();return {position:9,actual:o,expected:s,isValid:o===s}}async close(){await this.db.close();}};var v=E("BrowserDatabaseAdapter");function te(l){return l==null?"NULL":typeof l=="number"?l.toString():typeof l=="boolean"?l?"1":"0":`'${l.toString().replace(/'/g,"''")}'`}var k=class{constructor(t){this.queryCount=0;this.db=t,v.debug("Browser database adapter initialized");}async exec(t,r=[]){var a,n;this.queryCount++;let e=this.queryCount;try{v.debug({queryId:e,query:t,paramCount:r.length},"Executing browser query");let s=Date.now(),o=r.reduce((u,g,y)=>u.replace("?",te(g)),t),c=this.db.exec(o),d=Date.now()-s;return !c||c.length===0?(v.debug({queryId:e,executionTime:d},"Query returned no results"),[{columns:[],values:[]}]):(v.debug({queryId:e,executionTime:d,resultCount:c.length,rowCount:((n=(a=c[0])==null?void 0:a.values)==null?void 0:n.length)||0},"Query completed"),c.map(u=>({columns:u.columns,values:u.values})))}catch(s){throw v.error({queryId:e,query:t,error:s},"Browser database query error"),s}}async close(){v.debug("Closing browser database connection"),this.db.close();}},x=class{async createAdapter(t){v.debug({pathOrUrl:t},"Creating browser database adapter");try{if(!window.SQL){v.debug("Loading SQL.js");let n=await window.initSqlJs({locateFile:s=>`/${s}`});window.SQL=n;}v.debug({pathOrUrl:t},"Fetching database");let r=await fetch(t);if(r&&"ok"in r&&!r.ok)throw new Error(`Failed to load database: ${r.statusText}`);let e;try{e=await r.arrayBuffer();}catch{v.debug("Using empty array buffer for tests"),e=new ArrayBuffer(8);}v.debug({size:e.byteLength/1024/1024},"Database loaded");let a=new window.SQL.Database(new Uint8Array(e));return new k(a)}catch(r){throw v.error({pathOrUrl:t,error:r},"Failed to create browser database adapter"),r}}};var R=E("NodeDatabaseAdapter"),W=class{constructor(t){this.queryCount=0;R.debug({dbPath:t},"Opening database"),this.db=new re__default.default(t,{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(t,r=[]){this.queryCount++;let e=this.queryCount;try{R.debug({queryId:e,query:t,params:r},"Executing query");let a=Date.now(),s=this.db.prepare(t).all(...r),o=Date.now()-a;if(!s||s.length===0)return R.debug({queryId:e,executionTime:o},"Query returned no results"),[{columns:[],values:[]}];let c=Object.keys(s[0]),d=s.map(u=>c.map(g=>u[g]));return R.debug({queryId:e,executionTime:o,rowCount:s.length},"Query completed"),[{columns:c,values:d}]}catch(a){throw R.error({queryId:e,query:t,params:r,error:a},"Database query error"),a}}async close(){R.debug("Closing database connection"),this.db.close();}},U=class{async createAdapter(t){if(t.startsWith("libsql:")||t.startsWith("http"))throw new Error("Remote database connections are not supported in the CLI. Use a local SQLite file instead.");return new W(t)}};var I=E("DbUtils");function ce(){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 path.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 N=ce(),A=path.join(os.homedir(),".corgi-cache"),D=path.join(A,"vpic.lite.db");function le(){let l=[];return l.push(path.join(N,"..","..","dist","db","vpic.lite.db.gz")),l.push(path.join(N,"vpic.lite.db.gz")),l.push(path.join(N,"..","db","vpic.lite.db.gz")),l.push(path.join(N,"db","vpic.lite.db.gz")),l.push(path.join(N,"dist","db","vpic.lite.db.gz")),l}function de(){let l=[];return l.push(path.join(N,"..","..","db","vpic.lite.db")),l.push(path.join(N,"..","db","vpic.lite.db")),l.push(path.join(process.cwd(),"db","vpic.lite.db")),l}async function G(l={}){if(l.databasePath)return I.debug({path:l.databasePath},"Using explicitly provided database path"),l.databasePath;try{let t=le(),r=de();if(I.debug({CACHE_DIR:A,CACHE_DB_PATH:D},"Database paths being checked"),!l.forceFresh&&fs.existsSync(D))return I.debug({path:D},"Using cached database"),D;fs.existsSync(A)||(I.debug({dir:A},"Creating cache directory"),fs.mkdirSync(A,{recursive:!0})),I.debug("Checking for uncompressed database files...");for(let e of r)if(fs.existsSync(e))return I.debug({from:e,to:D},"Copying uncompressed database to cache"),await ge(e,D),D;I.debug("Checking for compressed database files...");for(let e of t)if(fs.existsSync(e))return I.debug({from:e,to:D},"Decompressing database to cache"),await ue(e,D),D;throw I.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(t){throw I.error({error:t},"Failed to prepare database"),new Error(`Failed to prepare database: ${t.message}`)}}async function ue(l,t){let r=zlib.createGunzip(),e=fs.createReadStream(l),a=fs.createWriteStream(t);try{await promises.pipeline(e,r,a),I.debug("Database decompression complete");}catch(n){throw I.error({error:n},"Database decompression failed"),n}}async function ge(l,t){let r=fs.createReadStream(l),e=fs.createWriteStream(t);try{await promises.pipeline(r,e),I.debug("File copy complete");}catch(a){throw I.error({error:a},"File copy failed"),a}}var me=E("index");async function j(l={}){let{databasePath:t,forceFresh:r=false,defaultOptions:e={},runtime:a=pe()}=l,n=await G({databasePath:t,forceFresh:r});me.debug({runtime:a,databasePath:n},"Creating VIN decoder");let s;if(a==="browser")s=await new x().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 U().createAdapter(n);return new $(s,e)}var $=class{constructor(t,r={}){this.decoder=new V(t),this.defaultOptions=r;}decode(t,r){let e={...this.defaultOptions,...r};return this.decoder.decode(t,e)}async close(){await this.decoder.close();}};function pe(){return typeof globalThis.__D1_FACTORY<"u"?"cloudflare":typeof window<"u"&&typeof window.document<"u"?"browser":"node"}var ye=E("cli"),w=new commander.Command;w.name("corgi").description("CORGI - Comprehensive Open Registry for Global Identification").version(process$1.version);w.command("decode <vin>").description("Decode a Vehicle Identification Number (VIN)").option("-d, --database <path>","Path to the VPIC database file").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",Ee).option("-v, --verbose","Enable verbose logging").action(async(l,t)=>{process.env.LOG_LEVEL=t.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={includePatternDetails:t.patterns,includeRawData:t.raw,includeDiagnostics:t.verbose};t.year&&(r.modelYear=t.year);let e=await j({databasePath:t.database,defaultOptions:r}),a=await e.decode(l);await e.close(),t.format==="json"?console.log(JSON.stringify(a,null,2)):be(a),process.exit(a.valid?0:1);}catch(r){ye.error({error:r},"Failed to decode VIN"),t.verbose?console.error(r):console.error(`Error: ${r instanceof Error?r.message:"Unknown error"}`),process.exit(1);}});w.action(()=>{w.help();});function be(l){let{vin:t,valid:r,components:e,errors:a}=l;console.log(`VIN: ${t}`),console.log(`Valid: ${r?"Yes":"No"}`),console.log(),e.vehicle&&(console.log("Vehicle Information:"),console.log(` Make: ${e.vehicle.make||"Unknown"}`),console.log(` Model: ${e.vehicle.model||"Unknown"}`),console.log(` Year: ${e.vehicle.year||"Unknown"}`),e.vehicle.trim&&console.log(` Trim: ${e.vehicle.trim}`),e.vehicle.series&&console.log(` Series: ${e.vehicle.series}`),e.vehicle.bodyStyle&&console.log(` Body Style: ${e.vehicle.bodyStyle}`),e.vehicle.driveType&&console.log(` Drive Type: ${e.vehicle.driveType}`),e.vehicle.transmission&&console.log(` Transmission: ${e.vehicle.transmission}`),e.vehicle.fuelType&&console.log(` Fuel Type: ${e.vehicle.fuelType}`),console.log()),e.engine&&(console.log("Engine Information:"),e.engine.model&&console.log(` Model: ${e.engine.model}`),e.engine.displacement&&console.log(` Displacement: ${e.engine.displacement}L`),e.engine.cylinders&&console.log(` Cylinders: ${e.engine.cylinders}`),e.engine.fuel&&console.log(` Fuel: ${e.engine.fuel}`),e.engine.power&&console.log(` Power: ${e.engine.power}`),console.log()),e.plant&&(console.log("Manufacturing Information:"),console.log(` Country: ${e.plant.country}`),e.plant.city&&console.log(` City: ${e.plant.city}`),e.plant.manufacturer&&console.log(` Plant: ${e.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 Ee(l){let t=parseInt(l,10);if(isNaN(t)||t<1900||t>2100)throw new Error("Year must be a number between 1900 and 2100");return t}w.parse();process.argv.slice(2).length||w.outputHelp();