@meterapp/vehicle-db 2.6.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 +67 -1
- package/dist/data.d.ts +14 -0
- package/dist/data.js +1 -1
- package/dist/index.d.ts +66 -0
- package/dist/index.js +121 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -48,6 +48,52 @@ export interface GetAvailableYearsOptions {
|
|
|
48
48
|
modelId?: number;
|
|
49
49
|
sourceId?: string;
|
|
50
50
|
}
|
|
51
|
+
export interface VehicleYearRange {
|
|
52
|
+
yearFrom: number;
|
|
53
|
+
yearTo: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* A sourced period in which a model shares one exterior design. Ranges may
|
|
57
|
+
* overlap when a facelift, body variant, or regional model changed during a
|
|
58
|
+
* calendar year; callers must not treat an overlap as render-equivalent.
|
|
59
|
+
*/
|
|
60
|
+
export interface VehicleAppearanceRange {
|
|
61
|
+
appearanceId: string;
|
|
62
|
+
makeId: number;
|
|
63
|
+
makeName: string;
|
|
64
|
+
modelName: string;
|
|
65
|
+
label: string;
|
|
66
|
+
bodyStyle: string;
|
|
67
|
+
yearFrom: number;
|
|
68
|
+
yearTo: number;
|
|
69
|
+
representativeYear: number;
|
|
70
|
+
regions: string[];
|
|
71
|
+
sourceName: string;
|
|
72
|
+
sourceUrl: string;
|
|
73
|
+
}
|
|
74
|
+
export interface GetModelAppearanceRangesOptions {
|
|
75
|
+
makeId: number;
|
|
76
|
+
modelName: string;
|
|
77
|
+
year?: number;
|
|
78
|
+
}
|
|
79
|
+
export interface GetModelRenderGroupsOptions extends GetModelAppearanceRangesOptions {
|
|
80
|
+
vehicleTypeId?: number;
|
|
81
|
+
sourceId?: string;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Safe cache groups for every available year. Sourced, unambiguous appearance
|
|
85
|
+
* periods are grouped; unknown and overlapping years deliberately stay as
|
|
86
|
+
* singleton exact-year groups.
|
|
87
|
+
*/
|
|
88
|
+
export interface VehicleRenderGroup {
|
|
89
|
+
groupId: string;
|
|
90
|
+
kind: "appearance" | "year";
|
|
91
|
+
years: number[];
|
|
92
|
+
yearFrom: number;
|
|
93
|
+
yearTo: number;
|
|
94
|
+
representativeYear: number;
|
|
95
|
+
appearance?: VehicleAppearanceRange;
|
|
96
|
+
}
|
|
51
97
|
/**
|
|
52
98
|
* Returns all vehicle types in the database.
|
|
53
99
|
*/
|
|
@@ -68,3 +114,23 @@ export declare function getModels(options?: GetModelsOptions): Model[];
|
|
|
68
114
|
* Gets available years, optionally filtered by vehicle identity and source.
|
|
69
115
|
*/
|
|
70
116
|
export declare function getAvailableYears(options?: GetAvailableYearsOptions): number[];
|
|
117
|
+
/**
|
|
118
|
+
* Compresses available catalog years into contiguous ranges. These ranges
|
|
119
|
+
* describe availability only; use getModelAppearanceRanges when exterior
|
|
120
|
+
* equivalence matters.
|
|
121
|
+
*/
|
|
122
|
+
export declare function getAvailableYearRanges(options?: GetAvailableYearsOptions): VehicleYearRange[];
|
|
123
|
+
/** Returns the curated, source-backed exterior-design periods for a model. */
|
|
124
|
+
export declare function getModelAppearanceRanges(options: GetModelAppearanceRangesOptions): VehicleAppearanceRange[];
|
|
125
|
+
/**
|
|
126
|
+
* Returns a complete, non-overlapping render plan for a model. A year is only
|
|
127
|
+
* grouped when exactly one verified appearance range contains it.
|
|
128
|
+
*/
|
|
129
|
+
export declare function getModelRenderGroups(options: GetModelRenderGroupsOptions): VehicleRenderGroup[];
|
|
130
|
+
/**
|
|
131
|
+
* Canonical year for cache/storage identity. Unknown, unavailable, and
|
|
132
|
+
* ambiguous years return themselves, which is the fail-closed behavior.
|
|
133
|
+
*/
|
|
134
|
+
export declare function getRepresentativeYear(options: GetModelRenderGroupsOptions & {
|
|
135
|
+
year: number;
|
|
136
|
+
}): number;
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,10 @@ exports.getDataSources = getDataSources;
|
|
|
8
8
|
exports.getMakes = getMakes;
|
|
9
9
|
exports.getModels = getModels;
|
|
10
10
|
exports.getAvailableYears = getAvailableYears;
|
|
11
|
+
exports.getAvailableYearRanges = getAvailableYearRanges;
|
|
12
|
+
exports.getModelAppearanceRanges = getModelAppearanceRanges;
|
|
13
|
+
exports.getModelRenderGroups = getModelRenderGroups;
|
|
14
|
+
exports.getRepresentativeYear = getRepresentativeYear;
|
|
11
15
|
const data_1 = __importDefault(require("./data"));
|
|
12
16
|
// ---------------------------------------------------------------------------
|
|
13
17
|
// Internal lookup maps (lazy)
|
|
@@ -41,6 +45,56 @@ function getSourceIds(sourceMask) {
|
|
|
41
45
|
}
|
|
42
46
|
return sourceIds;
|
|
43
47
|
}
|
|
48
|
+
function normalizeLookupName(value) {
|
|
49
|
+
return value.trim().toLocaleUpperCase("en-US").replace(/\s+/g, " ");
|
|
50
|
+
}
|
|
51
|
+
function compactYears(years) {
|
|
52
|
+
const sorted = [...new Set(years)].sort((left, right) => left - right);
|
|
53
|
+
const ranges = [];
|
|
54
|
+
for (const year of sorted) {
|
|
55
|
+
const previous = ranges[ranges.length - 1];
|
|
56
|
+
if (previous && previous.yearTo + 1 === year)
|
|
57
|
+
previous.yearTo = year;
|
|
58
|
+
else
|
|
59
|
+
ranges.push({ yearFrom: year, yearTo: year });
|
|
60
|
+
}
|
|
61
|
+
return ranges;
|
|
62
|
+
}
|
|
63
|
+
function modelNameYears(options) {
|
|
64
|
+
const sourceMask = options.sourceId == null ? undefined : getSourceMaskMap().get(options.sourceId);
|
|
65
|
+
if (options.sourceId != null && sourceMask == null)
|
|
66
|
+
return [];
|
|
67
|
+
const wantedModel = normalizeLookupName(options.modelName);
|
|
68
|
+
const years = new Set();
|
|
69
|
+
for (const model of data_1.default.models) {
|
|
70
|
+
if (model[1] !== options.makeId)
|
|
71
|
+
continue;
|
|
72
|
+
if (options.vehicleTypeId != null && model[4] !== options.vehicleTypeId)
|
|
73
|
+
continue;
|
|
74
|
+
if (sourceMask != null && (model[5] & sourceMask) === 0)
|
|
75
|
+
continue;
|
|
76
|
+
if (normalizeLookupName(data_1.default.modelNames[model[3]]) !== wantedModel)
|
|
77
|
+
continue;
|
|
78
|
+
years.add(model[0]);
|
|
79
|
+
}
|
|
80
|
+
return [...years].sort((left, right) => left - right);
|
|
81
|
+
}
|
|
82
|
+
function toAppearanceRange(range) {
|
|
83
|
+
return {
|
|
84
|
+
appearanceId: range.id,
|
|
85
|
+
makeId: range.make_id,
|
|
86
|
+
makeName: range.make_name,
|
|
87
|
+
modelName: range.model_name,
|
|
88
|
+
label: range.label,
|
|
89
|
+
bodyStyle: range.body_style,
|
|
90
|
+
yearFrom: range.year_from,
|
|
91
|
+
yearTo: range.year_to,
|
|
92
|
+
representativeYear: range.representative_year,
|
|
93
|
+
regions: [...range.regions],
|
|
94
|
+
sourceName: range.source_name,
|
|
95
|
+
sourceUrl: range.source_url,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
44
98
|
// ---------------------------------------------------------------------------
|
|
45
99
|
// Public API
|
|
46
100
|
// ---------------------------------------------------------------------------
|
|
@@ -156,3 +210,70 @@ function getAvailableYears(options = {}) {
|
|
|
156
210
|
}
|
|
157
211
|
return [...years].sort((a, b) => a - b);
|
|
158
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Compresses available catalog years into contiguous ranges. These ranges
|
|
215
|
+
* describe availability only; use getModelAppearanceRanges when exterior
|
|
216
|
+
* equivalence matters.
|
|
217
|
+
*/
|
|
218
|
+
function getAvailableYearRanges(options = {}) {
|
|
219
|
+
return compactYears(getAvailableYears(options));
|
|
220
|
+
}
|
|
221
|
+
/** Returns the curated, source-backed exterior-design periods for a model. */
|
|
222
|
+
function getModelAppearanceRanges(options) {
|
|
223
|
+
const wantedModel = normalizeLookupName(options.modelName);
|
|
224
|
+
return data_1.default.appearanceRanges
|
|
225
|
+
.filter((range) => range.make_id === options.makeId &&
|
|
226
|
+
normalizeLookupName(range.model_name) === wantedModel &&
|
|
227
|
+
(options.year == null ||
|
|
228
|
+
(range.year_from <= options.year && range.year_to >= options.year)))
|
|
229
|
+
.map(toAppearanceRange);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Returns a complete, non-overlapping render plan for a model. A year is only
|
|
233
|
+
* grouped when exactly one verified appearance range contains it.
|
|
234
|
+
*/
|
|
235
|
+
function getModelRenderGroups(options) {
|
|
236
|
+
const years = modelNameYears(options);
|
|
237
|
+
const appearances = getModelAppearanceRanges(options);
|
|
238
|
+
const groupYears = new Map();
|
|
239
|
+
const appearanceByGroup = new Map();
|
|
240
|
+
for (const year of years) {
|
|
241
|
+
const candidates = appearances.filter((appearance) => appearance.yearFrom <= year && appearance.yearTo >= year);
|
|
242
|
+
const groupId = candidates.length === 1 ? candidates[0].appearanceId : `year:${year}`;
|
|
243
|
+
const existing = groupYears.get(groupId);
|
|
244
|
+
if (existing)
|
|
245
|
+
existing.push(year);
|
|
246
|
+
else
|
|
247
|
+
groupYears.set(groupId, [year]);
|
|
248
|
+
if (candidates.length === 1)
|
|
249
|
+
appearanceByGroup.set(groupId, candidates[0]);
|
|
250
|
+
}
|
|
251
|
+
return [...groupYears.entries()]
|
|
252
|
+
.map(([groupId, groupedYears]) => {
|
|
253
|
+
const appearance = appearanceByGroup.get(groupId);
|
|
254
|
+
const preferred = appearance?.representativeYear;
|
|
255
|
+
const representativeYear = preferred != null && groupedYears.includes(preferred)
|
|
256
|
+
? preferred
|
|
257
|
+
: groupedYears.reduce((best, year) => preferred != null && Math.abs(year - preferred) < Math.abs(best - preferred)
|
|
258
|
+
? year
|
|
259
|
+
: best);
|
|
260
|
+
return {
|
|
261
|
+
groupId,
|
|
262
|
+
kind: appearance ? "appearance" : "year",
|
|
263
|
+
years: [...groupedYears],
|
|
264
|
+
yearFrom: groupedYears[0],
|
|
265
|
+
yearTo: groupedYears[groupedYears.length - 1],
|
|
266
|
+
representativeYear,
|
|
267
|
+
...(appearance ? { appearance } : {}),
|
|
268
|
+
};
|
|
269
|
+
})
|
|
270
|
+
.sort((left, right) => left.yearFrom - right.yearFrom || left.yearTo - right.yearTo);
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Canonical year for cache/storage identity. Unknown, unavailable, and
|
|
274
|
+
* ambiguous years return themselves, which is the fail-closed behavior.
|
|
275
|
+
*/
|
|
276
|
+
function getRepresentativeYear(options) {
|
|
277
|
+
const group = getModelRenderGroups(options).find((candidate) => candidate.years.includes(options.year));
|
|
278
|
+
return group?.representativeYear ?? options.year;
|
|
279
|
+
}
|