/** * Service for MET Norway's keyless Locationforecast API * (https://api.met.no/weatherapi/locationforecast/2.0/), published under * CC BY 4.0. * * **Routing position.** This is not a source a caller can ask for. It is * reached only when Open-Meteo fails *transiently* on an `auto`-routed non-US * `get_forecast`, and there is no `source: "metno"` to request — the tool's * schema is unchanged. * * **Classification — garnish wearing a contract's clothes, and it is garnish.** * A met.no failure does **not** propagate: the *original Open-Meteo error* * propagates, untouched, exactly as today. That is the only honest posture, * because met.no's success adds a forecast and its failure restores the current * behaviour — it can never manufacture an all-clear, and there is no coverage * claim for it to falsify. Two consequences ride on it: * * - **No retry layer on this path, anywhere.** By the time the fallback fires, * the caller has already waited out Open-Meteo's own attempts, so the budget * is one request. A 403 in particular is never retried: met.no's ToS warns * that sites exceeding its limit are throttled, and retrying a throttle is * how a throttle becomes a block. * - **No met.no error text ever reaches the user.** Every message below is * fixed, pre-written, and read only by the handler deciding whether the * fallback worked. The user sees Open-Meteo's message. * * **Terms of service, all four obligations, none of them cosmetic.** * An identifying User-Agent carrying contact information; conditional requests; * coordinates truncated to four decimals; and a request rate well inside the * published ceiling. Excess coordinate precision is a documented 403 cause, so * the truncation below is a correctness requirement rather than tidiness. * * **Errors are plain fixed-message `Error`s, never `ApiError`.** * `ApiServiceName` is a closed union and this is a peripheral service, so the * union is not edited. The API is keyless, so there is no secret to leak — the * house pattern is uniform and cheap to keep, and since none of these messages * reaches a user a detailed one would buy nothing and risk something. * * Live shapes verified 2026-09-11: `Last-Modified` and `Expires` are published * and **no `ETag`** is, so the conditional request is `If-Modified-Since` and * nothing in this repo's JMA precedent transfers but its structure. */ import type { MetnoDailyAggregate } from '../utils/metnoParse.js'; export interface MetnoServiceConfig { timeout?: number; /** Injectable clock, so the freshness and retention clocks are testable without faking timers. */ now?: () => number; } export declare class MetnoService { private client; private cache; private inFlight; private now; constructor(config?: MetnoServiceConfig); /** * The daily aggregate for one point, revalidated conditionally once it is * older than `METNO_FRESHNESS_MS`. * * @param latitude Latitude in degrees. * @param longitude Longitude in degrees. * @param timezone IANA zone (or fixed-offset zone) the days are bucketed in. * met.no publishes UTC timestamps and no timezone of its own, so the caller * names the zone and the render site discloses which one it used. */ getForecast(latitude: number, longitude: number, timezone: string): Promise; /** One upstream round trip, including the 304 paths. */ private pull; /** * Map a request failure to a fixed message. Never includes a URL, a response * body, a coordinate, or a raw axios error. */ private toMetnoError; /** * Log a failure with status and code only. * * The `Error` slot is deliberately left empty: the logger renders the message * and stack of whatever it is handed, and an axios error carries the request * URL — which here carries the caller's coordinates. */ private logFailure; } //# sourceMappingURL=metno.d.ts.map