Public Access
1
0
Files
leads4less/db/scripts/postal-import-utils.ts
T
pguerrerox cc00a439bf feat: add deep research planning and postal batch runs
Add a dedicated Deep Research view with postal-area preview overlays, batch execution, and bundled map results.

Also add postal dataset import tooling and fix local API networking and research insert issues needed to support the new workflow.
2026-04-05 18:05:04 +00:00

77 lines
2.0 KiB
TypeScript

import { readFile } from 'node:fs/promises';
export type FeatureGeometry = {
type: 'Polygon' | 'MultiPolygon';
coordinates: unknown;
};
export type GeoJsonFeature = {
type: 'Feature';
properties?: Record<string, unknown> | 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<string, unknown> | 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;
}