- nws.js: new fetchActiveAdvisories() hits the official NWS alerts feed
(api.weather.gov /alerts/active?point=lat,lon) and normalizes each advisory
into the app's alert shape, tagged source:'nws'. Adds classifyAdvisoryEvent
(maps NWS events -> app buckets: heat/wind/cold/storm/precip, else advisory),
nwsSeverity (Extreme/Severe->danger etc.), advisoryId (stable short id from
the URN), advisoryIcon, and local effectiveDate/expiresDate for dedup.
- notifications store: cached _nwsAdvisories + refreshAdvisories() (best
effort, cached 5 min, non-US/no-alerts safe). analyze() now merges
advisories via new pure mergeAdvisories(): an advisory that is officially
in force on a date supersedes the derived forecast alert of the same type
on that date, so the official NWS warning replaces the forecast guess. Both
are dismissible independently (nws-* vs forecast-* ids).
- New per-type toggle "advisory" (defaults on) across app/default settings and
SettingsDialog, so official advisories are configurable like every other
alert and the bell shows them with their own severity/icon.
- Wired: App.svelte calls refreshAdvisories() alongside analyze() on forecast
load and changes.
- Tests: 12 advisory tests (classifier, severity, id, normalize, dedup incl.
disable toggle) + 3 store integration tests (overlap dedup, non-overlap kept,
toggle-off). 107 total pass.
- Verified end-to-end in headless Chromium against the live NWS feed for
Rogers County, OK: the current Heat Advisory appears in the bell, and the
derived heat alerts for the days it covers are suppressed while a
non-overlapping day's forecast heat alert remains.
- notifications.svelte.js: analyze() iterated the reactive $state field
`alerts` right after writing it, inside an $effect. That read-after-write
made Svelte treat the effect as depending on `alerts`, so writing it
re-ran the effect forever -> effect_update_depth_exceeded, which killed the
click handler after setting the hash but before swapping the view. Iterate
the local newAlerts array instead.
- DailyForecast.svelte: use `precip` {@const} before its declaration caused
a 'Cannot access precip before initialization' TDZ error when the 7-day tab
rendered. Moved the declaration above its first use.
Both verified in a real headless Chromium: tabs now switch correctly (hash +
active state + content), no console/page errors. 81/81 tests pass.
- manifest.webmanifest + apple/maskable PNG icons; relative paths so the
app works both at dev root and under /weather
- plain-JS service worker (sw.js) with notificationclick/push handlers and
an SW-mediated showNotification() path for native Android notifications
- src/lib/pwa.js bootstraps registration and routes alerts to the SW
- Every alert kind (storm/precip/heat/cold/uv/wind) is now a per-type
preference toggle in Settings; removed the old alertThresholds.thunderstorm
boolean in favor of notificationTypes.storm
- Store dedups native notifications per forecast day and fires best-effort
native notifications for newly-appeared alerts
- inline-assets keeps manifest/sw/icons in dist; deploy.yml uploads + verifies
all PWA files via WebDAV