feat(schema): MeasurementType enum additions for Withings full coverage

Seven new enum values for v1.4.25 W5d. Source: Withings developer
guide — Data API → All Available Health Data — cross-referenced
against `.planning/research/withings-api-coverage.md` §3.

  - FAT_FREE_MASS (meastype 5) — Body+, Body Cardio, Body Comp, Body Scan
  - FAT_MASS (meastype 8) — same devices; mass form of BODY_FAT
  - MUSCLE_MASS (meastype 76) — Body+ family
  - SKIN_TEMPERATURE (meastype 73) — ScanWatch dermal reading. DISTINCT
    from BODY_TEMPERATURE: surface temps run ~32 °C, core ~37 °C, so
    they cannot share a bucket without corrupting analytics.
  - PULSE_WAVE_VELOCITY (meastype 91) — Body Cardio / Body Scan;
    indicator of arterial stiffness
  - VASCULAR_AGE (meastype 155) — Body Scan; composite of PWV + age
  - VISCERAL_FAT (meastype 170) — Body Comp / Body Scan; Withings'
    1–12 rating scale (not a percent)

Migration 0049 is additive only — every value is unconditional, no
backfill needed. The Withings client picks these values up once the
meastype → enum mapping lands (separate commit in this phase).

Unit map + plausibility ranges updated in `src/lib/validations/
measurement.ts` so the create-measurement validator accepts the new
types end-to-end.
This commit is contained in:
Marc-André Bombeck committed 2026-05-14 14:07:16 +02:00
1 parent 03f58c75b8
commit c50931b4e7
3 files changed
+74

No files matched your search

@@ -0,0 +1,25 @@
-- v1.4.25 W5d — Withings full coverage. Adds seven new
-- MeasurementType enum values so the Withings ingest path can land
-- body composition (Body+, Body Cardio, Body Comp, Body Scan),
-- skin temperature (ScanWatch dermal reading — DISTINCT from core
-- body temperature; sharing the bucket would corrupt analytics
-- because surface temps run ~32 °C and core ~37 °C), pulse-wave
-- velocity + vascular age (Body Cardio / Body Scan exclusives), and
-- visceral fat (Body Comp / Body Scan rating).
--
-- See `.planning/research/withings-api-coverage.md` §3 for the gap
-- scoring + the rationale of which meastypes ship in v1.4.25 vs
-- defer to v1.5.
--
-- Additive only, forward-only — every new enum value is unconditional
-- and no existing row needs to be backfilled. The Withings client
-- starts emitting these values once `src/lib/withings/client.ts`
-- gains the meastype → enum mapping (separate commit).
ALTER TYPE "measurement_type" ADD VALUE IF NOT EXISTS 'FAT_FREE_MASS';
ALTER TYPE "measurement_type" ADD VALUE IF NOT EXISTS 'FAT_MASS';
ALTER TYPE "measurement_type" ADD VALUE IF NOT EXISTS 'MUSCLE_MASS';
ALTER TYPE "measurement_type" ADD VALUE IF NOT EXISTS 'SKIN_TEMPERATURE';
ALTER TYPE "measurement_type" ADD VALUE IF NOT EXISTS 'PULSE_WAVE_VELOCITY';
ALTER TYPE "measurement_type" ADD VALUE IF NOT EXISTS 'VASCULAR_AGE';
ALTER TYPE "measurement_type" ADD VALUE IF NOT EXISTS 'VISCERAL_FAT';
+8
View File
@@ -255,6 +255,14 @@ enum MeasurementType {
WALKING_RUNNING_DISTANCE // metres — HealthKit `distanceWalkingRunning`; SI metres in DB, UI converts per locale.
VO2_MAX // mL/(kg·min) — HealthKit `vo2Max`; one sample every few days at most.
BODY_TEMPERATURE // celsius — HealthKit `bodyTemperature`; iOS DTO already encodes this enum value.
// ── v1.4.25 W5d Withings full coverage ──
FAT_FREE_MASS // kg — Withings meastype 5 (Body+, Body Cardio, Body Comp, Body Scan). Algorithmically = weight − fat mass.
FAT_MASS // kg — Withings meastype 8. Mass form of BODY_FAT (kg, not %).
MUSCLE_MASS // kg — Withings meastype 76. Body+ family.
SKIN_TEMPERATURE // celsius — Withings meastype 73 (ScanWatch dermal reading). DISTINCT from BODY_TEMPERATURE — surface temps run ~32 °C, core ~37 °C; sharing a bucket would corrupt analytics.
PULSE_WAVE_VELOCITY // m/s — Withings meastype 91 (Body Cardio / Body Scan). Indicator of arterial stiffness.
VASCULAR_AGE // years — Withings meastype 155 (Body Scan). Composite of PWV + age.
VISCERAL_FAT // rating — Withings meastype 170 (Body Comp / Body Scan). Withings' rating scale (1–12); not directly comparable to a percent.
@@map("measurement_type")
}