import { readFile, readdir } from 'fs/promises';
import { join } from 'path';
import { StateDataSchema, STATE_NAME_MAP } from '../types/stateData.js';
class LRUCache {
cache;
maxSize;
ttl;
constructor(maxSize = 100, ttlSeconds = 3600) {
this.cache = new Map();
this.maxSize = maxSize;
this.ttl = ttlSeconds * 1000;
}
get(key) {
const item = this.cache.get(key);
if (!item)
return null;
if (Date.now() - item.timestamp > this.ttl) {
this.cache.delete(key);
return null;
}
this.cache.delete(key);
this.cache.set(key, item);
return item.value;
}
set(key, value) {
if (this.cache.size >= this.maxSize) {
const firstKey = this.cache.keys().next().value;
if (firstKey) {
this.cache.delete(firstKey);
}
}
this.cache.set(key, { value, timestamp: Date.now() });
}
clear() {
this.cache.clear();
}
size() {
return this.cache.size;
}
has(key) {
return this.cache.has(key) && this.get(key) !== null;
}
}
export class DataLoader {
dataPath;
cache;
allStatesCache = null;
allStatesCacheTimestamp = 0;
cacheTTL;
constructor(dataPath, cacheTTL = 3600) {
this.dataPath = dataPath;
this.cacheTTL = cacheTTL * 1000;
this.cache = new LRUCache(200, cacheTTL);
}
normalizeStateName(input) {
const normalized = input.toLowerCase().trim();
if (STATE_NAME_MAP[normalized]) {
return STATE_NAME_MAP[normalized];
}
if (normalized.includes('-')) {
return normalized;
}
return normalized.replace(/\s+/g, '-');
}
async loadStateData(stateName) {
const normalizedName = this.normalizeStateName(stateName);
const cached = this.cache.get(normalizedName);
if (cached) {
return cached;
}
const filePath = join(this.dataPath, `${normalizedName}.json`);
try {
const fileContent = await readFile(filePath, 'utf-8');
const rawData = JSON.parse(fileContent);
const validatedData = StateDataSchema.parse(rawData);
this.cache.set(normalizedName, validatedData);
return validatedData;
}
catch (error) {
if (error.code === 'ENOENT') {
throw new Error(`State data not found: ${stateName}. Please check the state name format (e.g., 'new-jersey', 'pennsylvania').`);
}
throw new Error(`Failed to load state data for ${stateName}: ${error.message}`);
}
}
async loadAllStates() {
if (this.allStatesCache && Date.now() - this.allStatesCacheTimestamp < this.cacheTTL) {
return this.allStatesCache;
}
try {
const files = await readdir(this.dataPath);
const jsonFiles = files.filter(file => file.endsWith('.json'));
const statePromises = jsonFiles.map(async (file) => {
const stateName = file.replace('.json', '');
return this.loadStateData(stateName);
});
const states = await Promise.all(statePromises);
this.allStatesCache = states;
this.allStatesCacheTimestamp = Date.now();
return states;
}
catch (error) {
throw new Error(`Failed to load all states: ${error.message}`);
}
}
async getAvailableStates() {
try {
const files = await readdir(this.dataPath);
return files
.filter(file => file.endsWith('.json'))
.map(file => file.replace('.json', ''))
.sort();
}
catch (error) {
throw new Error(`Failed to get available states: ${error.message}`);
}
}
async stateExists(stateName) {
const normalizedName = this.normalizeStateName(stateName);
const availableStates = await this.getAvailableStates();
return availableStates.includes(normalizedName);
}
clearCache() {
this.cache.clear();
this.allStatesCache = null;
this.allStatesCacheTimestamp = 0;
}
getCacheStats() {
return {
size: this.cache.size(),
maxSize: 200,
ttlSeconds: this.cacheTTL / 1000,
allStatesCached: this.allStatesCache !== null
};
}
async loadMultipleStates(stateNames) {
const promises = stateNames.map(name => this.loadStateData(name));
return Promise.all(promises);
}
}
//# sourceMappingURL=dataLoader.js.map