@mapslibvn/core 0.4.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/LICENSE +21 -0
- package/README.md +37 -0
- package/THIRD_PARTY_NOTICES.md +393 -0
- package/dist/index.d.ts +424 -0
- package/dist/index.js +830 -0
- package/package.json +32 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
interface AttributionLink {
|
|
2
|
+
text: string;
|
|
3
|
+
href: string;
|
|
4
|
+
license?: string;
|
|
5
|
+
}
|
|
6
|
+
declare const ATTRIBUTION_LINKS: readonly AttributionLink[];
|
|
7
|
+
declare function attributionText(): string;
|
|
8
|
+
declare function attributionHtml(): string;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Nguồn POI và profile archive (spec 07/09 mục 4). Đây là nguồn sự thật duy nhất cho API, SDK và
|
|
12
|
+
* pipeline: thêm profile mới = thêm một dòng vào POI_SOURCE_PROFILES + một lần export-tiles.
|
|
13
|
+
*/
|
|
14
|
+
declare const POI_SOURCES: readonly ["osm", "overture", "fsq"];
|
|
15
|
+
type PoiSource = (typeof POI_SOURCES)[number];
|
|
16
|
+
declare const POI_SOURCE_PROFILES: {
|
|
17
|
+
readonly all: readonly ["osm", "overture", "fsq"];
|
|
18
|
+
readonly osm: readonly ["osm"];
|
|
19
|
+
readonly 'osm-fsq': readonly ["osm", "fsq"];
|
|
20
|
+
readonly 'overture-fsq': readonly ["overture", "fsq"];
|
|
21
|
+
readonly overture: readonly ["overture"];
|
|
22
|
+
readonly fsq: readonly ["fsq"];
|
|
23
|
+
};
|
|
24
|
+
type PoiSourceProfile = keyof typeof POI_SOURCE_PROFILES;
|
|
25
|
+
/**
|
|
26
|
+
* Mặc định ở mọi bề mặt (REST và SDK): cả ba nguồn, tức đúng hành vi trước khi có tuỳ chọn này.
|
|
27
|
+
* Đo 07/09/2026 cho thấy OSM chỉ là nguồn chính của 7 % POI, nên mặc định `osm` sẽ làm bản đồ mất
|
|
28
|
+
* ~93 % dữ liệu (`docs/evidence/poi-sources/do-truoc-primary-source.md`).
|
|
29
|
+
*/
|
|
30
|
+
declare const DEFAULT_POI_SOURCES: readonly PoiSource[];
|
|
31
|
+
/** Bỏ trùng, sắp theo thứ tự POI_SOURCES. Rỗng hoặc có giá trị lạ → null. */
|
|
32
|
+
declare function normalizePoiSources(list: readonly string[]): PoiSource[] | null;
|
|
33
|
+
/**
|
|
34
|
+
* Chuỗi `sources=` của REST và thuộc tính `sources` của web component: phân cách dấu phẩy,
|
|
35
|
+
* `all` là bí danh cả ba; undefined/rỗng → mặc định; giá trị lạ → null (caller quyết định lỗi).
|
|
36
|
+
*/
|
|
37
|
+
declare function parsePoiSourcesCsv(raw: string | undefined | null): PoiSource[] | null;
|
|
38
|
+
/** Khoá ổn định cho cache key và query string. */
|
|
39
|
+
declare function poiSourcesKey(sources: readonly PoiSource[]): string;
|
|
40
|
+
/** Tập nguồn → profile có archive; null nếu chưa build tổ hợp đó. */
|
|
41
|
+
declare function profileForSources(sources: readonly PoiSource[]): PoiSourceProfile | null;
|
|
42
|
+
/**
|
|
43
|
+
* Mệnh đề lọc dùng chung cho export-tiles và Places API; bảng `poi` phải có alias `p`.
|
|
44
|
+
* `arrayExpr` là biểu thức text[]: `$1::text[]` (postgres.js) hoặc `ARRAY['osm']::text[]` (pipeline).
|
|
45
|
+
* POI người dùng tạo có primary_source NULL và luôn được giữ.
|
|
46
|
+
*/
|
|
47
|
+
declare function poiSourceClause(arrayExpr: string): string;
|
|
48
|
+
|
|
49
|
+
/** Kiểu dữ liệu Places API (spec 6.1) dùng chung cho Worker và SDK. */
|
|
50
|
+
interface PlaceCategory {
|
|
51
|
+
code: string;
|
|
52
|
+
group: string;
|
|
53
|
+
name_vi: string;
|
|
54
|
+
name_en: string;
|
|
55
|
+
}
|
|
56
|
+
interface PlaceAddress {
|
|
57
|
+
housenumber?: string;
|
|
58
|
+
street?: string;
|
|
59
|
+
ward?: string;
|
|
60
|
+
province?: string;
|
|
61
|
+
text?: string;
|
|
62
|
+
}
|
|
63
|
+
interface Place {
|
|
64
|
+
id: string;
|
|
65
|
+
name: string;
|
|
66
|
+
category: PlaceCategory | null;
|
|
67
|
+
lat: number;
|
|
68
|
+
lng: number;
|
|
69
|
+
address: PlaceAddress;
|
|
70
|
+
contact?: Record<string, unknown> | null;
|
|
71
|
+
hours?: unknown;
|
|
72
|
+
quality_score: number | null;
|
|
73
|
+
status: 'active' | 'closed' | 'pending' | 'rejected';
|
|
74
|
+
updated_at: string;
|
|
75
|
+
}
|
|
76
|
+
interface PlaceSource {
|
|
77
|
+
source: 'osm' | 'overture' | 'fsq';
|
|
78
|
+
source_id: string;
|
|
79
|
+
role: 'primary' | 'secondary';
|
|
80
|
+
}
|
|
81
|
+
interface PlaceDetails extends Place {
|
|
82
|
+
sources: PlaceSource[];
|
|
83
|
+
attribution: {
|
|
84
|
+
text: string;
|
|
85
|
+
html: string;
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
type GeocodePrecision = 'rooftop' | 'alley' | 'interpolated' | 'street' | 'ward' | 'district' | 'province';
|
|
89
|
+
type AutocompleteType = 'poi' | 'street' | 'address' | 'area';
|
|
90
|
+
interface AutocompleteItem {
|
|
91
|
+
type: AutocompleteType;
|
|
92
|
+
id?: string;
|
|
93
|
+
name: string;
|
|
94
|
+
secondary: string;
|
|
95
|
+
lat: number;
|
|
96
|
+
lng: number;
|
|
97
|
+
precision?: GeocodePrecision;
|
|
98
|
+
score: number;
|
|
99
|
+
bbox?: [number, number, number, number];
|
|
100
|
+
/** Tên thay thế (OSM `alt_name`/`old_name`) đã khớp truy vấn, ví dụ "Công Lý" (spec 6.3). */
|
|
101
|
+
matched_alt?: string;
|
|
102
|
+
}
|
|
103
|
+
interface GeocodeMatched {
|
|
104
|
+
housenumber?: string;
|
|
105
|
+
street?: string;
|
|
106
|
+
ward?: string;
|
|
107
|
+
province?: string;
|
|
108
|
+
former?: {
|
|
109
|
+
ward?: string;
|
|
110
|
+
district?: string;
|
|
111
|
+
province?: string;
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
interface GeocodeItem {
|
|
115
|
+
lat: number;
|
|
116
|
+
lng: number;
|
|
117
|
+
precision: GeocodePrecision;
|
|
118
|
+
confidence: number;
|
|
119
|
+
matched: GeocodeMatched;
|
|
120
|
+
display_name: string;
|
|
121
|
+
bbox?: [number, number, number, number];
|
|
122
|
+
}
|
|
123
|
+
interface ReverseAddress {
|
|
124
|
+
approx_housenumber?: string;
|
|
125
|
+
street?: string;
|
|
126
|
+
ward?: string;
|
|
127
|
+
province?: string;
|
|
128
|
+
display_name: string;
|
|
129
|
+
}
|
|
130
|
+
interface ReverseResponse {
|
|
131
|
+
address: ReverseAddress;
|
|
132
|
+
nearest_poi: Place | null;
|
|
133
|
+
}
|
|
134
|
+
type EditKind = 'create' | 'update' | 'close' | 'reopen' | 'report';
|
|
135
|
+
/** Trường được phép sửa/khai khi đóng góp (spec 6.1 + 6.5). */
|
|
136
|
+
interface EditChanges {
|
|
137
|
+
name?: string;
|
|
138
|
+
lat?: number;
|
|
139
|
+
lng?: number;
|
|
140
|
+
category?: string;
|
|
141
|
+
housenumber?: string;
|
|
142
|
+
street?: string;
|
|
143
|
+
ward?: string;
|
|
144
|
+
province?: string;
|
|
145
|
+
address_text?: string;
|
|
146
|
+
contact?: {
|
|
147
|
+
phone?: string[];
|
|
148
|
+
website?: string[];
|
|
149
|
+
facebook?: string;
|
|
150
|
+
};
|
|
151
|
+
/** Chuỗi opening_hours OSM hoặc {osm: chuỗi}. */
|
|
152
|
+
hours?: string | {
|
|
153
|
+
osm: string;
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
interface SuggestEditRequest {
|
|
157
|
+
/** Bắt buộc trừ kind='create'. */
|
|
158
|
+
poi_id?: string;
|
|
159
|
+
kind: EditKind;
|
|
160
|
+
changes?: EditChanges;
|
|
161
|
+
photo_url?: string;
|
|
162
|
+
note?: string;
|
|
163
|
+
/** Chuỗi ổn định theo người dùng cuối do app nhúng cấp — server chỉ lưu bản băm. */
|
|
164
|
+
end_user_token: string;
|
|
165
|
+
}
|
|
166
|
+
interface SuggestEditResponse {
|
|
167
|
+
edit_id: number;
|
|
168
|
+
status: 'pending' | 'auto_approved';
|
|
169
|
+
/** POI đích; với kind='create' là id POI mới (pending cho tới khi được duyệt). */
|
|
170
|
+
poi_id: string | null;
|
|
171
|
+
}
|
|
172
|
+
/** POI đọc từ tile lớp `poi` khi người dùng bấm — SDK web và React Native dùng chung. */
|
|
173
|
+
interface PoiFeature {
|
|
174
|
+
id: string;
|
|
175
|
+
name: string;
|
|
176
|
+
category: string;
|
|
177
|
+
group: string;
|
|
178
|
+
lngLat: [number, number];
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
type Theme = 'light' | 'dark';
|
|
182
|
+
interface ClientOptions {
|
|
183
|
+
/** Khoá API dạng mlv_live_… */
|
|
184
|
+
apiKey: string;
|
|
185
|
+
/** Gốc API, ví dụ https://maps-api.example.com */
|
|
186
|
+
baseUrl: string;
|
|
187
|
+
/** Cho phép tiêm fetch cho test hoặc môi trường không có global fetch. */
|
|
188
|
+
fetch?: typeof globalThis.fetch;
|
|
189
|
+
/**
|
|
190
|
+
* Header thêm cho mọi request, ví dụ `X-Bundle-Id` cho khoá `mobile` (spec 6.4).
|
|
191
|
+
* Không ghi đè được `X-Api-Key`.
|
|
192
|
+
*/
|
|
193
|
+
headers?: Record<string, string>;
|
|
194
|
+
/**
|
|
195
|
+
* Tập nguồn POI cho bản đồ và Places API (spec 07/09). Mặc định cả ba nguồn.
|
|
196
|
+
* Áp cho autocomplete/search/nearby/reverse và `styleUrl`; `getPlace`/`geocode` không lọc.
|
|
197
|
+
*/
|
|
198
|
+
poiSources?: readonly PoiSource[];
|
|
199
|
+
}
|
|
200
|
+
interface AttributionResponse {
|
|
201
|
+
text: string;
|
|
202
|
+
html: string;
|
|
203
|
+
links: {
|
|
204
|
+
text: string;
|
|
205
|
+
href: string;
|
|
206
|
+
license?: string;
|
|
207
|
+
}[];
|
|
208
|
+
}
|
|
209
|
+
declare function createClient(options: ClientOptions): {
|
|
210
|
+
baseUrl: string;
|
|
211
|
+
attribution: () => Promise<AttributionResponse>;
|
|
212
|
+
styleUrl: (theme: Theme) => string;
|
|
213
|
+
autocomplete: (q: string, opts?: {
|
|
214
|
+
near?: [number, number];
|
|
215
|
+
limit?: number;
|
|
216
|
+
types?: AutocompleteType[];
|
|
217
|
+
}) => Promise<{
|
|
218
|
+
items: AutocompleteItem[];
|
|
219
|
+
}>;
|
|
220
|
+
search: (q: string, opts?: {
|
|
221
|
+
category?: string;
|
|
222
|
+
near?: [number, number];
|
|
223
|
+
radius?: number;
|
|
224
|
+
bbox?: [number, number, number, number];
|
|
225
|
+
limit?: number;
|
|
226
|
+
offset?: number;
|
|
227
|
+
}) => Promise<{
|
|
228
|
+
items: Place[];
|
|
229
|
+
total: number;
|
|
230
|
+
}>;
|
|
231
|
+
nearby: (opts: {
|
|
232
|
+
lat: number;
|
|
233
|
+
lng: number;
|
|
234
|
+
radius?: number;
|
|
235
|
+
category?: string;
|
|
236
|
+
limit?: number;
|
|
237
|
+
}) => Promise<{
|
|
238
|
+
items: Place[];
|
|
239
|
+
}>;
|
|
240
|
+
getPlace: (id: string) => Promise<PlaceDetails>;
|
|
241
|
+
geocode: (q: string, opts?: {
|
|
242
|
+
near?: [number, number];
|
|
243
|
+
limit?: number;
|
|
244
|
+
}) => Promise<{
|
|
245
|
+
items: GeocodeItem[];
|
|
246
|
+
}>;
|
|
247
|
+
reverse: (lat: number, lng: number) => Promise<ReverseResponse>;
|
|
248
|
+
/** Gửi đóng góp/sửa POI (spec 6.1). Khoá phải có scope edits:write. */
|
|
249
|
+
suggestEdit: (edit: SuggestEditRequest) => Promise<SuggestEditResponse>;
|
|
250
|
+
};
|
|
251
|
+
type MapsLibVNClient = ReturnType<typeof createClient>;
|
|
252
|
+
|
|
253
|
+
declare class MapsLibVNError extends Error {
|
|
254
|
+
readonly status: number;
|
|
255
|
+
readonly code: string;
|
|
256
|
+
readonly requestId: string | undefined;
|
|
257
|
+
constructor(status: number, code: string, message: string, requestId?: string);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** Từ đệm bị bỏ ở đầu tên POI khi so khớp (spec 5.3 bước 5; thêm `mtv`). Không dùng cho hiển thị. */
|
|
261
|
+
declare const NAME_FILLERS: string[];
|
|
262
|
+
/** Bỏ dấu tiếng Việt (giữ chữ hoa/thường), đ → d. */
|
|
263
|
+
declare function stripDiacritics(s: string): string;
|
|
264
|
+
/** Thay viết tắt (bảng abbrev.json) trên chuỗi đã lowercase + bỏ dấu; `f` trước số → `phuong`. */
|
|
265
|
+
declare function expandAbbrev(s: string): string;
|
|
266
|
+
/** Chuẩn hoá spec 5.3 bước 1–4: NFC → lowercase → bỏ dấu → viết tắt → bỏ dấu câu (giữ `/`, `-`) → gộp khoảng trắng. */
|
|
267
|
+
declare function normalizeVi(input: string): string;
|
|
268
|
+
/** Alias thương hiệu: chỉ thay khi biến thể đứng đầu chuỗi (hoặc bằng cả chuỗi). */
|
|
269
|
+
declare function applyBrandAlias(s: string): string;
|
|
270
|
+
/** Tên rút gọn để so khớp trigram (spec 5.3 bước 5–6). Nếu bỏ hết từ đệm mà rỗng thì giữ tên chuẩn hoá. */
|
|
271
|
+
declare function nameCore(input: string): string;
|
|
272
|
+
|
|
273
|
+
type AlleyKeyword = 'hem' | 'ngo' | 'ngach' | 'kiet';
|
|
274
|
+
/** Kết quả phân tích địa chỉ (spec 5.7). Trường vắng = không nhận diện được. */
|
|
275
|
+
interface ParsedAddress {
|
|
276
|
+
housenumber?: string;
|
|
277
|
+
alleyChain: string[];
|
|
278
|
+
houseInAlley?: string;
|
|
279
|
+
alleyKeyword?: AlleyKeyword;
|
|
280
|
+
street?: string;
|
|
281
|
+
streetNorm?: string;
|
|
282
|
+
ward?: string;
|
|
283
|
+
district?: string;
|
|
284
|
+
province?: string;
|
|
285
|
+
adminOriginal?: {
|
|
286
|
+
ward?: string;
|
|
287
|
+
district?: string;
|
|
288
|
+
province?: string;
|
|
289
|
+
};
|
|
290
|
+
confidence: number;
|
|
291
|
+
}
|
|
292
|
+
declare function parseAddress(input: string): ParsedAddress;
|
|
293
|
+
|
|
294
|
+
type AdminAliasKeyInput = Pick<ParsedAddress, 'ward' | 'district' | 'province' | 'adminOriginal'>;
|
|
295
|
+
/** Sinh khóa alias từ cụ thể đến rộng; pipeline quyết định khóa ngắn nào đủ duy nhất để publish. */
|
|
296
|
+
declare function adminAliasKeys(input: AdminAliasKeyInput): string[];
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Biến đổi style JSON thuần (không cần Map đang chạy) — dùng cho React Native, nơi wrapper
|
|
300
|
+
* không có API đổi layout property của lớp có sẵn. Web dùng `applyLanguage` lúc chạy nhưng
|
|
301
|
+
* chia sẻ `nameExpression`/`isNameLabelLayer` từ đây.
|
|
302
|
+
*/
|
|
303
|
+
type Lang = 'vi' | 'en';
|
|
304
|
+
interface StyleLayerLike {
|
|
305
|
+
id: string;
|
|
306
|
+
type: string;
|
|
307
|
+
source?: string | undefined;
|
|
308
|
+
layout?: Record<string, unknown> | undefined;
|
|
309
|
+
}
|
|
310
|
+
interface StyleLike {
|
|
311
|
+
layers?: readonly StyleLayerLike[] | undefined;
|
|
312
|
+
}
|
|
313
|
+
declare const POI_LAYER_ID = "poi";
|
|
314
|
+
declare function isPoiStyleLayer(layer: StyleLayerLike): boolean;
|
|
315
|
+
declare function nameExpression(lang: Lang): unknown[];
|
|
316
|
+
/** Lớp nhãn tên (symbol có `text-field` tham chiếu `name`), trừ lớp chủ quyền luôn tiếng Việt. */
|
|
317
|
+
declare function isNameLabelLayer(layer: StyleLayerLike): boolean;
|
|
318
|
+
/** Đổi nhãn sang `lang`. `vi` là mặc định của style nên trả nguyên object. */
|
|
319
|
+
declare function localizeStyle<T extends StyleLike>(style: T, lang: Lang): T;
|
|
320
|
+
/** Ẩn lớp POI bằng `layout.visibility = 'none'`; trả style mới. */
|
|
321
|
+
declare function hidePoiLayer<T extends StyleLike>(style: T): T;
|
|
322
|
+
|
|
323
|
+
interface SearchKeys {
|
|
324
|
+
/** `viKey(applyToponymAlias(name_norm))` — cột `name_key`/`alias_key`. */
|
|
325
|
+
nameKey: string;
|
|
326
|
+
/** Tên thay thế đã `normalizeVi`, nối `' | '` giữ biên từ; null khi không có. Thứ tự = `name_alt`. */
|
|
327
|
+
nameAltNorm: string | null;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Nguồn sự thật duy nhất cho cột dẫn xuất tìm kiếm (spec 05/09 mục 6.1–6.3). Pipeline, backfill và
|
|
331
|
+
* test đối chiếu dbtest đều gọi hàm này, để không tồn tại hai định nghĩa `name_key`.
|
|
332
|
+
*
|
|
333
|
+
* `nameNorm` phải đã `normalizeVi`. `nameAlt` là mảng gốc (chưa chuẩn hoá) của OSM; phần tử rỗng,
|
|
334
|
+
* trùng nhau sau chuẩn hoá, hoặc trùng chính `nameNorm` đều bị bỏ, nhưng thứ tự KHÔNG đổi —
|
|
335
|
+
* `matched_alt` trong API dựa vào việc `name_alt` và `string_to_array(name_alt_norm, ' | ')` thẳng
|
|
336
|
+
* hàng theo chỉ số, nên pipeline phải ghi `name_alt` đã lọc bằng `filterNameAlt` dưới đây.
|
|
337
|
+
*
|
|
338
|
+
* Dấu `|` trong tên gốc không phá dấu phân cách vì `normalizeVi` chỉ giữ `a-z0-9/-` và khoảng
|
|
339
|
+
* trắng — mọi ký tự khác thành khoảng trắng.
|
|
340
|
+
*/
|
|
341
|
+
declare function searchKeys(nameNorm: string, nameAlt: readonly string[] | null | undefined): SearchKeys;
|
|
342
|
+
/**
|
|
343
|
+
* Lọc `name_alt` theo đúng luật của `searchKeys`, để pipeline ghi mảng GỐC thẳng hàng với
|
|
344
|
+
* `name_alt_norm`. Trả về phần tử gốc, không phải dạng đã chuẩn hoá — cột `name_alt` dùng để
|
|
345
|
+
* hiển thị (`matched_alt`) nên phải giữ dấu.
|
|
346
|
+
*/
|
|
347
|
+
declare function filterNameAlt(nameNorm: string, nameAlt: readonly string[] | null | undefined): string[];
|
|
348
|
+
|
|
349
|
+
/** Chuỗi có dấu hiệu người dùng gõ telex/VNI mà chưa được bộ gõ chuyển thành dấu. */
|
|
350
|
+
declare function looksLikeTelex(normalized: string): boolean;
|
|
351
|
+
/**
|
|
352
|
+
* Gập telex/VNI còn sót: `aa→a`, `ee→e`, `oo→o`, `dd→d`, `aw→a`, `ow→o`, `uw→u`; bỏ `s f r x j`
|
|
353
|
+
* đứng cuối từ khi trước nó là nguyên âm hoặc phụ âm cuối hợp lệ (xem mục mở rộng dưới); bỏ chữ
|
|
354
|
+
* số 1–9 dính cuối từ.
|
|
355
|
+
*
|
|
356
|
+
* Chỉ áp cho **truy vấn**, không bao giờ cho dữ liệu — đây là bậc 3b, chạy khi mọi bậc trước rỗng
|
|
357
|
+
* và chỉ khi `looksLikeTelex` đúng. Mặc định **tắt** ở lần phát hành đầu (cờ `AUTOCOMPLETE_TELEX`),
|
|
358
|
+
* bật sau khi log `stage_hit` cho thấy tỷ lệ truy vấn rỗng khớp mẫu telex đáng kể.
|
|
359
|
+
*
|
|
360
|
+
* **Mở rộng so với spec 5.6, có lý do:** spec chỉ nói bỏ dấu "sau nguyên âm", nhưng trong telex
|
|
361
|
+
* thật dấu đứng ở CUỐI ÂM TIẾT, tức sau cả phụ âm cuối — `ddoongf` (Đồng) có `f` sau `ng`. Theo
|
|
362
|
+
* đúng chữ của spec thì `ddoongf` chỉ gập được thành `dongf`, còn nguyên chữ `f`, làm bậc 3b gần
|
|
363
|
+
* như vô dụng với âm tiết đóng. Nên nới thành: bỏ `s f r x j` ở cuối từ khi ngay trước nó là
|
|
364
|
+
* nguyên âm **hoặc** một phụ âm cuối hợp lệ của tiếng Việt (`c ch m n ng nh p t`).
|
|
365
|
+
*
|
|
366
|
+
* Điều kiện này chính là thứ giữ cho tên nước ngoài không bị cắt: `highlands` có `d` trước `s` mà
|
|
367
|
+
* `d` không phải phụ âm cuối hợp lệ → giữ nguyên; `starbucks` có `k` → giữ nguyên. Còn `viets` thì
|
|
368
|
+
* `t` là phụ âm cuối hợp lệ → thành `viet`.
|
|
369
|
+
*/
|
|
370
|
+
declare function foldTelex(normalized: string): string;
|
|
371
|
+
|
|
372
|
+
interface ToponymEntry {
|
|
373
|
+
/** Biến thể đã `normalizeVi`, khác dạng chuẩn. */
|
|
374
|
+
variants: string[];
|
|
375
|
+
/** Nguồn kiểm được: `osm:<type>/<id> <tag>` hoặc `wikipedia:vi:<trang>`. */
|
|
376
|
+
source: string;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Dạng chuẩn → biến thể địa danh, mỗi mục có nguồn (spec 05/09 mục 6.1). Pipeline và API dùng
|
|
380
|
+
* **cùng** bảng này: dữ liệu qua `searchKeys`, truy vấn qua nhánh `qAlias` ở bậc 1.
|
|
381
|
+
*
|
|
382
|
+
* Khoá `$comment` trong JSON bị bỏ khi nạp — nó là tài liệu biên soạn, không phải dữ liệu.
|
|
383
|
+
*/
|
|
384
|
+
declare const TOPONYM_ALIAS: Record<string, ToponymEntry>;
|
|
385
|
+
/**
|
|
386
|
+
* Thay biến thể địa danh bằng dạng chuẩn ở **bất kỳ vị trí** theo biên từ — khác
|
|
387
|
+
* `applyBrandAlias` vốn chỉ thay ở đầu chuỗi. Đầu vào phải đã `normalizeVi`.
|
|
388
|
+
*
|
|
389
|
+
* Dùng hai chỗ: dữ liệu (`searchKeys` → cột `name_key`) và truy vấn (nhánh `qAlias` ở bậc 1 của
|
|
390
|
+
* autocomplete). Nhờ nhánh truy vấn mà biến thể khớp được **ngay**, không phải chờ pipeline chạy
|
|
391
|
+
* lại để điền `name_key`.
|
|
392
|
+
*/
|
|
393
|
+
declare function applyToponymAlias(normalized: string): string;
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Khoá ngữ âm cho tiếng Việt không dấu (spec 05/09 mục 6.2).
|
|
397
|
+
*
|
|
398
|
+
* Đầu vào phải là chuỗi đã `normalizeVi` (và nên đã qua `applyToponymAlias` — xem `searchKeys`).
|
|
399
|
+
* Áp luật theo **từng từ** theo thứ tự đầu từ → âm cuối → i/y, rồi **nối không khoảng trắng**.
|
|
400
|
+
* Chỉ giữ `[a-z0-9]`, nên `/` và `-` trong số nhà bị bỏ.
|
|
401
|
+
*
|
|
402
|
+
* Việc nối giúp phần lớn ca dính/tách từ cho cùng khoá (`sai gon` = `saigon`), nhưng **không phải
|
|
403
|
+
* mọi ca**: luật đầu từ chỉ áp ở đầu TỪ, nên `nhac trang` → `nhaccan` (tr→c) trong khi `nhatrang`
|
|
404
|
+
* → `nhatran` (tr nằm giữa từ). Tương tự `plei ku` → `pleicu` nhưng `pleiku` → `pleiku`. Fixture
|
|
405
|
+
* `vi-key.csv` ghi rõ hai nhóm này.
|
|
406
|
+
*
|
|
407
|
+
* **Giới hạn thứ hai, nặng hơn, đo trên production 08/09/2026:** vì nối không khoảng trắng, khoá
|
|
408
|
+
* của một tên NHIỀU TỪ là một cục (`Hủ Tiếu Mỹ Tho Thanh Xuân` → `hutieumithothanhxuan`). Bậc 3
|
|
409
|
+
* lọc bằng `word_similarity(qKey, name_key)`, mà `word_similarity` so theo **ranh giới từ** trong
|
|
410
|
+
* chuỗi đích — với khoá một cục thì không còn ranh giới nào, nên `mitho` gần như không khớp. Hệ
|
|
411
|
+
* quả: bậc 3 chỉ cứu được tên **ngắn** (`kontum` → Kon Tum, `bin than` → Bình Thạnh) và bất lực
|
|
412
|
+
* với mọi tên dài. Đối chứng: `my tho` gõ ĐÚNG chính tả cũng chỉ đưa `area Phường Mỹ Tho` lên
|
|
413
|
+
* hạng 10, và đó là do `withAreaSlot` nhét vào suất cuối chứ không phải do điểm.
|
|
414
|
+
*
|
|
415
|
+
* Sửa gốc là sinh khoá **giữ ranh giới từ**, nhưng việc đó đổi cột `name_key`/`alias_key` nên phải
|
|
416
|
+
* migration + backfill lại 1,52 triệu POI và đo lại bộ mờ 40 truy vấn. Chưa làm; ghi ở đây để lần
|
|
417
|
+
* sau có căn cứ. Xem docs/evidence/search-keys/16-nghiem-thu-production.md.
|
|
418
|
+
*
|
|
419
|
+
* Dùng ở **bậc 3** của autocomplete, sau các bậc chính xác hơn, nên việc luật gộp hơi rộng
|
|
420
|
+
* (`gi/r/d`, `tr/ch`, âm cuối miền Nam) chỉ ảnh hưởng thứ tự trong nhóm mờ.
|
|
421
|
+
*/
|
|
422
|
+
declare function viKey(normalized: string): string;
|
|
423
|
+
|
|
424
|
+
export { ATTRIBUTION_LINKS, type AdminAliasKeyInput, type AlleyKeyword, type AttributionLink, type AttributionResponse, type AutocompleteItem, type AutocompleteType, type ClientOptions, DEFAULT_POI_SOURCES, type EditChanges, type EditKind, type GeocodeItem, type GeocodeMatched, type GeocodePrecision, type Lang, type MapsLibVNClient, MapsLibVNError, NAME_FILLERS, POI_LAYER_ID, POI_SOURCES, POI_SOURCE_PROFILES, type ParsedAddress, type Place, type PlaceAddress, type PlaceCategory, type PlaceDetails, type PlaceSource, type PoiFeature, type PoiSource, type PoiSourceProfile, type ReverseAddress, type ReverseResponse, type SearchKeys, type StyleLayerLike, type StyleLike, type SuggestEditRequest, type SuggestEditResponse, TOPONYM_ALIAS, type Theme, type ToponymEntry, adminAliasKeys, applyBrandAlias, applyToponymAlias, attributionHtml, attributionText, createClient, expandAbbrev, filterNameAlt, foldTelex, hidePoiLayer, isNameLabelLayer, isPoiStyleLayer, localizeStyle, looksLikeTelex, nameCore, nameExpression, normalizePoiSources, normalizeVi, parseAddress, parsePoiSourcesCsv, poiSourceClause, poiSourcesKey, profileForSources, searchKeys, stripDiacritics, viKey };
|