@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.
96 lines (94 loc) • 22.8 kB
JavaScript
var _={level:"info",enabled:true,name:"app"},x={fatal:0,error:1,warn:2,info:3,debug:4,trace:5},A=class g{constructor(e={}){this.globalLevel="info";this.options={..._,...e},this.globalLevel=this.options.level||"info";}shouldLog(e){return this.options.enabled?x[e]<=x[this.globalLevel]:false}formatMessage(e,a="",t="",n){let r=new Date().toISOString(),i=a?`[${a}]`:"";if(n&&typeof n=="object")try{let s=JSON.stringify(n);return `${r} ${e.toUpperCase()} ${i} ${t} ${s}`}catch{return `${r} ${e.toUpperCase()} ${i} ${t} [Object cannot be stringified]`}return `${r} ${e.toUpperCase()} ${i} ${t} ${n!==void 0?n:""}`}trace(e,a=""){this.shouldLog("trace")&&console.debug(this.formatMessage("trace",this.options.name,a,e));}debug(e,a=""){this.shouldLog("debug")&&console.debug(this.formatMessage("debug",this.options.name,a,e));}info(e,a=""){this.shouldLog("info")&&console.log(this.formatMessage("info",this.options.name,a,e));}warn(e,a=""){this.shouldLog("warn")&&console.warn(this.formatMessage("warn",this.options.name,a,e));}error(e,a=""){this.shouldLog("error")&&console.error(this.formatMessage("error",this.options.name,a,e));}fatal(e,a=""){this.shouldLog("fatal")&&console.error(this.formatMessage("fatal",this.options.name,a,e));}child(e,a={}){let t=new g({...this.options,name:e}),n={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(n).forEach(r=>{let i=r;t[i]=(s,c="")=>{n[i](typeof s=="object"&&s!==null?{...a,...s}:{...a,value:s},c);};}),t}setLevel(e){this.globalLevel=e;}},S=new A;function E(g,e={}){return S.child(g,e)}var T=class{constructor(e){this.queryCache=new Map;this.adapter=e;}async get(e,a=[]){var t,n;try{let r=`${e}:${JSON.stringify(a)}`;if(this.queryCache.has(r))return this.queryCache.get(r);let i=await this.adapter.exec(e,a);if(((n=(t=i[0])==null?void 0:t.values)==null?void 0:n.length)>0){let s={};return i[0].columns.forEach((c,l)=>{s[c]=i[0].values[0][l];}),this.queryCache.set(r,s),s}return this.queryCache.set(r,null),null}catch(r){throw S.error({error:r,sql:e,params:a},"Database get error"),r}}async query(e,a=[]){var t,n;try{let r=`query:${e}:${JSON.stringify(a)}`;if(this.queryCache.has(r))return this.queryCache.get(r);let i=await this.adapter.exec(e,a);if(((n=(t=i[0])==null?void 0:t.values)==null?void 0:n.length)>0){let c=i[0].values.map(l=>{let u={};return i[0].columns.forEach((p,b)=>{u[p]=l[b];}),u});return this.queryCache.set(r,c),c}let s=[];return this.queryCache.set(r,s),s}catch(r){throw S.error({error:r,sql:e,params:a},"Database query error"),r}}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 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,a){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,a,a])}async getPatterns(e){if(e.length===0)return [];let a=`
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(a,[])}async lookupValues(e,a){if(!e||a.length===0)return new Map;try{let t=a.map(()=>"?").join(","),n=`
SELECT CAST(Id AS TEXT) as Id, Name
FROM ${e}
WHERE CAST(Id AS TEXT) IN (${t})
`,r=await this.query(n,[...a]),i=new Map;for(let s of r)i.set(s.Id,s.Name);return i}catch(t){return S.warn({error:t,tableName:e,ids:a},"Lookup table query failed"),new Map}}};var O=E("PatternMatcher"),H=["DriveType","EngineModel","EngineConfiguration","FuelType","Transmission","BodyStyle","GrossVehicleWeightRating","GrossVehicleWeightRatingTo","GrossVehicleWeightRatingFrom","ChargerLevel","ElectrificationLevel","EVDriveUnit","BatteryType","Make","Model","Series","Trim","Turbo","DaytimeRunningLight","Plant","Country","DaytimeRunningLight","DestinationMarket","Conversion"],C=class{constructor(e){this.db=new T(e);}parsePositions(e){let a=[],t=0;for(;t<e.length;){let n=e[t],r=1;for(;t+r<e.length&&e[t+r]===n;)r++;a.push({start:t,length:r,value:n}),t+=r;}return a}isCharInRange(e,a){if(!a.startsWith("[")||!a.endsWith("]"))return e===a||a==="*";let t=a.slice(1,-1),n=0;for(;n<t.length;)if(n+2<t.length&&t[n+1]==="-"){let r=t[n].charCodeAt(0),i=t[n+2].charCodeAt(0),s=e.charCodeAt(0);if(s>=r&&s<=i)return true;n+=3;}else {if(e===t[n])return true;n++;}return false}matchesPattern(e,a){if(!e||!a)return false;let[t,...n]=a.split("|");if(n.length>0&&t.length===5){let r=n[0],i=e[0],s=r[1];return s==="*"||i===s}return this.matchesSimplePattern(e,t)}matchesSimplePattern(e,a){let t=0,n=0;for(;t<a.length&&n<e.length;){let r=a[t],i=e[n];if(r==="["){let s=a.indexOf("]",t);if(s===-1)return false;let c=a.substring(t,s+1);if(!this.isCharInRange(i,c))return false;t=s+1,n++;continue}if(r==="*"){if(t===a.length-1)return true;t++,n++;continue}if(i!==r)return false;t++,n++;}return t>=a.length||t===a.length-1&&a[t]==="*"}calculateConfidence(e,a){if(!e||!a)return 0;let[t,...n]=e.split("|");if(n.length>0&&t.length===5){let b=a,o=n[0][1];return o==="*"?.8:o===b?1:0}if(!this.matchesPattern(a,t))return 0;let r=0,i=0,s=0,c=0,l=0,u=0;for(;l<t.length&&u<a.length;){let b=t[l],f=a[u];if(b==="["){let o=t.indexOf("]",l);if(o===-1)break;t.substring(l,o+1).slice(1,-1).includes("-")?i+=.7:i+=.8,c++,l=o+1,u++;}else b==="*"?(s++,c++,l++,u++):(b===f&&r++,c++,l++,u++);}let p=(r*1+i+s*.5)/c;return Math.min(1,Math.max(0,p))}transformPatternMatch(e){let a=e.positions||[];return {element:e.elementName,code:e.elementCode,attributeId:e.attributeId,value:e.value,confidence:e.confidence,positions:a,schema:e.schemaName,metadata:{lookupTable:e.lookupTable,groupName:e.groupName,elementWeight:e.elementWeight,patternType:e.patternType,rawPattern:e.pattern}}}async getPatternMatches(e,a,t,n){let i=(await this.getRawPatternMatches(e,a,t,n)).filter(l=>l.elementName.toLowerCase().includes("plant")?l.confidence>.3:l.confidence>.5).map(l=>this.transformPatternMatch(l)),s={};i.forEach(l=>{let u=l.element;s[u]||(s[u]=[]),s[u].push(l);});let c=[];for(let[l,u]of Object.entries(s)){let p=u.sort((o,m)=>{var h,N;let d=((h=o.metadata)==null?void 0:h.elementWeight)??0,y=((N=m.metadata)==null?void 0:N.elementWeight)??0;return d!==y?y-d:m.confidence-o.confidence}),b=new Set,f=p.filter(o=>{let m=JSON.stringify({value:o.value,positions:o.positions.join(","),schema:o.schema});return b.has(m)?false:(b.add(m),true)});c=c.concat(f);}return c}async getRawPatternMatches(e,a,t,n){try{let r=await this.db.getValidSchemas(e,a);if(r.length===0)return O.debug({wmi:e,modelYear:a},"No valid schemas found"),[];let i=r.map(o=>o.SchemaId),c=(await this.db.getPatterns(i)).filter(o=>!(o.LookupTable&&(!H.includes(o.LookupTable)||o.LookupTable.includes("vNCSA")))),l={},u=[];for(let o of c)o.LookupTable?(l[o.LookupTable]||(l[o.LookupTable]=[]),l[o.LookupTable].push(o)):(o.ResolvedValue=o.AttributeId,u.push(o));for(let[o,m]of Object.entries(l)){let d=[...new Set(m.map(y=>String(y.AttributeId)))];if(d.length!==0)try{let y=await this.db.lookupValues(o,d);for(let h of m){let N=String(h.AttributeId);h.ResolvedValue=y.get(N)||h.AttributeId;}}catch(y){O.warn({error:y,tableName:o},"Lookup table resolution failed");for(let h of m)h.ResolvedValue=h.AttributeId;}}let p=[...u,...Object.values(l).flat()];p.sort((o,m)=>o.ElementWeight!==m.ElementWeight?m.ElementWeight-o.ElementWeight:o.Pattern.localeCompare(m.Pattern));let b=p.filter(o=>o.ElementName==="Model").map(o=>({...o,confidence:this.calculateConfidence(o.Pattern,t+n)})).sort((o,m)=>m.confidence-o.confidence),f=b.length>0?b[0].SchemaName:null;return p.map(o=>{var W;let m=o.Pattern,d=m.includes("|"),y=d?this.calculateConfidence(m,n[1]):this.calculateConfidence(m,t+n),h=y;o.ElementName.toLowerCase().includes("plant")&&(f?h=o.SchemaName===f?y:0:h=y*.5);let N=[],k=m.split("|")[0],F=d?9:3;for(let R=0;R<k.length;R++)k[R]!=="|"&&N.push(F+R);return {pattern:o.Pattern,elementId:o.ElementId,elementName:o.ElementName,element:o.ElementName,elementCode:o.ElementCode,groupName:o.GroupName,description:((W=o.Description)==null?void 0:W.toString())??null,lookupTable:o.LookupTable,attributeId:o.ResolvedValue?String(o.ResolvedValue):null,value:o.ResolvedValue?String(o.ResolvedValue):null,schemaName:o.SchemaName,yearFrom:o.YearFrom,yearTo:o.YearTo,confidence:h,keys:o.Pattern,elementWeight:o.ElementWeight,patternType:d?"VIS":"VDS",positions:N}})}catch(r){throw O.error({error:r,wmi:e,modelYear:a},"Error getting pattern matches"),r}}};var B=(d=>(d.SEDAN="Sedan",d.COUPE="Coupe",d.CONVERTIBLE="Convertible",d.HATCHBACK="Hatchback",d.SUV="SUV",d.CROSSOVER="Crossover",d.WAGON="Wagon",d.VAN="Van",d.MINIVAN="Minivan",d.PICKUP="Pickup",d.TRUCK="Truck",d.TRACTOR="Tractor",d.TRAILER="Trailer",d.BUS="Bus",d.MOTORCYCLE="Motorcycle",d.OTHER="Other",d))(B||{}),D={"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"},Y=(t=>(t.WARNING="warning",t.ERROR="error",t.FATAL="fatal",t))(Y||{}),q=(r=>(r.VALIDATION="validation",r.STRUCTURE="structure",r.LOOKUP="lookup",r.PATTERN="pattern",r.DATABASE="database",r))(q||{}),J=(o=>(o.INVALID_LENGTH="100",o.INVALID_CHARACTERS="101",o.INVALID_CHECK_DIGIT="200",o.INVALID_MODEL_YEAR="201",o.INVALID_REGION="202",o.WMI_NOT_FOUND="300",o.MANUFACTURER_NOT_FOUND="301",o.MAKE_NOT_FOUND="302",o.NO_PATTERNS_FOUND="400",o.LOW_CONFIDENCE_PATTERNS="401",o.CONFLICTING_PATTERNS="402",o.DATABASE_CONNECTION_ERROR="500",o.QUERY_ERROR="501",o.INVALID_RESULT="502",o))(J||{});var K=E("VINDecoder");async function U(g,e,a={}){return new P(e).decode(g,a)}var P=class{constructor(e){this.db=new T(e),this.patternMatcher=new C(e);}async decode(e,a={}){let t=performance.now?performance.now():Date.now(),n=e.toUpperCase().trim(),r={vin:n,valid:false,components:{},errors:[],metadata:{processingTime:0,confidence:0,schemaVersion:"1.0"}};a.includeDiagnostics&&(r.metadata.queries=[]),a.includeRawData&&(r.metadata.rawRecords=[]);try{let i=this.validateStructure(n);if(i.length>0)return r.errors=i,r.metadata.processingTime=Date.now()-t,r;let s=this.validateCheckDigit(n);r.components.checkDigit=s,s.isValid||r.errors.push({code:"200",category:"validation",severity:"warning",message:"Invalid check digit",positions:[8],expected:s.expected,actual:s.actual});let c=a.modelYear?{year:a.modelYear,source:"override",confidence:1}:this.determineModelYear(n);if(!c)return r.errors.push({code:"201",category:"validation",severity:"error",message:"Could not determine model year",positions:[9]}),r.metadata.processingTime=Date.now()-t,r;r.components.modelYear=c;let l=this.extractWMI(n),u=await this.db.getWMI(l);if(!u)return r.errors.push({code:"300",category:"lookup",severity:"error",message:"WMI not found in database",searchKey:l,searchType:"WMI"}),r.metadata.processingTime=Date.now()-t,r;r.components.wmi=u;try{let p=n.substring(3,9),b=n.substring(9,17),f=await this.patternMatcher.getPatternMatches(l,c.year,p,b);if(f.length>0){let o=f.filter(y=>{var h;return ((h=y.metadata)==null?void 0:h.patternType)==="VDS"}),m=f.filter(y=>{var h;return ((h=y.metadata)==null?void 0:h.patternType)==="VIS"});o.length>0&&(r.components.vds={raw:p,patterns:o}),m.length>0&&(r.components.vis={raw:b,patterns:m}),r.components.vehicle=this.extractVehicleInfo(f,u,c),r.components.plant=this.extractPlantInfo(f,n),r.components.engine=this.extractEngineInfo(f),a.includePatternDetails&&(r.patterns=f);let d=f.reduce((y,h)=>y+h.confidence,0)/f.length;r.metadata.confidence=d,r.metadata.matchedSchema=this.findPrimarySchema(f),r.metadata.totalPatterns=f.length,d<(a.confidenceThreshold||.5)&&r.errors.push({code:"401",category:"pattern",severity:"warning",message:"Low confidence in pattern matches",confidence:d});}else return r.errors.push({code:"400",category:"pattern",severity:"error",message:"No matching patterns found"}),r.metadata.processingTime=Date.now()-t,r}catch(p){return r.errors.push({code:"501",category:"database",severity:"error",message:"Error matching patterns",details:p instanceof Error?p.message:"Unknown error"}),r.metadata.processingTime=Date.now()-t,r}r.valid=r.errors.every(p=>p.severity==="warning")||r.errors.length===0;}catch(i){K.error({vin:e,error:i},"Decoder error"),r.errors.push({code:"501",category:"database",severity:"error",message:"Unexpected error during decoding",details:i instanceof Error?i.message:"Unknown error"});}return r.metadata.processingTime=performance.now?performance.now()-t:Date.now()-t,r}findPrimarySchema(e){var t;return (t=e.filter(n=>n.element==="Model").sort((n,r)=>r.confidence-n.confidence)[0])==null?void 0:t.schema}coerceBodyStyle(e){if(e in D)return D[e];console.log("bodyStyle",e);for(let[t,n]of Object.entries(D))if(e.toLowerCase().includes(t.toLowerCase())||t.toLowerCase().includes(e.toLowerCase()))return n;let a=e.toLowerCase();return a.includes("pickup")||a.includes("truck")?"Pickup":"Other"}extractVehicleInfo(e,a,t){let n={make:a.make||"",model:"",year:t.year,manufacturer:a.manufacturer},r=e.filter(i=>i.element==="Model"&&i.value).sort((i,s)=>{var u,p;let c=((u=i.metadata)==null?void 0:u.elementWeight)??0,l=((p=s.metadata)==null?void 0:p.elementWeight)??0;return c!==l?l-c:s.confidence-i.confidence});r.length>0&&(n.model=r[0].value);for(let i of e)if(i.value)switch(i.element){case "Make":n.make=i.value;break;case "Series":n.series=i.value;break;case "Trim":case "Trim Level":n.trim=i.value;break;case "Body Class":case "Body Style":n.bodyStyle=this.coerceBodyStyle(i.value);break;case "Drive Type":n.driveType=i.value;break;case "Fuel Type - Primary":n.fuelType=i.value;break;case "Fuel Type - Secondary":n.fuelType="Hybrid";break;case "Transmission":n.transmission=i.value;break;case "Doors":n.doors=i.value;break}return n}extractPlantInfo(e,a){let t,n,r;for(let s of e){if(!s.value)continue;let c=s.element.toLowerCase();c==="plant country"?t=s.value:c==="plant city"?n=s.value:c==="plant company name"&&(r=s.value);}let i=a[10];if(t)return {country:t,city:n,manufacturer:r,code:i}}extractEngineInfo(e){let a={},t=false;for(let n of e)if(n.value)switch(n.element){case "Engine Model":a.model=n.value,t=true;break;case "Engine Number of Cylinders":case "Cylinders":a.cylinders=n.value,t=true;break;case "Displacement (L)":a.displacement=n.value,t=true;break;case "Engine Brake (hp) From":case "Engine Power (KW)":a.power=n.value,t=true;break;case "Fuel Type - Primary":case "Fuel Type":a.fuel=n.value,t=true;break}return t?a:void 0}validateStructure(e){let a=[];if(e.length!==17)return a.push({code:"100",category:"structure",severity:"error",message:"Invalid VIN length"}),a;let t=[...e].reduce((n,r,i)=>(i===8?/[0-9X]/.test(r)||n.push({char:r,pos:i+1}):i===9?/[0-9A-HJ-NPR-Z]/.test(r)||n.push({char:r,pos:i+1}):/[0-9A-HJ-NPR-Z]/.test(r)||n.push({char:r,pos:i+1}),n),[]);return t.length>0&&a.push({code:"101",category:"structure",severity:"error",message:`Invalid characters: ${t.map(n=>`${n.char} at position ${n.pos}`).join(", ")}`,positions:t.map(n=>n.pos)}),a}extractWMI(e){let a=e.substring(0,3);return a[2]==="9"&&e.length>=14?a+e.substring(11,14):a}determineModelYear(e){let a=e[9].toUpperCase(),t=new Map;for(let s=0;s<8;s++)t.set(String.fromCharCode(65+s),1980+s);for(let s=0;s<5;s++)t.set(String.fromCharCode(74+s),1988+s);for(let s=0;s<9;s++){let c=String.fromCharCode(80+s);c!=="Q"&&c!=="U"&&t.set(c,1993+s);}for(let s=0;s<8;s++)t.set(String.fromCharCode(65+s),2010+s);for(let s=0;s<5;s++)t.set(String.fromCharCode(74+s),2018+s);for(let s=0;s<9;s++){let c=String.fromCharCode(80+s);c==="P"?t.set(c,2023):c==="R"?t.set(c,2024):c==="S"?t.set(c,2025):c==="T"?t.set(c,2026):c==="V"?t.set(c,2027):c==="W"?t.set(c,2028):c==="X"?t.set(c,2029):c==="Y"&&t.set(c,2030);}for(let s=1;s<=9;s++)t.set(String(s),2030+s);let n=t.get(a);if(!n)return null;let r=n,i=new Date().getFullYear()+1;return r>i&&(r-=30),{year:r,source:"position",confidence:1}}validateCheckDigit(e){let a=[8,7,6,5,4,3,2,10,0,9,8,7,6,5,4,3,2],t=c=>{let l=c.toUpperCase();if(/[0-9]/.test(l))return parseInt(l,10);if(/[A-Z]/.test(l))switch(l){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},r=[...e].reduce((c,l,u)=>c+t(l)*a[u],0)%11,i=r===10?"X":r.toString(),s=e[8].toUpperCase();return {position:9,actual:s,expected:i,isValid:s===i}}async close(){await this.db.close();}};var I=E("BrowserDatabaseAdapter");function $(g){return g==null?"NULL":typeof g=="number"?g.toString():typeof g=="boolean"?g?"1":"0":`'${g.toString().replace(/'/g,"''")}'`}var v=class{constructor(e){this.queryCount=0;this.db=e,I.debug("Browser database adapter initialized");}async exec(e,a=[]){var n,r;this.queryCount++;let t=this.queryCount;try{I.debug({queryId:t,query:e,paramCount:a.length},"Executing browser query");let i=Date.now(),s=a.reduce((u,p,b)=>u.replace("?",$(p)),e),c=this.db.exec(s),l=Date.now()-i;return !c||c.length===0?(I.debug({queryId:t,executionTime:l},"Query returned no results"),[{columns:[],values:[]}]):(I.debug({queryId:t,executionTime:l,resultCount:c.length,rowCount:((r=(n=c[0])==null?void 0:n.values)==null?void 0:r.length)||0},"Query completed"),c.map(u=>({columns:u.columns,values:u.values})))}catch(i){throw I.error({queryId:t,query:e,error:i},"Browser database query error"),i}}async close(){I.debug("Closing browser database connection"),this.db.close();}},w=class{async createAdapter(e){I.debug({pathOrUrl:e},"Creating browser database adapter");try{if(!window.SQL){I.debug("Loading SQL.js");let r=await window.initSqlJs({locateFile:i=>`/${i}`});window.SQL=r;}I.debug({pathOrUrl:e},"Fetching database");let a=await fetch(e);if(a&&"ok"in a&&!a.ok)throw new Error(`Failed to load database: ${a.statusText}`);let t;try{t=await a.arrayBuffer();}catch{I.debug("Using empty array buffer for tests"),t=new ArrayBuffer(8);}I.debug({size:t.byteLength/1024/1024},"Database loaded");let n=new window.SQL.Database(new Uint8Array(t));return new v(n)}catch(a){throw I.error({pathOrUrl:e,error:a},"Failed to create browser database adapter"),a}}};var L=class{constructor(e){this.db=e;}async exec(e,a=[]){var t,n;try{let r=await this.db.prepare(e).bind(...a).all();return [{columns:(t=r.results)!=null&&t[0]?Object.keys(r.results[0]):[],values:((n=r.results)==null?void 0:n.map(i=>Object.values(i)))||[]}]}catch(r){throw console.error("Database query error:",r),r}}async close(){}};function Q(g){return new L(g)}var M=E("browser"),V=class{constructor(e){this.adapterFactory=new w,this.databasePath=e.databasePath,this.defaultOptions=e.defaultOptions||{},M.debug({options:e},"Browser VIN decoder initialized");}async decode(e,a){M.debug({vin:e},"Decoding VIN");try{let t=await this.adapterFactory.createAdapter(this.databasePath),n={...this.defaultOptions,...a},r=await U(e,t,n);return await t.close(),r}catch(t){throw M.error({vin:e,error:t},"VIN decoding failed"),t}}};var pe=V;export{D as BODY_STYLE_MAP,B as BodyStyle,v as BrowserDatabaseAdapter,L as CloudflareD1Adapter,P as CoreVINDecoder,v as DatabaseAdapter,q as ErrorCategory,J as ErrorCode,Y as ErrorSeverity,V as VINDecoder,Q as createD1Adapter,U as decodeVIN,pe as default};