@charzing/vehicle-utils 0.1.1 → 0.2.1
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/dist/cjs/index.d.ts +121 -121
- package/dist/cjs/index.js +237 -85
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.d.mts +121 -121
- package/dist/esm/index.mjs +225 -81
- package/dist/esm/index.mjs.map +1 -1
- package/package.json +7 -3
package/dist/esm/index.d.mts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Firestore } from 'firebase/firestore';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* 브랜드 관련 타입 정의
|
|
3
5
|
*/
|
|
@@ -79,7 +81,7 @@ type ModelSlug = string;
|
|
|
79
81
|
/**
|
|
80
82
|
* 모델 매핑 결과
|
|
81
83
|
*/
|
|
82
|
-
interface ModelMapping {
|
|
84
|
+
interface ModelMapping$1 {
|
|
83
85
|
/** Firestore ID (소문자, 하이픈) */
|
|
84
86
|
firestoreId: ModelId;
|
|
85
87
|
/** 한글 모델명 */
|
|
@@ -450,12 +452,63 @@ declare function getStorageBrandName(brandId: BrandId): StorageBrandName;
|
|
|
450
452
|
* // }
|
|
451
453
|
* ```
|
|
452
454
|
*/
|
|
453
|
-
declare function getBrandMapping(brandInput: string): BrandMapping;
|
|
455
|
+
declare function getBrandMapping$1(brandInput: string): BrandMapping;
|
|
454
456
|
/**
|
|
455
457
|
* 지원되는 모든 브랜드 ID 목록
|
|
456
458
|
*/
|
|
457
459
|
declare const SUPPORTED_BRANDS: BrandId[];
|
|
458
460
|
|
|
461
|
+
/**
|
|
462
|
+
* 동적 브랜드 매핑 시스템
|
|
463
|
+
*
|
|
464
|
+
* Firestore에서 실시간으로 브랜드 정보를 조회하여 캐싱
|
|
465
|
+
* 새로운 브랜드 추가 시 코드 수정 불필요
|
|
466
|
+
*/
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Firestore에서 브랜드 정보를 조회하여 캐시 초기화
|
|
470
|
+
*
|
|
471
|
+
* @param firestore - Firestore 인스턴스
|
|
472
|
+
*
|
|
473
|
+
* @example
|
|
474
|
+
* ```typescript
|
|
475
|
+
* import { initializeBrandMappings } from '@charzing/vehicle-utils';
|
|
476
|
+
* import { db } from './firebase';
|
|
477
|
+
*
|
|
478
|
+
* // 앱 시작 시 한 번만 호출
|
|
479
|
+
* await initializeBrandMappings(db);
|
|
480
|
+
* ```
|
|
481
|
+
*/
|
|
482
|
+
declare function initializeBrandMappings(firestore: Firestore): Promise<void>;
|
|
483
|
+
/**
|
|
484
|
+
* 브랜드 매핑 정보 조회
|
|
485
|
+
*
|
|
486
|
+
* @param brandInput - 브랜드명 (한글/영문/ID 모두 가능)
|
|
487
|
+
* @returns 브랜드 매핑 정보 또는 null
|
|
488
|
+
*
|
|
489
|
+
* @throws {Error} 초기화되지 않은 경우
|
|
490
|
+
*
|
|
491
|
+
* @example
|
|
492
|
+
* ```typescript
|
|
493
|
+
* getBrandMapping('현대'); // { firestoreId: 'hyundai', ... }
|
|
494
|
+
* getBrandMapping('HYUNDAI'); // { firestoreId: 'hyundai', ... }
|
|
495
|
+
* getBrandMapping('hyundai'); // { firestoreId: 'hyundai', ... }
|
|
496
|
+
* ```
|
|
497
|
+
*/
|
|
498
|
+
declare function getBrandMapping(brandInput: string): BrandMapping | null;
|
|
499
|
+
/**
|
|
500
|
+
* 초기화 상태 확인
|
|
501
|
+
*/
|
|
502
|
+
declare function isInitialized$1(): boolean;
|
|
503
|
+
/**
|
|
504
|
+
* 캐시 초기화 (테스트용)
|
|
505
|
+
*/
|
|
506
|
+
declare function resetCache$1(): void;
|
|
507
|
+
/**
|
|
508
|
+
* 캐시된 브랜드 수
|
|
509
|
+
*/
|
|
510
|
+
declare function getCachedBrandCount(): number;
|
|
511
|
+
|
|
459
512
|
/**
|
|
460
513
|
* 모델 매핑 유틸리티
|
|
461
514
|
*
|
|
@@ -559,156 +612,103 @@ declare function getModelSlug(modelInput: string): ModelSlug;
|
|
|
559
612
|
* // }
|
|
560
613
|
* ```
|
|
561
614
|
*/
|
|
562
|
-
declare function getModelMapping(modelInput: string, englishName?: string, koreanName?: string): ModelMapping;
|
|
615
|
+
declare function getModelMapping$1(modelInput: string, englishName?: string, koreanName?: string): ModelMapping$1;
|
|
563
616
|
|
|
564
617
|
/**
|
|
565
|
-
*
|
|
618
|
+
* 동적 모델 매핑 시스템
|
|
566
619
|
*
|
|
567
|
-
*
|
|
620
|
+
* Firestore에서 실시간으로 모델 정보를 조회하여 캐싱
|
|
621
|
+
* 새로운 모델 추가 시 코드 수정 불필요
|
|
568
622
|
*/
|
|
569
623
|
|
|
570
624
|
/**
|
|
571
|
-
*
|
|
572
|
-
*
|
|
573
|
-
* @param params - 차량 이미지 파라미터
|
|
574
|
-
* @param extension - 파일 확장자 (기본: 'png')
|
|
575
|
-
* @returns 이미지 파일명
|
|
576
|
-
*
|
|
577
|
-
* @example
|
|
578
|
-
* ```typescript
|
|
579
|
-
* generateImageFilename({
|
|
580
|
-
* brand: 'hyundai',
|
|
581
|
-
* model: 'ioniq-5',
|
|
582
|
-
* year: 2024
|
|
583
|
-
* });
|
|
584
|
-
* // 'hyundai__ioniq-5__2024.png'
|
|
585
|
-
*
|
|
586
|
-
* generateImageFilename({
|
|
587
|
-
* brand: 'mini',
|
|
588
|
-
* model: 'cooper',
|
|
589
|
-
* year: 2025,
|
|
590
|
-
* trim: 'jcw'
|
|
591
|
-
* });
|
|
592
|
-
* // 'mini__cooper__2025__jcw.png'
|
|
593
|
-
*
|
|
594
|
-
* generateImageFilename({
|
|
595
|
-
* brand: 'Tesla',
|
|
596
|
-
* model: 'Model 3',
|
|
597
|
-
* year: 2024,
|
|
598
|
-
* trim: 'Highland'
|
|
599
|
-
* }, 'webp');
|
|
600
|
-
* // 'tesla__model-3__2024__highland.webp'
|
|
601
|
-
* ```
|
|
625
|
+
* 모델 매핑 정보
|
|
602
626
|
*/
|
|
603
|
-
|
|
627
|
+
interface ModelMapping {
|
|
628
|
+
firestoreId: string;
|
|
629
|
+
koreanName: string;
|
|
630
|
+
englishName: string;
|
|
631
|
+
storageName: string;
|
|
632
|
+
brandId: string;
|
|
633
|
+
}
|
|
604
634
|
/**
|
|
605
|
-
*
|
|
635
|
+
* Firestore에서 모델 정보를 조회하여 캐시 초기화
|
|
606
636
|
*
|
|
607
|
-
* @param
|
|
608
|
-
* @
|
|
637
|
+
* @param firestore - Firestore 인스턴스
|
|
638
|
+
* @param brandIds - 초기화할 브랜드 ID 목록 (생략 시 모든 브랜드)
|
|
609
639
|
*
|
|
610
640
|
* @example
|
|
611
641
|
* ```typescript
|
|
612
|
-
*
|
|
613
|
-
*
|
|
642
|
+
* // 모든 브랜드의 모델 초기화
|
|
643
|
+
* await initializeModelMappings(db);
|
|
614
644
|
*
|
|
615
|
-
*
|
|
616
|
-
*
|
|
645
|
+
* // 특정 브랜드만 초기화
|
|
646
|
+
* await initializeModelMappings(db, ['hyundai', 'kia']);
|
|
617
647
|
* ```
|
|
618
648
|
*/
|
|
619
|
-
declare function
|
|
649
|
+
declare function initializeModelMappings(firestore: Firestore, brandIds?: string[]): Promise<void>;
|
|
620
650
|
/**
|
|
621
|
-
*
|
|
651
|
+
* 모델 매핑 정보 조회
|
|
652
|
+
*
|
|
653
|
+
* @param brandId - 브랜드 ID
|
|
654
|
+
* @param modelInput - 모델명 (한글/영문/ID 모두 가능)
|
|
655
|
+
* @returns 모델 매핑 정보 또는 null
|
|
622
656
|
*
|
|
623
|
-
* @
|
|
624
|
-
* @returns 유효 여부
|
|
657
|
+
* @throws {Error} 초기화되지 않은 경우
|
|
625
658
|
*
|
|
626
659
|
* @example
|
|
627
660
|
* ```typescript
|
|
628
|
-
*
|
|
629
|
-
*
|
|
661
|
+
* getModelMapping('hyundai', '아이오닉 5'); // { firestoreId: 'ioniq-5', ... }
|
|
662
|
+
* getModelMapping('hyundai', 'IONIQ-5'); // { firestoreId: 'ioniq-5', ... }
|
|
663
|
+
* getModelMapping('hyundai', 'ioniq-5'); // { firestoreId: 'ioniq-5', ... }
|
|
630
664
|
* ```
|
|
631
665
|
*/
|
|
632
|
-
declare function
|
|
633
|
-
|
|
666
|
+
declare function getModelMapping(brandId: string, modelInput: string): ModelMapping | null;
|
|
634
667
|
/**
|
|
635
|
-
*
|
|
668
|
+
* 브랜드의 모든 모델 조회
|
|
636
669
|
*
|
|
637
|
-
*
|
|
670
|
+
* @param brandId - 브랜드 ID
|
|
671
|
+
* @returns 모델 매핑 배열
|
|
672
|
+
*/
|
|
673
|
+
declare function getBrandModels(brandId: string): ModelMapping[];
|
|
674
|
+
/**
|
|
675
|
+
* 초기화 상태 확인
|
|
676
|
+
*/
|
|
677
|
+
declare function isInitialized(): boolean;
|
|
678
|
+
/**
|
|
679
|
+
* 캐시 초기화 (테스트용)
|
|
680
|
+
*/
|
|
681
|
+
declare function resetCache(): void;
|
|
682
|
+
/**
|
|
683
|
+
* 캐시된 모델 수
|
|
638
684
|
*/
|
|
685
|
+
declare function getCachedModelCount(): number;
|
|
639
686
|
|
|
640
687
|
/**
|
|
641
|
-
* 차량 이미지 URL 생성
|
|
688
|
+
* Firebase Storage 차량 이미지 URL/경로 생성 유틸리티
|
|
642
689
|
*
|
|
643
|
-
*
|
|
644
|
-
*
|
|
645
|
-
* @returns 이미지 다운로드 URL
|
|
646
|
-
*
|
|
647
|
-
* @example
|
|
648
|
-
* ```typescript
|
|
649
|
-
* generateVehicleImageUrl({
|
|
650
|
-
* brand: 'hyundai',
|
|
651
|
-
* model: 'ioniq-5',
|
|
652
|
-
* year: 2024
|
|
653
|
-
* });
|
|
654
|
-
* // 'https://firebasestorage.googleapis.com/v0/b/charzing-d1600.firebasestorage.app/o/
|
|
655
|
-
* // vehicle-images%2FHYUNDAI%2FIONIQ-5%2F2024%2Fhyundai__ioniq-5__2024.png?alt=media'
|
|
656
|
-
*
|
|
657
|
-
* generateVehicleImageUrl({
|
|
658
|
-
* brand: 'mini',
|
|
659
|
-
* model: 'cooper',
|
|
660
|
-
* year: 2025,
|
|
661
|
-
* trim: 'jcw'
|
|
662
|
-
* });
|
|
663
|
-
* // 'https://firebasestorage.googleapis.com/v0/b/charzing-d1600.firebasestorage.app/o/
|
|
664
|
-
* // vehicle-images%2FMINI%2FCOOPER%2F2025%2Fmini__cooper__2025__jcw.png?alt=media'
|
|
665
|
-
* ```
|
|
690
|
+
* ⚠️ 주의: 이 파일은 순수 함수만 포함합니다.
|
|
691
|
+
* Firebase SDK나 업로드 로직은 포함하지 않습니다.
|
|
666
692
|
*/
|
|
667
|
-
|
|
693
|
+
interface ImagePathParams {
|
|
694
|
+
brandId: string;
|
|
695
|
+
modelId: string;
|
|
696
|
+
year: number;
|
|
697
|
+
trim?: string;
|
|
698
|
+
}
|
|
668
699
|
/**
|
|
669
|
-
* 차량 이미지
|
|
670
|
-
*
|
|
671
|
-
* @param params - 차량 이미지 파라미터
|
|
672
|
-
* @param options - URL 생성 옵션
|
|
673
|
-
* @returns 이미지 경로 정보
|
|
674
|
-
*
|
|
675
|
-
* @example
|
|
676
|
-
* ```typescript
|
|
677
|
-
* getVehicleImagePath({
|
|
678
|
-
* brand: 'hyundai',
|
|
679
|
-
* model: 'ioniq-5',
|
|
680
|
-
* year: 2024
|
|
681
|
-
* });
|
|
682
|
-
* // {
|
|
683
|
-
* // fullPath: 'vehicle-images/HYUNDAI/IONIQ-5/2024/hyundai__ioniq-5__2024.png',
|
|
684
|
-
* // directory: 'vehicle-images/HYUNDAI/IONIQ-5/2024',
|
|
685
|
-
* // filename: 'hyundai__ioniq-5__2024.png',
|
|
686
|
-
* // downloadUrl: 'https://...'
|
|
687
|
-
* // }
|
|
688
|
-
* ```
|
|
700
|
+
* 차량 이미지 파일명 생성
|
|
701
|
+
* 예: kia__niro-ev__2022__standard.png
|
|
689
702
|
*/
|
|
690
|
-
declare function
|
|
703
|
+
declare function generateImageFilename(params: ImagePathParams): string;
|
|
691
704
|
/**
|
|
692
|
-
*
|
|
693
|
-
*
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
*
|
|
698
|
-
* @example
|
|
699
|
-
* ```typescript
|
|
700
|
-
* generateFallbackUrls({
|
|
701
|
-
* brand: 'mini',
|
|
702
|
-
* model: 'cooper',
|
|
703
|
-
* year: 2025,
|
|
704
|
-
* trim: 'jcw'
|
|
705
|
-
* });
|
|
706
|
-
* // [
|
|
707
|
-
* // 'https://.../mini__cooper__2025__jcw.png', // 트림 이미지
|
|
708
|
-
* // 'https://.../mini__cooper__2025.png', // 기본 모델 이미지
|
|
709
|
-
* // ]
|
|
710
|
-
* ```
|
|
705
|
+
* Storage 경로 생성 (업로드용)
|
|
706
|
+
* 예: vehicle-images/KIA/NIRO-EV/2022/kia__niro-ev__2022__standard.png
|
|
707
|
+
*/
|
|
708
|
+
declare function generateStoragePath(params: ImagePathParams): string;
|
|
709
|
+
/**
|
|
710
|
+
* 완전한 Firebase Storage URL 생성 (읽기용)
|
|
711
711
|
*/
|
|
712
|
-
declare function
|
|
712
|
+
declare function generateVehicleImageUrl(params: ImagePathParams): string;
|
|
713
713
|
|
|
714
|
-
export { type BatteryInfo, type Brand, type BrandId, type BrandMapping, type CompletedVehicle, type DriveType, type FirebaseStorageConfig, type ImageFallbackOptions, type ImageFallbackStrategy, type ImageFilename, type ImagePath, type ImageUrlOptions, type Model, type ModelId, type ModelMapping, type ModelSlug, SUPPORTED_BRANDS, type StorageBrandName, type TrimId, type TrimMapping, type TrimSlug, type UserVehicle, type VehicleDetails, type VehicleImageParams, type VehicleModel, type VehicleTrim, type VehicleVariant, convertKoreanToEnglish, findBestModelMatch,
|
|
714
|
+
export { type BatteryInfo, type Brand, type BrandId, type BrandMapping, type CompletedVehicle, type DriveType, type FirebaseStorageConfig, type ImageFallbackOptions, type ImageFallbackStrategy, type ImageFilename, type ImagePath, type ImagePathParams, type ImageUrlOptions, type Model, type ModelId, type ModelMapping$1 as ModelMapping, type ModelSlug, SUPPORTED_BRANDS, type StorageBrandName, type TrimId, type TrimMapping, type TrimSlug, type UserVehicle, type VehicleDetails, type VehicleImageParams, type VehicleModel, type VehicleTrim, type VehicleVariant, convertKoreanToEnglish, findBestModelMatch, generateImageFilename, generateStoragePath, generateVehicleImageUrl, getBrandMapping$1 as getBrandMapping, getBrandModels, getBrandNameEnglish, getBrandNameKorean, getCachedBrandCount, getCachedModelCount, getBrandMapping as getDynamicBrandMapping, getModelMapping as getDynamicModelMapping, getModelMapping$1 as getModelMapping, getModelSlug, getStorageBrandName, initializeBrandMappings, initializeModelMappings, isInitialized$1 as isBrandMappingInitialized, isInitialized as isModelMappingInitialized, normalizeBrandId, normalizeModelId, resetCache$1 as resetBrandCache, resetCache as resetModelCache, toSlug };
|
package/dist/esm/index.mjs
CHANGED
|
@@ -115,6 +115,76 @@ var SUPPORTED_BRANDS = [
|
|
|
115
115
|
"porsche"
|
|
116
116
|
];
|
|
117
117
|
|
|
118
|
+
// src/brand/dynamic-mapping.ts
|
|
119
|
+
import { collection, getDocs } from "firebase/firestore";
|
|
120
|
+
var brandCache = /* @__PURE__ */ new Map();
|
|
121
|
+
var initialized = false;
|
|
122
|
+
async function initializeBrandMappings(firestore) {
|
|
123
|
+
if (initialized) {
|
|
124
|
+
console.log("[Brand Mapping] Already initialized");
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
console.log("[Brand Mapping] Initializing from Firestore...");
|
|
128
|
+
try {
|
|
129
|
+
const vehiclesRef = collection(firestore, "vehicles");
|
|
130
|
+
const snapshot = await getDocs(vehiclesRef);
|
|
131
|
+
snapshot.docs.forEach((doc) => {
|
|
132
|
+
const data = doc.data();
|
|
133
|
+
let storageName = data.englishName?.toUpperCase() || doc.id.toUpperCase();
|
|
134
|
+
if (doc.id === "mercedes-benz" || doc.id === "mercedes-maybach") {
|
|
135
|
+
storageName = "BENZ";
|
|
136
|
+
}
|
|
137
|
+
const mapping = {
|
|
138
|
+
firestoreId: doc.id,
|
|
139
|
+
koreanName: data.name || doc.id,
|
|
140
|
+
englishName: data.englishName || doc.id.toUpperCase(),
|
|
141
|
+
storageName
|
|
142
|
+
};
|
|
143
|
+
brandCache.set(doc.id, mapping);
|
|
144
|
+
brandCache.set(doc.id.toUpperCase(), mapping);
|
|
145
|
+
brandCache.set(doc.id.toLowerCase(), mapping);
|
|
146
|
+
brandCache.set(data.name, mapping);
|
|
147
|
+
brandCache.set(data.englishName, mapping);
|
|
148
|
+
brandCache.set(data.englishName?.toUpperCase(), mapping);
|
|
149
|
+
brandCache.set(data.englishName?.toLowerCase(), mapping);
|
|
150
|
+
});
|
|
151
|
+
initialized = true;
|
|
152
|
+
console.log(`[Brand Mapping] \u2705 Initialized with ${snapshot.docs.length} brands`);
|
|
153
|
+
} catch (error) {
|
|
154
|
+
console.error("[Brand Mapping] \u274C Initialization failed:", error);
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function getBrandMapping2(brandInput) {
|
|
159
|
+
if (!initialized) {
|
|
160
|
+
throw new Error(
|
|
161
|
+
"[Brand Mapping] Not initialized. Call initializeBrandMappings() first."
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
let mapping = brandCache.get(brandInput);
|
|
165
|
+
if (mapping) return mapping;
|
|
166
|
+
mapping = brandCache.get(brandInput.toLowerCase());
|
|
167
|
+
if (mapping) return mapping;
|
|
168
|
+
mapping = brandCache.get(brandInput.toUpperCase());
|
|
169
|
+
if (mapping) return mapping;
|
|
170
|
+
const trimmed = brandInput.trim();
|
|
171
|
+
mapping = brandCache.get(trimmed);
|
|
172
|
+
if (mapping) return mapping;
|
|
173
|
+
console.warn(`[Brand Mapping] Unknown brand: "${brandInput}"`);
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
function isInitialized() {
|
|
177
|
+
return initialized;
|
|
178
|
+
}
|
|
179
|
+
function resetCache() {
|
|
180
|
+
brandCache.clear();
|
|
181
|
+
initialized = false;
|
|
182
|
+
console.log("[Brand Mapping] Cache reset");
|
|
183
|
+
}
|
|
184
|
+
function getCachedBrandCount() {
|
|
185
|
+
return brandCache.size;
|
|
186
|
+
}
|
|
187
|
+
|
|
118
188
|
// src/model/mapping.ts
|
|
119
189
|
var KOREAN_TO_ENGLISH_MODEL = {
|
|
120
190
|
// MINI
|
|
@@ -200,114 +270,188 @@ function getModelMapping(modelInput, englishName, koreanName) {
|
|
|
200
270
|
};
|
|
201
271
|
}
|
|
202
272
|
|
|
203
|
-
// src/
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
273
|
+
// src/model/dynamic-mapping.ts
|
|
274
|
+
import { collection as collection2, getDocs as getDocs2 } from "firebase/firestore";
|
|
275
|
+
var modelCache = /* @__PURE__ */ new Map();
|
|
276
|
+
var brandModelsCache = /* @__PURE__ */ new Map();
|
|
277
|
+
var initialized2 = false;
|
|
278
|
+
async function initializeModelMappings(firestore, brandIds) {
|
|
279
|
+
console.log("[Model Mapping] Initializing from Firestore...");
|
|
280
|
+
try {
|
|
281
|
+
const vehiclesRef = collection2(firestore, "vehicles");
|
|
282
|
+
const brandsSnapshot = await getDocs2(vehiclesRef);
|
|
283
|
+
let processedBrands = 0;
|
|
284
|
+
let processedModels = 0;
|
|
285
|
+
for (const brandDoc of brandsSnapshot.docs) {
|
|
286
|
+
const brandId = brandDoc.id;
|
|
287
|
+
if (brandIds && !brandIds.includes(brandId)) {
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
const modelsRef = collection2(firestore, "vehicles", brandId, "models");
|
|
291
|
+
const modelsSnapshot = await getDocs2(modelsRef);
|
|
292
|
+
const brandModels = [];
|
|
293
|
+
modelsSnapshot.docs.forEach((modelDoc) => {
|
|
294
|
+
const data = modelDoc.data();
|
|
295
|
+
const mapping = {
|
|
296
|
+
firestoreId: modelDoc.id,
|
|
297
|
+
koreanName: data.name || modelDoc.id,
|
|
298
|
+
englishName: data.englishName || modelDoc.id.toUpperCase(),
|
|
299
|
+
storageName: data.englishName?.toUpperCase() || modelDoc.id.toUpperCase(),
|
|
300
|
+
brandId
|
|
301
|
+
};
|
|
302
|
+
brandModels.push(mapping);
|
|
303
|
+
processedModels++;
|
|
304
|
+
const keys = [
|
|
305
|
+
`${brandId}:${modelDoc.id}`,
|
|
306
|
+
// "hyundai:ioniq-5"
|
|
307
|
+
`${brandId}:${data.name}`,
|
|
308
|
+
// "hyundai:아이오닉 5"
|
|
309
|
+
`${brandId}:${data.englishName}`,
|
|
310
|
+
// "hyundai:IONIQ-5"
|
|
311
|
+
`${brandId}:${data.englishName?.toLowerCase()}`,
|
|
312
|
+
// "hyundai:ioniq-5"
|
|
313
|
+
`${brandId}:${modelDoc.id.toUpperCase()}`
|
|
314
|
+
// "hyundai:IONIQ-5"
|
|
315
|
+
];
|
|
316
|
+
keys.forEach((key) => {
|
|
317
|
+
if (key && !key.includes("undefined")) {
|
|
318
|
+
modelCache.set(key, mapping);
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
brandModelsCache.set(brandId, brandModels);
|
|
323
|
+
processedBrands++;
|
|
324
|
+
}
|
|
325
|
+
initialized2 = true;
|
|
326
|
+
console.log(
|
|
327
|
+
`[Model Mapping] \u2705 Initialized: ${processedBrands} brands, ${processedModels} models`
|
|
328
|
+
);
|
|
329
|
+
} catch (error) {
|
|
330
|
+
console.error("[Model Mapping] \u274C Initialization failed:", error);
|
|
331
|
+
throw error;
|
|
213
332
|
}
|
|
214
333
|
}
|
|
215
|
-
function
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
334
|
+
function getModelMapping2(brandId, modelInput) {
|
|
335
|
+
if (!initialized2) {
|
|
336
|
+
throw new Error(
|
|
337
|
+
"[Model Mapping] Not initialized. Call initializeModelMappings() first."
|
|
338
|
+
);
|
|
220
339
|
}
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
340
|
+
const keys = [
|
|
341
|
+
`${brandId}:${modelInput}`,
|
|
342
|
+
`${brandId}:${modelInput.toLowerCase()}`,
|
|
343
|
+
`${brandId}:${modelInput.toUpperCase()}`,
|
|
344
|
+
`${brandId}:${modelInput.trim()}`
|
|
345
|
+
];
|
|
346
|
+
for (const key of keys) {
|
|
347
|
+
const mapping = modelCache.get(key);
|
|
348
|
+
if (mapping) return mapping;
|
|
226
349
|
}
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
model
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
350
|
+
const brandModels = brandModelsCache.get(brandId);
|
|
351
|
+
if (brandModels) {
|
|
352
|
+
const normalizedInput = modelInput.toLowerCase().replace(/[\s\-]/g, "");
|
|
353
|
+
for (const model of brandModels) {
|
|
354
|
+
const normalizedModel = model.englishName.toLowerCase().replace(/[\s\-]/g, "");
|
|
355
|
+
if (normalizedModel === normalizedInput) {
|
|
356
|
+
return model;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
console.warn(`[Model Mapping] Unknown model: ${brandId}:${modelInput}`);
|
|
361
|
+
return null;
|
|
234
362
|
}
|
|
235
|
-
function
|
|
236
|
-
|
|
237
|
-
|
|
363
|
+
function getBrandModels(brandId) {
|
|
364
|
+
if (!initialized2) {
|
|
365
|
+
throw new Error(
|
|
366
|
+
"[Model Mapping] Not initialized. Call initializeModelMappings() first."
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
return brandModelsCache.get(brandId) || [];
|
|
238
370
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
var DEFAULT_BUCKET = "charzing-d1600.firebasestorage.app";
|
|
242
|
-
var DEFAULT_BASE_PATH = "vehicle-images";
|
|
243
|
-
function encodeStoragePath(path) {
|
|
244
|
-
return path.split("/").map((segment) => encodeURIComponent(segment)).join("%2F");
|
|
371
|
+
function isInitialized2() {
|
|
372
|
+
return initialized2;
|
|
245
373
|
}
|
|
246
|
-
function
|
|
247
|
-
|
|
248
|
-
|
|
374
|
+
function resetCache2() {
|
|
375
|
+
modelCache.clear();
|
|
376
|
+
brandModelsCache.clear();
|
|
377
|
+
initialized2 = false;
|
|
378
|
+
console.log("[Model Mapping] Cache reset");
|
|
249
379
|
}
|
|
250
|
-
function
|
|
251
|
-
|
|
252
|
-
bucket = DEFAULT_BUCKET,
|
|
253
|
-
extension = "png"
|
|
254
|
-
} = options;
|
|
255
|
-
const storageBrand = getStorageBrandName(params.brand);
|
|
256
|
-
const storageModel = toSlug(params.model).toUpperCase();
|
|
257
|
-
const year = params.year;
|
|
258
|
-
const filename = generateImageFilename(params, extension);
|
|
259
|
-
const fullPath = `${DEFAULT_BASE_PATH}/${storageBrand}/${storageModel}/${year}/${filename}`;
|
|
260
|
-
return createDownloadUrl(bucket, fullPath);
|
|
380
|
+
function getCachedModelCount() {
|
|
381
|
+
return modelCache.size;
|
|
261
382
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
directory,
|
|
277
|
-
filename,
|
|
278
|
-
downloadUrl
|
|
383
|
+
|
|
384
|
+
// src/image/url-generator.ts
|
|
385
|
+
var FIREBASE_STORAGE_BASE = "https://firebasestorage.googleapis.com/v0/b/charzing-d1600.firebasestorage.app/o";
|
|
386
|
+
function normalizeStorageBrand(firestoreBrandId) {
|
|
387
|
+
const storageMapping = {
|
|
388
|
+
"mercedes-benz": "BENZ",
|
|
389
|
+
"mercedes-maybach": "BENZ",
|
|
390
|
+
"hyundai": "HYUNDAI",
|
|
391
|
+
"kia": "KIA",
|
|
392
|
+
"tesla": "TESLA",
|
|
393
|
+
"BMW": "BMW",
|
|
394
|
+
"MINI": "MINI",
|
|
395
|
+
"audi": "AUDI",
|
|
396
|
+
"PORSCHE": "PORSCHE"
|
|
279
397
|
};
|
|
398
|
+
return storageMapping[firestoreBrandId] || firestoreBrandId.toUpperCase();
|
|
280
399
|
}
|
|
281
|
-
function
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
urls.push(generateVehicleImageUrl(params, options));
|
|
400
|
+
function normalizeStorageModel(firestoreModelId) {
|
|
401
|
+
if (firestoreModelId === firestoreModelId.toUpperCase()) {
|
|
402
|
+
return firestoreModelId;
|
|
285
403
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
404
|
+
return firestoreModelId.toUpperCase();
|
|
405
|
+
}
|
|
406
|
+
function normalizeStorageTrim(trim) {
|
|
407
|
+
if (!trim) return "";
|
|
408
|
+
return trim.toLowerCase().replace(/[\s\-]/g, "_").replace(/[^a-z0-9_]/g, "");
|
|
409
|
+
}
|
|
410
|
+
function generateImageFilename(params) {
|
|
411
|
+
const { brandId, modelId, year, trim } = params;
|
|
412
|
+
const brandLower = brandId.toLowerCase();
|
|
413
|
+
const modelLower = modelId.toLowerCase().replace(/[\s]/g, "-");
|
|
414
|
+
const trimPart = trim ? `__${normalizeStorageTrim(trim)}` : "";
|
|
415
|
+
return `${brandLower}__${modelLower}__${year}${trimPart}.png`;
|
|
416
|
+
}
|
|
417
|
+
function generateStoragePath(params) {
|
|
418
|
+
const { brandId, modelId, year } = params;
|
|
419
|
+
const storageBrand = normalizeStorageBrand(brandId);
|
|
420
|
+
const storageModel = normalizeStorageModel(modelId);
|
|
421
|
+
const filename = generateImageFilename(params);
|
|
422
|
+
return `vehicle-images/${storageBrand}/${storageModel}/${year}/${filename}`;
|
|
423
|
+
}
|
|
424
|
+
function generateVehicleImageUrl(params) {
|
|
425
|
+
const path = generateStoragePath(params);
|
|
426
|
+
const encodedPath = encodeURIComponent(path);
|
|
427
|
+
return `${FIREBASE_STORAGE_BASE}/${encodedPath}?alt=media`;
|
|
292
428
|
}
|
|
293
429
|
export {
|
|
294
430
|
SUPPORTED_BRANDS,
|
|
295
431
|
convertKoreanToEnglish,
|
|
296
432
|
findBestModelMatch,
|
|
297
|
-
generateFallbackUrls,
|
|
298
433
|
generateImageFilename,
|
|
434
|
+
generateStoragePath,
|
|
299
435
|
generateVehicleImageUrl,
|
|
300
436
|
getBrandMapping,
|
|
437
|
+
getBrandModels,
|
|
301
438
|
getBrandNameEnglish,
|
|
302
439
|
getBrandNameKorean,
|
|
440
|
+
getCachedBrandCount,
|
|
441
|
+
getCachedModelCount,
|
|
442
|
+
getBrandMapping2 as getDynamicBrandMapping,
|
|
443
|
+
getModelMapping2 as getDynamicModelMapping,
|
|
303
444
|
getModelMapping,
|
|
304
445
|
getModelSlug,
|
|
305
446
|
getStorageBrandName,
|
|
306
|
-
|
|
307
|
-
|
|
447
|
+
initializeBrandMappings,
|
|
448
|
+
initializeModelMappings,
|
|
449
|
+
isInitialized as isBrandMappingInitialized,
|
|
450
|
+
isInitialized2 as isModelMappingInitialized,
|
|
308
451
|
normalizeBrandId,
|
|
309
452
|
normalizeModelId,
|
|
310
|
-
|
|
453
|
+
resetCache as resetBrandCache,
|
|
454
|
+
resetCache2 as resetModelCache,
|
|
311
455
|
toSlug
|
|
312
456
|
};
|
|
313
457
|
//# sourceMappingURL=index.mjs.map
|