geothai
Version:
An npm package for Node.js to access detailed geographic data on Thailand, including provinces, districts, subdistricts, and postal code.
289 lines (278 loc) • 9.29 kB
JavaScript
import { existsSync, readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
//#region src/utils/resolve-data-path.ts
/**
* Resolves the path to a data file, checking production path first then falling back to development path
*
* @param filename - The name of the data file (e.g., "geo.json", "metadata.json")
* @param importMetaUrl - The import.meta.url from the calling module
* @returns The resolved path to the data file
*/
function resolveDataPath(filename, importMetaUrl) {
const currentDir = dirname(fileURLToPath(importMetaUrl));
const srcDir = currentDir.includes("/services") ? join(currentDir, "../..") : join(currentDir, "..");
const prodPath = join(srcDir, "dist/data", filename);
const devPath = join(srcDir, "data/data/v4", filename);
return existsSync(prodPath) ? prodPath : devPath;
}
//#endregion
//#region src/utils/cache.ts
var Cache = class {
cacheMap = /* @__PURE__ */ new Map();
defaultTTL = 3600 * 1e3;
has(key) {
const item = this.cacheMap.get(key);
if (item) {
if (item.expiry && Date.now() > item.expiry) {
this.cacheMap.delete(key);
return false;
}
return true;
}
return false;
}
get(key) {
if (this.has(key)) return this.cacheMap.get(key)?.value;
}
set(key, value, ttl) {
const expiry = Date.now() + (ttl ?? this.defaultTTL);
this.cacheMap.set(key, {
value,
expiry
});
}
clear() {
this.cacheMap.clear();
}
};
const cache = new Cache();
//#endregion
//#region src/utils/criteria-matcher.ts
/**
* Checks if an item matches the given search criteria.
*
* @param item - The item to check against the criteria
* @param criterion - The partial object containing the criteria to match
* @returns True if the item matches all criteria, false otherwise
*/
function matchCriteria(item, criterion) {
return Object.entries(criterion).every(([key, value]) => {
return item[key] === value;
});
}
//#endregion
//#region src/utils/create-service.ts
/**
* Creates a service for managing and querying a collection of data items.
*
* @param data - The array of data items to manage
* @param idKey - The key to use as the unique identifier for each item
* @returns An object with methods to query the data collection
*/
function createService(data, idKey) {
const dataMap = new Map(data.map((item) => [item[idKey], item]));
return {
getAll: () => Array.from(dataMap.values()),
getByCode: (code) => dataMap.get(Number(code)),
getByCriterion: (criterion) => {
return Array.from(dataMap.values()).filter((item) => matchCriteria(item, criterion));
}
};
}
//#endregion
//#region src/services/district.ts
const districts = JSON.parse(readFileSync(resolveDataPath("geo.json", import.meta.url), "utf-8")).flatMap((province) => province.districts);
const createDistrictService = (data) => {
return createService(data, "code");
};
const districtService = createService(districts, "code");
/**
* Retrieves all districts from the database.
*
* @returns An array of all district objects
*/
function getAllDistricts() {
const key = "districts";
const cached = cache.get(key);
if (cached) return cached;
const allDistricts = districtService.getAll();
cache.set(key, allDistricts);
return allDistricts;
}
/**
* Retrieves a district by its unique code.
*
* @param code - The unique identifier code of the district
* @returns The district object if found, undefined otherwise
*/
function getDistrictByCode(code) {
const key = `district-${code}`;
const cached = cache.get(key);
if (cached) return cached;
const district = districtService.getByCode(code);
cache.set(key, district);
return district;
}
/**
* Retrieves districts that match the specified search criteria.
*
* @param criterion - The partial district object containing search criteria
* @returns An array of districts matching the criteria
*/
function getDistrictsByCriterion(criterion) {
const key = `districts-${JSON.stringify(criterion)}`;
const cached = cache.get(key);
if (cached) return cached;
const matchedDistricts = districtService.getByCriterion(criterion);
cache.set(key, matchedDistricts);
return matchedDistricts;
}
//#endregion
//#region src/utils/record-to-array.ts
/**
* Converts a record object to an array of its values.
*
* @param record - The record object to convert
* @returns An array containing all values from the record
*/
function recordToArray(record) {
return Object.values(record);
}
//#endregion
//#region src/services/postal-code.ts
const postalCodes = recordToArray(JSON.parse(readFileSync(resolveDataPath("postal_lookup.json", import.meta.url), "utf-8")));
const createPostalCodeService = (data) => {
return createService(data, "postal_code");
};
const postalCodeService = createService(postalCodes, "postal_code");
/**
* Retrieves all postal codes from the database.
*
* @returns An array of all postal code objects
*/
function getAllPostalCodes() {
const key = "postal-codes";
const cached = cache.get(key);
if (cached) return cached;
const allPostalCodes = postalCodeService.getAll();
cache.set(key, allPostalCodes);
return allPostalCodes;
}
/**
* Retrieves a postal code by its unique code.
*
* @param code - The unique identifier code of the postal code
* @returns The postal code object if found, undefined otherwise
*/
function getPostalCode(code) {
const key = `postal-codes-${code}`;
const cached = cache.get(key);
if (cached) return cached;
const postalCode = postalCodeService.getByCode(code);
cache.set(key, postalCode);
return postalCode;
}
//#endregion
//#region src/services/province.ts
const provinces = JSON.parse(readFileSync(resolveDataPath("geo.json", import.meta.url), "utf-8"));
const createProvinceService = (data) => {
return createService(data, "code");
};
const provinceService = createService(provinces, "code");
/**
* Retrieves all provinces from the database.
*
* @returns An array of all province objects
*/
function getAllProvinces() {
const key = "provinces";
const cached = cache.get(key);
if (cached) return cached;
const allProvinces = provinceService.getAll();
cache.set(key, allProvinces);
return allProvinces;
}
/**
* Retrieves a province by its unique code.
*
* @param code - The unique identifier code of the province
* @returns The province object if found, undefined otherwise
*/
function getProvinceByCode(code) {
const key = `province-${code}`;
const cached = cache.get(key);
if (cached) return cached;
const province = provinceService.getByCode(code);
cache.set(key, province);
return province;
}
/**
* Retrieves provinces that match the specified search criteria.
*
* @param criterion - The partial province object containing search criteria
* @returns An array of provinces matching the criteria
*/
function getProvincesByCriterion(criterion) {
const key = `provinces-${JSON.stringify(criterion)}`;
const cached = cache.get(key);
if (cached) return cached;
const matchedProvinces = provinceService.getByCriterion(criterion);
cache.set(key, matchedProvinces);
return matchedProvinces;
}
//#endregion
//#region src/services/subdistrict.ts
const subdistricts = JSON.parse(readFileSync(resolveDataPath("geo.json", import.meta.url), "utf-8")).flatMap((province) => province.districts.flatMap((district) => district.subdistricts));
const createSubdistrictService = (data) => {
return createService(data, "code");
};
const subdistrictService = createService(subdistricts, "code");
/**
* Retrieves all subdistricts from the database.
*
* @returns An array of all subdistrict objects
*/
function getAllSubdistricts() {
const key = "subdistricts";
const cached = cache.get(key);
if (cached) return cached;
const allSubdistricts = subdistrictService.getAll();
cache.set(key, allSubdistricts);
return allSubdistricts;
}
/**
* Retrieves a subdistrict by its unique code.
*
* @param code - The unique identifier code of the subdistrict
* @returns The subdistrict object if found, undefined otherwise
*/
function getSubdistrictByCode(code) {
const key = `subdistrict-${code}`;
const cached = cache.get(key);
if (cached) return cached;
const subdistrict = subdistrictService.getByCode(code);
cache.set(key, subdistrict);
return subdistrict;
}
/**
* Retrieves subdistricts that match the specified search criteria.
*
* @param criterion - The partial subdistrict object containing search criteria
* @returns An array of subdistricts matching the criteria
*/
function getSubdistrictsByCriterion(criterion) {
const key = `subdistricts-${JSON.stringify(criterion)}`;
const cached = cache.get(key);
if (cached) return cached;
const matchedSubdistricts = subdistrictService.getByCriterion(criterion);
cache.set(key, matchedSubdistricts);
return matchedSubdistricts;
}
//#endregion
//#region src/index.ts
const metaData = JSON.parse(readFileSync(resolveDataPath("metadata.json", import.meta.url), "utf-8"));
const metadata = metaData;
//#endregion
export { createDistrictService, createPostalCodeService, createProvinceService, createSubdistrictService, getAllDistricts, getAllPostalCodes, getAllProvinces, getAllSubdistricts, getDistrictByCode, getDistrictsByCriterion, getPostalCode, getProvinceByCode, getProvincesByCriterion, getSubdistrictByCode, getSubdistrictsByCriterion, matchCriteria, metadata, provinces as rawProvinces };
//# sourceMappingURL=index.mjs.map