@meterapp/vehicle-db 1.0.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 ADDED
@@ -0,0 +1,162 @@
1
+ # vehicle-db
2
+
3
+ Offline NHTSA vehicle database. All U.S. vehicle makes and models from 1990–2026, bundled as a 3.8 MB SQLite file inside the package. No network requests needed.
4
+
5
+ Covers three vehicle types: **Passenger Car**, **Truck**, and **Multipurpose Passenger Vehicle (MPV)**.
6
+
7
+ Drop-in replacement for these NHTSA vPIC API endpoints:
8
+
9
+ - `GetMakesForVehicleType/{type}`
10
+ - `GetModelsForMakeYear/make/{make}/modelyear/{year}`
11
+ - `GetModelsForMakeIdYear/makeId/{id}/modelyear/{year}/vehicleType/{type}`
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ npm install @meterapp/vehicle-db
17
+ ```
18
+
19
+ > Requires Node.js 18+. Uses [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) under the hood.
20
+
21
+ ## Usage
22
+
23
+ ```typescript
24
+ import {
25
+ getVehicleTypes,
26
+ getMakesForVehicleType,
27
+ getModelsForMakeYear,
28
+ getAvailableYears,
29
+ close,
30
+ } from "@meterapp/vehicle-db";
31
+
32
+ // List available vehicle types
33
+ const types = getVehicleTypes();
34
+ // => Passenger Car, Truck, Multipurpose Passenger Vehicle (MPV)
35
+
36
+ // Get all truck makes for a given year
37
+ const makes = getMakesForVehicleType("Truck", 2024);
38
+ // => { Count: 171, Message: "...", Results: [...] }
39
+
40
+ // Get all models for a make and year (all vehicle types)
41
+ const models = getModelsForMakeYear("Toyota", 2024);
42
+ // => { Count: 27, Message: "...", Results: [...] }
43
+
44
+ // Filter models by vehicle type
45
+ const trucks = getModelsForMakeYear("Ford", 2024, "Truck");
46
+ // => { Count: 10, Message: "...", Results: [F-150, Ranger, ...] }
47
+
48
+ // See what years are available
49
+ const years = getAvailableYears();
50
+ // => [1990, 1991, ..., 2026]
51
+
52
+ // Optional: close the DB connection when you're done
53
+ close();
54
+ ```
55
+
56
+ ## API
57
+
58
+ ### `getVehicleTypes(): NHTSAResponse<VehicleType>`
59
+
60
+ Returns all vehicle types in the database.
61
+
62
+ ```typescript
63
+ { VehicleTypeId: number; VehicleTypeName: string }
64
+ ```
65
+
66
+ ### `getMakesForVehicleType(vehicleType: string, year: number): NHTSAResponse<MakeResult>`
67
+
68
+ Returns all makes for a given vehicle type and model year. Vehicle type is **case-insensitive**. Equivalent to:
69
+
70
+ ```
71
+ GET https://vpic.nhtsa.dot.gov/api/vehicles/GetMakesForVehicleType/truck?year=2024&format=json
72
+ ```
73
+
74
+ Each result contains:
75
+
76
+ ```typescript
77
+ {
78
+ MakeId: number; // e.g. 460
79
+ MakeName: string; // e.g. "FORD"
80
+ VehicleTypeId: number; // e.g. 3
81
+ VehicleTypeName: string; // e.g. "Truck"
82
+ }
83
+ ```
84
+
85
+ ### `getModelsForMakeYear(make: string, year: number, vehicleType?: string): NHTSAResponse<ModelResult>`
86
+
87
+ Returns all models for a given make and year. Make name is **case-insensitive**. Optionally filter by vehicle type.
88
+
89
+ ```
90
+ GET https://vpic.nhtsa.dot.gov/api/vehicles/GetModelsForMakeYear/make/Ford/modelyear/2024?format=json
91
+ ```
92
+
93
+ Each result contains:
94
+
95
+ ```typescript
96
+ {
97
+ Make_ID: number; // e.g. 460
98
+ Make_Name: string; // e.g. "FORD"
99
+ Model_ID: number; // e.g. 1801
100
+ Model_Name: string; // e.g. "F-150"
101
+ }
102
+ ```
103
+
104
+ ### `getAvailableYears(): number[]`
105
+
106
+ Returns all years present in the database, sorted ascending.
107
+
108
+ ### `close(): void`
109
+
110
+ Closes the SQLite connection. Safe to call multiple times. The connection reopens automatically on the next query.
111
+
112
+ ## Migrating from the NHTSA API
113
+
114
+ Replace your fetch calls directly:
115
+
116
+ ```diff
117
+ - const res = await fetch(
118
+ - `https://vpic.nhtsa.dot.gov/api/vehicles/GetMakesForVehicleType/car?year=${year}&format=json`
119
+ - );
120
+ - const makes = await res.json();
121
+ + const makes = getMakesForVehicleType("Passenger Car", year);
122
+ ```
123
+
124
+ ```diff
125
+ - const res = await fetch(
126
+ - `https://vpic.nhtsa.dot.gov/api/vehicles/GetModelsForMakeYear/make/${encodeURIComponent(make)}/modelyear/${year}?format=json`
127
+ - );
128
+ - const models = await res.json();
129
+ + const models = getModelsForMakeYear(make, year);
130
+ ```
131
+
132
+ The response shape (`Count`, `Message`, `SearchCriteria`, `Results`) matches the NHTSA API, so downstream code should work unchanged.
133
+
134
+ ## Database stats
135
+
136
+ | | |
137
+ |---|---|
138
+ | **Years** | 1990 – 2026 |
139
+ | **Vehicle types** | 3 (Passenger Car, Truck, MPV) |
140
+ | **Makes** | 400 |
141
+ | **Model entries** | 51,270 |
142
+ | **SQLite file size** | 3.8 MB |
143
+
144
+ ## Rebuilding the database
145
+
146
+ To refresh the data from the NHTSA API:
147
+
148
+ ```bash
149
+ npm run build:db
150
+ ```
151
+
152
+ Or for a specific year range:
153
+
154
+ ```bash
155
+ npm run build:db -- --start-year 2020 --end-year 2026
156
+ ```
157
+
158
+ > The NHTSA API rate-limits aggressively. The build script retries failed requests, but some obscure makes may be skipped. Major manufacturers (Toyota, Honda, Ford, BMW, etc.) are always fetched successfully.
159
+
160
+ ## License
161
+
162
+ ISC
package/data/nhtsa.db ADDED
Binary file
@@ -0,0 +1,46 @@
1
+ export interface VehicleType {
2
+ vehicleTypeId: number;
3
+ vehicleTypeName: string;
4
+ }
5
+ export interface Make {
6
+ makeId: number;
7
+ makeName: string;
8
+ }
9
+ export interface Model {
10
+ modelId: number;
11
+ modelName: string;
12
+ makeId: number;
13
+ makeName: string;
14
+ vehicleTypeId: number;
15
+ vehicleTypeName: string;
16
+ }
17
+ export interface GetMakesOptions {
18
+ year?: number;
19
+ vehicleTypeId?: number;
20
+ }
21
+ export interface GetModelsOptions {
22
+ year?: number;
23
+ vehicleTypeId?: number;
24
+ makeId?: number;
25
+ }
26
+ /**
27
+ * Returns all vehicle types in the database.
28
+ */
29
+ export declare function getVehicleTypes(): VehicleType[];
30
+ /**
31
+ * Returns makes, optionally filtered by year and/or vehicle type.
32
+ */
33
+ export declare function getMakes(options?: GetMakesOptions): Make[];
34
+ /**
35
+ * Returns models, optionally filtered by year, vehicle type, and/or make.
36
+ */
37
+ export declare function getModels(options?: GetModelsOptions): Model[];
38
+ /**
39
+ * Get all available years in the database.
40
+ */
41
+ export declare function getAvailableYears(): number[];
42
+ /**
43
+ * Close the database connection (optional cleanup).
44
+ */
45
+ export declare function close(): void;
46
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,IAAI;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAkBD;;GAEG;AACH,wBAAgB,eAAe,IAAI,WAAW,EAAE,CAU/C;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,OAAO,GAAE,eAAoB,GAAG,IAAI,EAAE,CAkC9D;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,GAAE,gBAAqB,GAAG,KAAK,EAAE,CA8CjE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,EAAE,CAM5C;AAED;;GAEG;AACH,wBAAgB,KAAK,IAAI,IAAI,CAK5B"}
package/dist/index.js ADDED
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getVehicleTypes = getVehicleTypes;
7
+ exports.getMakes = getMakes;
8
+ exports.getModels = getModels;
9
+ exports.getAvailableYears = getAvailableYears;
10
+ exports.close = close;
11
+ const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
12
+ const path_1 = __importDefault(require("path"));
13
+ const DB_PATH = path_1.default.join(__dirname, "..", "data", "nhtsa.db");
14
+ // ---------------------------------------------------------------------------
15
+ // Database singleton
16
+ // ---------------------------------------------------------------------------
17
+ let _db = null;
18
+ function getDb() {
19
+ if (!_db) {
20
+ _db = new better_sqlite3_1.default(DB_PATH, { readonly: true, fileMustExist: true });
21
+ }
22
+ return _db;
23
+ }
24
+ // ---------------------------------------------------------------------------
25
+ // Public API
26
+ // ---------------------------------------------------------------------------
27
+ /**
28
+ * Returns all vehicle types in the database.
29
+ */
30
+ function getVehicleTypes() {
31
+ const db = getDb();
32
+ const rows = db
33
+ .prepare("SELECT vehicle_type_id, vehicle_type_name FROM vehicle_types ORDER BY vehicle_type_name")
34
+ .all();
35
+ return rows.map((r) => ({
36
+ vehicleTypeId: r.vehicle_type_id,
37
+ vehicleTypeName: r.vehicle_type_name,
38
+ }));
39
+ }
40
+ /**
41
+ * Returns makes, optionally filtered by year and/or vehicle type.
42
+ */
43
+ function getMakes(options = {}) {
44
+ const db = getDb();
45
+ const conditions = [];
46
+ const params = [];
47
+ if (options.year != null) {
48
+ conditions.push("m.year = ?");
49
+ params.push(options.year);
50
+ }
51
+ if (options.vehicleTypeId != null) {
52
+ conditions.push("m.vehicle_type_id = ?");
53
+ params.push(options.vehicleTypeId);
54
+ }
55
+ let sql;
56
+ if (conditions.length > 0) {
57
+ sql = `SELECT DISTINCT mk.make_id, mk.make_name
58
+ FROM models m
59
+ JOIN makes mk USING (make_id)
60
+ WHERE ${conditions.join(" AND ")}
61
+ ORDER BY mk.make_name`;
62
+ }
63
+ else {
64
+ sql = `SELECT make_id, make_name FROM makes ORDER BY make_name`;
65
+ }
66
+ const rows = db.prepare(sql).all(...params);
67
+ return rows.map((r) => ({
68
+ makeId: r.make_id,
69
+ makeName: r.make_name,
70
+ }));
71
+ }
72
+ /**
73
+ * Returns models, optionally filtered by year, vehicle type, and/or make.
74
+ */
75
+ function getModels(options = {}) {
76
+ const db = getDb();
77
+ const conditions = [];
78
+ const params = [];
79
+ if (options.year != null) {
80
+ conditions.push("m.year = ?");
81
+ params.push(options.year);
82
+ }
83
+ if (options.vehicleTypeId != null) {
84
+ conditions.push("m.vehicle_type_id = ?");
85
+ params.push(options.vehicleTypeId);
86
+ }
87
+ if (options.makeId != null) {
88
+ conditions.push("m.make_id = ?");
89
+ params.push(options.makeId);
90
+ }
91
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
92
+ const sql = `
93
+ SELECT m.model_id, m.model_name, mk.make_id, mk.make_name,
94
+ vt.vehicle_type_id, vt.vehicle_type_name
95
+ FROM models m
96
+ JOIN makes mk USING (make_id)
97
+ JOIN vehicle_types vt USING (vehicle_type_id)
98
+ ${where}
99
+ ORDER BY m.model_name`;
100
+ const rows = db.prepare(sql).all(...params);
101
+ return rows.map((r) => ({
102
+ modelId: r.model_id,
103
+ modelName: r.model_name,
104
+ makeId: r.make_id,
105
+ makeName: r.make_name,
106
+ vehicleTypeId: r.vehicle_type_id,
107
+ vehicleTypeName: r.vehicle_type_name,
108
+ }));
109
+ }
110
+ /**
111
+ * Get all available years in the database.
112
+ */
113
+ function getAvailableYears() {
114
+ const db = getDb();
115
+ const rows = db
116
+ .prepare("SELECT DISTINCT year FROM models ORDER BY year")
117
+ .all();
118
+ return rows.map((r) => r.year);
119
+ }
120
+ /**
121
+ * Close the database connection (optional cleanup).
122
+ */
123
+ function close() {
124
+ if (_db) {
125
+ _db.close();
126
+ _db = null;
127
+ }
128
+ }
129
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;AAyDA,0CAUC;AAKD,4BAkCC;AAKD,8BA8CC;AAKD,8CAMC;AAKD,sBAKC;AAlLD,oEAAsC;AACtC,gDAAwB;AAExB,MAAM,OAAO,GAAG,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;AAmC/D,8EAA8E;AAC9E,qBAAqB;AACrB,8EAA8E;AAC9E,IAAI,GAAG,GAAuC,IAAI,CAAC;AAEnD,SAAS,KAAK;IACZ,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,GAAG,GAAG,IAAI,wBAAQ,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;GAEG;AACH,SAAgB,eAAe;IAC7B,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;IACnB,MAAM,IAAI,GAAG,EAAE;SACZ,OAAO,CAAC,yFAAyF,CAAC;SAClG,GAAG,EAA8D,CAAC;IAErE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtB,aAAa,EAAE,CAAC,CAAC,eAAe;QAChC,eAAe,EAAE,CAAC,CAAC,iBAAiB;KACrC,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;GAEG;AACH,SAAgB,QAAQ,CAAC,UAA2B,EAAE;IACpD,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;IACnB,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,MAAM,MAAM,GAAU,EAAE,CAAC;IAEzB,IAAI,OAAO,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;QACzB,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IACD,IAAI,OAAO,CAAC,aAAa,IAAI,IAAI,EAAE,CAAC;QAClC,UAAU,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACrC,CAAC;IAED,IAAI,GAAW,CAAC;IAChB,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,GAAG,GAAG;;;mBAGS,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC;iCACV,CAAC;IAChC,CAAC;SAAM,CAAC;QACN,GAAG,GAAG,yDAAyD,CAAC;IAClE,CAAC;IAED,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,MAAM,CAGvC,CAAC;IAEJ,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtB,MAAM,EAAE,CAAC,CAAC,OAAO;QACjB,QAAQ,EAAE,CAAC,CAAC,SAAS;KACtB,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;GAEG;AACH,SAAgB,SAAS,CAAC,UAA4B,EAAE;IACtD,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;IACnB,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,MAAM,MAAM,GAAU,EAAE,CAAC;IAEzB,IAAI,OAAO,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;QACzB,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IACD,IAAI,OAAO,CAAC,aAAa,IAAI,IAAI,EAAE,CAAC;QAClC,UAAU,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACrC,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;QAC3B,UAAU,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QACjC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAED,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAE/E,MAAM,GAAG,GAAG;;;;;;MAMR,KAAK;0BACe,CAAC;IAEzB,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,MAAM,CAOvC,CAAC;IAEJ,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtB,OAAO,EAAE,CAAC,CAAC,QAAQ;QACnB,SAAS,EAAE,CAAC,CAAC,UAAU;QACvB,MAAM,EAAE,CAAC,CAAC,OAAO;QACjB,QAAQ,EAAE,CAAC,CAAC,SAAS;QACrB,aAAa,EAAE,CAAC,CAAC,eAAe;QAChC,eAAe,EAAE,CAAC,CAAC,iBAAiB;KACrC,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;GAEG;AACH,SAAgB,iBAAiB;IAC/B,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;IACnB,MAAM,IAAI,GAAG,EAAE;SACZ,OAAO,CAAC,gDAAgD,CAAC;SACzD,GAAG,EAAwB,CAAC;IAC/B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AACjC,CAAC;AAED;;GAEG;AACH,SAAgB,KAAK;IACnB,IAAI,GAAG,EAAE,CAAC;QACR,GAAG,CAAC,KAAK,EAAE,CAAC;QACZ,GAAG,GAAG,IAAI,CAAC;IACb,CAAC;AACH,CAAC"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@meterapp/vehicle-db",
3
+ "version": "1.0.0",
4
+ "description": "Offline NHTSA vehicle makes/models database bundled as SQLite",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist",
9
+ "data/nhtsa.db"
10
+ ],
11
+ "scripts": {
12
+ "build:db": "tsx scripts/build-db.ts",
13
+ "build:ts": "tsc",
14
+ "build": "npm run build:ts",
15
+ "prepublishOnly": "npm run build",
16
+ "test": "vitest run"
17
+ },
18
+ "keywords": [
19
+ "nhtsa",
20
+ "vehicles",
21
+ "makes",
22
+ "models",
23
+ "offline",
24
+ "sqlite"
25
+ ],
26
+ "author": "",
27
+ "license": "ISC",
28
+ "type": "commonjs",
29
+ "dependencies": {
30
+ "better-sqlite3": "^12.6.2"
31
+ },
32
+ "devDependencies": {
33
+ "@types/better-sqlite3": "^7.6.13",
34
+ "@types/node": "^25.4.0",
35
+ "tsx": "^4.21.0",
36
+ "typescript": "^5.9.3",
37
+ "vitest": "^4.0.18"
38
+ }
39
+ }