Task groups 1–2 of gpx-parser-robustness. The parser trusted its input: `parseFloat(attr ?? "0")` turned a missing lat/lon into a 0,0 Null Island point (which passes range validation) and garbage into NaN that poisoned distance and gain/loss totals; `<rte>`/`rtept` files (Garmin courses, many exporters) parsed to zero track points and were rejected. - `parsePoint`: skip a trkpt/rtept whose lat/lon is missing or non-finite (no more 0,0 default); a non-finite `<ele>` becomes `undefined` so it never leaks NaN into totals. Parsing stays parseFloat-lenient (trailing junk like `471.0m` still accepted), gated by Number.isFinite. - Drop segments left with fewer than 2 points (render nothing / break distance math). - Parse `<rte>` as track segments appended after `<trk>` segments, rtept handled identically — route-only files now import. No GpxData shape change; well-formed files parse identically. Updated the geom single-point test to the new drop-invariant. Verified: gpx typecheck + lint clean, 76/76 tests pass, journal + planner typecheck unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2.5 KiB
2.5 KiB
1. Parser lenience
- 1.1 Add a finite-number coordinate gate in
packages/gpx/src/parse.ts: skiptrkpt/rteptwith missing or non-finitelat/lon; treat non-finite<ele>as undefined - 1.2 Drop segments with fewer than 2 surviving points
- 1.3 Unit tests: missing-lat point skipped, garbage lat skipped with finite stats, garbage ele → undefined with finite gain/loss, single-point segment dropped, well-formed files byte-identical output to before
2. Route (<rte>) support
- 2.1 Parse
<rte>/rteptas track segments (appended after<trk>segments), reusing the same point parsing and lenience - 2.2 Unit tests: route-only file yields one segment and passes
validateGpx; mixed trk+rte ordering; rtept with ele/time preserved
3. Timestamp repair
- 3.1 Create
packages/gpx/src/timestamp-repair.ts: per-segment pass — >50% invalid → drop all; else linear interpolation between valid neighbors with leading/trailing clamp; output ISO 8601 strings - 3.2 Wire the repair pass into
parseTracksoutput inparse.ts - 3.3 Unit tests (
timestamp-repair.test.ts): sparse invalid interpolated, >50% invalid dropped (andmovingTimereturns null), leading/trailing clamp, all-valid untouched, no-timestamps untouched
4. Metadata fallback
- 4.1 Fall back to first
<trk>/<rte><name>/<desc>when<metadata>lacks them; merge distinct<cmt>into description with blank-line separator, dedup identical - 4.2 Unit tests: track-name fallback, metadata precedence, cmt merge, identical cmt deduped
5. Fixture corpus
- 5.1 Create
packages/gpx/fixtures/with purpose-named files:route-only.gpx,missing-coords.gpx,garbage-ele.gpx,timestamps-partial.gpx,timestamps-mostly-invalid.gpx,multi-track.gpx,unicode-name.gpx,namespaced-extensions.gpx(Garmin/OsmAnd-style extensions) - 5.2 Add trimmed real-world exports for supported integrations (Komoot, Wahoo) with any personal data scrubbed
- 5.3 Extend
parse-node.test.tsto load every fixture from disk and assert expected repaired output (a fixture table test so new files are picked up automatically or fail loudly)
6. Verification
- 6.1 Typecheck + run full gpx package suite; confirm
GpxDatashape unchanged and journal/planner compile without changes - 6.2 Manual check: import a route-only GPX and a partially-broken GPX through the journal import flow; confirm both save with sane stats
- 6.3 Run
pnpm typecheck && pnpm lint && pnpm testand the e2e suites