import { readFile } from 'node:fs/promises'; export type FeatureGeometry = { type: 'Polygon' | 'MultiPolygon'; coordinates: unknown; }; export type GeoJsonFeature = { type: 'Feature'; properties?: Record | null; geometry?: FeatureGeometry | null; }; export type GeoJsonFeatureCollection = { type: 'FeatureCollection'; features: GeoJsonFeature[]; }; export type PostalDatasetConfig = { countryCode: 'US' | 'CA'; label: string; filePath: string; postalCodeKeys: string[]; displayNameKeys: string[]; }; export async function readFeatureCollection(filePath: string) { const raw = await readFile(filePath, 'utf8'); const parsed = JSON.parse(raw) as GeoJsonFeatureCollection; if (parsed.type !== 'FeatureCollection' || !Array.isArray(parsed.features)) { throw new Error(`Dataset at ${filePath} is not a valid GeoJSON FeatureCollection.`); } return parsed; } export function normalizePostalCode(countryCode: 'US' | 'CA', rawPostalCode: string) { const trimmed = rawPostalCode.trim(); if (countryCode === 'US') { const digits = trimmed.replace(/\D/g, '').slice(0, 5); return digits.length === 5 ? digits : null; } const compact = trimmed.replace(/\s+/g, '').toUpperCase(); return compact.length >= 3 ? compact.slice(0, 3) : null; } export function getStringProperty(properties: Record | null | undefined, keys: string[]) { if (!properties) { return null; } for (const key of keys) { const value = properties[key]; if (typeof value === 'string' && value.trim().length > 0) { return value.trim(); } if (typeof value === 'number' && Number.isFinite(value)) { return String(value); } } return null; } export function getFeatureGeometry(feature: GeoJsonFeature, filePath: string, index: number) { if (!feature.geometry || (feature.geometry.type !== 'Polygon' && feature.geometry.type !== 'MultiPolygon')) { throw new Error(`Feature ${index} in ${filePath} is missing a Polygon or MultiPolygon geometry.`); } return feature.geometry; }