/** * Service for interacting with the NOAA Aviation Weather Center's METAR API * — https://aviationweather.gov/api/data/metar * * Publishes decoded METAR (and SPECI) station observations worldwide as * keyless JSON, v4, no signup. Documented at 100 req/min with a descriptive * User-Agent advised. * * Three behaviours were verified live (2026-08-13, see the "Verified API * contract" section of `docs/metar-plan.md`) and are load-bearing here: * * - Empty and invalid bbox queries return **HTTP 204 with an empty body** * — not an empty JSON array. Calling `.json()`/`JSON.parse` on it * throws, so it must be handled before any parsing is attempted. * - A sustained burst produced **HTTP 502 with an HTML body** from the * Azure gateway fronting the API. Non-2xx responses must never be * parsed as JSON, and the raw body must never reach a thrown error * message. The endpoint recovered on retry, so the existing * retry/backoff pattern is required, not optional. * - Results carry no distance field and are not usefully sorted; nearest- * station selection is entirely client-side (see `src/utils/metarStation.ts`, * a later task). */ import type { BoundingBox, MetarObservation } from '../types/aviationWeather.js'; export interface AviationWeatherServiceConfig { baseURL?: string; timeout?: number; maxRetries?: number; } /** * Clamp a bounding box to valid lat/lon ranges (±90/±180) and guarantee the * result is never inverted (`minLon > maxLon`). Latitude never wraps, so an * inverted latitude pair after clamping can only mean the input was already * bogus and is corrected by swapping. Longitude can wrap at the * antimeridian; if independent clamping of `minLon`/`maxLon` would invert * the box, this narrows to the western edge rather than emit an inverted * bbox — the caller's tier-widening search (a later task) supplies * recovery, so no two-request antimeridian split is needed here. */ export declare function clampBoundingBox(bbox: BoundingBox): BoundingBox; /** * NOAA Aviation Weather Center METAR API client. No API key required. */ export declare class AviationWeatherService { private client; private cache; private maxRetries; constructor(config?: AviationWeatherServiceConfig); /** * Fetch decoded METAR observations for every station within a bounding * box. The box is clamped (never inverted) before the request and used * (rounded) as the cache key. Returns `[]` for an empty bbox result * (HTTP 204) rather than throwing. */ getMetarsInBoundingBox(bbox: BoundingBox): Promise; /** * Request with retry/backoff, mirroring `NOAAService.makeRequest`. * Retries only on the transient classes observed live — 502/503/504 and * network errors, both mapped to `ServiceUnavailableError` by * `handleError`. A 204 resolves normally (axios treats it as success) and * never reaches this catch block; other 4xx/5xx statuses are mapped to a * non-retryable `ApiError` and are not retried. * @private */ private makeRequest; /** * Map API errors to sanitized, typed errors. Non-2xx bodies that look * like HTML (the observed Azure gateway failure page) or otherwise * aren't JSON are never included in a thrown message. Only 502/503/504 * and network-level failures are mapped to `ServiceUnavailableError` * (the class `makeRequest` retries on); everything else maps to a * non-retryable `ApiError`. * @private */ private handleError; } //# sourceMappingURL=aviationWeather.d.ts.map