@nativesquare/soma 0.14.1 → 0.16.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.
Files changed (118) hide show
  1. package/dist/client/garmin.d.ts +31 -0
  2. package/dist/client/garmin.d.ts.map +1 -1
  3. package/dist/client/garmin.js +34 -0
  4. package/dist/client/garmin.js.map +1 -1
  5. package/dist/client/healthkit.d.ts +267 -0
  6. package/dist/client/healthkit.d.ts.map +1 -0
  7. package/dist/client/healthkit.js +600 -0
  8. package/dist/client/healthkit.js.map +1 -0
  9. package/dist/client/index.d.ts +3 -0
  10. package/dist/client/index.d.ts.map +1 -1
  11. package/dist/client/index.js +4 -0
  12. package/dist/client/index.js.map +1 -1
  13. package/dist/component/_generated/api.d.ts +26 -0
  14. package/dist/component/_generated/api.d.ts.map +1 -1
  15. package/dist/component/_generated/api.js.map +1 -1
  16. package/dist/component/healthkit/index.d.ts +14 -0
  17. package/dist/component/healthkit/index.d.ts.map +1 -0
  18. package/dist/{healthkit → component/healthkit}/index.js +11 -11
  19. package/dist/component/healthkit/index.js.map +1 -0
  20. package/dist/component/healthkit/transform/activity.d.ts +19 -0
  21. package/dist/component/healthkit/transform/activity.d.ts.map +1 -0
  22. package/dist/{healthkit → component/healthkit/transform}/activity.js +1 -1
  23. package/dist/component/healthkit/transform/activity.js.map +1 -0
  24. package/dist/{healthkit → component/healthkit/transform}/athlete.d.ts +3 -9
  25. package/dist/component/healthkit/transform/athlete.d.ts.map +1 -0
  26. package/dist/component/healthkit/transform/athlete.js.map +1 -0
  27. package/dist/component/healthkit/transform/body.d.ts +25 -0
  28. package/dist/component/healthkit/transform/body.d.ts.map +1 -0
  29. package/dist/component/healthkit/transform/body.js.map +1 -0
  30. package/dist/component/healthkit/transform/daily.d.ts +36 -0
  31. package/dist/component/healthkit/transform/daily.d.ts.map +1 -0
  32. package/dist/component/healthkit/transform/daily.js.map +1 -0
  33. package/dist/{healthkit/maps/activity-type.d.ts → component/healthkit/transform/maps/activityType.d.ts} +1 -1
  34. package/dist/component/healthkit/transform/maps/activityType.d.ts.map +1 -0
  35. package/dist/{healthkit/maps/activity-type.js → component/healthkit/transform/maps/activityType.js} +1 -1
  36. package/dist/component/healthkit/transform/maps/activityType.js.map +1 -0
  37. package/dist/{healthkit/maps/menstruation-flow.d.ts → component/healthkit/transform/maps/menstruationFlow.d.ts} +1 -1
  38. package/dist/component/healthkit/transform/maps/menstruationFlow.d.ts.map +1 -0
  39. package/dist/{healthkit/maps/menstruation-flow.js → component/healthkit/transform/maps/menstruationFlow.js} +2 -2
  40. package/dist/component/healthkit/transform/maps/menstruationFlow.js.map +1 -0
  41. package/dist/{healthkit/maps/sleep-level.d.ts → component/healthkit/transform/maps/sleepLevel.d.ts} +1 -1
  42. package/dist/component/healthkit/transform/maps/sleepLevel.d.ts.map +1 -0
  43. package/dist/{healthkit/maps/sleep-level.js → component/healthkit/transform/maps/sleepLevel.js} +2 -2
  44. package/dist/component/healthkit/transform/maps/sleepLevel.js.map +1 -0
  45. package/dist/{healthkit → component/healthkit/transform}/menstruation.d.ts +3 -17
  46. package/dist/component/healthkit/transform/menstruation.d.ts.map +1 -0
  47. package/dist/{healthkit → component/healthkit/transform}/menstruation.js +1 -1
  48. package/dist/component/healthkit/transform/menstruation.js.map +1 -0
  49. package/dist/component/healthkit/transform/nutrition.d.ts +25 -0
  50. package/dist/component/healthkit/transform/nutrition.d.ts.map +1 -0
  51. package/dist/component/healthkit/transform/nutrition.js.map +1 -0
  52. package/dist/component/healthkit/transform/sleep.d.ts +22 -0
  53. package/dist/component/healthkit/transform/sleep.d.ts.map +1 -0
  54. package/dist/{healthkit → component/healthkit/transform}/sleep.js +2 -2
  55. package/dist/component/healthkit/transform/sleep.js.map +1 -0
  56. package/dist/{healthkit → component/healthkit/transform}/utils.d.ts +1 -1
  57. package/dist/component/healthkit/transform/utils.d.ts.map +1 -0
  58. package/dist/component/healthkit/transform/utils.js.map +1 -0
  59. package/dist/component/healthkit/types.d.ts.map +1 -0
  60. package/dist/component/healthkit/types.js.map +1 -0
  61. package/package.json +3 -3
  62. package/src/client/garmin.ts +42 -0
  63. package/src/client/healthkit.ts +791 -0
  64. package/src/client/index.ts +4 -0
  65. package/src/component/_generated/api.ts +26 -0
  66. package/src/{healthkit → component/healthkit}/index.ts +46 -59
  67. package/src/component/healthkit/transform/activity.ts +115 -0
  68. package/src/{healthkit → component/healthkit/transform}/athlete.ts +4 -8
  69. package/src/{healthkit → component/healthkit/transform}/body.ts +3 -7
  70. package/src/{healthkit → component/healthkit/transform}/daily.ts +4 -10
  71. package/src/{healthkit/maps/menstruation-flow.ts → component/healthkit/transform/maps/menstruationFlow.ts} +1 -1
  72. package/src/{healthkit/maps/sleep-level.ts → component/healthkit/transform/maps/sleepLevel.ts} +1 -1
  73. package/src/{healthkit → component/healthkit/transform}/menstruation.ts +4 -8
  74. package/src/{healthkit → component/healthkit/transform}/nutrition.ts +3 -7
  75. package/src/{healthkit → component/healthkit/transform}/sleep.ts +5 -9
  76. package/src/{healthkit → component/healthkit/transform}/utils.ts +1 -1
  77. package/dist/healthkit/activity.d.ts +0 -75
  78. package/dist/healthkit/activity.d.ts.map +0 -1
  79. package/dist/healthkit/activity.js.map +0 -1
  80. package/dist/healthkit/athlete.d.ts.map +0 -1
  81. package/dist/healthkit/athlete.js.map +0 -1
  82. package/dist/healthkit/body.d.ts +0 -102
  83. package/dist/healthkit/body.d.ts.map +0 -1
  84. package/dist/healthkit/body.js.map +0 -1
  85. package/dist/healthkit/daily.d.ts +0 -119
  86. package/dist/healthkit/daily.d.ts.map +0 -1
  87. package/dist/healthkit/daily.js.map +0 -1
  88. package/dist/healthkit/index.d.ts +0 -21
  89. package/dist/healthkit/index.d.ts.map +0 -1
  90. package/dist/healthkit/index.js.map +0 -1
  91. package/dist/healthkit/maps/activity-type.d.ts.map +0 -1
  92. package/dist/healthkit/maps/activity-type.js.map +0 -1
  93. package/dist/healthkit/maps/menstruation-flow.d.ts.map +0 -1
  94. package/dist/healthkit/maps/menstruation-flow.js.map +0 -1
  95. package/dist/healthkit/maps/sleep-level.d.ts.map +0 -1
  96. package/dist/healthkit/maps/sleep-level.js.map +0 -1
  97. package/dist/healthkit/menstruation.d.ts.map +0 -1
  98. package/dist/healthkit/menstruation.js.map +0 -1
  99. package/dist/healthkit/nutrition.d.ts +0 -77
  100. package/dist/healthkit/nutrition.d.ts.map +0 -1
  101. package/dist/healthkit/nutrition.js.map +0 -1
  102. package/dist/healthkit/sleep.d.ts +0 -60
  103. package/dist/healthkit/sleep.d.ts.map +0 -1
  104. package/dist/healthkit/sleep.js.map +0 -1
  105. package/dist/healthkit/types.d.ts.map +0 -1
  106. package/dist/healthkit/types.js.map +0 -1
  107. package/dist/healthkit/utils.d.ts.map +0 -1
  108. package/dist/healthkit/utils.js.map +0 -1
  109. package/src/healthkit/activity.ts +0 -120
  110. /package/dist/{healthkit → component/healthkit/transform}/athlete.js +0 -0
  111. /package/dist/{healthkit → component/healthkit/transform}/body.js +0 -0
  112. /package/dist/{healthkit → component/healthkit/transform}/daily.js +0 -0
  113. /package/dist/{healthkit → component/healthkit/transform}/nutrition.js +0 -0
  114. /package/dist/{healthkit → component/healthkit/transform}/utils.js +0 -0
  115. /package/dist/{healthkit → component/healthkit}/types.d.ts +0 -0
  116. /package/dist/{healthkit → component/healthkit}/types.js +0 -0
  117. /package/src/{healthkit/maps/activity-type.ts → component/healthkit/transform/maps/activityType.ts} +0 -0
  118. /package/src/{healthkit → component/healthkit}/types.ts +0 -0
