thaizip 0.6.0 → 0.7.3

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/index.js CHANGED
@@ -2,8 +2,13 @@
2
2
  var TONE_MARKS = /[่-๋็์]/g;
3
3
  var ADDRESS_PREFIXES = /^(จังหวัด|อำเภอ|ตำบล|แขวง|เขต|จ\.|อ\.|ต\.|ข\.)/;
4
4
  function normalizeThaiAddressText(input) {
5
- if (!input) return "";
6
- let text = input.trim();
5
+ if (typeof input !== "string") {
6
+ throw new TypeError(
7
+ `[thaizip] normalizeThaiAddressText expected string, got ${input === null ? "null" : typeof input}`
8
+ );
9
+ }
10
+ if (input.length === 0) return "";
11
+ let text = input.normalize("NFC").trim();
7
12
  text = text.replace(ADDRESS_PREFIXES, "").trim();
8
13
  text = text.replace(TONE_MARKS, "");
9
14
  text = text.toLowerCase();
@@ -28,6 +33,69 @@ function extractTrigramsNormalized(text) {
28
33
  }
29
34
 
30
35
  // src/core/indexer.ts
36
+ function typeErr(table, id, field, expected, value) {
37
+ return new TypeError(`[thaizip] ${table} ${id}: expected ${expected} for ${field}, got ${typeof value}`);
38
+ }
39
+ function typeName(value) {
40
+ return value === null ? "null" : typeof value;
41
+ }
42
+ function validateRawData(data) {
43
+ if (data === null || typeof data !== "object") {
44
+ throw new TypeError(`[thaizip] validateRawData expected a RawData object, got ${typeName(data)}`);
45
+ }
46
+ const { provinces, amphures, tambons } = data;
47
+ if (!Array.isArray(provinces)) {
48
+ throw new TypeError(`[thaizip] RawData.provinces must be an array, got ${typeName(provinces)}`);
49
+ }
50
+ if (!Array.isArray(amphures)) {
51
+ throw new TypeError(`[thaizip] RawData.amphures must be an array, got ${typeName(amphures)}`);
52
+ }
53
+ if (!Array.isArray(tambons)) {
54
+ throw new TypeError(`[thaizip] RawData.tambons must be an array, got ${typeName(tambons)}`);
55
+ }
56
+ const seenProvinceIds = /* @__PURE__ */ new Set();
57
+ for (let i = 0; i < provinces.length; i++) {
58
+ const p = provinces[i];
59
+ if (p === null || typeof p !== "object") {
60
+ throw new TypeError(`[thaizip] province[${i}]: expected object, got ${typeName(p)}`);
61
+ }
62
+ if (typeof p.id !== "number") throw typeErr("province", p.id, "id", "number", p.id);
63
+ if (seenProvinceIds.has(p.id)) throw new TypeError(`[thaizip] duplicate province id: ${p.id}`);
64
+ seenProvinceIds.add(p.id);
65
+ if (typeof p.name_th !== "string") throw typeErr("province", p.id, "name_th", "string", p.name_th);
66
+ if (typeof p.name_en !== "string") throw typeErr("province", p.id, "name_en", "string", p.name_en);
67
+ }
68
+ const seenAmphureIds = /* @__PURE__ */ new Set();
69
+ for (let i = 0; i < amphures.length; i++) {
70
+ const a = amphures[i];
71
+ if (a === null || typeof a !== "object") {
72
+ throw new TypeError(`[thaizip] amphure[${i}]: expected object, got ${typeName(a)}`);
73
+ }
74
+ if (typeof a.id !== "number") throw typeErr("amphure", a.id, "id", "number", a.id);
75
+ if (seenAmphureIds.has(a.id)) throw new TypeError(`[thaizip] duplicate amphure id: ${a.id}`);
76
+ seenAmphureIds.add(a.id);
77
+ if (typeof a.name_th !== "string") throw typeErr("amphure", a.id, "name_th", "string", a.name_th);
78
+ if (typeof a.name_en !== "string") throw typeErr("amphure", a.id, "name_en", "string", a.name_en);
79
+ if (typeof a.province_id !== "number") throw typeErr("amphure", a.id, "province_id", "number", a.province_id);
80
+ }
81
+ const seenTambonIds = /* @__PURE__ */ new Set();
82
+ for (let i = 0; i < tambons.length; i++) {
83
+ const t = tambons[i];
84
+ if (t === null || typeof t !== "object") {
85
+ throw new TypeError(`[thaizip] tambon[${i}]: expected object, got ${typeName(t)}`);
86
+ }
87
+ if (typeof t.id !== "number") throw typeErr("tambon", t.id, "id", "number", t.id);
88
+ if (seenTambonIds.has(t.id)) throw new TypeError(`[thaizip] duplicate tambon id: ${t.id}`);
89
+ seenTambonIds.add(t.id);
90
+ if (typeof t.name_th !== "string") throw typeErr("tambon", t.id, "name_th", "string", t.name_th);
91
+ if (typeof t.name_en !== "string") throw typeErr("tambon", t.id, "name_en", "string", t.name_en);
92
+ const zipType = typeof t.zip_code;
93
+ if (zipType !== "string" && zipType !== "number") {
94
+ throw typeErr("tambon", t.id, "zip_code", "string or number", t.zip_code);
95
+ }
96
+ if (typeof t.amphure_id !== "number") throw typeErr("tambon", t.id, "amphure_id", "number", t.amphure_id);
97
+ }
98
+ }
31
99
  function addTrigrams(map, trigrams, idx) {
32
100
  for (const trigram of trigrams) {
33
101
  let set = map.get(trigram);
@@ -45,6 +113,9 @@ function combinedTrigrams(nameTh, nameEn) {
45
113
  return s;
46
114
  }
47
115
  function buildThaiAddressIndex(data, options) {
116
+ if (options?.validate !== false) {
117
+ validateRawData(data);
118
+ }
48
119
  const { provinces, amphures, tambons } = data;
49
120
  const provMap = new Map(
50
121
  provinces.filter((p) => !p.deleted_at).map((p) => [p.id, p])
@@ -63,6 +134,10 @@ function buildThaiAddressIndex(data, options) {
63
134
  const records = [];
64
135
  const map = /* @__PURE__ */ new Map();
65
136
  const zipIndex = /* @__PURE__ */ new Map();
137
+ const normTambon = [];
138
+ const normTambonEn = [];
139
+ const byProvince = /* @__PURE__ */ new Map();
140
+ const byAmphure = /* @__PURE__ */ new Map();
66
141
  for (const tambon of tambons) {
67
142
  if (tambon.deleted_at) continue;
68
143
  const amphure = ampMap.get(tambon.amphure_id);
@@ -92,70 +167,340 @@ function buildThaiAddressIndex(data, options) {
92
167
  const existing = zipIndex.get(record.zipCode);
93
168
  if (existing) existing.push(idx);
94
169
  else zipIndex.set(record.zipCode, [idx]);
95
- addTrigrams(map, extractTrigrams(record.tambonNameTh), idx);
96
- addTrigrams(map, extractTrigrams(record.tambonNameEn), idx);
170
+ const provList = byProvince.get(province.id);
171
+ if (provList) provList.push(idx);
172
+ else byProvince.set(province.id, [idx]);
173
+ const ampList = byAmphure.get(amphure.id);
174
+ if (ampList) ampList.push(idx);
175
+ else byAmphure.set(amphure.id, [idx]);
176
+ const normTh = normalizeThaiAddressText(record.tambonNameTh);
177
+ normTambon.push(normTh);
178
+ addTrigrams(map, extractTrigramsNormalized(normTh), idx);
179
+ const normEn = normalizeThaiAddressText(record.tambonNameEn);
180
+ normTambonEn.push(normEn);
181
+ addTrigrams(map, extractTrigramsNormalized(normEn), idx);
97
182
  addTrigrams(map, extractTrigrams(record.zipCode), idx);
98
183
  addTrigrams(map, provTrigrams.get(province.id), idx);
99
184
  addTrigrams(map, ampTrigrams.get(amphure.id), idx);
100
185
  }
101
- return { map, records, zipIndex };
186
+ const sortedZipKeys = [...zipIndex.keys()].sort();
187
+ const sortedZipPostings = sortedZipKeys.map((z) => zipIndex.get(z));
188
+ return { map, records, zipIndex, sortedZipKeys, sortedZipPostings, normTambon, normTambonEn, byProvince, byAmphure };
189
+ }
190
+
191
+ // src/core/romanize.ts
192
+ var ALIASES = /* @__PURE__ */ new Map([
193
+ // --- Bangkok khet (districts) — concatenated ("no space") forms ---------
194
+ ["phranakhon", "phra nakhon"],
195
+ ["nongchok", "nong chok"],
196
+ ["bangrak", "bang rak"],
197
+ ["bangkhen", "bang khen"],
198
+ ["bangkapi", "bang kapi"],
199
+ ["pathumwan", "pathum wan"],
200
+ ["patumwan", "pathum wan"],
201
+ ["pomprap", "pom prap sattru phai"],
202
+ ["phrakhanong", "phra khanong"],
203
+ ["minburi", "min buri"],
204
+ ["latkrabang", "lat krabang"],
205
+ ["ladkrabang", "lat krabang"],
206
+ ["yannawa", "yan nawa"],
207
+ ["phayathai", "phaya thai"],
208
+ ["thonburi", "thon buri"],
209
+ ["bangkokyai", "bangkok yai"],
210
+ ["huaikhwang", "huai khwang"],
211
+ ["huaykhwang", "huai khwang"],
212
+ ["huaykwang", "huai khwang"],
213
+ ["khlongsan", "khlong san"],
214
+ ["klongsan", "khlong san"],
215
+ ["talingchan", "taling chan"],
216
+ ["bangkoknoi", "bangkok noi"],
217
+ ["bangkhunthian", "bang khun thian"],
218
+ ["phasicharoen", "phasi charoen"],
219
+ ["nongkhaem", "nong khaem"],
220
+ ["ratburana", "rat burana"],
221
+ ["bangphlat", "bang phlat"],
222
+ ["dindaeng", "din daeng"],
223
+ ["buengkum", "bueng kum"],
224
+ ["bangsue", "bang sue"],
225
+ ["bangkholaem", "bang kho laem"],
226
+ ["khlongtoei", "khlong toei"],
227
+ ["klongtoei", "khlong toei"],
228
+ ["klongtoey", "khlong toei"],
229
+ ["khlongtoey", "khlong toei"],
230
+ ["suanluang", "suan luang"],
231
+ ["chomthong", "chom thong"],
232
+ ["donmueang", "don mueang"],
233
+ ["donmuang", "don mueang"],
234
+ ["latphrao", "lat phrao"],
235
+ ["ladprao", "lat phrao"],
236
+ ["lardprao", "lat phrao"],
237
+ ["lat prao", "lat phrao"],
238
+ ["bangkhae", "bang khae"],
239
+ ["laksi", "lak si"],
240
+ ["saimai", "sai mai"],
241
+ ["khannayao", "khan na yao"],
242
+ ["saphansung", "saphan sung"],
243
+ ["wangthonglang", "wang thonglang"],
244
+ ["khlongsamwa", "khlong sam wa"],
245
+ ["klongsamwa", "khlong sam wa"],
246
+ ["bangna", "bang na"],
247
+ ["thawiwatthana", "thawi watthana"],
248
+ ["thungkhru", "thung khru"],
249
+ ["bangbon", "bang bon"],
250
+ // --- Bangkok area / street nicknames with a common misspelling ----------
251
+ ["sathorn", "sathon"],
252
+ ["wattana", "watthana"],
253
+ ["silom", "si lom"],
254
+ // --- Provinces and other high-traffic areas ------------------------------
255
+ ["krungthep", "bangkok"],
256
+ ["korat", "nakhon ratchasima"],
257
+ ["nakornratchasima", "nakhon ratchasima"],
258
+ ["chiangmai", "chiang mai"],
259
+ ["chiangrai", "chiang rai"],
260
+ ["pathumthani", "pathum thani"],
261
+ ["patumthani", "pathum thani"],
262
+ ["samutprakan", "samut prakan"],
263
+ ["samutsakhon", "samut sakhon"],
264
+ ["samutsongkhram", "samut songkhram"],
265
+ ["nakhonpathom", "nakhon pathom"],
266
+ ["nakhonsawan", "nakhon sawan"],
267
+ ["nakhonsithammarat", "nakhon si thammarat"],
268
+ ["prachuapkhirikhan", "prachuap khiri khan"],
269
+ ["huahin", "hua hin"],
270
+ ["hatyai", "hat yai"],
271
+ ["hadyai", "hat yai"],
272
+ ["had yai", "hat yai"],
273
+ ["lopburi", "loburi"],
274
+ ["lop buri", "loburi"],
275
+ ["buengkan", "buogkan"],
276
+ ["bungkan", "buogkan"],
277
+ ["bung kan", "buogkan"],
278
+ ["udonthani", "udon thani"],
279
+ ["khonkaen", "khon kaen"],
280
+ ["suphanburi", "suphan buri"],
281
+ ["nongkhai", "nong khai"],
282
+ ["buriram", "buri ram"]
283
+ ]);
284
+ var NOISE_WORDS_RE = /\b(?:sub[- ]?district|district|province|changwat|amphoe|amphur)\b/g;
285
+ var WHITESPACE_RE = /\s+/g;
286
+ function cleanup(text) {
287
+ const stripped = text.replace(NOISE_WORDS_RE, " ").replace(WHITESPACE_RE, " ").trim();
288
+ return stripped.length > 0 ? stripped : text;
289
+ }
290
+ function applyRomanizationAliases(normalized) {
291
+ if (!normalized) return normalized;
292
+ const direct = ALIASES.get(normalized);
293
+ if (direct !== void 0) return direct;
294
+ const cleaned = cleanup(normalized);
295
+ if (cleaned === normalized) return normalized;
296
+ const aliasedCleaned = ALIASES.get(cleaned);
297
+ return aliasedCleaned !== void 0 ? aliasedCleaned : cleaned;
102
298
  }
103
299
 
104
300
  // src/core/search.ts
105
301
  var ZIP_CODE_RE = /^\d+$/;
302
+ var HAS_LATIN_RE = /[a-z]/i;
303
+ var TH_COLLATOR = new Intl.Collator("th");
304
+ function compareZip(a, b) {
305
+ if (a < b) return -1;
306
+ if (a > b) return 1;
307
+ return 0;
308
+ }
309
+ function sortZipMatches(matches, exactZip) {
310
+ matches.sort((a, b) => {
311
+ if (a.zipCode === exactZip && b.zipCode !== exactZip) return -1;
312
+ if (b.zipCode === exactZip && a.zipCode !== exactZip) return 1;
313
+ return compareZip(a.zipCode, b.zipCode);
314
+ });
315
+ return matches;
316
+ }
317
+ function normalizeLimit(value) {
318
+ if (Number.isNaN(value) || value === -Infinity) return 0;
319
+ if (value === Infinity) return value;
320
+ return Math.max(0, Math.floor(value));
321
+ }
322
+ function lookupByZipCode(index, zip, options) {
323
+ if (!index || !index.zipIndex || !index.records || typeof zip !== "string") return [];
324
+ const normalized = zip.trim();
325
+ if (!ZIP_CODE_RE.test(normalized) || normalized.length < 2) return [];
326
+ const zipLimit = normalizeLimit(options?.zipLimit ?? Infinity);
327
+ const matches = [];
328
+ const keys = index.sortedZipKeys;
329
+ const postings = index.sortedZipPostings;
330
+ if (keys !== void 0 && postings !== void 0) {
331
+ let lo = 0;
332
+ let hi = keys.length;
333
+ while (lo < hi) {
334
+ const mid = lo + hi >> 1;
335
+ if (keys[mid] < normalized) lo = mid + 1;
336
+ else hi = mid;
337
+ }
338
+ for (let i = lo; i < keys.length && keys[i].startsWith(normalized); i++) {
339
+ const indices = postings[i];
340
+ for (let j = 0; j < indices.length; j++) matches.push(index.records[indices[j]]);
341
+ }
342
+ } else {
343
+ for (const [z, indices] of index.zipIndex) {
344
+ if (z.startsWith(normalized)) {
345
+ for (const idx of indices) matches.push(index.records[idx]);
346
+ }
347
+ }
348
+ sortZipMatches(matches, normalized);
349
+ }
350
+ return matches.slice(0, zipLimit).map((record) => ({ ...record }));
351
+ }
352
+ function rankAgainstName(name, query) {
353
+ if (name === void 0 || name.length === 0) return 0;
354
+ if (name === query) return 3;
355
+ if (name.startsWith(query)) return 2;
356
+ if (name.includes(query)) return 1;
357
+ return 0;
358
+ }
359
+ function computeMatchRank(index, idx, query) {
360
+ const th = rankAgainstName(index.normTambon?.[idx], query);
361
+ if (th === 3) return 3;
362
+ const en = rankAgainstName(index.normTambonEn?.[idx], query);
363
+ return en > th ? en : th;
364
+ }
365
+ var scratchByIndex = /* @__PURE__ */ new WeakMap();
366
+ function getScratch(index) {
367
+ const n = index.records.length;
368
+ let s = scratchByIndex.get(index);
369
+ if (s === void 0 || s.counts.length < n) {
370
+ s = { counts: new Uint32Array(n), touched: new Int32Array(n) };
371
+ scratchByIndex.set(index, s);
372
+ }
373
+ return s;
374
+ }
106
375
  function searchThaiAddress(index, query, options) {
107
- const limit = options?.limit ?? 10;
376
+ const limit = normalizeLimit(options?.limit ?? 10);
108
377
  const threshold = options?.threshold ?? 0.4;
109
- if (!index || !query) return [];
378
+ if (!index || !index.map || !index.records || typeof query !== "string") return [];
379
+ if (query.length > 1e3) return [];
110
380
  const normalized = normalizeThaiAddressText(query);
111
381
  if (normalized.length === 0) return [];
112
382
  if (normalized.length > 300) return [];
113
383
  if (ZIP_CODE_RE.test(normalized)) {
114
384
  if (normalized.length < 2) return [];
115
- const matches = [];
116
- for (const [zip, indices] of index.zipIndex) {
117
- if (zip.startsWith(normalized)) {
118
- for (const idx of indices) matches.push(index.records[idx]);
119
- }
120
- }
121
- matches.sort((a, b) => {
122
- if (a.zipCode === normalized && b.zipCode !== normalized) return -1;
123
- if (b.zipCode === normalized && a.zipCode !== normalized) return 1;
124
- return a.zipCode.localeCompare(b.zipCode, void 0, { numeric: true });
385
+ return lookupByZipCode(index, normalized, {
386
+ zipLimit: options?.zipLimit ?? Infinity
125
387
  });
126
- return matches.slice(0, limit);
127
388
  }
128
- if (normalized.length < 3 && query.trim().length < 3) return [];
129
- const queryTrigrams = extractTrigramsNormalized(normalized);
130
- const hits = /* @__PURE__ */ new Map();
389
+ if (normalized.length < 3) return [];
390
+ const searchText = options?.romanizationAliases !== false && HAS_LATIN_RE.test(normalized) ? applyRomanizationAliases(normalized) : normalized;
391
+ const queryTrigrams = extractTrigramsNormalized(searchText);
392
+ const { counts, touched } = getScratch(index);
393
+ let touchedLen = 0;
131
394
  for (const trigram of queryTrigrams) {
132
395
  const candidates = index.map.get(trigram);
133
396
  if (!candidates) continue;
134
397
  for (const idx of candidates) {
135
- hits.set(idx, (hits.get(idx) ?? 0) + 1);
398
+ if (counts[idx] === 0) touched[touchedLen++] = idx;
399
+ counts[idx]++;
136
400
  }
137
401
  }
138
402
  const scored = [];
139
- for (const [idx, count] of hits) {
140
- const score = count / queryTrigrams.size;
403
+ const totalTrigrams = queryTrigrams.size;
404
+ for (let i = 0; i < touchedLen; i++) {
405
+ const idx = touched[i];
406
+ const score = counts[idx] / totalTrigrams;
407
+ counts[idx] = 0;
141
408
  if (score >= threshold) {
142
- scored.push({ idx, score });
409
+ scored.push({ idx, score, matchRank: computeMatchRank(index, idx, searchText) });
143
410
  }
144
411
  }
145
412
  scored.sort((a, b) => {
146
413
  if (b.score !== a.score) return b.score - a.score;
414
+ return b.matchRank - a.matchRank;
415
+ });
416
+ const windowSize = Math.max(limit * 4, 50);
417
+ const window = scored.slice(0, windowSize);
418
+ window.sort((a, b) => {
419
+ if (b.score !== a.score) return b.score - a.score;
420
+ if (b.matchRank !== a.matchRank) return b.matchRank - a.matchRank;
147
421
  const ra = index.records[a.idx];
148
422
  const rb = index.records[b.idx];
149
- return ra.provinceNameTh.localeCompare(rb.provinceNameTh, "th") || ra.amphureNameTh.localeCompare(rb.amphureNameTh, "th") || ra.tambonNameTh.localeCompare(rb.tambonNameTh, "th");
423
+ return TH_COLLATOR.compare(ra.provinceNameTh, rb.provinceNameTh) || TH_COLLATOR.compare(ra.amphureNameTh, rb.amphureNameTh) || TH_COLLATOR.compare(ra.tambonNameTh, rb.tambonNameTh);
424
+ });
425
+ return window.slice(0, limit).map(({ idx }) => ({ ...index.records[idx] }));
426
+ }
427
+
428
+ // src/core/enumerate.ts
429
+ var thCollator = new Intl.Collator("th");
430
+ function byNameTh(a, b) {
431
+ return thCollator.compare(a.nameTh, b.nameTh);
432
+ }
433
+ function listProvinces(index) {
434
+ if (!index || !index.records) return [];
435
+ const seen = /* @__PURE__ */ new Map();
436
+ if (index.byProvince) {
437
+ for (const [provinceId, recordIndices] of index.byProvince) {
438
+ if (seen.has(provinceId) || recordIndices.length === 0) continue;
439
+ const r = index.records[recordIndices[0]];
440
+ seen.set(provinceId, { id: provinceId, nameTh: r.provinceNameTh, nameEn: r.provinceNameEn });
441
+ }
442
+ } else {
443
+ for (const r of index.records) {
444
+ if (!seen.has(r.provinceId)) {
445
+ seen.set(r.provinceId, { id: r.provinceId, nameTh: r.provinceNameTh, nameEn: r.provinceNameEn });
446
+ }
447
+ }
448
+ }
449
+ return Array.from(seen.values()).sort(byNameTh);
450
+ }
451
+ function listAmphures(index, provinceId) {
452
+ if (!index || !index.records) return [];
453
+ const seen = /* @__PURE__ */ new Map();
454
+ const addFrom = (r) => {
455
+ if (!seen.has(r.amphureId)) {
456
+ seen.set(r.amphureId, {
457
+ id: r.amphureId,
458
+ nameTh: r.amphureNameTh,
459
+ nameEn: r.amphureNameEn,
460
+ provinceId: r.provinceId
461
+ });
462
+ }
463
+ };
464
+ if (index.byProvince) {
465
+ const recordIndices = index.byProvince.get(provinceId);
466
+ if (!recordIndices) return [];
467
+ for (const idx of recordIndices) addFrom(index.records[idx]);
468
+ } else {
469
+ for (const r of index.records) {
470
+ if (r.provinceId === provinceId) addFrom(r);
471
+ }
472
+ }
473
+ return Array.from(seen.values()).sort(byNameTh);
474
+ }
475
+ function listTambons(index, amphureId) {
476
+ if (!index || !index.records) return [];
477
+ const toSummary = (r) => ({
478
+ id: r.tambonId,
479
+ nameTh: r.tambonNameTh,
480
+ nameEn: r.tambonNameEn,
481
+ amphureId: r.amphureId,
482
+ zipCode: r.zipCode
150
483
  });
151
- return scored.slice(0, limit).map(({ idx }) => index.records[idx]);
484
+ let result;
485
+ if (index.byAmphure) {
486
+ const recordIndices = index.byAmphure.get(amphureId);
487
+ if (!recordIndices) return [];
488
+ result = recordIndices.map((idx) => toSummary(index.records[idx]));
489
+ } else {
490
+ result = index.records.filter((r) => r.amphureId === amphureId).map(toSummary);
491
+ }
492
+ return result.sort(byNameTh);
152
493
  }
153
494
 
154
495
  // src/core/formatter.ts
155
- function formatThaiAddressSuggestion(record) {
496
+ function formatThaiAddressSuggestion(record, options) {
497
+ const labelTh = `${record.tambonNameTh} > ${record.amphureNameTh} > ${record.provinceNameTh} ${record.zipCode}`;
498
+ const labelEn = `${record.tambonNameEn} > ${record.amphureNameEn} > ${record.provinceNameEn} ${record.zipCode}`;
156
499
  return {
157
500
  id: String(record.tambonId),
158
- label: `${record.tambonNameTh} > ${record.amphureNameTh} > ${record.provinceNameTh} ${record.zipCode}`,
501
+ label: options?.locale === "en" ? labelEn : labelTh,
502
+ labelTh,
503
+ labelEn,
159
504
  tambon: record.tambonNameTh,
160
505
  tambonEn: record.tambonNameEn,
161
506
  amphure: record.amphureNameTh,
@@ -184,6 +529,6 @@ function resolveThaiAddress(record) {
184
529
  };
185
530
  }
186
531
 
187
- export { buildThaiAddressIndex, formatThaiAddressSuggestion, normalizeThaiAddressText, resolveThaiAddress, searchThaiAddress };
532
+ export { applyRomanizationAliases, buildThaiAddressIndex, formatThaiAddressSuggestion, listAmphures, listProvinces, listTambons, lookupByZipCode, normalizeThaiAddressText, resolveThaiAddress, searchThaiAddress, validateRawData };
188
533
  //# sourceMappingURL=index.js.map
189
534
  //# sourceMappingURL=index.js.map