thaizip 0.4.0 → 0.5.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/data.cjs +4 -0
- package/dist/data.d.cts +2 -1
- package/dist/data.d.ts +2 -1
- package/dist/data.js +4 -1
- package/dist/index.cjs +3 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/data.cjs
CHANGED
|
@@ -120,6 +120,9 @@ function buildThaiAddressIndex(data) {
|
|
|
120
120
|
|
|
121
121
|
// src/data/loader.ts
|
|
122
122
|
var cached = null;
|
|
123
|
+
function clearDefaultIndex() {
|
|
124
|
+
cached = null;
|
|
125
|
+
}
|
|
123
126
|
async function loadDefaultIndex() {
|
|
124
127
|
if (cached) return cached;
|
|
125
128
|
const { p: p2, a: a2, t: t2 } = await Promise.resolve().then(() => (init_defaultData(), defaultData_exports));
|
|
@@ -131,4 +134,5 @@ async function loadDefaultIndex() {
|
|
|
131
134
|
return cached;
|
|
132
135
|
}
|
|
133
136
|
|
|
137
|
+
exports.clearDefaultIndex = clearDefaultIndex;
|
|
134
138
|
exports.loadDefaultIndex = loadDefaultIndex;
|
package/dist/data.d.cts
CHANGED
|
@@ -16,6 +16,7 @@ type TrigramIndex = {
|
|
|
16
16
|
zipIndex: Map<string, number[]>;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
+
declare function clearDefaultIndex(): void;
|
|
19
20
|
declare function loadDefaultIndex(): Promise<TrigramIndex>;
|
|
20
21
|
|
|
21
|
-
export { loadDefaultIndex };
|
|
22
|
+
export { clearDefaultIndex, loadDefaultIndex };
|
package/dist/data.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ type TrigramIndex = {
|
|
|
16
16
|
zipIndex: Map<string, number[]>;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
+
declare function clearDefaultIndex(): void;
|
|
19
20
|
declare function loadDefaultIndex(): Promise<TrigramIndex>;
|
|
20
21
|
|
|
21
|
-
export { loadDefaultIndex };
|
|
22
|
+
export { clearDefaultIndex, loadDefaultIndex };
|
package/dist/data.js
CHANGED
|
@@ -118,6 +118,9 @@ function buildThaiAddressIndex(data) {
|
|
|
118
118
|
|
|
119
119
|
// src/data/loader.ts
|
|
120
120
|
var cached = null;
|
|
121
|
+
function clearDefaultIndex() {
|
|
122
|
+
cached = null;
|
|
123
|
+
}
|
|
121
124
|
async function loadDefaultIndex() {
|
|
122
125
|
if (cached) return cached;
|
|
123
126
|
const { p: p2, a: a2, t: t2 } = await Promise.resolve().then(() => (init_defaultData(), defaultData_exports));
|
|
@@ -129,4 +132,4 @@ async function loadDefaultIndex() {
|
|
|
129
132
|
return cached;
|
|
130
133
|
}
|
|
131
134
|
|
|
132
|
-
export { loadDefaultIndex };
|
|
135
|
+
export { clearDefaultIndex, loadDefaultIndex };
|
package/dist/index.cjs
CHANGED
|
@@ -93,6 +93,8 @@ function buildThaiAddressIndex(data) {
|
|
|
93
93
|
}
|
|
94
94
|
return { map, records, zipIndex };
|
|
95
95
|
}
|
|
96
|
+
function clearDefaultIndex() {
|
|
97
|
+
}
|
|
96
98
|
|
|
97
99
|
// src/core/search.ts
|
|
98
100
|
var ZIP_CODE_RE = /^\d+$/;
|
|
@@ -119,7 +121,6 @@ function searchThaiAddress(index, query, options) {
|
|
|
119
121
|
return matches.slice(0, limit);
|
|
120
122
|
}
|
|
121
123
|
const queryTrigrams = extractTrigramsNormalized(normalized);
|
|
122
|
-
if (queryTrigrams.size === 0) return [];
|
|
123
124
|
const hits = /* @__PURE__ */ new Map();
|
|
124
125
|
for (const trigram of queryTrigrams) {
|
|
125
126
|
const candidates = index.map.get(trigram);
|
|
@@ -234,6 +235,7 @@ function useThaiAddressAutocomplete(options) {
|
|
|
234
235
|
}
|
|
235
236
|
|
|
236
237
|
exports.buildThaiAddressIndex = buildThaiAddressIndex;
|
|
238
|
+
exports.clearDefaultIndex = clearDefaultIndex;
|
|
237
239
|
exports.formatThaiAddressSuggestion = formatThaiAddressSuggestion;
|
|
238
240
|
exports.normalizeThaiAddressText = normalizeThaiAddressText;
|
|
239
241
|
exports.resolveThaiAddress = resolveThaiAddress;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/normalizer.ts","../src/core/trigrams.ts","../src/core/indexer.ts","../src/core/search.ts","../src/core/formatter.ts","../src/core/resolver.ts","../src/react/useThaiAddressAutocomplete.ts"],"names":["useState","useRef","useCallback","useEffect"],"mappings":"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","file":"index.cjs","sourcesContent":["const TONE_MARKS = /[\\u0E48-\\u0E4B\\u0E47\\u0E4C]/g\nconst ADDRESS_PREFIXES = /^(จังหวัด|อำเภอ|ตำบล|แขวง|เขต)/\n\nexport function normalizeThaiAddressText(input: string): string {\n if (!input) return ''\n let text = input.trim()\n text = text.replace(ADDRESS_PREFIXES, '').trim()\n text = text.replace(TONE_MARKS, '')\n text = text.toLowerCase()\n return text\n}\n","import { normalizeThaiAddressText } from './normalizer'\n\n// Public: for callers who start from raw (un-normalised) text — e.g. indexer\nexport function extractTrigrams(text: string): Set<string> {\n return extractTrigramsNormalized(normalizeThaiAddressText(text))\n}\n\n// Internal: for callers who already hold a normalised string — e.g. search.ts\nexport function extractTrigramsNormalized(text: string): Set<string> {\n const result = new Set<string>()\n if (text.length === 0) return result\n if (text.length < 3) {\n result.add(text)\n return result\n }\n for (let i = 0; i <= text.length - 3; i++) {\n result.add(text.slice(i, i + 3))\n }\n return result\n}\n","import { extractTrigrams } from './trigrams'\nimport type { RawData, ThaiAddressRecord, TrigramIndex } from '../types'\n\nexport function buildThaiAddressIndex(data: RawData): TrigramIndex {\n const { provinces, amphures, tambons } = data\n\n // Build lookup maps (filter deleted)\n const provMap = new Map(\n provinces.filter(p => !p.deleted_at).map(p => [p.id, p])\n )\n const ampMap = new Map(\n amphures.filter(a => !a.deleted_at).map(a => [a.id, a])\n )\n\n const records: ThaiAddressRecord[] = []\n const map = new Map<string, Set<number>>()\n const zipIndex = new Map<string, number[]>()\n\n for (const tambon of tambons) {\n if (tambon.deleted_at) continue\n\n const amphure = ampMap.get(tambon.amphure_id)\n if (!amphure) {\n console.warn(\n `[thaizip] skipping tambon id=${tambon.id} (${tambon.name_th}): ` +\n `amphure_id=${tambon.amphure_id} not found or deleted`\n )\n continue\n }\n\n const province = provMap.get(amphure.province_id)\n if (!province) continue\n\n const record: ThaiAddressRecord = {\n provinceId: province.id,\n provinceNameTh: province.name_th,\n provinceNameEn: province.name_en,\n amphureId: amphure.id,\n amphureNameTh: amphure.name_th,\n amphureNameEn: amphure.name_en,\n tambonId: tambon.id,\n tambonNameTh: tambon.name_th,\n tambonNameEn: tambon.name_en,\n zipCode: String(tambon.zip_code),\n }\n\n const idx = records.length\n records.push(record)\n\n // Build zip index\n const existing = zipIndex.get(record.zipCode)\n if (existing) existing.push(idx)\n else zipIndex.set(record.zipCode, [idx])\n\n const fields = [\n record.tambonNameTh,\n record.tambonNameEn,\n record.amphureNameTh,\n record.amphureNameEn,\n record.provinceNameTh,\n record.provinceNameEn,\n record.zipCode,\n ]\n\n for (const field of fields) {\n for (const trigram of extractTrigrams(field)) {\n let set = map.get(trigram)\n if (!set) {\n set = new Set()\n map.set(trigram, set)\n }\n set.add(idx)\n }\n }\n }\n\n return { map, records, zipIndex }\n}\n","import { normalizeThaiAddressText } from './normalizer'\nimport { extractTrigramsNormalized } from './trigrams'\nimport type { SearchOptions, ThaiAddressRecord, TrigramIndex } from '../types'\n\nconst ZIP_CODE_RE = /^\\d+$/\n\nexport function searchThaiAddress(\n index: TrigramIndex,\n query: string,\n options?: SearchOptions,\n): ThaiAddressRecord[] {\n const limit = options?.limit ?? 10\n const threshold = options?.threshold ?? 0.4\n\n if (!index || !query) return []\n const normalized = normalizeThaiAddressText(query)\n if (normalized.length === 0) return []\n if (normalized.length > 300) return []\n\n // Special case: zip code query (require at least 2 digits to avoid overly broad matches)\n if (ZIP_CODE_RE.test(normalized)) {\n if (normalized.length < 2) return []\n const matches: ThaiAddressRecord[] = []\n for (const [zip, indices] of index.zipIndex) {\n if (zip.startsWith(normalized)) {\n for (const idx of indices) matches.push(index.records[idx])\n }\n }\n // exact match first, then ascending zip code\n matches.sort((a, b) => {\n if (a.zipCode === normalized && b.zipCode !== normalized) return -1\n if (b.zipCode === normalized && a.zipCode !== normalized) return 1\n return a.zipCode.localeCompare(b.zipCode)\n })\n return matches.slice(0, limit)\n }\n\n const queryTrigrams = extractTrigramsNormalized(normalized)\n if (queryTrigrams.size === 0) return []\n\n // Accumulate hit counts per record index\n const hits = new Map<number, number>()\n for (const trigram of queryTrigrams) {\n const candidates = index.map.get(trigram)\n if (!candidates) continue\n for (const idx of candidates) {\n hits.set(idx, (hits.get(idx) ?? 0) + 1)\n }\n }\n\n const scored: { idx: number; score: number }[] = []\n for (const [idx, count] of hits) {\n const score = count / queryTrigrams.size\n if (score >= threshold) {\n scored.push({ idx, score })\n }\n }\n\n scored.sort((a, b) => {\n if (b.score !== a.score) return b.score - a.score\n const ra = index.records[a.idx]\n const rb = index.records[b.idx]\n return (\n ra.provinceNameTh.localeCompare(rb.provinceNameTh) ||\n ra.amphureNameTh.localeCompare(rb.amphureNameTh) ||\n ra.tambonNameTh.localeCompare(rb.tambonNameTh)\n )\n })\n\n return scored.slice(0, limit).map(({ idx }) => index.records[idx])\n}\n","import type { ThaiAddressRecord, ThaiAddressSuggestion } from '../types'\n\nexport function formatThaiAddressSuggestion(record: ThaiAddressRecord): ThaiAddressSuggestion {\n return {\n id: String(record.tambonId),\n label: `${record.tambonNameTh} > ${record.amphureNameTh} > ${record.provinceNameTh} ${record.zipCode}`,\n tambon: record.tambonNameTh,\n tambonEn: record.tambonNameEn,\n amphure: record.amphureNameTh,\n amphureEn: record.amphureNameEn,\n province: record.provinceNameTh,\n provinceEn: record.provinceNameEn,\n zipCode: record.zipCode,\n }\n}\n","import type { ThaiAddressRecord, ResolvedThaiAddress } from '../types'\n\nexport function resolveThaiAddress(record: ThaiAddressRecord): ResolvedThaiAddress {\n return {\n tambon: record.tambonNameTh,\n tambonEn: record.tambonNameEn,\n amphure: record.amphureNameTh,\n amphureEn: record.amphureNameEn,\n province: record.provinceNameTh,\n provinceEn: record.provinceNameEn,\n zipCode: record.zipCode,\n subdistrict: record.tambonNameTh,\n subdistrictEn: record.tambonNameEn,\n district: record.amphureNameTh,\n districtEn: record.amphureNameEn,\n postalCode: record.zipCode,\n }\n}\n","// NOTE (SZ-4): CJS consumers (`dist/index.cjs`) cannot tree-shake this hook out of the bundle.\n// If you are using the CJS build in a non-React environment, import only from the core entry\n// or use the ESM build with a bundler that supports tree-shaking.\nimport { useState, useEffect, useCallback, useRef } from 'react'\nimport { searchThaiAddress } from '../core/search'\nimport { formatThaiAddressSuggestion } from '../core/formatter'\nimport { resolveThaiAddress } from '../core/resolver'\nimport type {\n UseThaiAddressAutocompleteOptions,\n ThaiAddressRecord,\n ThaiAddressSuggestion,\n ResolvedThaiAddress,\n} from '../types'\n\nexport function useThaiAddressAutocomplete(options: UseThaiAddressAutocompleteOptions) {\n const { index, limit = 10, debounce: debounceMs = 200, threshold = 0.4 } = options\n\n const [query, setQueryState] = useState('')\n const [suggestions, setSuggestions] = useState<ThaiAddressSuggestion[]>([])\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const matchedRecordsMapRef = useRef<Map<string, ThaiAddressRecord>>(new Map())\n\n const setQuery = useCallback((value: string) => {\n setQueryState(value)\n }, [])\n\n useEffect(() => {\n if (timerRef.current) clearTimeout(timerRef.current)\n\n if (query.length === 0) {\n setSuggestions([])\n matchedRecordsMapRef.current = new Map()\n return\n }\n\n timerRef.current = setTimeout(() => {\n const records = searchThaiAddress(index, query, { limit, threshold })\n const formatted = records.map(formatThaiAddressSuggestion)\n const recordMap = new Map<string, ThaiAddressRecord>()\n for (let i = 0; i < records.length; i++) {\n recordMap.set(formatted[i].id, records[i])\n }\n matchedRecordsMapRef.current = recordMap\n setSuggestions(formatted)\n }, debounceMs)\n\n return () => {\n if (timerRef.current) clearTimeout(timerRef.current)\n }\n }, [query, index, limit, threshold, debounceMs])\n\n /**\n * Resolves a suggestion to a full ThaiAddress and clears the suggestions list.\n *\n * Note: `query` is intentionally left unchanged so the input field retains the\n * typed text. Call `clear()` afterwards if you want to reset the input as well.\n */\n const selectSuggestion = useCallback(\n (item: ThaiAddressSuggestion): ResolvedThaiAddress => {\n const record = matchedRecordsMapRef.current.get(item.id)\n if (!record) {\n throw new Error(`[thaizip] No record found for suggestion id \"${item.id}\". Make sure to use suggestions returned by this hook.`)\n }\n setSuggestions([])\n matchedRecordsMapRef.current = new Map()\n return resolveThaiAddress(record)\n },\n [],\n )\n\n const clear = useCallback(() => {\n setQueryState('')\n setSuggestions([])\n matchedRecordsMapRef.current = new Map()\n }, [])\n\n return {\n query,\n setQuery,\n suggestions,\n isOpen: query.length > 0 && suggestions.length > 0,\n selectSuggestion,\n clear,\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/normalizer.ts","../src/core/trigrams.ts","../src/core/indexer.ts","../src/data/loader.ts","../src/core/search.ts","../src/core/formatter.ts","../src/core/resolver.ts","../src/react/useThaiAddressAutocomplete.ts"],"names":["useState","useRef","useCallback","useEffect"],"mappings":"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","file":"index.cjs","sourcesContent":["const TONE_MARKS = /[\\u0E48-\\u0E4B\\u0E47\\u0E4C]/g\nconst ADDRESS_PREFIXES = /^(จังหวัด|อำเภอ|ตำบล|แขวง|เขต)/\n\nexport function normalizeThaiAddressText(input: string): string {\n if (!input) return ''\n let text = input.trim()\n text = text.replace(ADDRESS_PREFIXES, '').trim()\n text = text.replace(TONE_MARKS, '')\n text = text.toLowerCase()\n return text\n}\n","import { normalizeThaiAddressText } from './normalizer'\n\n// Public: for callers who start from raw (un-normalised) text — e.g. indexer\nexport function extractTrigrams(text: string): Set<string> {\n return extractTrigramsNormalized(normalizeThaiAddressText(text))\n}\n\n// Internal: for callers who already hold a normalised string — e.g. search.ts\nexport function extractTrigramsNormalized(text: string): Set<string> {\n const result = new Set<string>()\n if (text.length === 0) return result\n if (text.length < 3) {\n result.add(text)\n return result\n }\n for (let i = 0; i <= text.length - 3; i++) {\n result.add(text.slice(i, i + 3))\n }\n return result\n}\n","import { extractTrigrams } from './trigrams'\nimport type { RawData, ThaiAddressRecord, TrigramIndex } from '../types'\n\nexport function buildThaiAddressIndex(data: RawData): TrigramIndex {\n const { provinces, amphures, tambons } = data\n\n // Build lookup maps (filter deleted)\n const provMap = new Map(\n provinces.filter(p => !p.deleted_at).map(p => [p.id, p])\n )\n const ampMap = new Map(\n amphures.filter(a => !a.deleted_at).map(a => [a.id, a])\n )\n\n const records: ThaiAddressRecord[] = []\n const map = new Map<string, Set<number>>()\n const zipIndex = new Map<string, number[]>()\n\n for (const tambon of tambons) {\n if (tambon.deleted_at) continue\n\n const amphure = ampMap.get(tambon.amphure_id)\n if (!amphure) {\n console.warn(\n `[thaizip] skipping tambon id=${tambon.id} (${tambon.name_th}): ` +\n `amphure_id=${tambon.amphure_id} not found or deleted`\n )\n continue\n }\n\n const province = provMap.get(amphure.province_id)\n if (!province) continue\n\n const record: ThaiAddressRecord = {\n provinceId: province.id,\n provinceNameTh: province.name_th,\n provinceNameEn: province.name_en,\n amphureId: amphure.id,\n amphureNameTh: amphure.name_th,\n amphureNameEn: amphure.name_en,\n tambonId: tambon.id,\n tambonNameTh: tambon.name_th,\n tambonNameEn: tambon.name_en,\n zipCode: String(tambon.zip_code),\n }\n\n const idx = records.length\n records.push(record)\n\n // Build zip index\n const existing = zipIndex.get(record.zipCode)\n if (existing) existing.push(idx)\n else zipIndex.set(record.zipCode, [idx])\n\n const fields = [\n record.tambonNameTh,\n record.tambonNameEn,\n record.amphureNameTh,\n record.amphureNameEn,\n record.provinceNameTh,\n record.provinceNameEn,\n record.zipCode,\n ]\n\n for (const field of fields) {\n for (const trigram of extractTrigrams(field)) {\n let set = map.get(trigram)\n if (!set) {\n set = new Set()\n map.set(trigram, set)\n }\n set.add(idx)\n }\n }\n }\n\n return { map, records, zipIndex }\n}\n","import { buildThaiAddressIndex } from '../core/indexer'\nimport type { TrigramIndex } from '../types'\nimport type { CompactProvince, CompactAmphure, CompactTambon } from './defaultData'\n\nlet cached: TrigramIndex | null = null\n\nexport function clearDefaultIndex(): void {\n cached = null\n}\n\nexport async function loadDefaultIndex(): Promise<TrigramIndex> {\n if (cached) return cached\n const { p, a, t } = await import('./defaultData') as {\n p: CompactProvince[]\n a: CompactAmphure[]\n t: CompactTambon[]\n }\n cached = buildThaiAddressIndex({\n provinces: p.map(([id, name_th, name_en]) => ({ id, name_th, name_en, geography_id: 0, deleted_at: null })),\n amphures: a.map(([id, name_th, name_en, province_id]) => ({ id, name_th, name_en, province_id, deleted_at: null })),\n tambons: t.map(([id, name_th, name_en, amphure_id, zip_code]) => ({ id, name_th, name_en, amphure_id, zip_code: Number(zip_code), deleted_at: null })),\n })\n return cached\n}\n","import { normalizeThaiAddressText } from './normalizer'\nimport { extractTrigramsNormalized } from './trigrams'\nimport type { SearchOptions, ThaiAddressRecord, TrigramIndex } from '../types'\n\nconst ZIP_CODE_RE = /^\\d+$/\n\nexport function searchThaiAddress(\n index: TrigramIndex,\n query: string,\n options?: SearchOptions,\n): ThaiAddressRecord[] {\n const limit = options?.limit ?? 10\n const threshold = options?.threshold ?? 0.4\n\n if (!index || !query) return []\n const normalized = normalizeThaiAddressText(query)\n if (normalized.length === 0) return []\n if (normalized.length > 300) return []\n\n // Special case: zip code query (require at least 2 digits to avoid overly broad matches)\n if (ZIP_CODE_RE.test(normalized)) {\n if (normalized.length < 2) return []\n const matches: ThaiAddressRecord[] = []\n for (const [zip, indices] of index.zipIndex) {\n if (zip.startsWith(normalized)) {\n for (const idx of indices) matches.push(index.records[idx])\n }\n }\n // exact match first, then ascending zip code\n matches.sort((a, b) => {\n if (a.zipCode === normalized && b.zipCode !== normalized) return -1\n if (b.zipCode === normalized && a.zipCode !== normalized) return 1\n return a.zipCode.localeCompare(b.zipCode)\n })\n return matches.slice(0, limit)\n }\n\n const queryTrigrams = extractTrigramsNormalized(normalized)\n\n // Accumulate hit counts per record index\n const hits = new Map<number, number>()\n for (const trigram of queryTrigrams) {\n const candidates = index.map.get(trigram)\n if (!candidates) continue\n for (const idx of candidates) {\n hits.set(idx, (hits.get(idx) ?? 0) + 1)\n }\n }\n\n const scored: { idx: number; score: number }[] = []\n for (const [idx, count] of hits) {\n const score = count / queryTrigrams.size\n if (score >= threshold) {\n scored.push({ idx, score })\n }\n }\n\n scored.sort((a, b) => {\n if (b.score !== a.score) return b.score - a.score\n const ra = index.records[a.idx]\n const rb = index.records[b.idx]\n return (\n ra.provinceNameTh.localeCompare(rb.provinceNameTh) ||\n ra.amphureNameTh.localeCompare(rb.amphureNameTh) ||\n ra.tambonNameTh.localeCompare(rb.tambonNameTh)\n )\n })\n\n return scored.slice(0, limit).map(({ idx }) => index.records[idx])\n}\n","import type { ThaiAddressRecord, ThaiAddressSuggestion } from '../types'\n\nexport function formatThaiAddressSuggestion(record: ThaiAddressRecord): ThaiAddressSuggestion {\n return {\n id: String(record.tambonId),\n label: `${record.tambonNameTh} > ${record.amphureNameTh} > ${record.provinceNameTh} ${record.zipCode}`,\n tambon: record.tambonNameTh,\n tambonEn: record.tambonNameEn,\n amphure: record.amphureNameTh,\n amphureEn: record.amphureNameEn,\n province: record.provinceNameTh,\n provinceEn: record.provinceNameEn,\n zipCode: record.zipCode,\n }\n}\n","import type { ThaiAddressRecord, ResolvedThaiAddress } from '../types'\n\nexport function resolveThaiAddress(record: ThaiAddressRecord): ResolvedThaiAddress {\n return {\n tambon: record.tambonNameTh,\n tambonEn: record.tambonNameEn,\n amphure: record.amphureNameTh,\n amphureEn: record.amphureNameEn,\n province: record.provinceNameTh,\n provinceEn: record.provinceNameEn,\n zipCode: record.zipCode,\n subdistrict: record.tambonNameTh,\n subdistrictEn: record.tambonNameEn,\n district: record.amphureNameTh,\n districtEn: record.amphureNameEn,\n postalCode: record.zipCode,\n }\n}\n","// NOTE (SZ-4): CJS consumers (`dist/index.cjs`) cannot tree-shake this hook out of the bundle.\n// If you are using the CJS build in a non-React environment, import only from the core entry\n// or use the ESM build with a bundler that supports tree-shaking.\nimport { useState, useEffect, useCallback, useRef } from 'react'\nimport { searchThaiAddress } from '../core/search'\nimport { formatThaiAddressSuggestion } from '../core/formatter'\nimport { resolveThaiAddress } from '../core/resolver'\nimport type {\n UseThaiAddressAutocompleteOptions,\n ThaiAddressRecord,\n ThaiAddressSuggestion,\n ResolvedThaiAddress,\n} from '../types'\n\nexport function useThaiAddressAutocomplete(options: UseThaiAddressAutocompleteOptions) {\n const { index, limit = 10, debounce: debounceMs = 200, threshold = 0.4 } = options\n\n const [query, setQueryState] = useState('')\n const [suggestions, setSuggestions] = useState<ThaiAddressSuggestion[]>([])\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const matchedRecordsMapRef = useRef<Map<string, ThaiAddressRecord>>(new Map())\n\n const setQuery = useCallback((value: string) => {\n setQueryState(value)\n }, [])\n\n useEffect(() => {\n if (timerRef.current) clearTimeout(timerRef.current)\n\n if (query.length === 0) {\n setSuggestions([])\n matchedRecordsMapRef.current = new Map()\n return\n }\n\n timerRef.current = setTimeout(() => {\n const records = searchThaiAddress(index, query, { limit, threshold })\n const formatted = records.map(formatThaiAddressSuggestion)\n const recordMap = new Map<string, ThaiAddressRecord>()\n for (let i = 0; i < records.length; i++) {\n recordMap.set(formatted[i].id, records[i])\n }\n matchedRecordsMapRef.current = recordMap\n setSuggestions(formatted)\n }, debounceMs)\n\n return () => {\n if (timerRef.current) clearTimeout(timerRef.current)\n }\n }, [query, index, limit, threshold, debounceMs])\n\n /**\n * Resolves a suggestion to a full ThaiAddress and clears the suggestions list.\n *\n * Note: `query` is intentionally left unchanged so the input field retains the\n * typed text. Call `clear()` afterwards if you want to reset the input as well.\n */\n const selectSuggestion = useCallback(\n (item: ThaiAddressSuggestion): ResolvedThaiAddress => {\n const record = matchedRecordsMapRef.current.get(item.id)\n if (!record) {\n throw new Error(`[thaizip] No record found for suggestion id \"${item.id}\". Make sure to use suggestions returned by this hook.`)\n }\n setSuggestions([])\n matchedRecordsMapRef.current = new Map()\n return resolveThaiAddress(record)\n },\n [],\n )\n\n const clear = useCallback(() => {\n setQueryState('')\n setSuggestions([])\n matchedRecordsMapRef.current = new Map()\n }, [])\n\n return {\n query,\n setQuery,\n suggestions,\n isOpen: query.length > 0 && suggestions.length > 0,\n selectSuggestion,\n clear,\n }\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -86,6 +86,8 @@ type RawData = {
|
|
|
86
86
|
|
|
87
87
|
declare function buildThaiAddressIndex(data: RawData): TrigramIndex;
|
|
88
88
|
|
|
89
|
+
declare function clearDefaultIndex(): void;
|
|
90
|
+
|
|
89
91
|
declare function searchThaiAddress(index: TrigramIndex, query: string, options?: SearchOptions): ThaiAddressRecord[];
|
|
90
92
|
|
|
91
93
|
declare function formatThaiAddressSuggestion(record: ThaiAddressRecord): ThaiAddressSuggestion;
|
|
@@ -103,4 +105,4 @@ declare function useThaiAddressAutocomplete(options: UseThaiAddressAutocompleteO
|
|
|
103
105
|
clear: () => void;
|
|
104
106
|
};
|
|
105
107
|
|
|
106
|
-
export { type RawAmphure, type RawData, type RawGeography, type RawProvince, type RawTambon, type ResolvedThaiAddress, type SearchOptions, type ThaiAddressRecord, type ThaiAddressSuggestion, type TrigramIndex, type UseThaiAddressAutocompleteOptions, buildThaiAddressIndex, formatThaiAddressSuggestion, normalizeThaiAddressText, resolveThaiAddress, searchThaiAddress, useThaiAddressAutocomplete };
|
|
108
|
+
export { type RawAmphure, type RawData, type RawGeography, type RawProvince, type RawTambon, type ResolvedThaiAddress, type SearchOptions, type ThaiAddressRecord, type ThaiAddressSuggestion, type TrigramIndex, type UseThaiAddressAutocompleteOptions, buildThaiAddressIndex, clearDefaultIndex, formatThaiAddressSuggestion, normalizeThaiAddressText, resolveThaiAddress, searchThaiAddress, useThaiAddressAutocomplete };
|
package/dist/index.d.ts
CHANGED
|
@@ -86,6 +86,8 @@ type RawData = {
|
|
|
86
86
|
|
|
87
87
|
declare function buildThaiAddressIndex(data: RawData): TrigramIndex;
|
|
88
88
|
|
|
89
|
+
declare function clearDefaultIndex(): void;
|
|
90
|
+
|
|
89
91
|
declare function searchThaiAddress(index: TrigramIndex, query: string, options?: SearchOptions): ThaiAddressRecord[];
|
|
90
92
|
|
|
91
93
|
declare function formatThaiAddressSuggestion(record: ThaiAddressRecord): ThaiAddressSuggestion;
|
|
@@ -103,4 +105,4 @@ declare function useThaiAddressAutocomplete(options: UseThaiAddressAutocompleteO
|
|
|
103
105
|
clear: () => void;
|
|
104
106
|
};
|
|
105
107
|
|
|
106
|
-
export { type RawAmphure, type RawData, type RawGeography, type RawProvince, type RawTambon, type ResolvedThaiAddress, type SearchOptions, type ThaiAddressRecord, type ThaiAddressSuggestion, type TrigramIndex, type UseThaiAddressAutocompleteOptions, buildThaiAddressIndex, formatThaiAddressSuggestion, normalizeThaiAddressText, resolveThaiAddress, searchThaiAddress, useThaiAddressAutocomplete };
|
|
108
|
+
export { type RawAmphure, type RawData, type RawGeography, type RawProvince, type RawTambon, type ResolvedThaiAddress, type SearchOptions, type ThaiAddressRecord, type ThaiAddressSuggestion, type TrigramIndex, type UseThaiAddressAutocompleteOptions, buildThaiAddressIndex, clearDefaultIndex, formatThaiAddressSuggestion, normalizeThaiAddressText, resolveThaiAddress, searchThaiAddress, useThaiAddressAutocomplete };
|
package/dist/index.js
CHANGED
|
@@ -91,6 +91,8 @@ function buildThaiAddressIndex(data) {
|
|
|
91
91
|
}
|
|
92
92
|
return { map, records, zipIndex };
|
|
93
93
|
}
|
|
94
|
+
function clearDefaultIndex() {
|
|
95
|
+
}
|
|
94
96
|
|
|
95
97
|
// src/core/search.ts
|
|
96
98
|
var ZIP_CODE_RE = /^\d+$/;
|
|
@@ -117,7 +119,6 @@ function searchThaiAddress(index, query, options) {
|
|
|
117
119
|
return matches.slice(0, limit);
|
|
118
120
|
}
|
|
119
121
|
const queryTrigrams = extractTrigramsNormalized(normalized);
|
|
120
|
-
if (queryTrigrams.size === 0) return [];
|
|
121
122
|
const hits = /* @__PURE__ */ new Map();
|
|
122
123
|
for (const trigram of queryTrigrams) {
|
|
123
124
|
const candidates = index.map.get(trigram);
|
|
@@ -231,6 +232,6 @@ function useThaiAddressAutocomplete(options) {
|
|
|
231
232
|
};
|
|
232
233
|
}
|
|
233
234
|
|
|
234
|
-
export { buildThaiAddressIndex, formatThaiAddressSuggestion, normalizeThaiAddressText, resolveThaiAddress, searchThaiAddress, useThaiAddressAutocomplete };
|
|
235
|
+
export { buildThaiAddressIndex, clearDefaultIndex, formatThaiAddressSuggestion, normalizeThaiAddressText, resolveThaiAddress, searchThaiAddress, useThaiAddressAutocomplete };
|
|
235
236
|
//# sourceMappingURL=index.js.map
|
|
236
237
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/normalizer.ts","../src/core/trigrams.ts","../src/core/indexer.ts","../src/core/search.ts","../src/core/formatter.ts","../src/core/resolver.ts","../src/react/useThaiAddressAutocomplete.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["const TONE_MARKS = /[\\u0E48-\\u0E4B\\u0E47\\u0E4C]/g\nconst ADDRESS_PREFIXES = /^(จังหวัด|อำเภอ|ตำบล|แขวง|เขต)/\n\nexport function normalizeThaiAddressText(input: string): string {\n if (!input) return ''\n let text = input.trim()\n text = text.replace(ADDRESS_PREFIXES, '').trim()\n text = text.replace(TONE_MARKS, '')\n text = text.toLowerCase()\n return text\n}\n","import { normalizeThaiAddressText } from './normalizer'\n\n// Public: for callers who start from raw (un-normalised) text — e.g. indexer\nexport function extractTrigrams(text: string): Set<string> {\n return extractTrigramsNormalized(normalizeThaiAddressText(text))\n}\n\n// Internal: for callers who already hold a normalised string — e.g. search.ts\nexport function extractTrigramsNormalized(text: string): Set<string> {\n const result = new Set<string>()\n if (text.length === 0) return result\n if (text.length < 3) {\n result.add(text)\n return result\n }\n for (let i = 0; i <= text.length - 3; i++) {\n result.add(text.slice(i, i + 3))\n }\n return result\n}\n","import { extractTrigrams } from './trigrams'\nimport type { RawData, ThaiAddressRecord, TrigramIndex } from '../types'\n\nexport function buildThaiAddressIndex(data: RawData): TrigramIndex {\n const { provinces, amphures, tambons } = data\n\n // Build lookup maps (filter deleted)\n const provMap = new Map(\n provinces.filter(p => !p.deleted_at).map(p => [p.id, p])\n )\n const ampMap = new Map(\n amphures.filter(a => !a.deleted_at).map(a => [a.id, a])\n )\n\n const records: ThaiAddressRecord[] = []\n const map = new Map<string, Set<number>>()\n const zipIndex = new Map<string, number[]>()\n\n for (const tambon of tambons) {\n if (tambon.deleted_at) continue\n\n const amphure = ampMap.get(tambon.amphure_id)\n if (!amphure) {\n console.warn(\n `[thaizip] skipping tambon id=${tambon.id} (${tambon.name_th}): ` +\n `amphure_id=${tambon.amphure_id} not found or deleted`\n )\n continue\n }\n\n const province = provMap.get(amphure.province_id)\n if (!province) continue\n\n const record: ThaiAddressRecord = {\n provinceId: province.id,\n provinceNameTh: province.name_th,\n provinceNameEn: province.name_en,\n amphureId: amphure.id,\n amphureNameTh: amphure.name_th,\n amphureNameEn: amphure.name_en,\n tambonId: tambon.id,\n tambonNameTh: tambon.name_th,\n tambonNameEn: tambon.name_en,\n zipCode: String(tambon.zip_code),\n }\n\n const idx = records.length\n records.push(record)\n\n // Build zip index\n const existing = zipIndex.get(record.zipCode)\n if (existing) existing.push(idx)\n else zipIndex.set(record.zipCode, [idx])\n\n const fields = [\n record.tambonNameTh,\n record.tambonNameEn,\n record.amphureNameTh,\n record.amphureNameEn,\n record.provinceNameTh,\n record.provinceNameEn,\n record.zipCode,\n ]\n\n for (const field of fields) {\n for (const trigram of extractTrigrams(field)) {\n let set = map.get(trigram)\n if (!set) {\n set = new Set()\n map.set(trigram, set)\n }\n set.add(idx)\n }\n }\n }\n\n return { map, records, zipIndex }\n}\n","import { normalizeThaiAddressText } from './normalizer'\nimport { extractTrigramsNormalized } from './trigrams'\nimport type { SearchOptions, ThaiAddressRecord, TrigramIndex } from '../types'\n\nconst ZIP_CODE_RE = /^\\d+$/\n\nexport function searchThaiAddress(\n index: TrigramIndex,\n query: string,\n options?: SearchOptions,\n): ThaiAddressRecord[] {\n const limit = options?.limit ?? 10\n const threshold = options?.threshold ?? 0.4\n\n if (!index || !query) return []\n const normalized = normalizeThaiAddressText(query)\n if (normalized.length === 0) return []\n if (normalized.length > 300) return []\n\n // Special case: zip code query (require at least 2 digits to avoid overly broad matches)\n if (ZIP_CODE_RE.test(normalized)) {\n if (normalized.length < 2) return []\n const matches: ThaiAddressRecord[] = []\n for (const [zip, indices] of index.zipIndex) {\n if (zip.startsWith(normalized)) {\n for (const idx of indices) matches.push(index.records[idx])\n }\n }\n // exact match first, then ascending zip code\n matches.sort((a, b) => {\n if (a.zipCode === normalized && b.zipCode !== normalized) return -1\n if (b.zipCode === normalized && a.zipCode !== normalized) return 1\n return a.zipCode.localeCompare(b.zipCode)\n })\n return matches.slice(0, limit)\n }\n\n const queryTrigrams = extractTrigramsNormalized(normalized)\n if (queryTrigrams.size === 0) return []\n\n // Accumulate hit counts per record index\n const hits = new Map<number, number>()\n for (const trigram of queryTrigrams) {\n const candidates = index.map.get(trigram)\n if (!candidates) continue\n for (const idx of candidates) {\n hits.set(idx, (hits.get(idx) ?? 0) + 1)\n }\n }\n\n const scored: { idx: number; score: number }[] = []\n for (const [idx, count] of hits) {\n const score = count / queryTrigrams.size\n if (score >= threshold) {\n scored.push({ idx, score })\n }\n }\n\n scored.sort((a, b) => {\n if (b.score !== a.score) return b.score - a.score\n const ra = index.records[a.idx]\n const rb = index.records[b.idx]\n return (\n ra.provinceNameTh.localeCompare(rb.provinceNameTh) ||\n ra.amphureNameTh.localeCompare(rb.amphureNameTh) ||\n ra.tambonNameTh.localeCompare(rb.tambonNameTh)\n )\n })\n\n return scored.slice(0, limit).map(({ idx }) => index.records[idx])\n}\n","import type { ThaiAddressRecord, ThaiAddressSuggestion } from '../types'\n\nexport function formatThaiAddressSuggestion(record: ThaiAddressRecord): ThaiAddressSuggestion {\n return {\n id: String(record.tambonId),\n label: `${record.tambonNameTh} > ${record.amphureNameTh} > ${record.provinceNameTh} ${record.zipCode}`,\n tambon: record.tambonNameTh,\n tambonEn: record.tambonNameEn,\n amphure: record.amphureNameTh,\n amphureEn: record.amphureNameEn,\n province: record.provinceNameTh,\n provinceEn: record.provinceNameEn,\n zipCode: record.zipCode,\n }\n}\n","import type { ThaiAddressRecord, ResolvedThaiAddress } from '../types'\n\nexport function resolveThaiAddress(record: ThaiAddressRecord): ResolvedThaiAddress {\n return {\n tambon: record.tambonNameTh,\n tambonEn: record.tambonNameEn,\n amphure: record.amphureNameTh,\n amphureEn: record.amphureNameEn,\n province: record.provinceNameTh,\n provinceEn: record.provinceNameEn,\n zipCode: record.zipCode,\n subdistrict: record.tambonNameTh,\n subdistrictEn: record.tambonNameEn,\n district: record.amphureNameTh,\n districtEn: record.amphureNameEn,\n postalCode: record.zipCode,\n }\n}\n","// NOTE (SZ-4): CJS consumers (`dist/index.cjs`) cannot tree-shake this hook out of the bundle.\n// If you are using the CJS build in a non-React environment, import only from the core entry\n// or use the ESM build with a bundler that supports tree-shaking.\nimport { useState, useEffect, useCallback, useRef } from 'react'\nimport { searchThaiAddress } from '../core/search'\nimport { formatThaiAddressSuggestion } from '../core/formatter'\nimport { resolveThaiAddress } from '../core/resolver'\nimport type {\n UseThaiAddressAutocompleteOptions,\n ThaiAddressRecord,\n ThaiAddressSuggestion,\n ResolvedThaiAddress,\n} from '../types'\n\nexport function useThaiAddressAutocomplete(options: UseThaiAddressAutocompleteOptions) {\n const { index, limit = 10, debounce: debounceMs = 200, threshold = 0.4 } = options\n\n const [query, setQueryState] = useState('')\n const [suggestions, setSuggestions] = useState<ThaiAddressSuggestion[]>([])\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const matchedRecordsMapRef = useRef<Map<string, ThaiAddressRecord>>(new Map())\n\n const setQuery = useCallback((value: string) => {\n setQueryState(value)\n }, [])\n\n useEffect(() => {\n if (timerRef.current) clearTimeout(timerRef.current)\n\n if (query.length === 0) {\n setSuggestions([])\n matchedRecordsMapRef.current = new Map()\n return\n }\n\n timerRef.current = setTimeout(() => {\n const records = searchThaiAddress(index, query, { limit, threshold })\n const formatted = records.map(formatThaiAddressSuggestion)\n const recordMap = new Map<string, ThaiAddressRecord>()\n for (let i = 0; i < records.length; i++) {\n recordMap.set(formatted[i].id, records[i])\n }\n matchedRecordsMapRef.current = recordMap\n setSuggestions(formatted)\n }, debounceMs)\n\n return () => {\n if (timerRef.current) clearTimeout(timerRef.current)\n }\n }, [query, index, limit, threshold, debounceMs])\n\n /**\n * Resolves a suggestion to a full ThaiAddress and clears the suggestions list.\n *\n * Note: `query` is intentionally left unchanged so the input field retains the\n * typed text. Call `clear()` afterwards if you want to reset the input as well.\n */\n const selectSuggestion = useCallback(\n (item: ThaiAddressSuggestion): ResolvedThaiAddress => {\n const record = matchedRecordsMapRef.current.get(item.id)\n if (!record) {\n throw new Error(`[thaizip] No record found for suggestion id \"${item.id}\". Make sure to use suggestions returned by this hook.`)\n }\n setSuggestions([])\n matchedRecordsMapRef.current = new Map()\n return resolveThaiAddress(record)\n },\n [],\n )\n\n const clear = useCallback(() => {\n setQueryState('')\n setSuggestions([])\n matchedRecordsMapRef.current = new Map()\n }, [])\n\n return {\n query,\n setQuery,\n suggestions,\n isOpen: query.length > 0 && suggestions.length > 0,\n selectSuggestion,\n clear,\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/normalizer.ts","../src/core/trigrams.ts","../src/core/indexer.ts","../src/data/loader.ts","../src/core/search.ts","../src/core/formatter.ts","../src/core/resolver.ts","../src/react/useThaiAddressAutocomplete.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["const TONE_MARKS = /[\\u0E48-\\u0E4B\\u0E47\\u0E4C]/g\nconst ADDRESS_PREFIXES = /^(จังหวัด|อำเภอ|ตำบล|แขวง|เขต)/\n\nexport function normalizeThaiAddressText(input: string): string {\n if (!input) return ''\n let text = input.trim()\n text = text.replace(ADDRESS_PREFIXES, '').trim()\n text = text.replace(TONE_MARKS, '')\n text = text.toLowerCase()\n return text\n}\n","import { normalizeThaiAddressText } from './normalizer'\n\n// Public: for callers who start from raw (un-normalised) text — e.g. indexer\nexport function extractTrigrams(text: string): Set<string> {\n return extractTrigramsNormalized(normalizeThaiAddressText(text))\n}\n\n// Internal: for callers who already hold a normalised string — e.g. search.ts\nexport function extractTrigramsNormalized(text: string): Set<string> {\n const result = new Set<string>()\n if (text.length === 0) return result\n if (text.length < 3) {\n result.add(text)\n return result\n }\n for (let i = 0; i <= text.length - 3; i++) {\n result.add(text.slice(i, i + 3))\n }\n return result\n}\n","import { extractTrigrams } from './trigrams'\nimport type { RawData, ThaiAddressRecord, TrigramIndex } from '../types'\n\nexport function buildThaiAddressIndex(data: RawData): TrigramIndex {\n const { provinces, amphures, tambons } = data\n\n // Build lookup maps (filter deleted)\n const provMap = new Map(\n provinces.filter(p => !p.deleted_at).map(p => [p.id, p])\n )\n const ampMap = new Map(\n amphures.filter(a => !a.deleted_at).map(a => [a.id, a])\n )\n\n const records: ThaiAddressRecord[] = []\n const map = new Map<string, Set<number>>()\n const zipIndex = new Map<string, number[]>()\n\n for (const tambon of tambons) {\n if (tambon.deleted_at) continue\n\n const amphure = ampMap.get(tambon.amphure_id)\n if (!amphure) {\n console.warn(\n `[thaizip] skipping tambon id=${tambon.id} (${tambon.name_th}): ` +\n `amphure_id=${tambon.amphure_id} not found or deleted`\n )\n continue\n }\n\n const province = provMap.get(amphure.province_id)\n if (!province) continue\n\n const record: ThaiAddressRecord = {\n provinceId: province.id,\n provinceNameTh: province.name_th,\n provinceNameEn: province.name_en,\n amphureId: amphure.id,\n amphureNameTh: amphure.name_th,\n amphureNameEn: amphure.name_en,\n tambonId: tambon.id,\n tambonNameTh: tambon.name_th,\n tambonNameEn: tambon.name_en,\n zipCode: String(tambon.zip_code),\n }\n\n const idx = records.length\n records.push(record)\n\n // Build zip index\n const existing = zipIndex.get(record.zipCode)\n if (existing) existing.push(idx)\n else zipIndex.set(record.zipCode, [idx])\n\n const fields = [\n record.tambonNameTh,\n record.tambonNameEn,\n record.amphureNameTh,\n record.amphureNameEn,\n record.provinceNameTh,\n record.provinceNameEn,\n record.zipCode,\n ]\n\n for (const field of fields) {\n for (const trigram of extractTrigrams(field)) {\n let set = map.get(trigram)\n if (!set) {\n set = new Set()\n map.set(trigram, set)\n }\n set.add(idx)\n }\n }\n }\n\n return { map, records, zipIndex }\n}\n","import { buildThaiAddressIndex } from '../core/indexer'\nimport type { TrigramIndex } from '../types'\nimport type { CompactProvince, CompactAmphure, CompactTambon } from './defaultData'\n\nlet cached: TrigramIndex | null = null\n\nexport function clearDefaultIndex(): void {\n cached = null\n}\n\nexport async function loadDefaultIndex(): Promise<TrigramIndex> {\n if (cached) return cached\n const { p, a, t } = await import('./defaultData') as {\n p: CompactProvince[]\n a: CompactAmphure[]\n t: CompactTambon[]\n }\n cached = buildThaiAddressIndex({\n provinces: p.map(([id, name_th, name_en]) => ({ id, name_th, name_en, geography_id: 0, deleted_at: null })),\n amphures: a.map(([id, name_th, name_en, province_id]) => ({ id, name_th, name_en, province_id, deleted_at: null })),\n tambons: t.map(([id, name_th, name_en, amphure_id, zip_code]) => ({ id, name_th, name_en, amphure_id, zip_code: Number(zip_code), deleted_at: null })),\n })\n return cached\n}\n","import { normalizeThaiAddressText } from './normalizer'\nimport { extractTrigramsNormalized } from './trigrams'\nimport type { SearchOptions, ThaiAddressRecord, TrigramIndex } from '../types'\n\nconst ZIP_CODE_RE = /^\\d+$/\n\nexport function searchThaiAddress(\n index: TrigramIndex,\n query: string,\n options?: SearchOptions,\n): ThaiAddressRecord[] {\n const limit = options?.limit ?? 10\n const threshold = options?.threshold ?? 0.4\n\n if (!index || !query) return []\n const normalized = normalizeThaiAddressText(query)\n if (normalized.length === 0) return []\n if (normalized.length > 300) return []\n\n // Special case: zip code query (require at least 2 digits to avoid overly broad matches)\n if (ZIP_CODE_RE.test(normalized)) {\n if (normalized.length < 2) return []\n const matches: ThaiAddressRecord[] = []\n for (const [zip, indices] of index.zipIndex) {\n if (zip.startsWith(normalized)) {\n for (const idx of indices) matches.push(index.records[idx])\n }\n }\n // exact match first, then ascending zip code\n matches.sort((a, b) => {\n if (a.zipCode === normalized && b.zipCode !== normalized) return -1\n if (b.zipCode === normalized && a.zipCode !== normalized) return 1\n return a.zipCode.localeCompare(b.zipCode)\n })\n return matches.slice(0, limit)\n }\n\n const queryTrigrams = extractTrigramsNormalized(normalized)\n\n // Accumulate hit counts per record index\n const hits = new Map<number, number>()\n for (const trigram of queryTrigrams) {\n const candidates = index.map.get(trigram)\n if (!candidates) continue\n for (const idx of candidates) {\n hits.set(idx, (hits.get(idx) ?? 0) + 1)\n }\n }\n\n const scored: { idx: number; score: number }[] = []\n for (const [idx, count] of hits) {\n const score = count / queryTrigrams.size\n if (score >= threshold) {\n scored.push({ idx, score })\n }\n }\n\n scored.sort((a, b) => {\n if (b.score !== a.score) return b.score - a.score\n const ra = index.records[a.idx]\n const rb = index.records[b.idx]\n return (\n ra.provinceNameTh.localeCompare(rb.provinceNameTh) ||\n ra.amphureNameTh.localeCompare(rb.amphureNameTh) ||\n ra.tambonNameTh.localeCompare(rb.tambonNameTh)\n )\n })\n\n return scored.slice(0, limit).map(({ idx }) => index.records[idx])\n}\n","import type { ThaiAddressRecord, ThaiAddressSuggestion } from '../types'\n\nexport function formatThaiAddressSuggestion(record: ThaiAddressRecord): ThaiAddressSuggestion {\n return {\n id: String(record.tambonId),\n label: `${record.tambonNameTh} > ${record.amphureNameTh} > ${record.provinceNameTh} ${record.zipCode}`,\n tambon: record.tambonNameTh,\n tambonEn: record.tambonNameEn,\n amphure: record.amphureNameTh,\n amphureEn: record.amphureNameEn,\n province: record.provinceNameTh,\n provinceEn: record.provinceNameEn,\n zipCode: record.zipCode,\n }\n}\n","import type { ThaiAddressRecord, ResolvedThaiAddress } from '../types'\n\nexport function resolveThaiAddress(record: ThaiAddressRecord): ResolvedThaiAddress {\n return {\n tambon: record.tambonNameTh,\n tambonEn: record.tambonNameEn,\n amphure: record.amphureNameTh,\n amphureEn: record.amphureNameEn,\n province: record.provinceNameTh,\n provinceEn: record.provinceNameEn,\n zipCode: record.zipCode,\n subdistrict: record.tambonNameTh,\n subdistrictEn: record.tambonNameEn,\n district: record.amphureNameTh,\n districtEn: record.amphureNameEn,\n postalCode: record.zipCode,\n }\n}\n","// NOTE (SZ-4): CJS consumers (`dist/index.cjs`) cannot tree-shake this hook out of the bundle.\n// If you are using the CJS build in a non-React environment, import only from the core entry\n// or use the ESM build with a bundler that supports tree-shaking.\nimport { useState, useEffect, useCallback, useRef } from 'react'\nimport { searchThaiAddress } from '../core/search'\nimport { formatThaiAddressSuggestion } from '../core/formatter'\nimport { resolveThaiAddress } from '../core/resolver'\nimport type {\n UseThaiAddressAutocompleteOptions,\n ThaiAddressRecord,\n ThaiAddressSuggestion,\n ResolvedThaiAddress,\n} from '../types'\n\nexport function useThaiAddressAutocomplete(options: UseThaiAddressAutocompleteOptions) {\n const { index, limit = 10, debounce: debounceMs = 200, threshold = 0.4 } = options\n\n const [query, setQueryState] = useState('')\n const [suggestions, setSuggestions] = useState<ThaiAddressSuggestion[]>([])\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const matchedRecordsMapRef = useRef<Map<string, ThaiAddressRecord>>(new Map())\n\n const setQuery = useCallback((value: string) => {\n setQueryState(value)\n }, [])\n\n useEffect(() => {\n if (timerRef.current) clearTimeout(timerRef.current)\n\n if (query.length === 0) {\n setSuggestions([])\n matchedRecordsMapRef.current = new Map()\n return\n }\n\n timerRef.current = setTimeout(() => {\n const records = searchThaiAddress(index, query, { limit, threshold })\n const formatted = records.map(formatThaiAddressSuggestion)\n const recordMap = new Map<string, ThaiAddressRecord>()\n for (let i = 0; i < records.length; i++) {\n recordMap.set(formatted[i].id, records[i])\n }\n matchedRecordsMapRef.current = recordMap\n setSuggestions(formatted)\n }, debounceMs)\n\n return () => {\n if (timerRef.current) clearTimeout(timerRef.current)\n }\n }, [query, index, limit, threshold, debounceMs])\n\n /**\n * Resolves a suggestion to a full ThaiAddress and clears the suggestions list.\n *\n * Note: `query` is intentionally left unchanged so the input field retains the\n * typed text. Call `clear()` afterwards if you want to reset the input as well.\n */\n const selectSuggestion = useCallback(\n (item: ThaiAddressSuggestion): ResolvedThaiAddress => {\n const record = matchedRecordsMapRef.current.get(item.id)\n if (!record) {\n throw new Error(`[thaizip] No record found for suggestion id \"${item.id}\". Make sure to use suggestions returned by this hook.`)\n }\n setSuggestions([])\n matchedRecordsMapRef.current = new Map()\n return resolveThaiAddress(record)\n },\n [],\n )\n\n const clear = useCallback(() => {\n setQueryState('')\n setSuggestions([])\n matchedRecordsMapRef.current = new Map()\n }, [])\n\n return {\n query,\n setQuery,\n suggestions,\n isOpen: query.length > 0 && suggestions.length > 0,\n selectSuggestion,\n clear,\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thaizip",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Fast, fuzzy Thai address autocomplete library (headless + React)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"thai",
|
|
@@ -68,6 +68,6 @@
|
|
|
68
68
|
}
|
|
69
69
|
},
|
|
70
70
|
"engines": {
|
|
71
|
-
"node": ">=
|
|
71
|
+
"node": ">=18"
|
|
72
72
|
}
|
|
73
73
|
}
|