thaizip 0.5.1 → 0.7.2

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