@meterapp/vehicle-db 2.5.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,8 +2,16 @@
2
2
 
3
3
  An offline, international vehicle make/model catalog for Node.js and TypeScript. The package combines U.S. model-year data, the UK registered fleet, Asian-origin vehicles registered in New Zealand, Malaysian registration transactions, and an Indian manufacturer catalog into one small, zero-dependency API. It never makes runtime network requests.
4
4
 
5
+ The current snapshot spans **1990–2026** and includes **1,145 makes**, **14,841 model names**, and **132,491 deduplicated model-year entries** from **5 data sources**.
6
+
5
7
  The catalog covers seven vehicle types: **Motorcycle**, **Passenger Car**, **Truck**, **Bus**, **Multipurpose Passenger Vehicle (MPV)**, **Auto Rickshaw**, and **Other Vehicle**. The Asia-Pacific sources add Japanese domestic and kei models, Chinese EVs, Indian and Korean vehicles, Southeast Asian makes such as Perodua and Proton, and additional motorcycles and commercial vehicles.
6
8
 
9
+ ## Demo and playground
10
+
11
+ This catalog is the backbone of the [Car Image API](https://car-imgs.vercel.app) — an AI-native API that renders studio-quality, transparent-background images of every vehicle listed here (six camera angles, 15 colors, PNG/WebP/JPG, $1 per 1,000 images). Explore the data in the [interactive playground](https://car-imgs.vercel.app/playground) or browse vehicles at [car-imgs.vercel.app/cars](https://car-imgs.vercel.app/cars).
12
+
13
+ **Missing a vehicle?** Add it here and it becomes renderable in the API on the next release — see [CONTRIBUTING.md](CONTRIBUTING.md).
14
+
7
15
  ## Install
8
16
 
9
17
  ```bash
@@ -21,6 +29,10 @@ import {
21
29
  getMakes,
22
30
  getModels,
23
31
  getAvailableYears,
32
+ getAvailableYearRanges,
33
+ getModelAppearanceRanges,
34
+ getModelRenderGroups,
35
+ getRepresentativeYear,
24
36
  } from "@meterapp/vehicle-db";
25
37
 
26
38
  // Inspect provenance and coverage before querying.
@@ -75,9 +87,100 @@ const malaysiaMakes = getMakes({
75
87
  // Existing queries remain valid.
76
88
  const toyota = getMakes({ year: 2024 }).find((make) => make.makeName === "TOYOTA");
77
89
  const toyotaModels = getModels({ makeId: toyota!.makeId, year: 2024 });
78
- const years = getAvailableYears();
90
+ const years = getAvailableYears({
91
+ makeId: toyota!.makeId,
92
+ vehicleTypeId: 2,
93
+ sourceId: "nhtsa-vpic",
94
+ });
95
+
96
+ // Availability and exterior equivalence are intentionally separate.
97
+ const availability = getAvailableYearRanges({ makeId: toyota!.makeId });
98
+ const arteonAppearances = getModelAppearanceRanges({
99
+ makeId: getMakes().find((make) => make.makeName === "VOLKSWAGEN")!.makeId,
100
+ modelName: "Arteon",
101
+ });
102
+
103
+ // Safe cache identity: verified ranges share a representative year; unknown
104
+ // and overlapping transition years return the requested year unchanged.
105
+ const renderYear = getRepresentativeYear({
106
+ makeId: arteonAppearances[0].makeId,
107
+ modelName: "Arteon",
108
+ year: 2018,
109
+ }); // 2019
79
110
  ```
80
111
 
112
+ ## Appearance ranges and image reuse
113
+
114
+ A model being available in consecutive years does **not** prove that its exterior stayed the
115
+ same. `getAvailableYearRanges()` only compresses catalog availability for display. Sourced
116
+ generation/facelift facts live in `data/appearance-ranges.json` and are returned by
117
+ `getModelAppearanceRanges()` with their body style, market, and evidence URL.
118
+
119
+ `getModelRenderGroups()` turns those facts into a complete cache plan. It groups a year only when
120
+ exactly one verified appearance range covers it. Years with no evidence, and transition years
121
+ covered by two overlapping ranges, remain singleton exact-year groups. `getRepresentativeYear()`
122
+ is the convenience lookup for storage/cache keys and has the same fail-closed behavior.
123
+
124
+ This split is deliberate. NHTSA vPIC provides model-year and body-class data but no global
125
+ generation identifier; Wikidata is CC0 but generation and facelift coverage is sparse. Broad
126
+ third-party generation dumps commonly carry restrictive or share-alike terms and overlapping
127
+ records, so they are useful for research but are not imported automatically. Verified ranges are
128
+ small, reviewable facts linked to first-party or equivalently authoritative public evidence.
129
+
130
+ NHTSA wheelbase, dimensions, doors, series, and truck cab/bed fields can rank possible body-family
131
+ matches. They are candidate evidence only: related platforms and trims can still have visibly
132
+ different bodywork. `appearanceId` is intentionally stricter than a derived `body_family_id` and
133
+ is the only grouping allowed to drive render reuse. See CONTRIBUTING for the review criteria.
134
+
135
+ ```typescript
136
+ const volkswagen = getMakes().find((make) => make.makeName === "VOLKSWAGEN")!;
137
+
138
+ getModelAppearanceRanges({ makeId: volkswagen.makeId, modelName: "Arteon" });
139
+ // Original Arteon: 2017–2020, Fastback, with Volkswagen evidence
140
+ // Updated Arteon: 2020–2026, Fastback, with Volkswagen evidence
141
+
142
+ getModelRenderGroups({ makeId: volkswagen.makeId, modelName: "Arteon" });
143
+ // 2017–2019 share one render; overlapping 2020 stays exact; 2021–2026 share one render.
144
+ ```
145
+
146
+ ## Driver search and autocomplete
147
+
148
+ The optional, zero-dependency search entry point turns free-form driver input into canonical catalog candidates. Its index is built lazily on the first search, and matching runs entirely in memory without network requests.
149
+
150
+ ```typescript
151
+ import { searchVehicles } from "@meterapp/vehicle-db/search";
152
+
153
+ const suggestions = searchVehicles("2020 toy cam", {
154
+ sourceId: "nhtsa-vpic",
155
+ vehicleTypeId: 2,
156
+ limit: 10,
157
+ });
158
+
159
+ const camry = suggestions.find(
160
+ (result) => result.kind === "model" && result.modelName === "Camry",
161
+ );
162
+
163
+ if (camry?.kind === "model") {
164
+ // A query containing a year has one directly selectable variant.
165
+ const selection = camry.variants[0];
166
+ // Persist year, makeId, modelId, and vehicleTypeId — not display text alone.
167
+ }
168
+ ```
169
+
170
+ Search accepts year, make, and model tokens in any order. It recognizes common punctuation variants such as `F-150`/`f150` and `CR-V`/`crv`, a small set of make aliases such as `VW` and `Chevy`, and conservative misspellings. Results are ranked deterministically by exact, prefix, token, substring, then fuzzy match. Fuzzy matching is only used when no lexical result exists.
171
+
172
+ Queries without a year group a canonical make/model/type across all available years rather than returning duplicate suggestions for every year. Each model result contains `variants`, which provides the preferred catalog `modelId` for every selectable year.
173
+
174
+ For the simplest driver experience:
175
+
176
+ 1. Infer the relevant market and pass its `sourceId`; year semantics differ by source.
177
+ 2. Offer one field labelled “Search year, make, or model” and begin suggesting after two characters.
178
+ 3. If the driver enters only a year, use `getMakes({ year, ... })` for the next step; a year-only search intentionally returns no arbitrary alphabetical suggestions.
179
+ 4. On empty input, show application-owned recent vehicles. The catalog intentionally does not pretend that record frequency is vehicle popularity.
180
+ 5. Use the manual fallback Year → Make → Model. `getAvailableYears(options)` supports every step.
181
+
182
+ Applications with regional telemetry can request more candidates and rerank **within the same `matchKind`**. Text quality and market compatibility should remain ahead of behavioral popularity so an exact result never loses to an unrelated popular vehicle.
183
+
81
184
  ## API
82
185
 
83
186
  ### `getDataSources(): DataSource[]`
@@ -133,10 +236,11 @@ interface Make {
133
236
 
134
237
  ### `getModels(options?): Model[]`
135
238
 
136
- Returns models, optionally filtered by `year`, `vehicleTypeId`, `makeId`, and/or `sourceId`. `sourceIds` preserves provenance when equivalent records appear in more than one source.
239
+ Returns models, optionally filtered by `year`, `vehicleTypeId`, `makeId`, `modelId`, and/or `sourceId`. `sourceIds` preserves provenance when equivalent records appear in more than one source.
137
240
 
138
241
  ```typescript
139
242
  getModels({ makeId: 474, year: 2024 });
243
+ getModels({ modelId: 2469, year: 2024 });
140
244
  getModels({ year: 2024, vehicleTypeId: 1 });
141
245
  getModels({ year: 2024, vehicleTypeId: 5, sourceId: "uk-dft-vehicle-licensing" });
142
246
  ```
@@ -153,9 +257,32 @@ interface Model {
153
257
  }
154
258
  ```
155
259
 
156
- ### `getAvailableYears(): number[]`
260
+ ### `getAvailableYears(options?): number[]`
261
+
262
+ Returns years present in the combined catalog, optionally filtered by `makeId`, `modelId`, `vehicleTypeId`, and/or `sourceId`. Results are sorted ascending.
263
+
264
+ ```typescript
265
+ getAvailableYears();
266
+ getAvailableYears({ makeId: 448, vehicleTypeId: 2, sourceId: "nhtsa-vpic" });
267
+ getAvailableYears({ modelId: 2469, sourceId: "nhtsa-vpic" });
268
+ ```
269
+
270
+ ### Appearance and year-range APIs
271
+
272
+ - `getAvailableYearRanges(options?): VehicleYearRange[]` compresses consecutive availability
273
+ years; it makes no exterior-equivalence claim.
274
+ - `getModelAppearanceRanges({ makeId, modelName, year? }): VehicleAppearanceRange[]` returns
275
+ verified generation/facelift periods and evidence. Overlap is preserved.
276
+ - `getModelRenderGroups({ makeId, modelName, vehicleTypeId?, sourceId? }): VehicleRenderGroup[]`
277
+ returns safe, non-overlapping cache groups for every available year.
278
+ - `getRepresentativeYear({ makeId, modelName, year, ... }): number` returns the canonical cache
279
+ year for an unambiguous verified group, otherwise the requested year.
280
+
281
+ ### `searchVehicles(query, options?): VehicleSearchResult[]`
282
+
283
+ Import from `@meterapp/vehicle-db/search`. Returns deterministic make and grouped model candidates for free-form input. Options include `year`, `vehicleTypeId`, `sourceId`, `limit` (default 10, maximum 100), and `fuzzy` (default `true`).
157
284
 
158
- Returns all years present in the combined catalog, sorted ascending.
285
+ Model results contain `years` and a year-specific `variants` array. Make results intentionally omit models, allowing applications to transition into the existing `getModels` flow after the driver chooses a make.
159
286
 
160
287
  ## Data sources
161
288
 
package/dist/data.d.ts CHANGED
@@ -24,6 +24,20 @@ interface CompactData {
24
24
  }[];
25
25
  modelNames: string[];
26
26
  models: [number, number, number, number, number, number][];
27
+ appearanceRanges: {
28
+ id: string;
29
+ make_id: number;
30
+ make_name: string;
31
+ model_name: string;
32
+ label: string;
33
+ body_style: string;
34
+ year_from: number;
35
+ year_to: number;
36
+ representative_year: number;
37
+ regions: string[];
38
+ source_name: string;
39
+ source_url: string;
40
+ }[];
27
41
  }
28
42
  declare const data: CompactData;
29
43
  export default data;