noaa_nws_adapter 0.0.5
noaa_nws_adapter: ^0.0.5 copied to clipboard
NOAA / NWS direct-consume wrapper — smallest-slice GeoJSON client for api.weather.gov active winter alerts by point. Pure Dart.
Changelog #
0.0.5 #
- Republish from the embedded-target Dart 3.10.1 SDK (Flutter 3.38.3) to correct a stale
^3.11.0SDK floor in the previously-published artifact. No source or behavior change; the source already declaredsdk: ^3.10.0. Restorespub getfor embedded/automotive Dart consumers on Dart 3.10.x.
0.0.4 — 2026-05-10 — Pana score recovery (Theme α P4) #
- Add minimal
example/main.dartfor pana documentation scoring. - Apply
dart formatto clear any formatter findings. - No SDK source changes; metadata + format pass only.
0.0.3 — 2026-05-06 — Typed alert area (polygon + circle) #
Adds a typed geographic-area record so consumers can present a precise
geographic scope (polygon or circle) to the driver rather than an
abstract zone identifier or a free-form area description string. The
existing areaDesc free-form field is preserved unchanged for
back-compat.
The driver-facing rationale: "winter alert covers a precise geographic area on the route, not just an abstract zone identifier." The polygon shape supports the integrator's per-route geofence-class question — does this alert intersect my route? — without flying out to a separate gazetteer service.
CAP area schema verbatim citation (CAP-v1.2 §3.2.4):
area/polygon— "The paired values of points defining a polygon that delineates the affected area of the alert message". Vertex list as decimal-degree pairs; closed shape (first == last).area/circle— "The paired values of a point and radius delineating the affected area of the alert message". Format"<lat>,<lon> <radius_km>".
Added #
WinterAlertAreavalue-object (Equatable) with two fields:polygon: List<GeoPoint>(empty when publisher did not declare a polygon) andcircle: WinterAlertCircle?(null when publisher did not declare a circle). Both null/empty →WinterAlertArea.isEmpty == true(caller falls back toareaDesc).WinterAlertCirclevalue-object (Equatable) withcenter: GeoPointandradiusKm: double.GeoPointvalue-object (Equatable) — WGS84 lat/lon pair. We deliberately do not depend onlatlong2here to keep thenoaa_nws_adapterboundary narrow per smallest-slice discipline; integrators wishing to hand off to a geometry library translate at their boundary.WinterAlert.area: WinterAlertArea?field. Non-null when the feature carried a parseable geometry or CAP circle. Construction withoutareais permitted (default null) for back-compat.WinterAlert.fromFeature(feature)factory: builds a record from the full GeoJSON feature envelope (properties + geometry); parses the typed area in addition to the existing field set.WinterAlert.parseArea(geometry, parameters)static helper — visible for direct testing.- Exports:
WinterAlertArea,WinterAlertCircle,GeoPoint.
Changed #
NoaaNwsClient.parseFeatureCollectionnow usesWinterAlert.fromFeature(instead offromProperties) so the typed area is populated through the HTTP path. Records produced by the production code path now carryareawhen the feature's geometry is parseable.
Tests #
- 6 new tests covering: polygon parse from typical NWS GeoJSON
geometry; circle parse from CAP-style
parameters.circletext; area-not-present (null geometry →area = null); MultiPolygon takes first polygon's first ring; invalid coordinates (non-numeric / wrong shape) skip vertex; negative radius rejected.
Unchanged (back-compat) #
- All 0.0.1 + 0.0.2 surface unchanged.
WinterAlert.fromPropertiesstill works and returnsarea: null(the properties dict alone does not carry geometry). Existing callers readingareaDescsee the same value as before; new callers can opt in toareafor the typed shape. NoaaNwsClient.fetchActiveWinterAlertsAPI surface unchanged.- Equatable
propsextended (additive); equality semantics for pre-0.0.3 records (constructed withoutarea) match prior shape since the defaultarea = nullis consistent.
0.0.2 — 2026-05-04 — Bounded retry-with-backoff #
Adds an opt-in retry policy for transient publisher-network failures.
Default behavior unchanged for back-compat: passing no retryPolicy
to NoaaNwsClient still produces the 0.0.1 single-attempt semantics
where the caller decides backoff.
The driver-facing rationale: a single transient failure (5xx, brief connection reset, brief DNS hiccup) should not silently drop the alert reach for the driver in unexpected snow. The retry policy is conservative on intent (transient-only; never retries 4xx) and bounded on attempts so an integrator's logs remain honest about how many calls actually went out.
Added #
NoaaNwsRetryPolicyvalue-object:maxRetries,baseDelay,retryableStatusCodes. Default constructor: 3 retries, 1s base, retries on{408, 429, 500, 502, 503, 504}. Exponential backoff perdelayForAttempt(retryIndex)(1s, 2s, 4s on default).NoaaNwsRetryPolicy.noneconstant preserves 0.0.1 behavior.NoaaNwsClient.retryPolicyparameter: defaults toNoaaNwsRetryPolicy.none(back-compat). Pass an explicitNoaaNwsRetryPolicy()to opt in.NoaaNwsClient.sleepparameter: injectable sleep function for tests; defaults toFuture.delayed.
Changed #
NoaaNwsClient.fetchActiveWinterAlerts()now applies the configuredretryPolicyto transient failures (5xx / 408 / 429 / transport-class). 4xx (other than 408 / 429) is NOT retried — these are client-class errors where retry produces the same failure and wastes the publisher's quota. After retry exhaust, the most recent failure surfaces asNoaaNwsHttpExceptionwith the same shape as 0.0.1.
Tests #
- 9 new tests covering: default-policy delays (1s, 2s, 4s); none-policy zero-retries; status-code retryability table (5xx + 408 + 429 retryable, 4xx not); retry-success path (503 then 200); retry-exhaust (4 consecutive 503 → exception); 4xx-no-retry (404 → 1 call only); retry-on-429; observed-sleep-durations; default-back-compat (no retry without explicit policy).
Unchanged (back-compat) #
- All 0.0.1 surface unchanged.
NoaaNwsClient(userAgent: ...)withoutretryPolicybehaves identically to 0.0.1.
0.0.1 — 2026-05-03 #
Initial publish.
NoaaNwsClientstateless HTTP wrapper forhttps://api.weather.gov/alerts/active?point={lat},{lon}(User-Agent required; mandatory at construction).WinterAlertEquatable model carrying CAP-class fields (event,severity,certainty,urgency,status,messageType,headline,areaDesc,effective,expires,description,instruction).- CAP enums with safe
unknowndefault:AlertSeverity,AlertCertainty,AlertUrgency,AlertStatus,AlertMessageType. kNwsWinterEventTypes14-string catalogue (Winter Storm Warning / Watch, Winter Weather Advisory, Blizzard Warning, Ice Storm Warning, Heavy Freezing Spray Warning / Watch, Lake Effect Snow Warning, Freeze Warning / Watch, Freezing Fog Advisory, Cold Weather Advisory, Extreme Cold Warning / Watch).NoaaNwsHttpException,NoaaNwsParseExceptiontyped error surfaces.- Default filter
actualOnly: trueexcludesTest/Exercise/System/Draftentries from production driver-facing flow. - Malformed CAP features skipped (warn-and-continue) rather than aborting the whole response.
- 25 tests covering value-object, CAP-enum mapping, parse-layer error surfaces, MockClient round-trip, header propagation, transport failure surfaces.
- BSD-3-Clause license (matches the rest of SNGNav).
- Pure Dart, no Flutter dependency.