wayfarer-weather/lib/weather/types.ts
2026-10-31 21:50:28 -05:00

141 lines
4 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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;
/** Forecast horizon available on the configured tier. */
limits(): { forecastDays: number; hourlyStepHours: 1 | 3 };
getForecast(lat: number, lon: number): Promise<ProviderForecast>;
}
/** Configured provider metadata exposed to the browser (never includes keys). */
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}
}