UNPKG

@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
/** * @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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