@dangahagan/weather-mcp 1.26.0 → 1.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +11 -10
  2. package/dist/config/api.d.ts +2 -2
  3. package/dist/config/api.js +2 -2
  4. package/dist/config/cache.d.ts +1 -0
  5. package/dist/config/cache.d.ts.map +1 -1
  6. package/dist/config/cache.js +15 -0
  7. package/dist/config/cache.js.map +1 -1
  8. package/dist/data/jmaAreas.d.ts +47 -0
  9. package/dist/data/jmaAreas.d.ts.map +1 -0
  10. package/dist/data/jmaAreas.js +1636 -0
  11. package/dist/data/jmaAreas.js.map +1 -0
  12. package/dist/handlers/alertsHandler.d.ts +7 -3
  13. package/dist/handlers/alertsHandler.d.ts.map +1 -1
  14. package/dist/handlers/alertsHandler.js +303 -4
  15. package/dist/handlers/alertsHandler.js.map +1 -1
  16. package/dist/handlers/weatherSummaryHandler.d.ts +2 -1
  17. package/dist/handlers/weatherSummaryHandler.d.ts.map +1 -1
  18. package/dist/handlers/weatherSummaryHandler.js +11 -2
  19. package/dist/handlers/weatherSummaryHandler.js.map +1 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +16 -5
  22. package/dist/index.js.map +1 -1
  23. package/dist/services/googleWeather.d.ts +3 -2
  24. package/dist/services/googleWeather.d.ts.map +1 -1
  25. package/dist/services/googleWeather.js +3 -2
  26. package/dist/services/googleWeather.js.map +1 -1
  27. package/dist/services/jma.d.ts +174 -0
  28. package/dist/services/jma.d.ts.map +1 -0
  29. package/dist/services/jma.js +371 -0
  30. package/dist/services/jma.js.map +1 -0
  31. package/dist/types/jma.d.ts +128 -0
  32. package/dist/types/jma.d.ts.map +1 -0
  33. package/dist/types/jma.js +22 -0
  34. package/dist/types/jma.js.map +1 -0
  35. package/dist/utils/jmaAreaResolver.d.ts +83 -0
  36. package/dist/utils/jmaAreaResolver.d.ts.map +1 -0
  37. package/dist/utils/jmaAreaResolver.js +151 -0
  38. package/dist/utils/jmaAreaResolver.js.map +1 -0
  39. package/dist/utils/jmaParse.d.ts +111 -0
  40. package/dist/utils/jmaParse.d.ts.map +1 -0
  41. package/dist/utils/jmaParse.js +359 -0
  42. package/dist/utils/jmaParse.js.map +1 -0
  43. package/dist/utils/jmaWarningNames.d.ts +165 -0
  44. package/dist/utils/jmaWarningNames.d.ts.map +1 -0
  45. package/dist/utils/jmaWarningNames.js +259 -0
  46. package/dist/utils/jmaWarningNames.js.map +1 -0
  47. package/dist/utils/pointInPolygon.d.ts +12 -2
  48. package/dist/utils/pointInPolygon.d.ts.map +1 -1
  49. package/dist/utils/pointInPolygon.js.map +1 -1
  50. package/package.json +3 -2
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![npm version](https://badge.fury.io/js/@dangahagan%2Fweather-mcp.svg)](https://www.npmjs.com/package/@dangahagan/weather-mcp)
4
4
  [![MCP Registry](https://img.shields.io/badge/MCP-Registry-blue)](https://registry.modelcontextprotocol.io/v0/servers?search=io.github.dgahagan/weather-mcp)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
- [![Tests](https://img.shields.io/badge/tests-3%2C011%20passing-brightgreen)](./docs/testing/TEST_SUITE_README.md)
6
+ [![Tests](https://img.shields.io/badge/tests-3%2C130%20passing-brightgreen)](./docs/testing/TEST_SUITE_README.md)
7
7
  [![Node](https://img.shields.io/badge/node-%3E%3D18-339933?logo=node.js&logoColor=white)](https://nodejs.org)
8
8
 
9
9
  **Give your AI assistant real weather data — 17 tools, zero API keys, zero signup, zero cost.**
@@ -58,7 +58,7 @@ Choose this one if you want:
58
58
 
59
59
  - **Genuinely free** — every data source is a free public API. No trial that expires, no credit card, no rate-limited "free tier" bait.
60
60
  - **No API keys** — install to first forecast in under a minute. Nothing to configure, nothing to leak into a repo. ([Three optional keys](#optional-api-keys) add extras if you want them; the default configuration needs none.)
61
- - **Fully open source** — MIT licensed, readable TypeScript, 3,011 tests. Audit it, fork it, fix it.
61
+ - **Fully open source** — MIT licensed, readable TypeScript, 3,130 tests. Audit it, fork it, fix it.
62
62
  - **Privacy-respecting** — your queries go directly from your machine to public weather APIs. No middleman server, no telemetry.
63
63
  - **Breadth** — 17 tools covering weather, safety hazards (lightning, floods, wildfires), marine conditions, air quality, and historical data back to 1940. Most weather MCPs stop at forecasts.
64
64
 
@@ -72,7 +72,7 @@ All 17 tools, documented in detail in **[docs/TOOLS.md](./docs/TOOLS.md)**:
72
72
  |------|-------------|----------|
73
73
  | `get_forecast` | Daily/hourly forecasts up to 16 days by coordinates, saved location, or city name (NOAA publishes 7 days daily and ~6.5 days hourly in the US, and says so when you ask for more; `source="openmeteo"` reaches the full range); sunrise/sunset, UV, precipitation probability, optional climate-normals comparison, optional moon phase & twilight almanac, optional five-model agreement comparison | 🌍 Global |
74
74
  | `get_current_conditions` | Current weather: temperature, wind, humidity, pressure; NOAA station observations in the US (plus heat index/wind chill, snow depth, optional NOAA fire-weather indices), Open-Meteo model data elsewhere (with an optional computed Fosberg fire-weather index), or real airport station observations worldwide with `source="metar"`. Always states the observation's age, and automatically falls back to a fresher nearby station when the nearest one has gone dark. In extreme conditions automatically adds frostbite time-to-onset (computed wind chill) or heat-stress context (estimated WBGT) | 🌍 Global |
75
- | `get_alerts` | Active watches, warnings, and advisories sorted by severity; official national warnings for the US (NOAA), Canada (ECCC), 38 European countries (MeteoAlarm), India (NDMA SACHET), the Philippines (PAGASA), and Indonesia (BMKG) | 🇺🇸 🇨🇦 🇪🇺 🇮🇳 🇵🇭 🇮🇩 |
75
+ | `get_alerts` | Active watches, warnings, and advisories sorted by severity; official national warnings for the US (NOAA), Canada (ECCC), 38 European countries (MeteoAlarm), India (NDMA SACHET), the Philippines (PAGASA), Indonesia (BMKG), and Japan (JMA) | 🇺🇸 🇨🇦 🇪🇺 🇮🇳 🇵🇭 🇮🇩 🇯🇵 |
76
76
  | `get_historical_weather` | Hourly/daily observations from 1940 to present | 🌍 Global |
77
77
  | `get_weather_summary` | One-call overview combining current conditions, forecast, and alerts (optionally air quality and lightning) | 🌍 Global |
78
78
  | `search_location` | Geocode place names to coordinates ("Paris" → 48.85, 2.35) | 🌍 Global |
@@ -97,7 +97,7 @@ All 17 tools, documented in detail in **[docs/TOOLS.md](./docs/TOOLS.md)**:
97
97
  ## Feature highlights
98
98
 
99
99
  - **Smart source selection** — US queries use NOAA (detailed, includes forecaster narratives); everywhere else uses Open-Meteo. You never pick; it just works.
100
- - **International weather alerts** — `get_alerts` routes by country: NOAA in the US, Environment and Climate Change Canada alerts in Canada, the official national warnings of 38 European countries via EUMETNET MeteoAlarm, and the national CAP feeds of India (NDMA SACHET), the Philippines (PAGASA) and Indonesia (BMKG) — shown unmodified, with the issuing service credited. Where a feed publishes geometry inline, warnings are matched to your exact point by the alert's own polygon rather than to the whole country (the Philippines and Indonesia today; Europe remains country-level). Border cities like Toronto get the right country's alerts, not the nearest bounding box's.
100
+ - **International weather alerts** — `get_alerts` routes by country: NOAA in the US, Environment and Climate Change Canada alerts in Canada, the official national warnings of 38 European countries via EUMETNET MeteoAlarm, the national CAP feeds of India (NDMA SACHET), the Philippines (PAGASA) and Indonesia (BMKG), and the Japan Meteorological Agency's own disaster-prevention feed in Japan — shown unmodified, with the issuing service credited. Where a feed publishes geometry inline, warnings are matched to your exact point by the alert's own polygon rather than to the whole country (the Philippines and Indonesia today; Europe remains country-level). Japanese warnings are matched to your point by JMA warning area, with the Japanese name shown as published and an English gloss beside it. Border cities like Toronto get the right country's alerts, not the nearest bounding box's.
101
101
  - **Real observations worldwide** — ask for what a station is *actually reporting* and `get_current_conditions` will read the nearest airport's METAR (`source="metar"`): a genuine instrument reading anywhere on earth, with the station, its distance, and the observation age always stated. Outside the US this is the difference between a measurement and a model estimate.
102
102
  - **Model agreement** — ask *"how confident is this forecast?"* and `compare_models=true` compares five global models (GFS, ECMWF, ICON, GEM, UKMO) in one request, summarizing where they agree and where they split rather than dumping five forecasts. Spread across models is a proxy for uncertainty, not a guarantee — a tight spread can still be wrong, and the output says so.
103
103
  - **Ensemble spread** — the sibling question: *"how confident is the model itself?"* `ensemble_spread=true` summarizes ECMWF ENS's 50 perturbed members — per-day High/Moderate/Low confidence, interquartile temperature bands, and how many members produce rain — instead of one deterministic forecast. Member fractions are raw model output, **not calibrated probabilities**: a confident ensemble can still be wrong, and the output says so. Mutually exclusive with `compare_models`; the two answer different questions.
@@ -127,6 +127,7 @@ All free, all public, no authentication required:
127
127
  | [NDMA SACHET](https://sachet.ndma.gov.in/) | Official Indian warnings (CAP; polygon geometry served from a separate endpoint, so alerts are matched at country level) — public domain | India |
128
128
  | [PAGASA](https://publicalert.pagasa.dost.gov.ph/) | Official Philippine warnings (CAP, inline polygons) — CC BY 4.0 | Philippines |
129
129
  | [BMKG](https://www.bmkg.go.id/) | Official Indonesian nowcast warnings (CAP, inline polygons) — attribution required | Indonesia |
130
+ | [JMA](https://www.jma.go.jp/bosai/warning/) | Official Japanese weather warnings from the disaster-prevention XML feed, matched by class10 warning area — Government Standard Terms of Use; attribution `出典:気象庁ホームページ` | Japan |
130
131
  | [NIFC WFIGS](https://data-nifc.opendata.arcgis.com/) | Active wildfire perimeters and incidents | US |
131
132
  | [NASA FIRMS](https://firms.modaps.eosdis.nasa.gov/) | Satellite fire detections (VIIRS, near real-time) | Global |
132
133
  | [RainViewer](https://www.rainviewer.com/api.html) | Precipitation radar imagery | Global |
@@ -268,7 +269,7 @@ Supported on `get_forecast`, `get_current_conditions`, and `get_historical_weath
268
269
  | `NCEI_API_TOKEN` | — | Optional [free NCEI token](https://www.ncdc.noaa.gov/cdo-web/token) for official NOAA climate normals (US); falls back to Open-Meteo automatically. See [Optional API keys](#optional-api-keys) |
269
270
  | `FIRMS_MAP_KEY` | — | Optional [free FIRMS key](https://firms.modaps.eosdis.nasa.gov/api/map_key/) for targeted wildfire queries and up to 5 days of detection history. See [Optional API keys](#optional-api-keys) |
270
271
  | `GOOGLE_POLLEN_API_KEY` | — | Optional key for pollen outside Europe (incl. the US). **Requires a Google Cloud billing account** — free tier is 5,000 lookups/month. See [Optional API keys](#optional-api-keys) and [the setup guide](./docs/GOOGLE_POLLEN_KEY_SETUP.md) |
271
- | `GOOGLE_WEATHER_API_KEY` | — | Optional key for official weather alerts beyond the US, Canada, Europe, India, the Philippines, and Indonesia (~45+ more territories). **Requires a Google Cloud billing account.** See [Optional API keys](#optional-api-keys) and [the setup guide](./docs/GOOGLE_WEATHER_KEY_SETUP.md) |
272
+ | `GOOGLE_WEATHER_API_KEY` | — | Optional key for official weather alerts beyond the US, Canada, Europe, India, the Philippines, Indonesia, and Japan (~45+ more territories). **Requires a Google Cloud billing account.** See [Optional API keys](#optional-api-keys) and [the setup guide](./docs/GOOGLE_WEATHER_KEY_SETUP.md) |
272
273
 
273
274
  For caching architecture details, see [.github/CACHING.md](./.github/CACHING.md).
274
275
 
@@ -287,7 +288,7 @@ still works; it just returns the keyless answer.
287
288
  | [`NCEI_API_TOKEN`](https://www.ncdc.noaa.gov/cdo-web/token) | Free registration (email) | Official NOAA station climate normals for US locations | Normals are computed from the Open-Meteo reanalysis archive — global, and the path virtually every user is already on |
288
289
  | [`FIRMS_MAP_KEY`](https://firms.modaps.eosdis.nasa.gov/api/map_key/) | Free registration (email) | Targeted wildfire bbox queries with 1–5 days of detection history | Keyless 24-hour regional detection files — `get_wildfire_info` still works globally |
289
290
  | `GOOGLE_POLLEN_API_KEY` | **Free tier, but requires a Google Cloud billing account (credit card on file)** — 5,000 lookups/month free, ~$10/1,000 after. [Setup guide](./docs/GOOGLE_POLLEN_KEY_SETUP.md) | Grass/tree/weed Universal Pollen Index outside Europe, including the US (65+ countries) | European pollen via CAMS still works keyless; elsewhere no pollen section renders |
290
- | `GOOGLE_WEATHER_API_KEY` | **Free tier, but requires a Google Cloud billing account (credit card on file)** — the Weather API bills under a Maps Platform Essentials SKU; check Google's current allowance. [Setup guide](./docs/GOOGLE_WEATHER_KEY_SETUP.md) | Official weather alerts for ~45+ more territories — Australia, Japan, Brazil, Mexico and others ([Google's coverage list](https://developers.google.com/maps/documentation/weather/coverage) is authoritative) | US, Canadian, European, Indian, Philippine, and Indonesian alerts still work keyless; elsewhere `get_alerts` returns today's not-covered message |
291
+ | `GOOGLE_WEATHER_API_KEY` | **Free tier, but requires a Google Cloud billing account (credit card on file)** — the Weather API bills under a Maps Platform Essentials SKU; check Google's current allowance. [Setup guide](./docs/GOOGLE_WEATHER_KEY_SETUP.md) | Official weather alerts for ~45+ more territories — Australia, Brazil, Mexico and others ([Google's coverage list](https://developers.google.com/maps/documentation/weather/coverage) is authoritative) | US, Canadian, European, Indian, Philippine, Indonesian, and Japanese alerts still work keyless; elsewhere `get_alerts` returns today's not-covered message |
291
292
 
292
293
  The NCEI and FIRMS keys are true free registrations. **The two Google keys are
293
294
  not** — each has a free usage tier, but Google requires a billing account with
@@ -354,15 +355,15 @@ Being honest about what free public data can and can't do:
354
355
  | Historical weather (1940+) | ✅ (>7 days old) | Station-level detail for last 7 days |
355
356
  | Air quality, marine, radar, lightning | ✅ | — |
356
357
  | Current conditions | ✅ (model data, or real station observations via `source="metar"`) | Station observations via NOAA (richer detail) |
357
- | Weather alerts | ✅ US, Canada, 38 European countries, India, the Philippines, and Indonesia keyless; ~45+ more territories with an optional key | NWS zone-level precision; Europe and India country-level; Philippines and Indonesia polygon-level |
358
+ | Weather alerts | ✅ US, Canada, 38 European countries, India, the Philippines, Indonesia, and Japan keyless; ~45+ more territories with an optional key | NWS zone-level precision; Europe and India country-level; Philippines and Indonesia polygon-level; Japan warning-area level |
358
359
  | River conditions | ✅ (GloFAS modeled discharge; Environment Agency gauge observations in Great Britain) | Gauge observations + official flood stages via NWPS |
359
360
  | Wildfires | ✅ (FIRMS satellite detections) | Named incidents with acreage + containment via NIFC |
360
361
  | Fire weather | ✅ (computed Fosberg index + dryness context) | NOAA-published Haines, grassland, red-flag indices |
361
362
  | Pollen | 🇪🇺 Europe keyless (CAMS, grains/m³); elsewhere needs an optional key | Universal Pollen Index with `GOOGLE_POLLEN_API_KEY` |
362
363
 
363
364
  - European alerts are matched at **country level** — the keyless MeteoAlarm feeds carry no region polygons, so warnings for a large country may not affect the requested point; the output says so. Canadian alerts use a real bbox query with polygon-backed features.
364
- - **Philippine and Indonesian alerts are matched by the alert's own polygon**, so a warning is shown only when it actually covers the requested point — finer than Europe's country-level matching. **Indian alerts are currently country-level**: SACHET publishes each alert's geometry from a separate endpoint that is not reliably reachable from servers, so Indian warnings are listed with an explicit note that they may not affect your exact location, exactly as Europe's are. Any alert whose geometry cannot be loaded is always listed rather than dropped — never silently treated as "not near you".
365
- - Outside the US, Canada, Europe, India, the Philippines, and Indonesia, alerts need an optional [`GOOGLE_WEATHER_API_KEY`](#optional-api-keys); without one `get_alerts` says plainly that the region isn't covered rather than guessing. With a key, Google aggregates official national feeds for roughly 45 more territories — Australia, Japan, Brazil and Mexico among them, with [Google's coverage page](https://developers.google.com/maps/documentation/weather/coverage) as the authoritative list. Matching is by **provider polygon**, so coverage alignment may not be exact and an empty answer means "no alerts found", not a guarantee of coverage; the output says so both ways. Alert text appears in the publisher's source language. **The US, Canada, Europe, India, the Philippines, and Indonesia never contact Google** — those authorities stay first choice, key or no key — and a key failure surfaces loudly rather than degrading to a possibly-false all-clear.
365
+ - **Philippine and Indonesian alerts are matched by the alert's own polygon**, and **Japanese alerts by JMA warning area**, so a warning is shown only when it actually covers the requested point — finer than Europe's country-level matching. Japan resolves to one of the 143 areas JMA itself publishes warnings for; a point outside all of them, or one whose area is missing from the office's own bulletin, says so rather than reporting an all-clear. **Indian alerts are currently country-level**: SACHET publishes each alert's geometry from a separate endpoint that is not reliably reachable from servers, so Indian warnings are listed with an explicit note that they may not affect your exact location, exactly as Europe's are. Any alert whose geometry cannot be loaded is always listed rather than dropped — never silently treated as "not near you".
366
+ - Outside the US, Canada, Europe, India, the Philippines, Indonesia, and Japan, alerts need an optional [`GOOGLE_WEATHER_API_KEY`](#optional-api-keys); without one `get_alerts` says plainly that the region isn't covered rather than guessing. With a key, Google aggregates official national feeds for roughly 45 more territories — Australia, Brazil and Mexico among them, with [Google's coverage page](https://developers.google.com/maps/documentation/weather/coverage) as the authoritative list. Matching is by **provider polygon**, so coverage alignment may not be exact and an empty answer means "no alerts found", not a guarantee of coverage; the output says so both ways. Alert text appears in the publisher's source language. **The US, Canada, Europe, India, the Philippines, Indonesia, and Japan never contact Google** — those authorities stay first choice, key or no key — and a key failure surfaces loudly rather than degrading to a possibly-false all-clear.
366
367
  - International current conditions default to **model-interpolated** values at the exact coordinates. `source="metar"` returns a **real instrument reading** instead — but from the nearest airport, which may be tens of km away and up to an hour old. The output always names the station, its distance and bearing, and the observation age, so the tradeoff is visible rather than assumed. METAR coverage follows airports, so remote land and open ocean have real gaps.
367
368
  - Historical data older than 7 days comes from reanalysis models (9–25km grid), not direct station observations, and trails real time by ~5 days.
368
369
  - Non-US wildfire results are **satellite heat detections, not managed incidents** — no fire names, sizes, or containment percentages exist in the data, detections can include industrial heat sources or agricultural burns, and a clear result is not an all-clear (cloud cover hides fires; small or new fires evade detection). The output frames all of this explicitly. Keyless data covers the last 24 hours; an optional free [`FIRMS_MAP_KEY`](https://firms.modaps.eosdis.nasa.gov/api/map_key/) unlocks targeted queries and up to 5 days of detection history (`day_range`).
@@ -379,7 +380,7 @@ Being honest about what free public data can and can't do:
379
380
  ```bash
380
381
  npm run build # Compile TypeScript
381
382
  npm run dev # Run in development mode
382
- npm test # Run all 3,011 tests
383
+ npm test # Run all 3,130 tests
383
384
  npm run test:coverage # Coverage report
384
385
  npm run audit # Dependency vulnerability scan
385
386
  ```
@@ -87,11 +87,11 @@ export declare function isGooglePollenKeyAvailable(): boolean;
87
87
  * The **Weather API** must be enabled on the project.
88
88
  *
89
89
  * Benefits of providing a key:
90
- * - Official weather alerts for ~45+ additional territories (Australia, Japan,
90
+ * - Official weather alerts for ~45+ additional territories (Australia,
91
91
  * Brazil, Mexico, and others) via the Google Weather API
92
92
  *
93
93
  * If not provided:
94
- * - US, Canadian, European, Indian, Philippine, and Indonesian alerts
94
+ * - US, Canadian, European, Indian, Philippine, Indonesian and Japanese alerts
95
95
  * continue to work keyless
96
96
  * - Alerts outside these regions are unavailable
97
97
  * - No setup required
@@ -93,11 +93,11 @@ export function isGooglePollenKeyAvailable() {
93
93
  * The **Weather API** must be enabled on the project.
94
94
  *
95
95
  * Benefits of providing a key:
96
- * - Official weather alerts for ~45+ additional territories (Australia, Japan,
96
+ * - Official weather alerts for ~45+ additional territories (Australia,
97
97
  * Brazil, Mexico, and others) via the Google Weather API
98
98
  *
99
99
  * If not provided:
100
- * - US, Canadian, European, Indian, Philippine, and Indonesian alerts
100
+ * - US, Canadian, European, Indian, Philippine, Indonesian and Japanese alerts
101
101
  * continue to work keyless
102
102
  * - Alerts outside these regions are unavailable
103
103
  * - No setup required
@@ -34,6 +34,7 @@ export declare const CacheConfig: {
34
34
  readonly firmsAreaQuery: number;
35
35
  readonly firmsRegionalFile: number;
36
36
  readonly capDocument: number;
37
+ readonly jmaIndex: number;
37
38
  readonly eaLatestReadings: number;
38
39
  readonly eaStationDetail: number;
39
40
  };
@@ -1 +1 @@
1
- {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/config/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAkDH,eAAO,MAAM,WAAW;aAEtB,OAAO;aAIP,OAAO;aAIP,YAAY;aAGZ,GAAG;iBAGD,eAAe;iBAIf,SAAS;iBAIT,QAAQ;iBAIR,QAAQ;iBAIR,iBAAiB;iBAIjB,MAAM;iBAIN,gBAAgB;iBAIhB,cAAc;iBAKd,OAAO;iBAIP,aAAa;iBAMb,cAAc;iBAOd,eAAe;iBAMf,YAAY;iBAMZ,OAAO;iBAIP,cAAc;iBAMd,iBAAiB;iBAQjB,YAAY;iBAQZ,cAAc;iBAKd,cAAc;iBAKd,cAAc;iBAKd,iBAAiB;iBAOjB,WAAW;iBAMX,gBAAgB;iBAYhB,eAAe;;CAET,CAAC;AAEX;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAYrE"}
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/config/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAkDH,eAAO,MAAM,WAAW;aAEtB,OAAO;aAIP,OAAO;aAIP,YAAY;aAGZ,GAAG;iBAGD,eAAe;iBAIf,SAAS;iBAIT,QAAQ;iBAIR,QAAQ;iBAIR,iBAAiB;iBAIjB,MAAM;iBAIN,gBAAgB;iBAIhB,cAAc;iBAKd,OAAO;iBAIP,aAAa;iBAMb,cAAc;iBAOd,eAAe;iBAMf,YAAY;iBAMZ,OAAO;iBAIP,cAAc;iBAMd,iBAAiB;iBAQjB,YAAY;iBAQZ,cAAc;iBAKd,cAAc;iBAKd,cAAc;iBAKd,iBAAiB;iBAOjB,WAAW;iBAgBX,QAAQ;iBAMR,gBAAgB;iBAYhB,eAAe;;CAET,CAAC;AAEX;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAYrE"}
@@ -153,6 +153,21 @@ export const CacheConfig = {
153
153
  // 24h entry rather than Infinity so a long-running server's LRU is not
154
154
  // pinned with dead alerts.
155
155
  capDocument: 24 * HOUR,
156
+ // JMA Atom index (feed/extra_l.xml) — **retention**, not freshness.
157
+ //
158
+ // These are two different clocks and the entry needs both. Freshness is how
159
+ // long the server will answer without asking JMA at all, and that reuses
160
+ // `alerts` (5 minutes) as an interval checked *inside* the entry. Retention
161
+ // is how long the parsed index is kept so that a conditional revalidation
162
+ // has something to reuse — because a 304 carries zero bytes, and reusing a
163
+ // cached parse is the entire point of sending `If-None-Match`. Holding both
164
+ // on one 5-minute TTL would discard the parse at exactly the moment the
165
+ // ETag became useful, so the revalidation could never fire.
166
+ //
167
+ // An hour is safe at any age: a 304 means JMA itself says the parse is
168
+ // current. It bounds the LRU instead — the parsed index is ~8,600 small
169
+ // entry objects covering seven days of bulletins.
170
+ jmaIndex: 1 * HOUR,
156
171
  // UK Environment Agency bulk latest-readings pull (GET /data/readings?latest&
157
172
  // parameter=level). Every EA gauge measure publishes on a 15-minute period
158
173
  // (period: 900), so a shorter TTL buys nothing real — the value cannot
@@ -1 +1 @@
1
- {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/config/cache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/config/cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * JMA class10 warning-area geometry — generated, do not hand-edit.
3
+ *
4
+ * Generated by `scripts/generate-jma-areas.mjs`. Regenerate with:
5
+ * npm run generate:jma-areas
6
+ *
7
+ * Sources:
8
+ * - https://www.jma.go.jp/bosai/common/const/geojson/class10s.json
9
+ * - https://www.jma.go.jp/bosai/common/const/area.json
10
+ * Generated: 2026-09-03
11
+ *
12
+ * Ring coordinates are `[lat, lon]` pairs (swapped from GeoJSON's
13
+ * `[lon, lat]` order) to match `pointInAnyRing` in
14
+ * `src/utils/pointInPolygon.ts`. Rounded to 3 decimal places (~110 m);
15
+ * consecutive duplicate points (an artifact of rounding) are dropped.
16
+ * Outer rings only — the source feed has zero interior rings.
17
+ *
18
+ * `officeCode` is undefined for exactly one area (code `hoppo`), which has
19
+ * no entry in the upstream area.json and therefore no issuing office.
20
+ *
21
+ * TWO NAMING TRAPS FOR ANY RENDERER READING THIS DATA:
22
+ * 1. `hoppo` carries upstream's own map label, which is byte-identical
23
+ * to real area 014010 ("根室地方" / "Nemuro"). 014010 has an issuing
24
+ * office and does receive warnings. Rendering `hoppo`'s name beside a
25
+ * "no issuing office" note therefore states something false about
26
+ * 014010. Key the no-office case off `officeCode === undefined` and do
27
+ * not print the name for it.
28
+ * 2. Names are NOT unique. "北部" / "Northern Region" appears 17 times and
29
+ * "南部" / "Southern Region" 18 times — they are prefecture sub-region
30
+ * labels, meaningful only beside their parent office. Never present a
31
+ * bare area name as if it identified a place.
32
+ */
33
+ /** One class10 JMA warning area. */
34
+ export interface JmaClass10Area {
35
+ /** class10 area code, e.g. "130010". */
36
+ readonly code: string;
37
+ /** Japanese name, e.g. "東京地方". */
38
+ readonly name: string;
39
+ /** English name, e.g. "Tokyo Region". */
40
+ readonly enName: string;
41
+ /** Parent JMA office code. Undefined for the one area with no issuing office. */
42
+ readonly officeCode?: string;
43
+ /** Outer-ring polygons, each a list of [lat, lon] pairs. */
44
+ readonly rings: ReadonlyArray<ReadonlyArray<readonly [number, number]>>;
45
+ }
46
+ export declare const JMA_CLASS10_AREAS: readonly JmaClass10Area[];
47
+ //# sourceMappingURL=jmaAreas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jmaAreas.d.ts","sourceRoot":"","sources":["../../src/data/jmaAreas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,oCAAoC;AACpC,MAAM,WAAW,cAAc;IAC7B,wCAAwC;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kCAAkC;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yCAAyC;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,iFAAiF;IACjF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,4DAA4D;IAC5D,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;CACzE;AAED,eAAO,MAAM,iBAAiB,EAAE,SAAS,cAAc,EAkkDtD,CAAC"}