export interface Coordinates { lat: number; lon: number; } export type SourceErrorReason = 'missing_api_key' | 'invalid_api_key'; export interface SourceResult { sourceId: string; ok: boolean; fetchedAt: Date; data: T | null; error?: string; /** Structured error category — set when the failure is user-actionable (e.g. key config). */ reason?: SourceErrorReason; /** Env var the user should set/fix when `reason` points at a key problem. */ envVar?: string; latencyMs: number; /** Distance to nearest monitoring station, when available */ stationDistanceKm?: number; } export interface ConfigHint { sourceId: string; envVar: string; reason: SourceErrorReason; /** Human-readable guidance (LLM-friendly); includes setup URL when known. */ message: string; /** Approx. confidence score this source contributes (1 / applicable count). */ confidenceImpact: number; } export interface WeatherData { tempC: number; feelsLikeC: number; humidityPct: number; windKmh: number; precipitationMm: number; condition: string; uvIndex: number; } export interface SeismicEvent { id: string; magnitude: number; depthKm: number; distanceKm: number; place: string; timeUtc: string; tsunami: boolean; } export interface SeismicData { recentEvents: SeismicEvent[]; nearestEventDistanceKm: number | null; maxMagnitude: number | null; } export interface FireHotspot { lat: number; lon: number; brightness: number; confidence: number; distanceKm: number; } export interface FireData { hotspotsNearby: FireHotspot[]; totalHotspots100km: number; totalHotspots500km: number; maxBrightness: number | null; nearestDistanceKm: number | null; } export interface AirQualityData { aqi: number; pm25: number; pm10: number; no2: number; o3: number; co?: number; category: string; dominantPollutant: string; /** Which source provided the data */ source: 'openaq' | 'open-meteo-aq'; /** Distance to nearest station (OpenAQ only) */ stationDistanceKm?: number; /** * Pollutant keys whose current value exceeds the WHO 2021 24-hr guideline. * Subset of {'pm25','pm10','no2','o3','co'}. Empty array when nothing exceeds. */ whoExceedances: string[]; } export interface FloodData { returnPeriod: '< 5y' | '> 5y' | '> 20y' | '> 100y'; dischargeM3s: number | null; forecastDays: number; riverName: string | null; } export interface SpaceWeatherData { kpIndex: number; kpCategory: string; solarWindSpeedKms: number | null; geomagneticStorm: boolean; auroraAlert: boolean; } export interface VolcanicActivity { volcanoName: string; region: string; activityLevel: string; date: string; lat: number; lon: number; distanceKm: number; } export interface VolcanicData { recentActivity: VolcanicActivity[]; nearbyCount: number; } export interface TsunamiWarning { id: string; severity: string; area: string; issuedAt: string; description: string; } export interface TsunamiData { activeWarnings: TsunamiWarning[]; hasActiveWarning: boolean; } export interface NWSAlert { id: string; event: string; severity: 'Extreme' | 'Severe' | 'Moderate' | 'Minor' | 'Unknown'; urgency: string; headline: string; description: string; onset: string; expires: string; } export interface NWSData { activeAlerts: NWSAlert[]; totalAlerts: number; } export interface MarineData { seaSurfaceTempC: number | null; waveHeightM: number | null; currentSpeedKms: number | null; seaLevelAnomalyM: number | null; } export type LayerSource = 'realtime' | 'forecast' | 'decayed'; export interface ForecastDay { date: string; dayOffset: number; weather?: { tempMin: number; tempMax: number; tempMean: number; tempStddev: number; precipitationMm: number; precipitationProbability: number; precipitationStddev: number; windMaxKmh: number; windMean: number; windStddev: number; uvMax: number; source: LayerSource; }; airQuality?: { pm25: number; pm10: number; aqi: number; source: LayerSource; }; flood?: { dischargeM3s: number; trend: 'rising' | 'falling' | 'stable'; source: LayerSource; }; spaceWeather?: { kpIndex: number; category: string; source: LayerSource; }; seismic?: { score: number; decayFactor: number; source: LayerSource; }; fire?: { score: number; decayFactor: number; source: LayerSource; }; volcanic?: { score: number; source: LayerSource; }; tsunami?: { active: boolean; expiresAt: string | null; source: LayerSource; }; nwsAlerts?: { count: number; maxSeverity: string | null; source: LayerSource; }; marine?: { waveHeightM: number | null; source: LayerSource; }; risk: { score: number; level: RiskLevel; mainFactors: string[]; }; confidence: { overall: number; level: ConfidenceLevel; }; } export interface ForecastResponse { location: Coordinates; /** Full real-time snapshot (day 0 baseline) */ current: AggregatedConditions; days: ForecastDay[]; models: string[]; sources: string[]; generatedAt: string; } export interface CompareSourcesResponse { location: Coordinates; timestampUtc: string; sources: Record; discrepancies: Discrepancy[]; } export type ConfidenceLevel = 'reliable' | 'partial' | 'limited' | 'estimate'; export interface ConfidenceScore { overall: number; level: ConfidenceLevel; label: string; /** Source ids whose scope includes this location */ applicableSources: string[]; /** Subset of applicable sources that returned ok data within freshness tolerance */ okSources: string[]; /** Applicable sources that either failed or returned stale data */ failedSources: string[]; /** Sources excluded from the ratio because they don't apply at this location */ notApplicableSources: string[]; } export type RiskLevel = 'minimal' | 'low' | 'moderate' | 'high' | 'critical'; export interface RiskAssessment { overallScore: number; level: RiskLevel; mainFactors: string[]; layerScores: { weather?: number; seismic?: number; fire?: number; airQuality?: number; flood?: number; space?: number; volcanic?: number; }; } export interface AggregatedConditions { location: Coordinates; timestampUtc: string; sourcesQueried: string[]; sourcesFailed: string[]; weather: WeatherData | null; seismic: SeismicData | null; fire: FireData | null; airQuality: AirQualityData | null; flood: FloodData | null; spaceWeather: SpaceWeatherData | null; volcanic: VolcanicData | null; tsunami: TsunamiData | null; nwsAlerts: NWSData | null; marine: MarineData | null; gdacs: import('../sources/gdacs.js').GdacsData | null; confidence: ConfidenceScore; risk: RiskAssessment; discrepancies: Discrepancy[]; /** Actionable setup hints (missing/invalid API keys). Empty when all sources are configured. */ configHints: ConfigHint[]; } export interface Discrepancy { id: string; timestampUtc: string; location: Coordinates; field: string; sourceA: string; valueA: number; sourceB: string; valueB: number; delta: number; relativeDelta: number; expected?: boolean; } export interface AggregatorOptions { radiusKm?: number; firmsKey?: string; }