@lineai/municipal-intel
Version:
AI-first municipal data API providing natural language descriptions of building permits and planning applications from major US cities
265 lines • 18.4 kB
JavaScript
/**
* @lineai/municipal-intel
*
* Access municipal planning applications, building permits, and construction
* activity data from major US cities.
*/
// Types
export * from './types';
// Clients
export * from './clients';
// Registry
export * from './registry';
// Main API class
import { ClientFactory } from './clients';
import { sourceRegistry } from './registry';
/**
* Main municipal intelligence API
*/
export class MunicipalIntel {
clientFactory;
registry;
constructor(config = {}) {
this.clientFactory = new ClientFactory(config);
this.registry = sourceRegistry;
}
/**
* Search municipal projects
*/
async search(params) {
let source;
if (params.municipalityId) {
// Search specific municipality
const foundSource = this.registry.getSource(params.municipalityId);
if (!foundSource) {
throw new Error(`Municipality not found: ${params.municipalityId}`);
}
source = foundSource;
}
else {
// Default to first available source if no municipality specified
const sources = this.registry.getImplementationReadySources();
if (sources.length === 0) {
throw new Error('No available sources for search');
}
source = sources[0];
}
const client = this.clientFactory.createClient(source, params);
return client.search();
}
/**
* Get a project by ID from a specific source
*/
async getProject(sourceId, projectId) {
const source = this.registry.getSource(sourceId);
if (!source) {
throw new Error(`Source not found: ${sourceId}`);
}
// Create minimal params for getProject - we only need the municipalityId
const params = { municipalityId: sourceId };
const client = this.clientFactory.createClient(source, params);
return client.getProject(projectId);
}
/**
* Get a project by its URL
*/
async getByUrl(url) {
const sourceId = this.extractSourceFromUrl(url);
if (!sourceId) {
throw new Error(`Cannot determine source from URL: ${url}`);
}
const source = this.registry.getSource(sourceId);
if (!source) {
throw new Error(`Source not found: ${sourceId}`);
}
const params = { municipalityId: sourceId };
const client = this.clientFactory.createClient(source, params);
return client.getByUrl(url);
}
/**
* Extract source ID from municipal-intel URL
*/
extractSourceFromUrl(url) {
try {
const urlObj = new URL(url);
// Expected format: /projects/{sourceId}/{datasetId}/{projectId}
const pathParts = urlObj.pathname.split('/');
if (pathParts.length >= 4 && pathParts[1] === 'projects') {
return pathParts[2]; // Return the sourceId part
}
return null;
}
catch (error) {
return null;
}
}
/**
* Get available municipalities with their datasets (AI Discovery API)
*/
getAvailableMunicipalities() {
const sources = this.registry.getAllSources();
return sources.map(source => ({
id: source.id,
name: source.name,
state: source.state,
datasets: source.api?.datasets
? Object.entries(source.api.datasets).map(([id, dataset]) => ({
id,
name: dataset.name
}))
: []
}));
}
/**
* Get search capabilities for a municipality
*/
getSearchCapabilities(municipalityId) {
const source = this.registry.getSource(municipalityId);
if (!source) {
throw new Error(`Municipality not found: ${municipalityId}`);
}
// For now, return capabilities based on what our Socrata client supports
// This could be enhanced to be source-specific
const supportedFilters = ['submitDateFrom', 'submitDateTo', 'statuses', 'addresses', 'keywords'];
const supportedSorts = ['submitDate', 'address'];
const limitations = [];
// Check if value field is available for value filters
if (source.api?.datasets) {
const primaryDataset = Object.values(source.api.datasets)[0];
if (primaryDataset?.fieldMappings?.value) {
supportedFilters.push('minValue', 'maxValue');
supportedSorts.push('value');
}
else {
limitations.push('No value field available - minValue/maxValue filters not supported');
}
// Check if approval date is available
if (primaryDataset?.fieldMappings?.approvalDate) {
supportedFilters.push('approvalDateFrom', 'approvalDateTo');
supportedSorts.push('approvalDate');
}
}
return {
supportedFilters,
supportedSorts,
limitations: limitations.length > 0 ? limitations : undefined
};
}
/**
* Get field schema for a dataset
*/
getDatasetSchema(municipalityId, datasetId) {
const source = this.registry.getSource(municipalityId);
if (!source || !source.api?.datasets) {
throw new Error(`Municipality or datasets not found: ${municipalityId}`);
}
// Get the specified dataset or the first one
const dataset = datasetId
? source.api.datasets[datasetId]
: Object.values(source.api.datasets)[0];
if (!dataset) {
throw new Error(`Dataset not found: ${datasetId || 'default'}`);
}
// Convert fields to schema format
const fieldMappings = dataset.fieldMappings || {};
const searchableLogicalFields = Object.keys(fieldMappings);
return dataset.fields.map(fieldName => {
const isSearchable = Object.values(fieldMappings).includes(fieldName);
// Determine field type based on field name patterns
let type = 'string';
if (fieldName.includes('date') || fieldName.includes('_date')) {
type = 'date';
}
else if (fieldName.includes('cost') || fieldName.includes('value') || fieldName.includes('amount')) {
type = 'number';
}
return {
name: fieldName,
type,
searchable: isSearchable,
description: isSearchable
? `Searchable field mapped to: ${searchableLogicalFields.find(logical => fieldMappings[logical] === fieldName)}`
: undefined
};
});
}
/**
* Get available sources
*/
getSources(filters) {
let sources = this.registry.getAllSources();
if (filters?.state) {
const filterState = filters.state.toLowerCase();
sources = sources.filter(s => s.state.toLowerCase() === filterState);
}
if (filters?.type) {
sources = sources.filter(s => s.type === filters.type);
}
if (filters?.priority) {
sources = sources.filter(s => s.priority === filters.priority);
}
if (filters?.enabled !== undefined) {
sources = sources.filter(s => (s.enabled !== false) === filters.enabled);
}
return sources;
}
// /**
// * Check health of a specific source
// */
// async healthCheck(sourceId: string) {
// const source = this.registry.getSource(sourceId);
// if (!source) {
// throw new Error(`Source not found: ${sourceId}`);
// }
//
// const client = this.clientFactory.createClient(source);
// const health = await client.healthCheck();
//
// // Update registry with health info
// this.registry.updateSourceStatus(sourceId, {
// lastChecked: health.lastChecked.toISOString(),
// lastError: health.status === 'unhealthy' ? health.error : undefined
// });
//
// return health;
// }
/**
* Set universal Socrata authentication token
*/
setSocrataToken(token) {
this.clientFactory.setSocrataToken(token);
}
/**
* Register a new source at runtime
*/
registerSource(source) {
this.registry.registerSource(source);
}
/**
* Unregister a runtime source
*/
unregisterSource(id) {
return this.registry.unregisterSource(id);
}
/**
* Check if a source is built-in or runtime-added
*/
isBuiltInSource(id) {
return this.registry.isBuiltInSource(id);
}
/**
* Get registry information
*/
getRegistryInfo() {
return this.registry.getRegistryInfo();
}
}
/**
* Create a new MunicipalIntel instance
*/
export function createMunicipalIntel(config) {
return new MunicipalIntel(config);
}
// Default export
export default MunicipalIntel;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7O0dBS0c7QUFFSCxRQUFRO0FBQ1IsY0FBYyxTQUFTLENBQUM7QUFFeEIsVUFBVTtBQUNWLGNBQWMsV0FBVyxDQUFDO0FBRTFCLFdBQVc7QUFDWCxjQUFjLFlBQVksQ0FBQztBQUUzQixpQkFBaUI7QUFDakIsT0FBTyxFQUFFLGFBQWEsRUFBdUIsTUFBTSxXQUFXLENBQUM7QUFDL0QsT0FBTyxFQUFFLGNBQWMsRUFBeUIsTUFBTSxZQUFZLENBQUM7QUFHbkU7O0dBRUc7QUFDSCxNQUFNLE9BQU8sY0FBYztJQUNqQixhQUFhLENBQWdCO0lBQzdCLFFBQVEsQ0FBd0I7SUFFeEMsWUFBWSxTQUE4QixFQUFFO1FBQzFDLElBQUksQ0FBQyxhQUFhLEdBQUcsSUFBSSxhQUFhLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDL0MsSUFBSSxDQUFDLFFBQVEsR0FBRyxjQUFjLENBQUM7SUFDakMsQ0FBQztJQUVEOztPQUVHO0lBQ0gsS0FBSyxDQUFDLE1BQU0sQ0FBQyxNQUE2QjtRQUN4QyxJQUFJLE1BQXVCLENBQUM7UUFFNUIsSUFBSSxNQUFNLENBQUMsY0FBYyxFQUFFO1lBQ3pCLCtCQUErQjtZQUMvQixNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsY0FBYyxDQUFDLENBQUM7WUFDbkUsSUFBSSxDQUFDLFdBQVcsRUFBRTtnQkFDaEIsTUFBTSxJQUFJLEtBQUssQ0FBQywyQkFBMkIsTUFBTSxDQUFDLGNBQWMsRUFBRSxDQUFDLENBQUM7YUFDckU7WUFDRCxNQUFNLEdBQUcsV0FBVyxDQUFDO1NBQ3RCO2FBQU07WUFDTCxpRUFBaUU7WUFDakUsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyw2QkFBNkIsRUFBRSxDQUFDO1lBQzlELElBQUksT0FBTyxD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