wayfarer-weather/lib/weather/types.ts

142 lines
4 KiB
TypeScript
Raw Normal View History

2026-10-31 21:20:16 -05:00
/**
* The one shape the UI understands.
*
* Every provider returns wildly different JSON. Each adapter in ./providers
* translates its provider's response into these types, so the UI never has
* to know which provider it is talking to.
*
* Rules every adapter follows:
* - Units are metric: °C, km/h, hPa, km, mm.
* - Times are ISO-8601 instants in UTC ("2026-10-21T23:00:00.000Z").
* The UI formats them in the location's time zone.
* - A reading the provider doesn't offer is `null`, and the UI hides it.
*/
export type ProviderId = "openweather" | "weatherapi" | "tomorrow" | "openmeteo";
/** A deliberately small set of conditions — one per pixel icon. */
export type Condition = "clear" | "clouds" | "rain" | "storm" | "snow" | "fog";
export interface Place {
name: string;
region?: string;
country?: string;
lat: number;
lon: number;
}
export interface CurrentWeather {
time: string;
tempC: number;
feelsLikeC: number;
condition: Condition;
description: string; // human words, e.g. "light rain"
isDay: boolean;
humidity: number; // %
windKph: number;
gustKph: number | null;
windDeg: number | null; // direction the wind blows FROM, 0 = north
pressureHpa: number | null;
dewPointC: number | null;
visibilityKm: number | null;
uvIndex: number | null;
cloudCover: number | null; // %
precipMm: number | null; // in the last hour
}
export interface HourlyForecast {
time: string; // start of the hour (or 3-hour block)
tempC: number;
feelsLikeC: number | null;
condition: Condition;
isDay: boolean;
precipChance: number | null; // %
precipMm: number | null;
windKph: number;
gustKph: number | null;
windDeg: number | null;
humidity: number | null;
uvIndex: number | null;
}
export interface DailyForecast {
time: string; // any instant inside that local day (midday where possible)
highC: number;
lowC: number;
condition: Condition;
summary: string | null; // provider's own one-liner, if it has one
precipChance: number | null;
precipMm: number | null;
windKph: number | null; // daily max
gustKph: number | null;
windDeg: number | null;
uvIndexMax: number | null;
humidity: number | null; // daily average
sunrise: string | null;
sunset: string | null;
}
export type AlertSeverity = "extreme" | "severe" | "moderate" | "minor" | "unknown";
export interface WeatherAlert {
title: string;
severity: AlertSeverity;
source: string; // who issued it, e.g. "NWS Twin Cities/Chanhassen MN"
starts: string | null;
ends: string | null;
description: string;
instruction: string | null;
}
export interface AirQuality {
usAqi: number; // US EPA scale, 0–500
pm25: number | null; // µg/m³
pm10: number | null;
ozone: number | null;
no2: number | null;
}
/** What one provider adapter returns. */
export interface ProviderForecast {
current: CurrentWeather;
hourly: HourlyForecast[];
daily: DailyForecast[];
hourlyStepHours: 1 | 3;
timeZone: string | null; // IANA name, if the provider tells us
alerts: WeatherAlert[];
}
/** What /api/weather returns: the provider's forecast plus extras from other sources. */
export interface WeatherReport extends ProviderForecast {
provider: ProviderId;
timeZone: string | null;
airQuality: AirQuality | null;
alertSource: string | null;
fetchedAt: string;
}
/** The contract each provider adapter fulfils. */
export interface WeatherProvider {
id: ProviderId;
label: string;
/** Name of the env var holding its key, or null if no key is needed. */
envKey: string | null;
2026-10-31 21:50:28 -05:00
/** Forecast horizon available on the configured tier. */
2026-10-31 21:20:16 -05:00
limits(): { forecastDays: number; hourlyStepHours: 1 | 3 };
getForecast(lat: number, lon: number): Promise<ProviderForecast>;
}
2026-10-31 21:50:28 -05:00
/** Configured provider metadata exposed to the browser (never includes keys). */
2026-10-31 21:20:16 -05:00
export interface ProviderInfo {
id: ProviderId;
label: string;
forecastDays: number;
hourlyStepHours: 1 | 3;
}
/** Map overlays the radar panel can show. */
export interface RadarFrame {
time: string;
url: string; // Leaflet tile template with {z}/{x}/{y}
}