@nativesquare/soma 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/index.d.ts +7 -128
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +31 -112
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +80 -4
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +135 -261
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/garmin/private.d.ts +475 -0
- package/dist/component/garmin/private.d.ts.map +1 -0
- package/dist/component/garmin/private.js +1614 -0
- package/dist/component/garmin/private.js.map +1 -0
- package/dist/component/garmin/public.d.ts +155 -0
- package/dist/component/garmin/public.d.ts.map +1 -0
- package/dist/component/garmin/public.js +787 -0
- package/dist/component/garmin/public.js.map +1 -0
- package/dist/component/garmin/schemas/activity.d.ts +94 -0
- package/dist/component/garmin/schemas/activity.d.ts.map +1 -0
- package/dist/component/garmin/schemas/activity.js +27 -0
- package/dist/component/garmin/schemas/activity.js.map +1 -0
- package/dist/component/garmin/schemas/activityDetails.d.ts +146 -0
- package/dist/component/garmin/schemas/activityDetails.d.ts.map +1 -0
- package/dist/component/garmin/schemas/activityDetails.js +27 -0
- package/dist/component/garmin/schemas/activityDetails.js.map +1 -0
- package/dist/component/garmin/schemas/bloodPressure.d.ts +38 -0
- package/dist/component/garmin/schemas/bloodPressure.d.ts.map +1 -0
- package/dist/component/garmin/schemas/bloodPressure.js +27 -0
- package/dist/component/garmin/schemas/bloodPressure.js.map +1 -0
- package/dist/component/garmin/schemas/bodyCompositions.d.ts +42 -0
- package/dist/component/garmin/schemas/bodyCompositions.d.ts.map +1 -0
- package/dist/component/garmin/schemas/bodyCompositions.js +27 -0
- package/dist/component/garmin/schemas/bodyCompositions.js.map +1 -0
- package/dist/component/garmin/schemas/dailies.d.ts +98 -0
- package/dist/component/garmin/schemas/dailies.d.ts.map +1 -0
- package/dist/component/garmin/schemas/dailies.js +27 -0
- package/dist/component/garmin/schemas/dailies.js.map +1 -0
- package/dist/component/garmin/schemas/epochs.d.ts +54 -0
- package/dist/component/garmin/schemas/epochs.d.ts.map +1 -0
- package/dist/component/garmin/schemas/epochs.js +27 -0
- package/dist/component/garmin/schemas/epochs.js.map +1 -0
- package/dist/component/garmin/schemas/healthSnapshot.d.ts +48 -0
- package/dist/component/garmin/schemas/healthSnapshot.d.ts.map +1 -0
- package/dist/component/garmin/schemas/healthSnapshot.js +27 -0
- package/dist/component/garmin/schemas/healthSnapshot.js.map +1 -0
- package/dist/component/garmin/schemas/hrvSummary.d.ts +40 -0
- package/dist/component/garmin/schemas/hrvSummary.d.ts.map +1 -0
- package/dist/component/garmin/schemas/hrvSummary.js +27 -0
- package/dist/component/garmin/schemas/hrvSummary.js.map +1 -0
- package/dist/component/garmin/schemas/manuallyUpdatedActivities.d.ts +94 -0
- package/dist/component/garmin/schemas/manuallyUpdatedActivities.d.ts.map +1 -0
- package/dist/component/garmin/schemas/manuallyUpdatedActivities.js +27 -0
- package/dist/component/garmin/schemas/manuallyUpdatedActivities.js.map +1 -0
- package/dist/component/garmin/schemas/menstrualCycleTracking.d.ts +100 -0
- package/dist/component/garmin/schemas/menstrualCycleTracking.d.ts.map +1 -0
- package/dist/component/garmin/schemas/menstrualCycleTracking.js +28 -0
- package/dist/component/garmin/schemas/menstrualCycleTracking.js.map +1 -0
- package/dist/component/garmin/schemas/moveIQ.d.ts +40 -0
- package/dist/component/garmin/schemas/moveIQ.d.ts.map +1 -0
- package/dist/component/garmin/schemas/moveIQ.js +27 -0
- package/dist/component/garmin/schemas/moveIQ.js.map +1 -0
- package/dist/component/garmin/schemas/pulseOx.d.ts +38 -0
- package/dist/component/garmin/schemas/pulseOx.d.ts.map +1 -0
- package/dist/component/garmin/schemas/pulseOx.js +28 -0
- package/dist/component/garmin/schemas/pulseOx.js.map +1 -0
- package/dist/component/garmin/schemas/respiration.d.ts +34 -0
- package/dist/component/garmin/schemas/respiration.d.ts.map +1 -0
- package/dist/component/garmin/schemas/respiration.js +28 -0
- package/dist/component/garmin/schemas/respiration.js.map +1 -0
- package/dist/component/garmin/schemas/skinTemperature.d.ts +36 -0
- package/dist/component/garmin/schemas/skinTemperature.d.ts.map +1 -0
- package/dist/component/garmin/schemas/skinTemperature.js +28 -0
- package/dist/component/garmin/schemas/skinTemperature.js.map +1 -0
- package/dist/component/garmin/schemas/sleeps.d.ts +88 -0
- package/dist/component/garmin/schemas/sleeps.d.ts.map +1 -0
- package/dist/component/garmin/schemas/sleeps.js +27 -0
- package/dist/component/garmin/schemas/sleeps.js.map +1 -0
- package/dist/component/garmin/schemas/stress.d.ts +70 -0
- package/dist/component/garmin/schemas/stress.d.ts.map +1 -0
- package/dist/component/garmin/schemas/stress.js +28 -0
- package/dist/component/garmin/schemas/stress.js.map +1 -0
- package/dist/component/garmin/schemas/userMetrics.d.ts +36 -0
- package/dist/component/garmin/schemas/userMetrics.d.ts.map +1 -0
- package/dist/component/garmin/schemas/userMetrics.js +28 -0
- package/dist/component/garmin/schemas/userMetrics.js.map +1 -0
- package/dist/component/garmin/transform/activity.d.ts +13 -0
- package/dist/component/garmin/transform/activity.d.ts.map +1 -0
- package/dist/component/garmin/transform/activity.js +111 -0
- package/dist/component/garmin/transform/activity.js.map +1 -0
- package/dist/component/garmin/transform/activityDetails.d.ts +13 -0
- package/dist/component/garmin/transform/activityDetails.d.ts.map +1 -0
- package/dist/component/garmin/transform/activityDetails.js +173 -0
- package/dist/component/garmin/transform/activityDetails.js.map +1 -0
- package/dist/component/garmin/transform/bloodPressure.d.ts +12 -0
- package/dist/component/garmin/transform/bloodPressure.d.ts.map +1 -0
- package/dist/component/garmin/transform/bloodPressure.js +33 -0
- package/dist/component/garmin/transform/bloodPressure.js.map +1 -0
- package/dist/component/garmin/transform/bodyCompositions.d.ts +12 -0
- package/dist/component/garmin/transform/bodyCompositions.d.ts.map +1 -0
- package/dist/component/garmin/transform/bodyCompositions.js +42 -0
- package/dist/component/garmin/transform/bodyCompositions.js.map +1 -0
- package/dist/component/garmin/transform/dailies.d.ts +12 -0
- package/dist/component/garmin/transform/dailies.d.ts.map +1 -0
- package/dist/component/garmin/transform/dailies.js +132 -0
- package/dist/component/garmin/transform/dailies.js.map +1 -0
- package/dist/component/garmin/transform/epochs.d.ts +13 -0
- package/dist/component/garmin/transform/epochs.d.ts.map +1 -0
- package/dist/component/garmin/transform/epochs.js +76 -0
- package/dist/component/garmin/transform/epochs.js.map +1 -0
- package/dist/component/garmin/transform/healthSnapshot.d.ts +12 -0
- package/dist/component/garmin/transform/healthSnapshot.d.ts.map +1 -0
- package/dist/component/garmin/transform/healthSnapshot.js +111 -0
- package/dist/component/garmin/transform/healthSnapshot.js.map +1 -0
- package/dist/component/garmin/transform/hrvSummary.d.ts +12 -0
- package/dist/component/garmin/transform/hrvSummary.d.ts.map +1 -0
- package/dist/component/garmin/transform/hrvSummary.js +45 -0
- package/dist/component/garmin/transform/hrvSummary.js.map +1 -0
- package/dist/component/garmin/transform/manuallyUpdatedActivities.d.ts +11 -0
- package/dist/component/garmin/transform/manuallyUpdatedActivities.d.ts.map +1 -0
- package/dist/component/garmin/transform/manuallyUpdatedActivities.js +20 -0
- package/dist/component/garmin/transform/manuallyUpdatedActivities.js.map +1 -0
- package/dist/component/garmin/transform/menstrualCycleTracking.d.ts +10 -0
- package/dist/component/garmin/transform/menstrualCycleTracking.d.ts.map +1 -0
- package/dist/component/garmin/transform/menstrualCycleTracking.js +43 -0
- package/dist/component/garmin/transform/menstrualCycleTracking.js.map +1 -0
- package/dist/component/garmin/transform/moveIQ.d.ts +17 -0
- package/dist/component/garmin/transform/moveIQ.d.ts.map +1 -0
- package/dist/component/garmin/transform/moveIQ.js +41 -0
- package/dist/component/garmin/transform/moveIQ.js.map +1 -0
- package/dist/component/garmin/transform/pulseOx.d.ts +12 -0
- package/dist/component/garmin/transform/pulseOx.d.ts.map +1 -0
- package/dist/component/garmin/transform/pulseOx.js +46 -0
- package/dist/component/garmin/transform/pulseOx.js.map +1 -0
- package/dist/component/garmin/transform/respiration.d.ts +12 -0
- package/dist/component/garmin/transform/respiration.d.ts.map +1 -0
- package/dist/component/garmin/transform/respiration.js +54 -0
- package/dist/component/garmin/transform/respiration.js.map +1 -0
- package/dist/component/garmin/transform/skinTemperature.d.ts +12 -0
- package/dist/component/garmin/transform/skinTemperature.d.ts.map +1 -0
- package/dist/component/garmin/transform/skinTemperature.js +38 -0
- package/dist/component/garmin/transform/skinTemperature.js.map +1 -0
- package/dist/component/garmin/transform/sleeps.d.ts +55 -0
- package/dist/component/garmin/transform/sleeps.d.ts.map +1 -0
- package/dist/component/garmin/transform/sleeps.js +120 -0
- package/dist/component/garmin/transform/sleeps.js.map +1 -0
- package/dist/component/garmin/transform/stress.d.ts +12 -0
- package/dist/component/garmin/transform/stress.d.ts.map +1 -0
- package/dist/component/garmin/transform/stress.js +56 -0
- package/dist/component/garmin/transform/stress.js.map +1 -0
- package/dist/component/garmin/transform/userMetrics.d.ts +12 -0
- package/dist/component/garmin/transform/userMetrics.d.ts.map +1 -0
- package/dist/component/garmin/transform/userMetrics.js +48 -0
- package/dist/component/garmin/transform/userMetrics.js.map +1 -0
- package/dist/component/garmin/types/garmin.d.ts +21 -0
- package/dist/component/garmin/types/garmin.d.ts.map +1 -0
- package/dist/component/garmin/types/garmin.js +6 -0
- package/dist/component/garmin/types/garmin.js.map +1 -0
- package/dist/component/garmin/types/zod/zod.gen.d.ts +1319 -0
- package/dist/component/garmin/types/zod/zod.gen.d.ts.map +1 -0
- package/dist/component/garmin/types/zod/zod.gen.js +784 -0
- package/dist/component/garmin/types/zod/zod.gen.js.map +1 -0
- package/dist/component/garmin/webhooks.d.ts +141 -0
- package/dist/component/garmin/webhooks.d.ts.map +1 -0
- package/dist/component/garmin/webhooks.js +766 -0
- package/dist/component/garmin/webhooks.js.map +1 -0
- package/dist/component/private.d.ts +4 -4
- package/dist/component/public.d.ts +333 -333
- package/dist/component/schema.d.ts +133 -133
- package/dist/component/strava/private.d.ts +30 -0
- package/dist/component/strava/private.d.ts.map +1 -0
- package/dist/component/strava/private.js +71 -0
- package/dist/component/strava/private.js.map +1 -0
- package/dist/component/{strava.d.ts → strava/public.d.ts} +3 -31
- package/dist/component/strava/public.d.ts.map +1 -0
- package/dist/component/{strava.js → strava/public.js} +22 -101
- package/dist/component/strava/public.js.map +1 -0
- package/dist/component/validators/activity.d.ts +6 -0
- package/dist/component/validators/activity.d.ts.map +1 -1
- package/dist/component/validators/activity.js.map +1 -1
- package/dist/component/validators/body.d.ts +20 -14
- package/dist/component/validators/body.d.ts.map +1 -1
- package/dist/component/validators/body.js.map +1 -1
- package/dist/component/validators/daily.d.ts +6 -0
- package/dist/component/validators/daily.d.ts.map +1 -1
- package/dist/component/validators/daily.js.map +1 -1
- package/dist/component/validators/enums.d.ts +1 -1
- package/dist/component/validators/menstruation.d.ts +5 -0
- package/dist/component/validators/menstruation.d.ts.map +1 -1
- package/dist/component/validators/menstruation.js.map +1 -1
- package/dist/garmin/client.js.map +1 -1
- package/dist/garmin/index.d.ts +0 -2
- package/dist/garmin/index.d.ts.map +1 -1
- package/dist/garmin/index.js +0 -1
- package/dist/garmin/index.js.map +1 -1
- package/dist/garmin/sync.d.ts.map +1 -1
- package/dist/garmin/sync.js +3 -2
- package/dist/garmin/sync.js.map +1 -1
- package/dist/garmin/types.d.ts +1 -1
- package/dist/garmin/types.d.ts.map +1 -1
- package/dist/validators.d.ts +31 -28
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +2 -2
- package/dist/validators.js.map +1 -1
- package/package.json +4 -7
- package/src/client/index.ts +41 -172
- package/src/component/_generated/api.ts +96 -4
- package/src/component/_generated/component.ts +252 -284
- package/src/{garmin → component/garmin}/auth.ts +8 -1
- package/src/component/garmin/client.ts +39 -0
- package/src/component/garmin/private.ts +1798 -0
- package/src/component/garmin/public.ts +938 -0
- package/src/component/garmin/schemas/activity.ts +40 -0
- package/src/component/garmin/schemas/activityDetails.ts +45 -0
- package/src/component/garmin/schemas/bloodPressure.ts +38 -0
- package/src/component/garmin/schemas/bodyCompositions.ts +38 -0
- package/src/component/garmin/schemas/dailies.ts +38 -0
- package/src/component/garmin/schemas/epochs.ts +38 -0
- package/src/component/garmin/schemas/healthSnapshot.ts +38 -0
- package/src/component/garmin/schemas/hrvSummary.ts +38 -0
- package/src/component/garmin/schemas/manuallyUpdatedActivities.ts +49 -0
- package/src/component/garmin/schemas/menstrualCycleTracking.ts +39 -0
- package/src/component/garmin/schemas/moveIQ.ts +38 -0
- package/src/component/garmin/schemas/pulseOx.ts +39 -0
- package/src/component/garmin/schemas/respiration.ts +39 -0
- package/src/component/garmin/schemas/skinTemperature.ts +39 -0
- package/src/component/garmin/schemas/sleeps.ts +38 -0
- package/src/component/garmin/schemas/stress.ts +43 -0
- package/src/component/garmin/schemas/userMetrics.ts +39 -0
- package/src/component/garmin/transform/activity.ts +143 -0
- package/src/component/garmin/transform/activityDetails.ts +236 -0
- package/src/{garmin → component/garmin/transform}/bloodPressure.ts +39 -41
- package/src/component/garmin/transform/bodyCompositions.ts +51 -0
- package/src/component/garmin/transform/dailies.ts +179 -0
- package/src/component/garmin/transform/epochs.ts +94 -0
- package/src/component/garmin/transform/healthSnapshot.ts +152 -0
- package/src/component/garmin/transform/hrvSummary.ts +56 -0
- package/src/component/garmin/transform/manuallyUpdatedActivities.ts +27 -0
- package/src/{garmin/maps/activity-type.ts → component/garmin/transform/maps/activityType.ts} +116 -116
- package/src/{garmin/maps/sleep-level.ts → component/garmin/transform/maps/sleepLevel.ts} +22 -22
- package/src/component/garmin/transform/menstrualCycleTracking.ts +48 -0
- package/src/component/garmin/transform/moveIQ.ts +48 -0
- package/src/{garmin → component/garmin/transform}/plannedWorkout.ts +328 -333
- package/src/component/garmin/transform/pulseOx.ts +64 -0
- package/src/component/garmin/transform/respiration.ts +73 -0
- package/src/component/garmin/transform/skinTemperature.ts +44 -0
- package/src/component/garmin/transform/sleeps.ts +159 -0
- package/src/component/garmin/transform/stress.ts +78 -0
- package/src/component/garmin/transform/userMetrics.ts +56 -0
- package/src/component/garmin/types/specs/training-api-workouts.json +699 -0
- package/src/component/garmin/types/trainingApiWorkouts/client/client.gen.ts +290 -0
- package/src/component/garmin/types/trainingApiWorkouts/client/index.ts +25 -0
- package/src/component/garmin/types/trainingApiWorkouts/client/types.gen.ts +214 -0
- package/src/component/garmin/types/trainingApiWorkouts/client/utils.gen.ts +316 -0
- package/src/component/garmin/types/trainingApiWorkouts/client.gen.ts +16 -0
- package/src/component/garmin/types/trainingApiWorkouts/core/auth.gen.ts +41 -0
- package/src/component/garmin/types/trainingApiWorkouts/core/bodySerializer.gen.ts +82 -0
- package/src/component/garmin/types/trainingApiWorkouts/core/params.gen.ts +169 -0
- package/src/component/garmin/types/trainingApiWorkouts/core/pathSerializer.gen.ts +171 -0
- package/src/component/garmin/types/trainingApiWorkouts/core/queryKeySerializer.gen.ts +117 -0
- package/src/component/garmin/types/trainingApiWorkouts/core/serverSentEvents.gen.ts +243 -0
- package/src/component/garmin/types/trainingApiWorkouts/core/types.gen.ts +104 -0
- package/src/component/garmin/types/trainingApiWorkouts/core/utils.gen.ts +140 -0
- package/src/component/garmin/types/trainingApiWorkouts/index.ts +4 -0
- package/src/component/garmin/types/trainingApiWorkouts/sdk.gen.ts +126 -0
- package/src/component/garmin/types/trainingApiWorkouts/types.gen.ts +387 -0
- package/src/component/garmin/types/trainingApiWorkouts/zod.gen.ts +423 -0
- package/src/component/garmin/types/wellnessApi/client/client.gen.ts +290 -0
- package/src/component/garmin/types/wellnessApi/client/index.ts +25 -0
- package/src/component/garmin/types/wellnessApi/client/types.gen.ts +214 -0
- package/src/component/garmin/types/wellnessApi/client/utils.gen.ts +316 -0
- package/src/component/garmin/types/wellnessApi/client.gen.ts +16 -0
- package/src/component/garmin/types/wellnessApi/core/auth.gen.ts +41 -0
- package/src/component/garmin/types/wellnessApi/core/bodySerializer.gen.ts +82 -0
- package/src/component/garmin/types/wellnessApi/core/params.gen.ts +169 -0
- package/src/component/garmin/types/wellnessApi/core/pathSerializer.gen.ts +171 -0
- package/src/component/garmin/types/wellnessApi/core/queryKeySerializer.gen.ts +117 -0
- package/src/component/garmin/types/wellnessApi/core/serverSentEvents.gen.ts +243 -0
- package/src/component/garmin/types/wellnessApi/core/types.gen.ts +104 -0
- package/src/component/garmin/types/wellnessApi/core/utils.gen.ts +140 -0
- package/src/component/garmin/types/wellnessApi/index.ts +4 -0
- package/src/component/garmin/types/wellnessApi/sdk.gen.ts +207 -0
- package/src/component/garmin/types/wellnessApi/types.gen.ts +2942 -0
- package/src/component/garmin/types/wellnessApi/zod.gen.ts +878 -0
- package/src/component/garmin/utils.ts +25 -0
- package/src/component/garmin/webhooks.ts +852 -0
- package/src/component/strava/private.ts +89 -0
- package/src/component/{strava.ts → strava/public.ts} +341 -404
- package/src/component/validators/activity.ts +5 -0
- package/src/component/validators/body.ts +5 -0
- package/src/component/validators/daily.ts +5 -0
- package/src/component/validators/menstruation.ts +5 -1
- package/src/component/validators/plannedWorkout.ts +5 -0
- package/src/validators.ts +12 -2
- package/dist/component/garmin.d.ts +0 -366
- package/dist/component/garmin.d.ts.map +0 -1
- package/dist/component/garmin.js +0 -1481
- package/dist/component/garmin.js.map +0 -1
- package/dist/component/strava.d.ts.map +0 -1
- package/dist/component/strava.js.map +0 -1
- package/dist/garmin/activity.d.ts +0 -92
- package/dist/garmin/activity.d.ts.map +0 -1
- package/dist/garmin/activity.js +0 -201
- package/dist/garmin/activity.js.map +0 -1
- package/src/component/garmin.ts +0 -1722
- package/src/garmin/activity.test.ts +0 -170
- package/src/garmin/activity.ts +0 -265
- package/src/garmin/auth.test.ts +0 -103
- package/src/garmin/body.ts +0 -59
- package/src/garmin/client.ts +0 -886
- package/src/garmin/daily.ts +0 -215
- package/src/garmin/hrv.ts +0 -57
- package/src/garmin/index.ts +0 -145
- package/src/garmin/maps/activity-type.test.ts +0 -78
- package/src/garmin/menstruation.ts +0 -44
- package/src/garmin/pulseOx.ts +0 -45
- package/src/garmin/respiration.ts +0 -55
- package/src/garmin/skinTemp.ts +0 -42
- package/src/garmin/sleep.test.ts +0 -109
- package/src/garmin/sleep.ts +0 -176
- package/src/garmin/stressDetails.ts +0 -71
- package/src/garmin/sync.ts +0 -566
- package/src/garmin/types.ts +0 -268
- package/src/garmin/userMetrics.ts +0 -50
- package/src/garmin/wellness-api.d.ts +0 -5637
- /package/src/{garmin/spec → component/garmin/types/specs}/wellness-api.json +0 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// ─── Stress Details Webhook Zod Schemas ───────────────────────────────────────
|
|
2
|
+
// Runtime validation schemas for Garmin Stress Details webhook payloads.
|
|
3
|
+
//
|
|
4
|
+
// The base schema (zClientStress) is auto-generated from the
|
|
5
|
+
// OpenAPI spec. We extend it here with webhook-specific fields (userId,
|
|
6
|
+
// userAccessToken).
|
|
7
|
+
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { zBodyBatteryActivityEvent, zClientStress } from "../types/wellnessApi/zod.gen.js";
|
|
10
|
+
|
|
11
|
+
// ─── Push Mode ──────────────────────────────────────────────────────────────
|
|
12
|
+
// Push payloads contain full stress details data plus webhook envelope fields.
|
|
13
|
+
|
|
14
|
+
export const garminStressPushItemSchema = zClientStress.extend({
|
|
15
|
+
// Webhook envelope fields
|
|
16
|
+
userId: z.string(),
|
|
17
|
+
userAccessToken: z.string().optional(),
|
|
18
|
+
// Override bigint → number (openapi-ts maps int64 to bigint in zod)
|
|
19
|
+
bodyBatteryActivityEvents: z.array(
|
|
20
|
+
zBodyBatteryActivityEvent.extend({ activityId: z.coerce.number().optional() }),
|
|
21
|
+
).optional(),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export const garminStressPushPayloadSchema = z.object({
|
|
25
|
+
stressDetails: z.array(garminStressPushItemSchema),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// ─── Ping Mode ──────────────────────────────────────────────────────────────
|
|
29
|
+
// Ping payloads are lightweight notifications — not in any OpenAPI spec.
|
|
30
|
+
|
|
31
|
+
export const garminStressPingItemSchema = z.object({
|
|
32
|
+
userId: z.string(),
|
|
33
|
+
callbackURL: z.string().optional(), // absent for deregistration notifications
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export const garminStressPingPayloadSchema = z.object({
|
|
37
|
+
stressDetails: z.array(garminStressPingItemSchema),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// ─── Inferred Types ─────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
export type GarminStressPushItem = z.infer<typeof garminStressPushItemSchema>;
|
|
43
|
+
export type GarminStressPingItem = z.infer<typeof garminStressPingItemSchema>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// ─── User Metrics Webhook Zod Schemas ─────────────────────────────────────────
|
|
2
|
+
// Runtime validation schemas for Garmin User Metrics webhook payloads.
|
|
3
|
+
//
|
|
4
|
+
// The base schema (zClientUserMetrics) is auto-generated from the
|
|
5
|
+
// OpenAPI spec. We extend it here with webhook-specific fields (userId,
|
|
6
|
+
// userAccessToken).
|
|
7
|
+
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { zClientUserMetrics } from "../types/wellnessApi/zod.gen.js";
|
|
10
|
+
|
|
11
|
+
// ─── Push Mode ──────────────────────────────────────────────────────────────
|
|
12
|
+
// Push payloads contain full user metrics data plus webhook envelope fields.
|
|
13
|
+
|
|
14
|
+
export const garminUserMetricsPushItemSchema = zClientUserMetrics.extend({
|
|
15
|
+
// Webhook envelope fields
|
|
16
|
+
userId: z.string(),
|
|
17
|
+
userAccessToken: z.string().optional(),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export const garminUserMetricsPushPayloadSchema = z.object({
|
|
21
|
+
userMetrics: z.array(garminUserMetricsPushItemSchema),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// ─── Ping Mode ──────────────────────────────────────────────────────────────
|
|
25
|
+
// Ping payloads are lightweight notifications — not in any OpenAPI spec.
|
|
26
|
+
|
|
27
|
+
export const garminUserMetricsPingItemSchema = z.object({
|
|
28
|
+
userId: z.string(),
|
|
29
|
+
callbackURL: z.string().optional(), // absent for deregistration notifications
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export const garminUserMetricsPingPayloadSchema = z.object({
|
|
33
|
+
userMetrics: z.array(garminUserMetricsPingItemSchema),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// ─── Inferred Types ─────────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
export type GarminUserMetricsPushItem = z.infer<typeof garminUserMetricsPushItemSchema>;
|
|
39
|
+
export type GarminUserMetricsPingItem = z.infer<typeof garminUserMetricsPingItemSchema>;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// ─── Activity Transformer ────────────────────────────────────────────────────
|
|
2
|
+
// Transforms a Garmin activity push item into the Soma Activity schema shape.
|
|
3
|
+
|
|
4
|
+
import { mapActivityType } from "./maps/activityType.js";
|
|
5
|
+
import type { SomaActivity } from "../../validators/activity.js";
|
|
6
|
+
import type { ClientActivity } from "../types/wellnessApi/types.gen.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Transform a Garmin activity push item into a Soma Activity document shape.
|
|
10
|
+
*
|
|
11
|
+
* Accepts activity summaries from webhook push payloads. These contain
|
|
12
|
+
* summary-level fields only (no laps, samples, or power data).
|
|
13
|
+
*
|
|
14
|
+
* The returned object is ready to be spread into an `ingestActivity` call
|
|
15
|
+
* alongside `connectionId` and `userId`.
|
|
16
|
+
*/
|
|
17
|
+
export function transformActivity(activity: ClientActivity): SomaActivity {
|
|
18
|
+
const startMs = (activity.startTimeInSeconds ?? 0) * 1000;
|
|
19
|
+
const endMs = startMs + (activity.durationInSeconds ?? 0) * 1000;
|
|
20
|
+
const startDate = new Date(startMs).toISOString();
|
|
21
|
+
const endDate = new Date(endMs).toISOString();
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
metadata: {
|
|
25
|
+
summary_id: activity.summaryId ?? String(activity.activityId),
|
|
26
|
+
start_time: startDate,
|
|
27
|
+
end_time: endDate,
|
|
28
|
+
type: mapActivityType(activity.activityType ?? "OTHER"),
|
|
29
|
+
upload_type: activity.manual ? 2 : 1,
|
|
30
|
+
name: activity.activityName,
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
active_durations_data: {
|
|
34
|
+
activity_seconds: activity.durationInSeconds,
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
calories_data: buildCaloriesData(activity),
|
|
38
|
+
|
|
39
|
+
device_data: activity.deviceName
|
|
40
|
+
? { name: activity.deviceName }
|
|
41
|
+
: undefined,
|
|
42
|
+
|
|
43
|
+
distance_data: buildDistanceData(activity),
|
|
44
|
+
|
|
45
|
+
heart_rate_data: buildHeartRateData(activity),
|
|
46
|
+
|
|
47
|
+
movement_data: buildMovementData(activity),
|
|
48
|
+
|
|
49
|
+
position_data: buildPositionData(activity),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
54
|
+
|
|
55
|
+
function buildCaloriesData(activity: ClientActivity) {
|
|
56
|
+
if (activity.activeKilocalories == null) return undefined;
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
net_activity_calories: activity.activeKilocalories,
|
|
60
|
+
total_burned_calories: activity.activeKilocalories,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function buildDistanceData(activity: ClientActivity) {
|
|
65
|
+
const elevGain = activity.totalElevationGainInMeters;
|
|
66
|
+
const elevLoss = activity.totalElevationLossInMeters;
|
|
67
|
+
|
|
68
|
+
if (activity.distanceInMeters == null && elevGain == null) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
summary: {
|
|
74
|
+
distance_meters: activity.distanceInMeters,
|
|
75
|
+
steps: activity.steps,
|
|
76
|
+
elevation:
|
|
77
|
+
elevGain != null
|
|
78
|
+
? {
|
|
79
|
+
gain_actual_meters: elevGain,
|
|
80
|
+
loss_actual_meters: elevLoss,
|
|
81
|
+
}
|
|
82
|
+
: undefined,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function buildHeartRateData(activity: ClientActivity) {
|
|
88
|
+
if (
|
|
89
|
+
activity.averageHeartRateInBeatsPerMinute == null &&
|
|
90
|
+
activity.maxHeartRateInBeatsPerMinute == null
|
|
91
|
+
) {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
summary: {
|
|
97
|
+
avg_hr_bpm: activity.averageHeartRateInBeatsPerMinute,
|
|
98
|
+
max_hr_bpm: activity.maxHeartRateInBeatsPerMinute,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function buildMovementData(activity: ClientActivity) {
|
|
104
|
+
const avgCadence =
|
|
105
|
+
activity.averageRunCadenceInStepsPerMinute ??
|
|
106
|
+
activity.averageBikeCadenceInRoundsPerMinute;
|
|
107
|
+
const maxCadence =
|
|
108
|
+
activity.maxRunCadenceInStepsPerMinute ??
|
|
109
|
+
activity.maxBikeCadenceInRoundsPerMinute;
|
|
110
|
+
|
|
111
|
+
if (
|
|
112
|
+
activity.averageSpeedInMetersPerSecond == null &&
|
|
113
|
+
activity.maxSpeedInMetersPerSecond == null &&
|
|
114
|
+
avgCadence == null
|
|
115
|
+
) {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
avg_speed_meters_per_second: activity.averageSpeedInMetersPerSecond,
|
|
121
|
+
max_speed_meters_per_second: activity.maxSpeedInMetersPerSecond,
|
|
122
|
+
avg_pace_minutes_per_kilometer: activity.averagePaceInMinutesPerKilometer,
|
|
123
|
+
max_pace_minutes_per_kilometer: activity.maxPaceInMinutesPerKilometer,
|
|
124
|
+
avg_cadence_rpm: avgCadence,
|
|
125
|
+
max_cadence_rpm: maxCadence,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function buildPositionData(activity: ClientActivity) {
|
|
130
|
+
if (
|
|
131
|
+
activity.startingLatitudeInDegree == null ||
|
|
132
|
+
activity.startingLongitudeInDegree == null
|
|
133
|
+
) {
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
start_pos_lat_lng_deg: [
|
|
139
|
+
activity.startingLatitudeInDegree,
|
|
140
|
+
activity.startingLongitudeInDegree,
|
|
141
|
+
],
|
|
142
|
+
};
|
|
143
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
// ─── Activity Details Transformer ────────────────────────────────────────────
|
|
2
|
+
// Transforms a Garmin activity detail push item into the Soma Activity schema.
|
|
3
|
+
// Enriches the summary-level data from transformActivity with detailed
|
|
4
|
+
// samples (GPS, heart rate, speed, cadence, power, etc.) and lap data.
|
|
5
|
+
|
|
6
|
+
import type { SomaActivity } from "../../validators/activity.js";
|
|
7
|
+
import type {
|
|
8
|
+
ClientActivityDetail,
|
|
9
|
+
ClientLap,
|
|
10
|
+
Sample,
|
|
11
|
+
} from "../types/wellnessApi/types.gen.js";
|
|
12
|
+
import { transformActivity } from "./activity.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Transform a Garmin activity detail push item into a Soma Activity document.
|
|
16
|
+
*
|
|
17
|
+
* Accepts activity details from webhook push payloads. These contain
|
|
18
|
+
* a summary plus per-second samples and lap markers.
|
|
19
|
+
*
|
|
20
|
+
* The returned object is ready to be spread into an `ingestActivity` call
|
|
21
|
+
* alongside `connectionId` and `userId`.
|
|
22
|
+
*/
|
|
23
|
+
export function transformActivityDetails(
|
|
24
|
+
detail: ClientActivityDetail,
|
|
25
|
+
): SomaActivity {
|
|
26
|
+
// Start from summary-level transform
|
|
27
|
+
const summary = detail.summary;
|
|
28
|
+
if (!summary) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`Activity detail ${detail.summaryId ?? "unknown"} has no summary`,
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const base = transformActivity(summary);
|
|
35
|
+
|
|
36
|
+
// Override summary_id to use the detail-level one if present
|
|
37
|
+
if (detail.summaryId) {
|
|
38
|
+
base.metadata.summary_id = detail.summaryId;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const samples = detail.samples ?? [];
|
|
42
|
+
const laps = detail.laps ?? [];
|
|
43
|
+
|
|
44
|
+
// Enrich with detailed sample data
|
|
45
|
+
return {
|
|
46
|
+
...base,
|
|
47
|
+
heart_rate_data: buildDetailedHeartRateData(base, samples),
|
|
48
|
+
distance_data: buildDetailedDistanceData(base, samples),
|
|
49
|
+
movement_data: buildDetailedMovementData(base, samples),
|
|
50
|
+
position_data: buildDetailedPositionData(base, samples),
|
|
51
|
+
power_data: buildDetailedPowerData(base, samples),
|
|
52
|
+
lap_data: buildLapData(laps),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
function toIso(epochSeconds: number): string {
|
|
59
|
+
return new Date(epochSeconds * 1000).toISOString();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function buildDetailedHeartRateData(
|
|
63
|
+
base: SomaActivity,
|
|
64
|
+
samples: Sample[],
|
|
65
|
+
): SomaActivity["heart_rate_data"] {
|
|
66
|
+
const hrSamples = samples.filter((s) => s.heartRate != null);
|
|
67
|
+
if (hrSamples.length === 0) return base.heart_rate_data;
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
...base.heart_rate_data,
|
|
71
|
+
detailed: {
|
|
72
|
+
hr_samples: hrSamples.map((s) => ({
|
|
73
|
+
timestamp: s.startTimeInSeconds
|
|
74
|
+
? toIso(s.startTimeInSeconds)
|
|
75
|
+
: undefined,
|
|
76
|
+
bpm: s.heartRate ?? undefined,
|
|
77
|
+
timer_duration_seconds: s.timerDurationInSeconds ?? undefined,
|
|
78
|
+
})),
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function buildDetailedDistanceData(
|
|
84
|
+
base: SomaActivity,
|
|
85
|
+
samples: Sample[],
|
|
86
|
+
): SomaActivity["distance_data"] {
|
|
87
|
+
const distanceSamples = samples.filter(
|
|
88
|
+
(s) => s.totalDistanceInMeters != null,
|
|
89
|
+
);
|
|
90
|
+
const elevationSamples = samples.filter(
|
|
91
|
+
(s) => s.elevationInMeters != null,
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
if (distanceSamples.length === 0 && elevationSamples.length === 0) {
|
|
95
|
+
return base.distance_data;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
...base.distance_data,
|
|
100
|
+
detailed: {
|
|
101
|
+
distance_samples:
|
|
102
|
+
distanceSamples.length > 0
|
|
103
|
+
? distanceSamples.map((s) => ({
|
|
104
|
+
timestamp: s.startTimeInSeconds
|
|
105
|
+
? toIso(s.startTimeInSeconds)
|
|
106
|
+
: undefined,
|
|
107
|
+
distance_meters: s.totalDistanceInMeters ?? undefined,
|
|
108
|
+
timer_duration_seconds: s.timerDurationInSeconds ?? undefined,
|
|
109
|
+
}))
|
|
110
|
+
: undefined,
|
|
111
|
+
elevation_samples:
|
|
112
|
+
elevationSamples.length > 0
|
|
113
|
+
? elevationSamples.map((s) => ({
|
|
114
|
+
timestamp: s.startTimeInSeconds
|
|
115
|
+
? toIso(s.startTimeInSeconds)
|
|
116
|
+
: undefined,
|
|
117
|
+
elev_meters: s.elevationInMeters ?? undefined,
|
|
118
|
+
timer_duration_seconds: s.timerDurationInSeconds ?? undefined,
|
|
119
|
+
}))
|
|
120
|
+
: undefined,
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function buildDetailedMovementData(
|
|
126
|
+
base: SomaActivity,
|
|
127
|
+
samples: Sample[],
|
|
128
|
+
): SomaActivity["movement_data"] {
|
|
129
|
+
const speedSamples = samples.filter(
|
|
130
|
+
(s) => s.speedMetersPerSecond != null,
|
|
131
|
+
);
|
|
132
|
+
const cadenceSamples = samples.filter(
|
|
133
|
+
(s) =>
|
|
134
|
+
s.stepsPerMinute != null ||
|
|
135
|
+
s.bikeCadenceInRPM != null ||
|
|
136
|
+
s.swimCadenceInStrokesPerMinute != null,
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
if (speedSamples.length === 0 && cadenceSamples.length === 0) {
|
|
140
|
+
return base.movement_data;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
...base.movement_data,
|
|
145
|
+
speed_samples:
|
|
146
|
+
speedSamples.length > 0
|
|
147
|
+
? speedSamples.map((s) => ({
|
|
148
|
+
timestamp: s.startTimeInSeconds
|
|
149
|
+
? toIso(s.startTimeInSeconds)
|
|
150
|
+
: undefined,
|
|
151
|
+
speed_meters_per_second: s.speedMetersPerSecond ?? undefined,
|
|
152
|
+
timer_duration_seconds: s.timerDurationInSeconds ?? undefined,
|
|
153
|
+
}))
|
|
154
|
+
: undefined,
|
|
155
|
+
cadence_samples:
|
|
156
|
+
cadenceSamples.length > 0
|
|
157
|
+
? cadenceSamples.map((s) => ({
|
|
158
|
+
timestamp: s.startTimeInSeconds
|
|
159
|
+
? toIso(s.startTimeInSeconds)
|
|
160
|
+
: undefined,
|
|
161
|
+
cadence_rpm:
|
|
162
|
+
s.stepsPerMinute ??
|
|
163
|
+
s.bikeCadenceInRPM ??
|
|
164
|
+
s.swimCadenceInStrokesPerMinute ??
|
|
165
|
+
undefined,
|
|
166
|
+
timer_duration_seconds: s.timerDurationInSeconds ?? undefined,
|
|
167
|
+
}))
|
|
168
|
+
: undefined,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function buildDetailedPositionData(
|
|
173
|
+
base: SomaActivity,
|
|
174
|
+
samples: Sample[],
|
|
175
|
+
): SomaActivity["position_data"] {
|
|
176
|
+
const posSamples = samples.filter(
|
|
177
|
+
(s) => s.latitudeInDegree != null && s.longitudeInDegree != null,
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
if (posSamples.length === 0) return base.position_data;
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
...base.position_data,
|
|
184
|
+
position_samples: posSamples.map((s) => ({
|
|
185
|
+
timestamp: s.startTimeInSeconds
|
|
186
|
+
? toIso(s.startTimeInSeconds)
|
|
187
|
+
: undefined,
|
|
188
|
+
coords_lat_lng_deg: [s.latitudeInDegree ?? 0, s.longitudeInDegree ?? 0],
|
|
189
|
+
timer_duration_seconds: s.timerDurationInSeconds ?? undefined,
|
|
190
|
+
})),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function buildDetailedPowerData(
|
|
195
|
+
base: SomaActivity,
|
|
196
|
+
samples: Sample[],
|
|
197
|
+
): SomaActivity["power_data"] {
|
|
198
|
+
const powerSamples = samples.filter((s) => s.powerInWatts != null);
|
|
199
|
+
|
|
200
|
+
if (powerSamples.length === 0) return base.power_data;
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
...base.power_data,
|
|
204
|
+
power_samples: powerSamples.map((s) => ({
|
|
205
|
+
timestamp: s.startTimeInSeconds
|
|
206
|
+
? toIso(s.startTimeInSeconds)
|
|
207
|
+
: undefined,
|
|
208
|
+
watts: s.powerInWatts ?? undefined,
|
|
209
|
+
timer_duration_seconds: s.timerDurationInSeconds ?? undefined,
|
|
210
|
+
})),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function buildLapData(
|
|
215
|
+
laps: ClientLap[],
|
|
216
|
+
): SomaActivity["lap_data"] {
|
|
217
|
+
if (laps.length === 0) return undefined;
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
laps: laps.map((lap, i) => {
|
|
221
|
+
const startTime = lap.startTimeInSeconds
|
|
222
|
+
? toIso(lap.startTimeInSeconds)
|
|
223
|
+
: undefined;
|
|
224
|
+
// Estimate end time from next lap's start, if available
|
|
225
|
+
const nextLap = laps[i + 1];
|
|
226
|
+
const endTime = nextLap?.startTimeInSeconds
|
|
227
|
+
? toIso(nextLap.startTimeInSeconds)
|
|
228
|
+
: undefined;
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
start_time: startTime,
|
|
232
|
+
end_time: endTime,
|
|
233
|
+
};
|
|
234
|
+
}),
|
|
235
|
+
};
|
|
236
|
+
}
|
|
@@ -1,41 +1,39 @@
|
|
|
1
|
-
// ─── Blood Pressure Transformer ──────────────────────────────────────────────
|
|
2
|
-
// Transforms Garmin blood pressure
|
|
3
|
-
|
|
4
|
-
import type {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
};
|
|
41
|
-
}
|
|
1
|
+
// ─── Blood Pressure Transformer ──────────────────────────────────────────────
|
|
2
|
+
// Transforms a Garmin blood pressure push item into the Soma Body schema shape.
|
|
3
|
+
|
|
4
|
+
import type { SomaBody } from "../../validators/body.js";
|
|
5
|
+
import type { ClientBloodPressure } from "../types/wellnessApi/types.gen.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Transform a Garmin blood pressure push item into a Soma Body document shape.
|
|
9
|
+
*
|
|
10
|
+
* Blood pressure records are ingested into the `body` table via `ingestBody`,
|
|
11
|
+
* keyed by `metadata.start_time + metadata.end_time`.
|
|
12
|
+
*
|
|
13
|
+
* Returns `null` when neither systolic nor diastolic data is present.
|
|
14
|
+
*/
|
|
15
|
+
export function transformBloodPressure(bp: ClientBloodPressure): SomaBody | null {
|
|
16
|
+
const measurementMs = (bp.measurementTimeInSeconds ?? 0) * 1000;
|
|
17
|
+
const timestamp = new Date(measurementMs).toISOString();
|
|
18
|
+
|
|
19
|
+
if (bp.systolic == null && bp.diastolic == null) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
metadata: {
|
|
25
|
+
start_time: timestamp,
|
|
26
|
+
end_time: timestamp,
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
blood_pressure_data: {
|
|
30
|
+
blood_pressure_samples: [
|
|
31
|
+
{
|
|
32
|
+
timestamp,
|
|
33
|
+
diastolic_bp: bp.diastolic,
|
|
34
|
+
systolic_bp: bp.systolic,
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// ─── Body Compositions Transformer ───────────────────────────────────────────
|
|
2
|
+
// Transforms a Garmin body composition push item into the Soma Body schema shape.
|
|
3
|
+
|
|
4
|
+
import type { SomaBody } from "../../validators/body.js";
|
|
5
|
+
import type { ClientBodyComp } from "../types/wellnessApi/types.gen.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Transform a Garmin body composition push item into a Soma Body document shape.
|
|
9
|
+
*
|
|
10
|
+
* Body composition records are ingested into the `body` table via `ingestBody`,
|
|
11
|
+
* keyed by `metadata.start_time + metadata.end_time`.
|
|
12
|
+
*
|
|
13
|
+
* Returns `null` when no meaningful measurement data is present.
|
|
14
|
+
*/
|
|
15
|
+
export function transformBodyComposition(body: ClientBodyComp): SomaBody | null {
|
|
16
|
+
const measurementMs = (body.measurementTimeInSeconds ?? 0) * 1000;
|
|
17
|
+
const timestamp = new Date(measurementMs).toISOString();
|
|
18
|
+
|
|
19
|
+
if (
|
|
20
|
+
body.weightInGrams == null &&
|
|
21
|
+
body.bodyFatInPercent == null &&
|
|
22
|
+
body.bodyMassIndex == null &&
|
|
23
|
+
body.muscleMassInGrams == null &&
|
|
24
|
+
body.boneMassInGrams == null &&
|
|
25
|
+
body.bodyWaterInPercent == null
|
|
26
|
+
) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
metadata: {
|
|
32
|
+
start_time: timestamp,
|
|
33
|
+
end_time: timestamp,
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
measurements_data: {
|
|
37
|
+
measurements: [
|
|
38
|
+
{
|
|
39
|
+
measurement_time: timestamp,
|
|
40
|
+
weight_kg:
|
|
41
|
+
body.weightInGrams != null ? body.weightInGrams / 1000 : undefined,
|
|
42
|
+
BMI: body.bodyMassIndex,
|
|
43
|
+
bodyfat_percentage: body.bodyFatInPercent,
|
|
44
|
+
muscle_mass_g: body.muscleMassInGrams,
|
|
45
|
+
bone_mass_g: body.boneMassInGrams,
|
|
46
|
+
water_percentage: body.bodyWaterInPercent,
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|