Mapping guides
Bidirectional mapping to and from incumbent schemas is OpenBody’s primary success
measure and its adoption wedge: the standard is a universal adapter. These guides are
built from the real mappers in openbody-ts/src/mappers
— pure input → { records, warnings } functions, round-trip tested (npm test).
Structurally unusable input throws a typed MapperInputError; anything merely degraded,
skipped, or defaulted is reported on the returned warnings channel instead. The mappers
resolve source exercise names to canonical registry ids where the
registry covers them, falling back to the lossless opaque form otherwise.
Supported formats
All mappers are importable from the openbody-ts package root and run in both node and the
browser. The in-browser converter wires all of them to a drag-and-drop
page. (Bundling the package for the browser needs a toolchain that understands JSON import
attributes — import … with { type: "json" }, used for the vendored schema and crosswalk;
modern esbuild / Vite / Rollup / Webpack 5 all do. Node needs nothing extra.)
| Format | Direction | Works in | Caveats |
|---|---|---|---|
| Hevy (workout CSV) | import | library + browser tool | |
| Strong (workout CSV) | import | library + browser tool | ,- or ;-delimited exports both parse |
| Strava (activity + streams JSON) | import | library + browser tool | the library maps the API’s activity+streams responses (streams must include time); the browser tool reads the bulk-export activities.csv instead |
Apple Health / Health Connect (export.xml) | import | library + browser tool | |
| FIT (recorded activity or structured workout) | import | library + browser tool | library takes pre-decoded FIT messages (why); the browser tool decodes the .fit binary for you but maps recorded activities only |
| GPX (GPS track) | import | library + browser tool | untimed tracks degrade honestly (no fabricated timing) |
| TCX (Garmin Training Center XML) | import | library + browser tool | |
| Fitbit (Google Takeout folder) | import | library + browser tool | multi-select the Takeout JSON files; timestamps need a utcOffset |
| Concept2 (Logbook season CSV) | import | library + browser tool | one row per workout — per-stroke detail isn’t in this export |
| theCrag (logbook CSV) | import | library + browser tool | |
| Strong (CSV out — also imports into Hevy) | export | library + browser tool | best-effort by design: losses reported machine-readably (contract) |
The browser tool accepts every import format above — Hevy, Strong, Apple Health, Strava,
GPX, TCX, FIT, Concept2, theCrag, and Fitbit (Google Takeout) — decoding each entirely in
your browser, and it re-imports an OpenBody JSON you exported earlier. CSV timestamps carry
no UTC offset at the source, so
the CSV-based mappers take an optional utcOffset and are timezone-independent — the same
bytes map to the same records on any machine.
Mapping principles (§10.1)
- Canonical-plus-residue. Map each source construct to its canonical OpenBody equivalent; carry anything with no canonical home in namespaced extensions or, for identity, the opaque fallback. Nothing is dropped.
- Lazy terminology. Source type tokens are preserved source-namespaced on ingest and resolved to external codes (LOINC / SNOMED / UCUM / MET) only on output.
- Provenance preserved. Source platform, app, and device become registry-backed
provenance tokens; the source’s own key becomes
clientRecordId. - Round-trip. Source → canonical → source MUST preserve meaning under the §8.3 equivalence rule.