Adds `deflockapp://node?id=<nodeId>` deep link support: parses the node
ID, fetches the OSM node via the public API, and delivers it via an
`onNodeDeepLink` callback for HomeScreen to register. Includes URL
parsing unit tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract duplicated retry logic from OverpassService and RoutingService
into a shared resilience framework in service_policy.dart:
- ResiliencePolicy: configurable retries, backoff, and HTTP timeout
- executeWithFallback: retry loop with primary→fallback endpoint chain
- ErrorDisposition enum: abort / fallback / retry classification
- ServicePolicy + ServicePolicyResolver: per-service compliance rules
(rate limits, caching, concurrency) for OSMF and third-party services
- ServiceRateLimiter: async semaphore-based concurrency and rate control
OverpassService now hits overpass.deflock.org first, falls back to
overpass-api.de. RoutingService hits api.dontgetflocked.com first,
falls back to alprwatch.org. Both use per-service error classifiers
to determine retry vs fallback vs abort behavior.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add node id tie-breaker to sort comparator so equal-distance nodes
have deterministic ordering across renders (prevents flicker)
- Log validNodesCount instead of allNodes.length so the message
reflects the actual post-filter count
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sort nodes by squared distance from viewport center before applying the
render limit, so visible nodes always make the cut instead of arbitrary
selection causing gaps that shift as you pan.
Also: inject node provider for testability, deduplicate validity filter,
and reduce debug log spam to state transitions only.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>