@@ -41,9 +41,11 @@ export type {
41
41
  } from "./types.js";
42
42
  import type { HttpRouter } from "convex/server";
43
43
  import { SomaGarmin } from "./garmin.js";
44
+ import { SomaHealthKit } from "./healthkit.js";
44
45
  import { SomaStrava } from "./strava.js";
45
46
 
46
47
  export { SomaGarmin } from "./garmin.js";
48
+ export { SomaHealthKit } from "./healthkit.js";
47
49
  export { SomaStrava } from "./strava.js";
48
50
  export { STRAVA_OAUTH_CALLBACK_PATH, STRAVA_WEBHOOK_BASE_PATH } from "./strava.js";
49
51
  export { GARMIN_OAUTH_CALLBACK_PATH, GARMIN_WEBHOOK_BASE_PATH } from "./garmin.js";
@@ -91,6 +93,7 @@ export class Soma {
91
93
  private garminConfig?: SomaGarminConfig;
92
94
 
93
95
  public readonly garmin: SomaGarmin;
96
+ public readonly healthkit: SomaHealthKit;
94
97
  public readonly strava: SomaStrava;
95
98
 
96
99
  constructor(
@@ -101,6 +104,7 @@ export class Soma {
101
104
  this.garminConfig = options?.garmin ?? this.readGarminEnv();
102
105
 
103
106
  this.garmin = new SomaGarmin(this.component, () => this.requireGarminConfig());
107
+ this.healthkit = new SomaHealthKit(this.component);
104
108
  this.strava = new SomaStrava(this.component, () => this.requireStravaConfig());
105
109
  }
106
110
 
@@ -55,6 +55,19 @@ import type * as garmin_types_wellnessApi_client_index from "../garmin/types/wel
55
55
  import type * as garmin_types_wellnessApi_index from "../garmin/types/wellnessApi/index.js";
56
56
  import type * as garmin_utils from "../garmin/utils.js";
57
57
  import type * as garmin_webhooks from "../garmin/webhooks.js";
58
+ import type * as healthkit_index from "../healthkit/index.js";
59
+ import type * as healthkit_transform_activity from "../healthkit/transform/activity.js";
60
+ import type * as healthkit_transform_athlete from "../healthkit/transform/athlete.js";
61
+ import type * as healthkit_transform_body from "../healthkit/transform/body.js";
62
+ import type * as healthkit_transform_daily from "../healthkit/transform/daily.js";
63
+ import type * as healthkit_transform_maps_activityType from "../healthkit/transform/maps/activityType.js";
64
+ import type * as healthkit_transform_maps_menstruationFlow from "../healthkit/transform/maps/menstruationFlow.js";
65
+ import type * as healthkit_transform_maps_sleepLevel from "../healthkit/transform/maps/sleepLevel.js";
66
+ import type * as healthkit_transform_menstruation from "../healthkit/transform/menstruation.js";
67
+ import type * as healthkit_transform_nutrition from "../healthkit/transform/nutrition.js";
68
+ import type * as healthkit_transform_sleep from "../healthkit/transform/sleep.js";
69
+ import type * as healthkit_transform_utils from "../healthkit/transform/utils.js";
70
+ import type * as healthkit_types from "../healthkit/types.js";
58
71
  import type * as private_ from "../private.js";
59
72
  import type * as public_ from "../public.js";
60
73
  import type * as strava_auth from "../strava/auth.js";
@@ -137,6 +150,19 @@ const fullApi: ApiFromModules<{
137
150
  "garmin/types/wellnessApi/index": typeof garmin_types_wellnessApi_index;
138
151
  "garmin/utils": typeof garmin_utils;
139
152
  "garmin/webhooks": typeof garmin_webhooks;
153
+ "healthkit/index": typeof healthkit_index;
154
+ "healthkit/transform/activity": typeof healthkit_transform_activity;
155
+ "healthkit/transform/athlete": typeof healthkit_transform_athlete;
156
+ "healthkit/transform/body": typeof healthkit_transform_body;
157
+ "healthkit/transform/daily": typeof healthkit_transform_daily;
158
+ "healthkit/transform/maps/activityType": typeof healthkit_transform_maps_activityType;
159
+ "healthkit/transform/maps/menstruationFlow": typeof healthkit_transform_maps_menstruationFlow;
160
+ "healthkit/transform/maps/sleepLevel": typeof healthkit_transform_maps_sleepLevel;
161
+ "healthkit/transform/menstruation": typeof healthkit_transform_menstruation;
162
+ "healthkit/transform/nutrition": typeof healthkit_transform_nutrition;
163
+ "healthkit/transform/sleep": typeof healthkit_transform_sleep;
164
+ "healthkit/transform/utils": typeof healthkit_transform_utils;
165
+ "healthkit/types": typeof healthkit_types;
140
166
  private: typeof private_;
141
167
  public: typeof public_;
142
168
  "strava/auth": typeof strava_auth;
@@ -1,59 +1,46 @@
1
- // ─── @nativesquare/soma/healthkit ─────────────────────────────────────────────
2
- // Apple HealthKit → Soma schema transformers.
3
- //
4
- // Pure TypeScript functions with zero runtime dependencies.
5
- // Compatible with any HealthKit library (react-native-health, expo-health, etc.)
6
-
7
- // ── Transformers ─────────────────────────────────────────────────────────────
8
- export { transformWorkout } from "./activity.js";
9
- export type { ActivityData } from "./activity.js";
10
-
11
- export { transformSleep } from "./sleep.js";
12
- export type { SleepData } from "./sleep.js";
13
-
14
- export { transformBody } from "./body.js";
15
- export type { BodyData } from "./body.js";
16
-
17
- export { transformDaily, transformDailyFromSummary } from "./daily.js";
18
- export type { DailyData } from "./daily.js";
19
-
20
- export { transformNutrition } from "./nutrition.js";
21
- export type { NutritionData } from "./nutrition.js";
22
-
23
- export { transformMenstruation } from "./menstruation.js";
24
- export type { MenstruationData } from "./menstruation.js";
25
-
26
- export { transformAthlete } from "./athlete.js";
27
- export type { AthleteData } from "./athlete.js";
28
-
29
- // ── Enum Maps ────────────────────────────────────────────────────────────────
30
- export { mapActivityType } from "./maps/activity-type.js";
31
- export { mapSleepLevel, isAsleepCategory } from "./maps/sleep-level.js";
32
- export { mapMenstruationFlow } from "./maps/menstruation-flow.js";
33
-
34
- // ── Types ────────────────────────────────────────────────────────────────────
35
- export type {
36
- HKQuantitySample,
37
- HKCategorySample,
38
- HKWorkout,
39
- HKWorkoutRoute,
40
- HKActivitySummary,
41
- HKDevice,
42
- HKSource,
43
- HKCharacteristics,
44
- HKBiologicalSex,
45
- HKQuantityTypeIdentifier,
46
- HKCategoryTypeIdentifier,
47
- HKSleepCategoryValue,
48
- HKMenstrualFlowCategoryValue,
49
- } from "./types.js";
50
-
51
- export { HKSleepCategory, HKMenstrualFlowCategory } from "./types.js";
52
-
53
- // ── Utilities ────────────────────────────────────────────────────────────────
54
- export {
55
- diffSeconds,
56
- dayRange,
57
- sampleTimeRange,
58
- buildDeviceData,
59
- } from "./utils.js";
1
+ // ─── @nativesquare/soma/healthkit ─────────────────────────────────────────────
2
+ // Apple HealthKit → Soma schema transformers.
3
+ //
4
+ // Pure TypeScript functions with zero runtime dependencies.
5
+ // Compatible with any HealthKit library (react-native-health, expo-health, etc.)
6
+
7
+ // ── Transformers ─────────────────────────────────────────────────────────────
8
+ export { transformWorkout } from "./transform/activity.js";
9
+ export { transformSleep } from "./transform/sleep.js";
10
+ export { transformBody } from "./transform/body.js";
11
+ export { transformDaily, transformDailyFromSummary } from "./transform/daily.js";
12
+ export { transformNutrition } from "./transform/nutrition.js";
13
+ export { transformMenstruation } from "./transform/menstruation.js";
14
+ export { transformAthlete } from "./transform/athlete.js";
15
+
16
+ // ── Enum Maps ────────────────────────────────────────────────────────────────
17
+ export { mapActivityType } from "./transform/maps/activityType.js";
18
+ export { mapSleepLevel, isAsleepCategory } from "./transform/maps/sleepLevel.js";
19
+ export { mapMenstruationFlow } from "./transform/maps/menstruationFlow.js";
20
+
21
+ // ── Types ────────────────────────────────────────────────────────────────────
22
+ export type {
23
+ HKQuantitySample,
24
+ HKCategorySample,
25
+ HKWorkout,
26
+ HKWorkoutRoute,
27
+ HKActivitySummary,
28
+ HKDevice,
29
+ HKSource,
30
+ HKCharacteristics,
31
+ HKBiologicalSex,
32
+ HKQuantityTypeIdentifier,
33
+ HKCategoryTypeIdentifier,
34
+ HKSleepCategoryValue,
35
+ HKMenstrualFlowCategoryValue,
36
+ } from "./types.js";
37
+
38
+ export { HKSleepCategory, HKMenstrualFlowCategory } from "./types.js";
39
+
40
+ // ── Utilities ────────────────────────────────────────────────────────────────
41
+ export {
42
+ diffSeconds,
43
+ dayRange,
44
+ sampleTimeRange,
45
+ buildDeviceData,
46
+ } from "./transform/utils.js";
@@ -0,0 +1,115 @@
1
+ // ─── Activity Transformer ────────────────────────────────────────────────────
2
+ // Transforms an Apple HealthKit HKWorkout into the Soma Activity schema shape.
3
+
4
+ import type { HKWorkout } from "../types.js";
5
+ import type { SomaActivity } from "../../validators/activity.js";
6
+ import { mapActivityType } from "./maps/activityType.js";
7
+ import { buildDeviceData } from "./utils.js";
8
+
9
+ /**
10
+ * Transform an HKWorkout into a Soma Activity document shape.
11
+ *
12
+ * The returned object is ready to be spread into an `ingestActivity` call
13
+ * alongside `connectionId` and `userId`.
14
+ *
15
+ * @param workout - The HKWorkout from HealthKit
16
+ * @returns Soma Activity fields (without connectionId/userId)
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * const data = transformWorkout(hkWorkout);
21
+ * await soma.ingestActivity(ctx, { connectionId, userId, ...data });
22
+ * ```
23
+ */
24
+ export function transformWorkout(workout: HKWorkout): SomaActivity {
25
+ const heartRateSamples = workout.heartRateSamples;
26
+ const hrValues = heartRateSamples?.map((s) => s.value) ?? [];
27
+
28
+ return {
29
+ metadata: {
30
+ summary_id: workout.uuid,
31
+ start_time: workout.startDate,
32
+ end_time: workout.endDate,
33
+ type: mapActivityType(workout.workoutActivityType),
34
+ upload_type: 1 as const, // Automatic
35
+ name: undefined as string | undefined,
36
+ },
37
+
38
+ active_durations_data: {
39
+ activity_seconds: workout.duration,
40
+ },
41
+
42
+ calories_data:
43
+ workout.totalEnergyBurned != null
44
+ ? {
45
+ total_burned_calories: workout.totalEnergyBurned,
46
+ }
47
+ : undefined,
48
+
49
+ device_data: buildDeviceData(workout.source, workout.device),
50
+
51
+ distance_data:
52
+ workout.totalDistance != null ||
53
+ workout.totalSwimmingStrokeCount != null ||
54
+ workout.totalFlightsClimbed != null
55
+ ? {
56
+ summary: {
57
+ distance_meters: workout.totalDistance,
58
+ steps: undefined as number | undefined,
59
+ floors_climbed: workout.totalFlightsClimbed,
60
+ swimming:
61
+ workout.totalSwimmingStrokeCount != null
62
+ ? { num_strokes: workout.totalSwimmingStrokeCount }
63
+ : undefined,
64
+ },
65
+ }
66
+ : undefined,
67
+
68
+ heart_rate_data:
69
+ heartRateSamples && heartRateSamples.length > 0
70
+ ? {
71
+ detailed: {
72
+ hr_samples: heartRateSamples.map((s) => ({
73
+ timestamp: s.startDate,
74
+ bpm: s.value,
75
+ })),
76
+ },
77
+ summary: {
78
+ avg_hr_bpm:
79
+ hrValues.length > 0
80
+ ? hrValues.reduce((a, b) => a + b, 0) / hrValues.length
81
+ : undefined,
82
+ max_hr_bpm:
83
+ hrValues.length > 0 ? Math.max(...hrValues) : undefined,
84
+ min_hr_bpm:
85
+ hrValues.length > 0 ? Math.min(...hrValues) : undefined,
86
+ },
87
+ }
88
+ : undefined,
89
+
90
+ position_data:
91
+ workout.routeData && workout.routeData.length > 0
92
+ ? {
93
+ position_samples: workout.routeData.flatMap((route) =>
94
+ route.locations.map((loc) => ({
95
+ timestamp: loc.timestamp,
96
+ coords_lat_lng_deg: [loc.latitude, loc.longitude],
97
+ })),
98
+ ),
99
+ start_pos_lat_lng_deg: (() => {
100
+ const first = workout.routeData[0]?.locations[0];
101
+ return first
102
+ ? [first.latitude, first.longitude]
103
+ : undefined;
104
+ })(),
105
+ end_pos_lat_lng_deg: (() => {
106
+ const lastRoute =
107
+ workout.routeData[workout.routeData.length - 1];
108
+ const last =
109
+ lastRoute?.locations[lastRoute.locations.length - 1];
110
+ return last ? [last.latitude, last.longitude] : undefined;
111
+ })(),
112
+ }
113
+ : undefined,
114
+ };
115
+ }
@@ -2,12 +2,8 @@
2
2
  // Transforms Apple HealthKit user characteristics into the Soma Athlete schema.
3
3
  // NOTE: HealthKit exposes very limited profile data compared to other providers.
4
4
 
5
- import type { HKCharacteristics } from "./types.js";
6
-
7
- /**
8
- * The output shape of {@link transformAthlete}.
9
- */
10
- export type AthleteData = ReturnType<typeof transformAthlete>;
5
+ import type { SomaAthlete } from "../../validators/athlete.js";
6
+ import type { HKCharacteristics } from "../types.js";
11
7
 
12
8
  /**
13
9
  * Transform HealthKit user characteristics into a Soma Athlete document shape.
@@ -25,7 +21,7 @@ export type AthleteData = ReturnType<typeof transformAthlete>;
25
21
  * await soma.ingestAthlete(ctx, { connectionId, userId, ...data });
26
22
  * ```
27
23
  */
28
- export function transformAthlete(characteristics: HKCharacteristics) {
24
+ export function transformAthlete(characteristics: HKCharacteristics): SomaAthlete {
29
25
  const sexMap: Record<string, string> = {
30
26
  female: "female",
31
27
  male: "male",
@@ -35,7 +31,7 @@ export function transformAthlete(characteristics: HKCharacteristics) {
35
31
  return {
36
32
  sex:
37
33
  characteristics.biologicalSex &&
38
- characteristics.biologicalSex !== "notSet"
34
+ characteristics.biologicalSex !== "notSet"
39
35
  ? sexMap[characteristics.biologicalSex]
40
36
  : undefined,
41
37
  date_of_birth: characteristics.dateOfBirth,
@@ -1,7 +1,8 @@
1
1
  // ─── Body Transformer ────────────────────────────────────────────────────────
2
2
  // Transforms Apple HealthKit body-related quantity samples into the Soma Body schema.
3
3
 
4
- import type { HKQuantitySample } from "./types.js";
4
+ import type { HKQuantitySample } from "../types.js";
5
+ import type { SomaBody } from "../../validators/body.js";
5
6
  import {
6
7
  filterByType,
7
8
  avgValue,
@@ -11,11 +12,6 @@ import {
11
12
  buildDeviceData,
12
13
  } from "./utils.js";
13
14
 
14
- /**
15
- * The output shape of {@link transformBody}.
16
- */
17
- export type BodyData = ReturnType<typeof transformBody>;
18
-
19
15
  /**
20
16
  * Transform a mixed array of HealthKit body-related quantity samples into a
21
17
  * Soma Body document shape.
@@ -37,7 +33,7 @@ export type BodyData = ReturnType<typeof transformBody>;
37
33
  export function transformBody(
38
34
  samples: HKQuantitySample[],
39
35
  timeRange?: { start_time: string; end_time: string },
40
- ) {
36
+ ): SomaBody {
41
37
  const range = timeRange ?? sampleTimeRange(samples);
42
38
 
43
39
  // ── Heart rate ─────────────────────────────────────────────────────────
@@ -1,7 +1,8 @@
1
1
  // ─── Daily Transformer ───────────────────────────────────────────────────────
2
2
  // Transforms Apple HealthKit daily activity data into the Soma Daily schema.
3
3
 
4
- import type { HKQuantitySample, HKActivitySummary } from "./types.js";
4
+ import type { HKQuantitySample, HKActivitySummary } from "../types.js";
5
+ import type { SomaDaily } from "../../validators/daily.js";
5
6
  import {
6
7
  filterByType,
7
8
  sumValues,
@@ -11,13 +12,6 @@ import {
11
12
  buildDeviceData,
12
13
  } from "./utils.js";
13
14
 
14
- /**
15
- * The output shape of {@link transformDaily} and {@link transformDailyFromSummary}.
16
- */
17
- export type DailyData =
18
- | ReturnType<typeof transformDaily>
19
- | ReturnType<typeof transformDailyFromSummary>;
20
-
21
15
  /**
22
16
  * Transform an array of HealthKit quantity samples for a single day into a
23
17
  * Soma Daily document shape.
@@ -39,7 +33,7 @@ export type DailyData =
39
33
  export function transformDaily(
40
34
  samples: HKQuantitySample[],
41
35
  timeRange?: { start_time: string; end_time: string },
42
- ) {
36
+ ): SomaDaily {
43
37
  const range = timeRange ?? sampleTimeRange(samples);
44
38
 
45
39
  // ── Activity samples ───────────────────────────────────────────────────
@@ -222,7 +216,7 @@ export function transformDaily(
222
216
  * @param summary - The HKActivitySummary from HealthKit
223
217
  * @returns Soma Daily fields (without connectionId/userId)
224
218
  */
225
- export function transformDailyFromSummary(summary: HKActivitySummary) {
219
+ export function transformDailyFromSummary(summary: HKActivitySummary): SomaDaily {
226
220
  const range = dayRange(summary.dateComponents);
227
221
 
228
222
  return {
@@ -4,7 +4,7 @@
4
4
  // HK values: https://developer.apple.com/documentation/healthkit/hkcategoryvaluemenstrualflow
5
5
  // Terra MenstruationFlow: 0=UNKNOWN, 1=NONE, 2=LIGHT, 3=MEDIUM, 4=HEAVY, 5=HAD
6
6
 
7
- import { HKMenstrualFlowCategory } from "../types.js";
7
+ import { HKMenstrualFlowCategory } from "../../types.js";
8
8
 
9
9
  const menstruationFlowMap: Record<number, number> = {
10
10
  [HKMenstrualFlowCategory.Unspecified]: 5, // Unspecified → HAD (flow occurred, amount unknown)
@@ -4,7 +4,7 @@
4
4
  // HK values: https://developer.apple.com/documentation/healthkit/hkcategoryvaluesleepanalysis
5
5
  // Terra SleepLevel: 0=Unknown, 1=Awake, 2=Sleeping, 3=OutOfBed, 4=Light, 5=Deep, 6=REM
6
6
 
7
- import { HKSleepCategory } from "../types.js";
7
+ import { HKSleepCategory } from "../../types.js";
8
8
 
9
9
  const sleepLevelMap: Record<number, number> = {
10
10
  [HKSleepCategory.InBed]: 2, // InBed → Sleeping (generic)
@@ -1,15 +1,11 @@
1
1
  // ─── Menstruation Transformer ─────────────────────────────────────────────────
2
2
  // Transforms Apple HealthKit menstrual flow samples into the Soma Menstruation schema.
3
3
 
4
- import type { HKCategorySample } from "./types.js";
5
- import { mapMenstruationFlow } from "./maps/menstruation-flow.js";
4
+ import type { HKCategorySample } from "../types.js";
5
+ import type { SomaMenstruation } from "../../validators/menstruation.js";
6
+ import { mapMenstruationFlow } from "./maps/menstruationFlow.js";
6
7
  import { sampleTimeRange } from "./utils.js";
7
8
 
8
- /**
9
- * The output shape of {@link transformMenstruation}.
10
- */
11
- export type MenstruationData = ReturnType<typeof transformMenstruation>;
12
-
13
9
  /**
14
10
  * Transform an array of HealthKit menstrual flow category samples into a
15
11
  * Soma Menstruation document shape.
@@ -27,7 +23,7 @@ export type MenstruationData = ReturnType<typeof transformMenstruation>;
27
23
  export function transformMenstruation(
28
24
  samples: HKCategorySample[],
29
25
  timeRange?: { start_time: string; end_time: string },
30
- ) {
26
+ ): SomaMenstruation {
31
27
  if (samples.length === 0) {
32
28
  throw new Error(
33
29
  "transformMenstruation requires at least one menstrual flow sample",
@@ -1,14 +1,10 @@
1
1
  // ─── Nutrition Transformer ────────────────────────────────────────────────────
2
2
  // Transforms Apple HealthKit dietary quantity samples into the Soma Nutrition schema.
3
3
 
4
- import type { HKQuantitySample } from "./types.js";
4
+ import type { HKQuantitySample } from "../types.js";
5
+ import type { SomaNutrition } from "../../validators/nutrition.js";
5
6
  import { filterByType, sumValues, sampleTimeRange } from "./utils.js";
6
7
 
7
- /**
8
- * The output shape of {@link transformNutrition}.
9
- */
10
- export type NutritionData = ReturnType<typeof transformNutrition>;
11
-
12
8
  /**
13
9
  * Transform an array of HealthKit dietary quantity samples into a
14
10
  * Soma Nutrition document shape.
@@ -30,7 +26,7 @@ export type NutritionData = ReturnType<typeof transformNutrition>;
30
26
  export function transformNutrition(
31
27
  samples: HKQuantitySample[],
32
28
  timeRange?: { start_time: string; end_time: string },
33
- ) {
29
+ ): SomaNutrition {
34
30
  const range = timeRange ?? sampleTimeRange(samples);
35
31
 
36
32
  const sum = (type: string) => {
@@ -1,16 +1,12 @@
1
1
  // ─── Sleep Transformer ───────────────────────────────────────────────────────
2
2
  // Transforms Apple HealthKit sleep analysis samples into the Soma Sleep schema.
3
3
 
4
- import type { HKCategorySample } from "./types.js";
5
- import { HKSleepCategory } from "./types.js";
6
- import { mapSleepLevel, isAsleepCategory } from "./maps/sleep-level.js";
4
+ import type { HKCategorySample } from "../types.js";
5
+ import type { SomaSleep } from "../../validators/sleep.js";
6
+ import { HKSleepCategory } from "../types.js";
7
+ import { mapSleepLevel, isAsleepCategory } from "./maps/sleepLevel.js";
7
8
  import { diffSeconds, buildDeviceData } from "./utils.js";
8
9
 
9
- /**
10
- * The output shape of {@link transformSleep}.
11
- */
12
- export type SleepData = ReturnType<typeof transformSleep>;
13
-
14
10
  /**
15
11
  * Transform an array of HealthKit sleep analysis category samples into a
16
12
  * Soma Sleep document shape.
@@ -29,7 +25,7 @@ export type SleepData = ReturnType<typeof transformSleep>;
29
25
  * await soma.ingestSleep(ctx, { connectionId, userId, ...data });
30
26
  * ```
31
27
  */
32
- export function transformSleep(samples: HKCategorySample[]) {
28
+ export function transformSleep(samples: HKCategorySample[]): SomaSleep {
33
29
  if (samples.length === 0) {
34
30
  throw new Error("transformSleep requires at least one sleep sample");
35
31
  }
@@ -1,7 +1,7 @@
1
1
  // ─── Shared Utilities ────────────────────────────────────────────────────────
2
2
  // Pure helper functions used across HealthKit transformer modules.
3
3
 
4
- import type { HKDevice, HKQuantitySample, HKSource } from "./types.js";
4
+ import type { HKDevice, HKQuantitySample, HKSource } from "../types.js";
5
5
 
6
6
  /**
7
7
  * Compute the difference in seconds between two ISO-8601 timestamps.
@@ -1,75 +0,0 @@
1
- import type { HKWorkout } from "./types.js";
2
- /**
3
- * The output shape of {@link transformWorkout}, matching the Soma Activity
4
- * validator minus `connectionId` and `userId` (added at ingestion time).
5
- */
6
- export type ActivityData = ReturnType<typeof transformWorkout>;
7
- /**
8
- * Transform an HKWorkout into a Soma Activity document shape.
9
- *
10
- * The returned object is ready to be spread into an `ingestActivity` call
11
- * alongside `connectionId` and `userId`.
12
- *
13
- * @param workout - The HKWorkout from HealthKit
14
- * @returns Soma Activity fields (without connectionId/userId)
15
- *
16
- * @example
17
- * ```ts
18
- * const data = transformWorkout(hkWorkout);
19
- * await soma.ingestActivity(ctx, { connectionId, userId, ...data });
20
- * ```
21
- */
22
- export declare function transformWorkout(workout: HKWorkout): {
23
- metadata: {
24
- summary_id: string;
25
- start_time: string;
26
- end_time: string;
27
- type: number;
28
- upload_type: 1;
29
- name: string | undefined;
30
- };
31
- active_durations_data: {
32
- activity_seconds: number;
33
- };
34
- calories_data: {
35
- total_burned_calories: number;
36
- } | undefined;
37
- device_data: {
38
- name?: string;
39
- manufacturer?: string;
40
- hardware_version?: string;
41
- software_version?: string;
42
- } | undefined;
43
- distance_data: {
44
- summary: {
45
- distance_meters: number | undefined;
46
- steps: number | undefined;
47
- floors_climbed: number | undefined;
48
- swimming: {
49
- num_strokes: number;
50
- } | undefined;
51
- };
52
- } | undefined;
53
- heart_rate_data: {
54
- detailed: {
55
- hr_samples: {
56
- timestamp: string;
57
- bpm: number;
58
- }[];
59
- };
60
- summary: {
61
- avg_hr_bpm: number | undefined;
62
- max_hr_bpm: number | undefined;
63
- min_hr_bpm: number | undefined;
64
- };
65
- } | undefined;
66
- position_data: {
67
- position_samples: {
68
- timestamp: string;
69
- coords_lat_lng_deg: number[];
70
- }[];
71
- start_pos_lat_lng_deg: number[] | undefined;
72
- end_pos_lat_lng_deg: number[] | undefined;
73
- } | undefined;
74
- };
75
- //# sourceMappingURL=activity.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"activity.d.ts","sourceRoot":"","sources":["../../src/healthkit/activity.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAI5C;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE/D;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,SAAS;;;;;;;cAW1B,MAAM,GAAG,SAAS;;;;;;;;;;;;;;;;;mBAuBT,MAAM,GAAG,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyDnD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"activity.js","sourceRoot":"","sources":["../../src/healthkit/activity.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"athlete.d.ts","sourceRoot":"","sources":["../../src/healthkit/athlete.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE9D;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,eAAe,EAAE,iBAAiB;;;EAelE"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"athlete.js","sourceRoot":"","sources":["../../src/healthkit/athlete.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,gFAAgF;AAChF,iFAAiF;AASjF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,gBAAgB,CAAC,eAAkC;IACjE,MAAM,MAAM,GAA2B;QACrC,MAAM,EAAE,QAAQ;QAChB,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,OAAO;KACf,CAAC;IAEF,OAAO;QACL,GAAG,EACD,eAAe,CAAC,aAAa;YAC7B,eAAe,CAAC,aAAa,KAAK,QAAQ;YACxC,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,aAAa,CAAC;YACvC,CAAC,CAAC,SAAS;QACf,aAAa,EAAE,eAAe,CAAC,WAAW;KAC3C,CAAC;AACJ,CAAC"}