@nativesquare/soma 0.1.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/LICENSE +201 -0
- package/README.md +142 -0
- package/dist/client/_generated/_ignore.d.ts +1 -0
- package/dist/client/_generated/_ignore.d.ts.map +1 -0
- package/dist/client/_generated/_ignore.js +3 -0
- package/dist/client/_generated/_ignore.js.map +1 -0
- package/dist/client/index.d.ts +279 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +264 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/types.d.ts +5 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +2 -0
- package/dist/client/types.js.map +1 -0
- package/dist/component/_generated/api.d.ts +62 -0
- package/dist/component/_generated/api.d.ts.map +1 -0
- package/dist/component/_generated/api.js +31 -0
- package/dist/component/_generated/api.js.map +1 -0
- package/dist/component/_generated/component.d.ts +1063 -0
- package/dist/component/_generated/component.d.ts.map +1 -0
- package/dist/component/_generated/component.js +11 -0
- package/dist/component/_generated/component.js.map +1 -0
- package/dist/component/_generated/dataModel.d.ts +46 -0
- package/dist/component/_generated/dataModel.d.ts.map +1 -0
- package/dist/component/_generated/dataModel.js +11 -0
- package/dist/component/_generated/dataModel.js.map +1 -0
- package/dist/component/_generated/server.d.ts +121 -0
- package/dist/component/_generated/server.d.ts.map +1 -0
- package/dist/component/_generated/server.js +78 -0
- package/dist/component/_generated/server.js.map +1 -0
- package/dist/component/convex.config.d.ts +3 -0
- package/dist/component/convex.config.d.ts.map +1 -0
- package/dist/component/convex.config.js +3 -0
- package/dist/component/convex.config.js.map +1 -0
- package/dist/component/private.d.ts +33 -0
- package/dist/component/private.d.ts.map +1 -0
- package/dist/component/private.js +45 -0
- package/dist/component/private.js.map +1 -0
- package/dist/component/public.d.ts +1107 -0
- package/dist/component/public.d.ts.map +1 -0
- package/dist/component/public.js +310 -0
- package/dist/component/public.js.map +1 -0
- package/dist/component/schema.d.ts +4419 -0
- package/dist/component/schema.d.ts.map +1 -0
- package/dist/component/schema.js +106 -0
- package/dist/component/schema.js.map +1 -0
- package/dist/component/validators/activity.d.ts +747 -0
- package/dist/component/validators/activity.d.ts.map +1 -0
- package/dist/component/validators/activity.js +146 -0
- package/dist/component/validators/activity.js.map +1 -0
- package/dist/component/validators/athlete.d.ts +18 -0
- package/dist/component/validators/athlete.d.ts.map +1 -0
- package/dist/component/validators/athlete.js +25 -0
- package/dist/component/validators/athlete.js.map +1 -0
- package/dist/component/validators/body.d.ts +634 -0
- package/dist/component/validators/body.d.ts.map +1 -0
- package/dist/component/validators/body.js +70 -0
- package/dist/component/validators/body.js.map +1 -0
- package/dist/component/validators/connection.d.ts +7 -0
- package/dist/component/validators/connection.d.ts.map +1 -0
- package/dist/component/validators/connection.js +16 -0
- package/dist/component/validators/connection.js.map +1 -0
- package/dist/component/validators/daily.d.ts +650 -0
- package/dist/component/validators/daily.d.ts.map +1 -0
- package/dist/component/validators/daily.js +119 -0
- package/dist/component/validators/daily.js.map +1 -0
- package/dist/component/validators/enums.d.ts +24 -0
- package/dist/component/validators/enums.d.ts.map +1 -0
- package/dist/component/validators/enums.js +69 -0
- package/dist/component/validators/enums.js.map +1 -0
- package/dist/component/validators/index.d.ts +13 -0
- package/dist/component/validators/index.d.ts.map +1 -0
- package/dist/component/validators/index.js +16 -0
- package/dist/component/validators/index.js.map +1 -0
- package/dist/component/validators/menstruation.d.ts +51 -0
- package/dist/component/validators/menstruation.d.ts.map +1 -0
- package/dist/component/validators/menstruation.js +32 -0
- package/dist/component/validators/menstruation.js.map +1 -0
- package/dist/component/validators/nutrition.d.ts +498 -0
- package/dist/component/validators/nutrition.d.ts.map +1 -0
- package/dist/component/validators/nutrition.js +31 -0
- package/dist/component/validators/nutrition.js.map +1 -0
- package/dist/component/validators/plannedWorkout.d.ts +277 -0
- package/dist/component/validators/plannedWorkout.d.ts.map +1 -0
- package/dist/component/validators/plannedWorkout.js +105 -0
- package/dist/component/validators/plannedWorkout.js.map +1 -0
- package/dist/component/validators/samples.d.ts +609 -0
- package/dist/component/validators/samples.d.ts.map +1 -0
- package/dist/component/validators/samples.js +336 -0
- package/dist/component/validators/samples.js.map +1 -0
- package/dist/component/validators/shared.d.ts +402 -0
- package/dist/component/validators/shared.d.ts.map +1 -0
- package/dist/component/validators/shared.js +146 -0
- package/dist/component/validators/shared.js.map +1 -0
- package/dist/component/validators/sleep.d.ts +438 -0
- package/dist/component/validators/sleep.d.ts.map +1 -0
- package/dist/component/validators/sleep.js +95 -0
- package/dist/component/validators/sleep.js.map +1 -0
- package/dist/healthkit/activity.d.ts +75 -0
- package/dist/healthkit/activity.d.ts.map +1 -0
- package/dist/healthkit/activity.js +93 -0
- package/dist/healthkit/activity.js.map +1 -0
- package/dist/healthkit/athlete.d.ts +26 -0
- package/dist/healthkit/athlete.d.ts.map +1 -0
- package/dist/healthkit/athlete.js +34 -0
- package/dist/healthkit/athlete.js.map +1 -0
- package/dist/healthkit/body.d.ts +102 -0
- package/dist/healthkit/body.d.ts.map +1 -0
- package/dist/healthkit/body.js +167 -0
- package/dist/healthkit/body.js.map +1 -0
- package/dist/healthkit/daily.d.ts +119 -0
- package/dist/healthkit/daily.d.ts.map +1 -0
- package/dist/healthkit/daily.js +160 -0
- package/dist/healthkit/daily.js.map +1 -0
- package/dist/healthkit/index.d.ts +21 -0
- package/dist/healthkit/index.d.ts.map +1 -0
- package/dist/healthkit/index.js +21 -0
- package/dist/healthkit/index.js.map +1 -0
- package/dist/healthkit/maps/activity-type.d.ts +6 -0
- package/dist/healthkit/maps/activity-type.d.ts.map +1 -0
- package/dist/healthkit/maps/activity-type.js +184 -0
- package/dist/healthkit/maps/activity-type.js.map +1 -0
- package/dist/healthkit/maps/menstruation-flow.d.ts +6 -0
- package/dist/healthkit/maps/menstruation-flow.d.ts.map +1 -0
- package/dist/healthkit/maps/menstruation-flow.js +21 -0
- package/dist/healthkit/maps/menstruation-flow.js.map +1 -0
- package/dist/healthkit/maps/sleep-level.d.ts +11 -0
- package/dist/healthkit/maps/sleep-level.d.ts.map +1 -0
- package/dist/healthkit/maps/sleep-level.js +32 -0
- package/dist/healthkit/maps/sleep-level.js.map +1 -0
- package/dist/healthkit/menstruation.d.ts +35 -0
- package/dist/healthkit/menstruation.d.ts.map +1 -0
- package/dist/healthkit/menstruation.js +37 -0
- package/dist/healthkit/menstruation.js.map +1 -0
- package/dist/healthkit/nutrition.d.ts +77 -0
- package/dist/healthkit/nutrition.d.ts.map +1 -0
- package/dist/healthkit/nutrition.js +135 -0
- package/dist/healthkit/nutrition.js.map +1 -0
- package/dist/healthkit/sleep.d.ts +60 -0
- package/dist/healthkit/sleep.d.ts.map +1 -0
- package/dist/healthkit/sleep.js +108 -0
- package/dist/healthkit/sleep.js.map +1 -0
- package/dist/healthkit/types.d.ts +94 -0
- package/dist/healthkit/types.d.ts.map +1 -0
- package/dist/healthkit/types.js +26 -0
- package/dist/healthkit/types.js.map +1 -0
- package/dist/healthkit/utils.d.ts +63 -0
- package/dist/healthkit/utils.d.ts.map +1 -0
- package/dist/healthkit/utils.js +93 -0
- package/dist/healthkit/utils.js.map +1 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +6 -0
- package/dist/react/index.js.map +1 -0
- package/package.json +112 -0
- package/src/client/_generated/_ignore.ts +1 -0
- package/src/client/index.ts +371 -0
- package/src/client/types.ts +18 -0
- package/src/component/_generated/api.ts +78 -0
- package/src/component/_generated/component.ts +1090 -0
- package/src/component/_generated/dataModel.ts +60 -0
- package/src/component/_generated/server.ts +156 -0
- package/src/component/convex.config.ts +3 -0
- package/src/component/private.ts +50 -0
- package/src/component/public.ts +358 -0
- package/src/component/schema.ts +115 -0
- package/src/component/validators/activity.ts +216 -0
- package/src/component/validators/athlete.ts +25 -0
- package/src/component/validators/body.ts +114 -0
- package/src/component/validators/connection.ts +16 -0
- package/src/component/validators/daily.ts +173 -0
- package/src/component/validators/enums.ts +119 -0
- package/src/component/validators/index.ts +16 -0
- package/src/component/validators/menstruation.ts +36 -0
- package/src/component/validators/nutrition.ts +37 -0
- package/src/component/validators/plannedWorkout.ts +110 -0
- package/src/component/validators/samples.ts +380 -0
- package/src/component/validators/shared.ts +165 -0
- package/src/component/validators/sleep.ts +133 -0
- package/src/healthkit/activity.ts +120 -0
- package/src/healthkit/athlete.ts +43 -0
- package/src/healthkit/body.ts +266 -0
- package/src/healthkit/daily.ts +245 -0
- package/src/healthkit/index.ts +59 -0
- package/src/healthkit/maps/activity-type.ts +185 -0
- package/src/healthkit/maps/menstruation-flow.ts +23 -0
- package/src/healthkit/maps/sleep-level.ts +37 -0
- package/src/healthkit/menstruation.ts +52 -0
- package/src/healthkit/nutrition.ts +162 -0
- package/src/healthkit/sleep.ts +136 -0
- package/src/healthkit/types.ts +219 -0
- package/src/healthkit/utils.ts +122 -0
- package/src/react/index.ts +7 -0
- package/src/test.ts +18 -0
|
@@ -0,0 +1,59 @@
|
|
|
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";
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// ─── HKWorkoutActivityType → Terra ActivityType ──────────────────────────────
|
|
2
|
+
// Maps Apple HealthKit workout activity type raw values to Terra's ActivityType
|
|
3
|
+
// enum used by the Soma schema.
|
|
4
|
+
//
|
|
5
|
+
// HK values: https://developer.apple.com/documentation/healthkit/hkworkoutactivitytype
|
|
6
|
+
// Terra values: https://docs.tryterra.co/reference/health-and-fitness-api/data-models#activitytype
|
|
7
|
+
|
|
8
|
+
const activityTypeMap: Record<number, number> = {
|
|
9
|
+
// HK AmericanFootball (1) → Terra American Football (27)
|
|
10
|
+
1: 27,
|
|
11
|
+
// HK Archery (2) → Terra Archery (119)
|
|
12
|
+
2: 119,
|
|
13
|
+
// HK AustralianFootball (3) → Terra Australian Football (28)
|
|
14
|
+
3: 28,
|
|
15
|
+
// HK Badminton (4) → Terra Badminton (10)
|
|
16
|
+
4: 10,
|
|
17
|
+
// HK Baseball (5) → Terra Baseball (11)
|
|
18
|
+
5: 11,
|
|
19
|
+
// HK Basketball (6) → Terra Basketball (12)
|
|
20
|
+
6: 12,
|
|
21
|
+
// HK Bowling (7) → Terra Other (108)
|
|
22
|
+
7: 108,
|
|
23
|
+
// HK Boxing (8) → Terra Boxing (20)
|
|
24
|
+
8: 20,
|
|
25
|
+
// HK Climbing (9) → Terra Rock Climbing (52)
|
|
26
|
+
9: 52,
|
|
27
|
+
// HK Cricket (10) → Terra Cricket (23)
|
|
28
|
+
10: 23,
|
|
29
|
+
// HK CrossTraining (11) → Terra Crossfit (113)
|
|
30
|
+
11: 113,
|
|
31
|
+
// HK Curling (12) → Terra Curling (106)
|
|
32
|
+
12: 106,
|
|
33
|
+
// HK Cycling (13) → Terra Biking (1)
|
|
34
|
+
13: 1,
|
|
35
|
+
// HK Dance (14) → Terra Dancing (24)
|
|
36
|
+
14: 24,
|
|
37
|
+
// HK DanceInspiredTraining (15) → Terra Dancing (24)
|
|
38
|
+
15: 24,
|
|
39
|
+
// HK Elliptical (16) → Terra Elliptical (25)
|
|
40
|
+
16: 25,
|
|
41
|
+
// HK EquestrianSports (17) → Terra Horseback Riding (37)
|
|
42
|
+
17: 37,
|
|
43
|
+
// HK Fencing (18) → Terra Fencing (26)
|
|
44
|
+
18: 26,
|
|
45
|
+
// HK Fishing (19) → Terra Fishing (131)
|
|
46
|
+
19: 131,
|
|
47
|
+
// HK FunctionalStrengthTraining (20) → Terra Strength Training (80)
|
|
48
|
+
20: 80,
|
|
49
|
+
// HK Golf (21) → Terra Golf (32)
|
|
50
|
+
21: 32,
|
|
51
|
+
// HK Gymnastics (22) → Terra Gymnastics (33)
|
|
52
|
+
22: 33,
|
|
53
|
+
// HK Handball (23) → Terra Handball (34)
|
|
54
|
+
23: 34,
|
|
55
|
+
// HK Hiking (24) → Terra Hiking (35)
|
|
56
|
+
24: 35,
|
|
57
|
+
// HK Hockey (25) → Terra Hockey (36)
|
|
58
|
+
25: 36,
|
|
59
|
+
// HK Hunting (26) → Terra Other (108)
|
|
60
|
+
26: 108,
|
|
61
|
+
// HK Lacrosse (27) → Terra Lacrosse (124)
|
|
62
|
+
27: 124,
|
|
63
|
+
// HK MartialArts (28) → Terra Martial Arts (44)
|
|
64
|
+
28: 44,
|
|
65
|
+
// HK MindAndBody (29) → Terra Meditation (45)
|
|
66
|
+
29: 45,
|
|
67
|
+
// HK MixedMetabolicCardioTraining (30) → Terra Cardio Training (123)
|
|
68
|
+
30: 123,
|
|
69
|
+
// HK PaddleSports (31) → Terra Paddling (129)
|
|
70
|
+
31: 129,
|
|
71
|
+
// HK Play (32) → Terra Other (108)
|
|
72
|
+
32: 108,
|
|
73
|
+
// HK PreparationAndRecovery (33) → Terra Stretching (125)
|
|
74
|
+
33: 125,
|
|
75
|
+
// HK Racquetball (34) → Terra Racquetball (51)
|
|
76
|
+
34: 51,
|
|
77
|
+
// HK Rowing (35) → Terra Rowing (53)
|
|
78
|
+
35: 53,
|
|
79
|
+
// HK Rugby (36) → Terra Rugby (55)
|
|
80
|
+
36: 55,
|
|
81
|
+
// HK Running (37) → Terra Running (8)
|
|
82
|
+
37: 8,
|
|
83
|
+
// HK Sailing (38) → Terra Sailing (59)
|
|
84
|
+
38: 59,
|
|
85
|
+
// HK SkatingSports (39) → Terra Skating (62)
|
|
86
|
+
39: 62,
|
|
87
|
+
// HK SnowSports (40) → Terra Skiing (65)
|
|
88
|
+
40: 65,
|
|
89
|
+
// HK Soccer (41) → Terra English Football (29)
|
|
90
|
+
41: 29,
|
|
91
|
+
// HK Softball (42) → Terra Softball (120)
|
|
92
|
+
42: 120,
|
|
93
|
+
// HK Squash (43) → Terra Squash (76)
|
|
94
|
+
43: 76,
|
|
95
|
+
// HK StairClimbing (44) → Terra Stair Climbing (77)
|
|
96
|
+
44: 77,
|
|
97
|
+
// HK SurfingSports (45) → Terra Surfing (81)
|
|
98
|
+
45: 81,
|
|
99
|
+
// HK Swimming (46) → Terra Swimming (82)
|
|
100
|
+
46: 82,
|
|
101
|
+
// HK TableTennis (47) → Terra Table Tennis (85)
|
|
102
|
+
47: 85,
|
|
103
|
+
// HK Tennis (48) → Terra Tennis (87)
|
|
104
|
+
48: 87,
|
|
105
|
+
// HK TrackAndField (49) → Terra Running (8)
|
|
106
|
+
49: 8,
|
|
107
|
+
// HK TraditionalStrengthTraining (50) → Terra Strength Training (80)
|
|
108
|
+
50: 80,
|
|
109
|
+
// HK Volleyball (51) → Terra Volleyball (89)
|
|
110
|
+
51: 89,
|
|
111
|
+
// HK Walking (52) → Terra Walking (7)
|
|
112
|
+
52: 7,
|
|
113
|
+
// HK WaterFitness (53) → Terra Swimming (82)
|
|
114
|
+
53: 82,
|
|
115
|
+
// HK WaterPolo (54) → Terra Waterpolo (96)
|
|
116
|
+
54: 96,
|
|
117
|
+
// HK WaterSports (55) → Terra Other (108)
|
|
118
|
+
55: 108,
|
|
119
|
+
// HK Wrestling (56) → Terra Martial Arts (44)
|
|
120
|
+
56: 44,
|
|
121
|
+
// HK Yoga (57) → Terra Yoga (100)
|
|
122
|
+
57: 100,
|
|
123
|
+
// HK Barre (58) → Terra Dancing (24)
|
|
124
|
+
58: 24,
|
|
125
|
+
// HK CoreTraining (59) → Terra Calisthenics (21)
|
|
126
|
+
59: 21,
|
|
127
|
+
// HK CrossCountrySkiing (60) → Terra Cross Country Skiing (67)
|
|
128
|
+
60: 67,
|
|
129
|
+
// HK DownhillSkiing (61) → Terra Downhill Skiing (68)
|
|
130
|
+
61: 68,
|
|
131
|
+
// HK Flexibility (62) → Terra Stretching (125)
|
|
132
|
+
62: 125,
|
|
133
|
+
// HK HighIntensityIntervalTraining (63) → Terra HIIT (114)
|
|
134
|
+
63: 114,
|
|
135
|
+
// HK JumpRope (64) → Terra Jumping Rope (39)
|
|
136
|
+
64: 39,
|
|
137
|
+
// HK Kickboxing (65) → Terra Kickboxing (42)
|
|
138
|
+
65: 42,
|
|
139
|
+
// HK Pilates (66) → Terra Pilates (49)
|
|
140
|
+
66: 49,
|
|
141
|
+
// HK Snowboarding (67) → Terra Snowboarding (73)
|
|
142
|
+
67: 73,
|
|
143
|
+
// HK Stairs (68) → Terra Stair Climbing (77)
|
|
144
|
+
68: 77,
|
|
145
|
+
// HK StepTraining (69) → Terra Stair Climbing Machine (78)
|
|
146
|
+
69: 78,
|
|
147
|
+
// HK WheelchairWalkPace (70) → Terra Wheelchair (98)
|
|
148
|
+
70: 98,
|
|
149
|
+
// HK WheelchairRunPace (71) → Terra Wheelchair (98)
|
|
150
|
+
71: 98,
|
|
151
|
+
// HK TaiChi (72) → Terra Meditation (45)
|
|
152
|
+
72: 45,
|
|
153
|
+
// HK MixedCardio (73) → Terra Cardio Training (123)
|
|
154
|
+
73: 123,
|
|
155
|
+
// HK HandCycling (74) → Terra Handbiking (14)
|
|
156
|
+
74: 14,
|
|
157
|
+
// HK DiscSports (75) → Terra Frisbee (30)
|
|
158
|
+
75: 30,
|
|
159
|
+
// HK FitnessGaming (76) → Terra Other (108)
|
|
160
|
+
76: 108,
|
|
161
|
+
// HK CardioDance (77) → Terra Dancing (24)
|
|
162
|
+
77: 24,
|
|
163
|
+
// HK SocialDance (78) → Terra Dancing (24)
|
|
164
|
+
78: 24,
|
|
165
|
+
// HK Pickleball (79) → Terra Other (108)
|
|
166
|
+
79: 108,
|
|
167
|
+
// HK Cooldown (80) → Terra Stretching (125)
|
|
168
|
+
80: 125,
|
|
169
|
+
// HK SwimBikeRun (82) → Terra Triathlon (126)
|
|
170
|
+
82: 126,
|
|
171
|
+
// HK Transition (83) → Terra Other (108)
|
|
172
|
+
83: 108,
|
|
173
|
+
// HK UnderwaterDiving (84) → Terra Diving (102)
|
|
174
|
+
84: 102,
|
|
175
|
+
// HK Other (3000) → Terra Other (108)
|
|
176
|
+
3000: 108,
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Map an HKWorkoutActivityType raw value to the Terra ActivityType enum.
|
|
181
|
+
* Returns Terra "Other" (108) for unknown types.
|
|
182
|
+
*/
|
|
183
|
+
export function mapActivityType(hkActivityType: number): number {
|
|
184
|
+
return activityTypeMap[hkActivityType] ?? 108;
|
|
185
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// ─── HK Menstrual Flow → Terra MenstruationFlow ─────────────────────────────
|
|
2
|
+
// Maps Apple HealthKit menstrual flow category values to Terra's MenstruationFlow enum.
|
|
3
|
+
//
|
|
4
|
+
// HK values: https://developer.apple.com/documentation/healthkit/hkcategoryvaluemenstrualflow
|
|
5
|
+
// Terra MenstruationFlow: 0=UNKNOWN, 1=NONE, 2=LIGHT, 3=MEDIUM, 4=HEAVY, 5=HAD
|
|
6
|
+
|
|
7
|
+
import { HKMenstrualFlowCategory } from "../types.js";
|
|
8
|
+
|
|
9
|
+
const menstruationFlowMap: Record<number, number> = {
|
|
10
|
+
[HKMenstrualFlowCategory.Unspecified]: 5, // Unspecified → HAD (flow occurred, amount unknown)
|
|
11
|
+
[HKMenstrualFlowCategory.Light]: 2, // Light → LIGHT
|
|
12
|
+
[HKMenstrualFlowCategory.Medium]: 3, // Medium → MEDIUM
|
|
13
|
+
[HKMenstrualFlowCategory.Heavy]: 4, // Heavy → HEAVY
|
|
14
|
+
[HKMenstrualFlowCategory.None]: 1, // None → NONE
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Map an HKCategoryValueMenstrualFlow value to the Terra MenstruationFlow enum.
|
|
19
|
+
* Returns 0 (UNKNOWN) for unrecognized values.
|
|
20
|
+
*/
|
|
21
|
+
export function mapMenstruationFlow(hkFlowValue: number): number {
|
|
22
|
+
return menstruationFlowMap[hkFlowValue] ?? 0;
|
|
23
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// ─── HK Sleep Category → Terra SleepLevel ────────────────────────────────────
|
|
2
|
+
// Maps Apple HealthKit sleep analysis category values to Terra's SleepLevel enum.
|
|
3
|
+
//
|
|
4
|
+
// HK values: https://developer.apple.com/documentation/healthkit/hkcategoryvaluesleepanalysis
|
|
5
|
+
// Terra SleepLevel: 0=Unknown, 1=Awake, 2=Sleeping, 3=OutOfBed, 4=Light, 5=Deep, 6=REM
|
|
6
|
+
|
|
7
|
+
import { HKSleepCategory } from "../types.js";
|
|
8
|
+
|
|
9
|
+
const sleepLevelMap: Record<number, number> = {
|
|
10
|
+
[HKSleepCategory.InBed]: 2, // InBed → Sleeping (generic)
|
|
11
|
+
[HKSleepCategory.AsleepUnspecified]: 2, // AsleepUnspecified → Sleeping
|
|
12
|
+
[HKSleepCategory.Awake]: 1, // Awake → Awake
|
|
13
|
+
[HKSleepCategory.AsleepCore]: 4, // AsleepCore → Light
|
|
14
|
+
[HKSleepCategory.AsleepDeep]: 5, // AsleepDeep → Deep
|
|
15
|
+
[HKSleepCategory.AsleepREM]: 6, // AsleepREM → REM
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Map an HKCategoryValueSleepAnalysis value to the Terra SleepLevel enum.
|
|
20
|
+
* Returns 0 (Unknown) for unrecognized values.
|
|
21
|
+
*/
|
|
22
|
+
export function mapSleepLevel(hkSleepValue: number): number {
|
|
23
|
+
return sleepLevelMap[hkSleepValue] ?? 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Returns true if the HK sleep category represents an "asleep" state
|
|
28
|
+
* (not awake, not just in bed).
|
|
29
|
+
*/
|
|
30
|
+
export function isAsleepCategory(hkSleepValue: number): boolean {
|
|
31
|
+
return (
|
|
32
|
+
hkSleepValue === HKSleepCategory.AsleepUnspecified ||
|
|
33
|
+
hkSleepValue === HKSleepCategory.AsleepCore ||
|
|
34
|
+
hkSleepValue === HKSleepCategory.AsleepDeep ||
|
|
35
|
+
hkSleepValue === HKSleepCategory.AsleepREM
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// ─── Menstruation Transformer ─────────────────────────────────────────────────
|
|
2
|
+
// Transforms Apple HealthKit menstrual flow samples into the Soma Menstruation schema.
|
|
3
|
+
|
|
4
|
+
import type { HKCategorySample } from "./types.js";
|
|
5
|
+
import { mapMenstruationFlow } from "./maps/menstruation-flow.js";
|
|
6
|
+
import { sampleTimeRange } from "./utils.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The output shape of {@link transformMenstruation}.
|
|
10
|
+
*/
|
|
11
|
+
export type MenstruationData = ReturnType<typeof transformMenstruation>;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Transform an array of HealthKit menstrual flow category samples into a
|
|
15
|
+
* Soma Menstruation document shape.
|
|
16
|
+
*
|
|
17
|
+
* @param samples - Array of HKCategorySample with sampleType "HKCategoryTypeIdentifierMenstrualFlow"
|
|
18
|
+
* @param timeRange - Optional explicit time range; auto-detected from samples if omitted
|
|
19
|
+
* @returns Soma Menstruation fields (without connectionId/userId)
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const data = transformMenstruation(hkMenstrualSamples);
|
|
24
|
+
* await soma.ingestMenstruation(ctx, { connectionId, userId, ...data });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function transformMenstruation(
|
|
28
|
+
samples: HKCategorySample[],
|
|
29
|
+
timeRange?: { start_time: string; end_time: string },
|
|
30
|
+
) {
|
|
31
|
+
if (samples.length === 0) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
"transformMenstruation requires at least one menstrual flow sample",
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const range = timeRange ?? sampleTimeRange(samples);
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
metadata: {
|
|
41
|
+
start_time: range.start_time,
|
|
42
|
+
end_time: range.end_time,
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
menstruation_data: {
|
|
46
|
+
menstruation_flow: samples.map((s) => ({
|
|
47
|
+
timestamp: s.startDate,
|
|
48
|
+
flow: mapMenstruationFlow(s.value),
|
|
49
|
+
})),
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// ─── Nutrition Transformer ────────────────────────────────────────────────────
|
|
2
|
+
// Transforms Apple HealthKit dietary quantity samples into the Soma Nutrition schema.
|
|
3
|
+
|
|
4
|
+
import type { HKQuantitySample } from "./types.js";
|
|
5
|
+
import { filterByType, sumValues, sampleTimeRange } from "./utils.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The output shape of {@link transformNutrition}.
|
|
9
|
+
*/
|
|
10
|
+
export type NutritionData = ReturnType<typeof transformNutrition>;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Transform an array of HealthKit dietary quantity samples into a
|
|
14
|
+
* Soma Nutrition document shape.
|
|
15
|
+
*
|
|
16
|
+
* Accepts samples with dietary type identifiers (protein, carbs, fat, water,
|
|
17
|
+
* vitamins, minerals, etc.) and aggregates them into Soma's macros/micros
|
|
18
|
+
* summary format.
|
|
19
|
+
*
|
|
20
|
+
* @param samples - Array of HKQuantitySample with dietary type identifiers
|
|
21
|
+
* @param timeRange - Optional explicit time range; auto-detected from samples if omitted
|
|
22
|
+
* @returns Soma Nutrition fields (without connectionId/userId)
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const data = transformNutrition(hkDietarySamples);
|
|
27
|
+
* await soma.ingestNutrition(ctx, { connectionId, userId, ...data });
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export function transformNutrition(
|
|
31
|
+
samples: HKQuantitySample[],
|
|
32
|
+
timeRange?: { start_time: string; end_time: string },
|
|
33
|
+
) {
|
|
34
|
+
const range = timeRange ?? sampleTimeRange(samples);
|
|
35
|
+
|
|
36
|
+
const sum = (type: string) => {
|
|
37
|
+
const filtered = filterByType(samples, type);
|
|
38
|
+
return filtered.length > 0 ? sumValues(filtered) : undefined;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// ── Macros ─────────────────────────────────────────────────────────────
|
|
42
|
+
const calories = sum("HKQuantityTypeIdentifierDietaryEnergyConsumed");
|
|
43
|
+
const protein = sum("HKQuantityTypeIdentifierDietaryProtein");
|
|
44
|
+
const carbs = sum("HKQuantityTypeIdentifierDietaryCarbohydrates");
|
|
45
|
+
const fat = sum("HKQuantityTypeIdentifierDietaryFatTotal");
|
|
46
|
+
const saturatedFat = sum("HKQuantityTypeIdentifierDietaryFatSaturated");
|
|
47
|
+
const fiber = sum("HKQuantityTypeIdentifierDietaryFiber");
|
|
48
|
+
const sugar = sum("HKQuantityTypeIdentifierDietarySugar");
|
|
49
|
+
const cholesterol = sum("HKQuantityTypeIdentifierDietaryCholesterol");
|
|
50
|
+
const sodium = sum("HKQuantityTypeIdentifierDietarySodium");
|
|
51
|
+
|
|
52
|
+
// ── Micros ─────────────────────────────────────────────────────────────
|
|
53
|
+
const calcium = sum("HKQuantityTypeIdentifierDietaryCalcium");
|
|
54
|
+
const iron = sum("HKQuantityTypeIdentifierDietaryIron");
|
|
55
|
+
const potassium = sum("HKQuantityTypeIdentifierDietaryPotassium");
|
|
56
|
+
const vitaminA = sum("HKQuantityTypeIdentifierDietaryVitaminA");
|
|
57
|
+
const vitaminB6 = sum("HKQuantityTypeIdentifierDietaryVitaminB6");
|
|
58
|
+
const vitaminB12 = sum("HKQuantityTypeIdentifierDietaryVitaminB12");
|
|
59
|
+
const vitaminC = sum("HKQuantityTypeIdentifierDietaryVitaminC");
|
|
60
|
+
const vitaminD = sum("HKQuantityTypeIdentifierDietaryVitaminD");
|
|
61
|
+
const vitaminE = sum("HKQuantityTypeIdentifierDietaryVitaminE");
|
|
62
|
+
const vitaminK = sum("HKQuantityTypeIdentifierDietaryVitaminK");
|
|
63
|
+
const zinc = sum("HKQuantityTypeIdentifierDietaryZinc");
|
|
64
|
+
const magnesium = sum("HKQuantityTypeIdentifierDietaryMagnesium");
|
|
65
|
+
const manganese = sum("HKQuantityTypeIdentifierDietaryManganese");
|
|
66
|
+
const copper = sum("HKQuantityTypeIdentifierDietaryCopper");
|
|
67
|
+
const selenium = sum("HKQuantityTypeIdentifierDietarySelenium");
|
|
68
|
+
const chromium = sum("HKQuantityTypeIdentifierDietaryChromium");
|
|
69
|
+
const folate = sum("HKQuantityTypeIdentifierDietaryFolate");
|
|
70
|
+
const biotin = sum("HKQuantityTypeIdentifierDietaryBiotin");
|
|
71
|
+
const niacin = sum("HKQuantityTypeIdentifierDietaryNiacin");
|
|
72
|
+
const phosphorus = sum("HKQuantityTypeIdentifierDietaryPhosphorus");
|
|
73
|
+
const riboflavin = sum("HKQuantityTypeIdentifierDietaryRiboflavin");
|
|
74
|
+
const thiamin = sum("HKQuantityTypeIdentifierDietaryThiamin");
|
|
75
|
+
const caffeine = sum("HKQuantityTypeIdentifierDietaryCaffeine");
|
|
76
|
+
const iodine = sum("HKQuantityTypeIdentifierDietaryIodine");
|
|
77
|
+
const chloride = sum("HKQuantityTypeIdentifierDietaryChloride");
|
|
78
|
+
const pantothenicAcid = sum(
|
|
79
|
+
"HKQuantityTypeIdentifierDietaryPanthothenicAcid",
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// ── Fats breakdown ─────────────────────────────────────────────────────
|
|
83
|
+
const monoFat = sum("HKQuantityTypeIdentifierDietaryFatMonounsaturated");
|
|
84
|
+
const polyFat = sum("HKQuantityTypeIdentifierDietaryFatPolyunsaturated");
|
|
85
|
+
|
|
86
|
+
// ── Water ──────────────────────────────────────────────────────────────
|
|
87
|
+
const waterSamples = filterByType(
|
|
88
|
+
samples,
|
|
89
|
+
"HKQuantityTypeIdentifierDietaryWater",
|
|
90
|
+
);
|
|
91
|
+
const waterMl =
|
|
92
|
+
waterSamples.length > 0 ? sumValues(waterSamples) * 1000 : undefined; // L → mL
|
|
93
|
+
|
|
94
|
+
const hasMacros =
|
|
95
|
+
calories != null ||
|
|
96
|
+
protein != null ||
|
|
97
|
+
carbs != null ||
|
|
98
|
+
fat != null;
|
|
99
|
+
|
|
100
|
+
const hasMicros =
|
|
101
|
+
calcium != null ||
|
|
102
|
+
iron != null ||
|
|
103
|
+
vitaminC != null ||
|
|
104
|
+
zinc != null ||
|
|
105
|
+
magnesium != null;
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
metadata: {
|
|
109
|
+
start_time: range.start_time,
|
|
110
|
+
end_time: range.end_time,
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
summary: {
|
|
114
|
+
macros: hasMacros
|
|
115
|
+
? {
|
|
116
|
+
calories,
|
|
117
|
+
protein_g: protein,
|
|
118
|
+
carbohydrates_g: carbs,
|
|
119
|
+
fat_g: fat,
|
|
120
|
+
saturated_fat_g: saturatedFat,
|
|
121
|
+
fiber_g: fiber,
|
|
122
|
+
sugar_g: sugar,
|
|
123
|
+
cholesterol_mg: cholesterol,
|
|
124
|
+
sodium_mg: sodium,
|
|
125
|
+
}
|
|
126
|
+
: undefined,
|
|
127
|
+
micros: hasMicros
|
|
128
|
+
? {
|
|
129
|
+
calcium_mg: calcium,
|
|
130
|
+
iron_mg: iron,
|
|
131
|
+
potassium_mg: potassium,
|
|
132
|
+
vitamin_A_mg: vitaminA,
|
|
133
|
+
vitamin_B6_mg: vitaminB6,
|
|
134
|
+
vitamin_B12_mg: vitaminB12,
|
|
135
|
+
vitamin_C_mg: vitaminC,
|
|
136
|
+
vitamin_D_mg: vitaminD,
|
|
137
|
+
vitamin_E_mg: vitaminE,
|
|
138
|
+
vitamin_K_mg: vitaminK,
|
|
139
|
+
zinc_mg: zinc,
|
|
140
|
+
magnesium_mg: magnesium,
|
|
141
|
+
manganese_mg: manganese,
|
|
142
|
+
copper_mg: copper,
|
|
143
|
+
selenium_mg: selenium,
|
|
144
|
+
chromium_mg: chromium,
|
|
145
|
+
folate_mg: folate,
|
|
146
|
+
biotin_mg: biotin,
|
|
147
|
+
niacin_mg: niacin,
|
|
148
|
+
phosphorus_mg: phosphorus,
|
|
149
|
+
riboflavin_mg: riboflavin,
|
|
150
|
+
thiamin_mg: thiamin,
|
|
151
|
+
caffeine_mg: caffeine,
|
|
152
|
+
iodine_mg: iodine,
|
|
153
|
+
chloride_mg: chloride,
|
|
154
|
+
pantothenic_acid_mg: pantothenicAcid,
|
|
155
|
+
monounsaturated_fat_g: monoFat,
|
|
156
|
+
polyunsaturated_fat_g: polyFat,
|
|
157
|
+
}
|
|
158
|
+
: undefined,
|
|
159
|
+
water_ml: waterMl,
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// ─── Sleep Transformer ───────────────────────────────────────────────────────
|
|
2
|
+
// Transforms Apple HealthKit sleep analysis samples into the Soma Sleep schema.
|
|
3
|
+
|
|
4
|
+
import type { HKCategorySample } from "./types.js";
|
|
5
|
+
import { HKSleepCategory } from "./types.js";
|
|
6
|
+
import { mapSleepLevel, isAsleepCategory } from "./maps/sleep-level.js";
|
|
7
|
+
import { diffSeconds, buildDeviceData } from "./utils.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The output shape of {@link transformSleep}.
|
|
11
|
+
*/
|
|
12
|
+
export type SleepData = ReturnType<typeof transformSleep>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Transform an array of HealthKit sleep analysis category samples into a
|
|
16
|
+
* Soma Sleep document shape.
|
|
17
|
+
*
|
|
18
|
+
* HealthKit stores sleep as individual `HKCategorySample` records — one per
|
|
19
|
+
* sleep stage segment. This function aggregates them into a single session
|
|
20
|
+
* with computed durations, a hypnogram, and efficiency score.
|
|
21
|
+
*
|
|
22
|
+
* @param samples - Array of HKCategorySample with sampleType "HKCategoryTypeIdentifierSleepAnalysis".
|
|
23
|
+
* Should represent a single sleep session (all samples from one night).
|
|
24
|
+
* @returns Soma Sleep fields (without connectionId/userId)
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* const data = transformSleep(hkSleepSamples);
|
|
29
|
+
* await soma.ingestSleep(ctx, { connectionId, userId, ...data });
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export function transformSleep(samples: HKCategorySample[]) {
|
|
33
|
+
if (samples.length === 0) {
|
|
34
|
+
throw new Error("transformSleep requires at least one sleep sample");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Sort by start time
|
|
38
|
+
const sorted = [...samples].sort(
|
|
39
|
+
(a, b) => new Date(a.startDate).getTime() - new Date(b.startDate).getTime(),
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const sessionStart = sorted[0].startDate;
|
|
43
|
+
const sessionEnd = sorted[sorted.length - 1].endDate;
|
|
44
|
+
|
|
45
|
+
// Compute stage durations
|
|
46
|
+
let deepSeconds = 0;
|
|
47
|
+
let lightSeconds = 0;
|
|
48
|
+
let remSeconds = 0;
|
|
49
|
+
let awakeSeconds = 0;
|
|
50
|
+
let asleepUnspecifiedSeconds = 0;
|
|
51
|
+
let inBedSeconds = 0;
|
|
52
|
+
let wakeupEvents = 0;
|
|
53
|
+
let remEvents = 0;
|
|
54
|
+
|
|
55
|
+
for (const sample of sorted) {
|
|
56
|
+
const duration = diffSeconds(sample.startDate, sample.endDate);
|
|
57
|
+
|
|
58
|
+
switch (sample.value) {
|
|
59
|
+
case HKSleepCategory.AsleepDeep:
|
|
60
|
+
deepSeconds += duration;
|
|
61
|
+
break;
|
|
62
|
+
case HKSleepCategory.AsleepCore:
|
|
63
|
+
lightSeconds += duration;
|
|
64
|
+
break;
|
|
65
|
+
case HKSleepCategory.AsleepREM:
|
|
66
|
+
remSeconds += duration;
|
|
67
|
+
remEvents++;
|
|
68
|
+
break;
|
|
69
|
+
case HKSleepCategory.Awake:
|
|
70
|
+
awakeSeconds += duration;
|
|
71
|
+
wakeupEvents++;
|
|
72
|
+
break;
|
|
73
|
+
case HKSleepCategory.AsleepUnspecified:
|
|
74
|
+
asleepUnspecifiedSeconds += duration;
|
|
75
|
+
break;
|
|
76
|
+
case HKSleepCategory.InBed:
|
|
77
|
+
inBedSeconds += duration;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const totalAsleepSeconds =
|
|
83
|
+
deepSeconds + lightSeconds + remSeconds + asleepUnspecifiedSeconds;
|
|
84
|
+
const totalInBedSeconds = diffSeconds(sessionStart, sessionEnd);
|
|
85
|
+
const sleepEfficiency =
|
|
86
|
+
totalInBedSeconds > 0 ? totalAsleepSeconds / totalInBedSeconds : undefined;
|
|
87
|
+
|
|
88
|
+
// Build hypnogram
|
|
89
|
+
const hypnogramSamples = sorted
|
|
90
|
+
.filter((s) => s.value !== HKSleepCategory.InBed)
|
|
91
|
+
.map((s) => ({
|
|
92
|
+
timestamp: s.startDate,
|
|
93
|
+
level: mapSleepLevel(s.value),
|
|
94
|
+
}));
|
|
95
|
+
|
|
96
|
+
// Determine if this is a nap (< 3 hours)
|
|
97
|
+
const isNap = totalInBedSeconds < 3 * 60 * 60;
|
|
98
|
+
|
|
99
|
+
const firstSample = sorted[0];
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
metadata: {
|
|
103
|
+
start_time: sessionStart,
|
|
104
|
+
end_time: sessionEnd,
|
|
105
|
+
summary_id: firstSample.uuid,
|
|
106
|
+
upload_type: 2 as const, // Automatic
|
|
107
|
+
is_nap: isNap,
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
device_data: buildDeviceData(firstSample.source, firstSample.device),
|
|
111
|
+
|
|
112
|
+
sleep_durations_data: {
|
|
113
|
+
asleep: {
|
|
114
|
+
duration_asleep_state_seconds: totalAsleepSeconds,
|
|
115
|
+
duration_deep_sleep_state_seconds:
|
|
116
|
+
deepSeconds > 0 ? deepSeconds : undefined,
|
|
117
|
+
duration_light_sleep_state_seconds:
|
|
118
|
+
lightSeconds > 0 ? lightSeconds : undefined,
|
|
119
|
+
duration_REM_sleep_state_seconds:
|
|
120
|
+
remSeconds > 0 ? remSeconds : undefined,
|
|
121
|
+
num_REM_events: remEvents > 0 ? remEvents : undefined,
|
|
122
|
+
},
|
|
123
|
+
awake: {
|
|
124
|
+
duration_awake_state_seconds:
|
|
125
|
+
awakeSeconds > 0 ? awakeSeconds : undefined,
|
|
126
|
+
num_wakeup_events: wakeupEvents > 0 ? wakeupEvents : undefined,
|
|
127
|
+
},
|
|
128
|
+
hypnogram_samples:
|
|
129
|
+
hypnogramSamples.length > 0 ? hypnogramSamples : undefined,
|
|
130
|
+
other: {
|
|
131
|
+
duration_in_bed_seconds: totalInBedSeconds,
|
|
132
|
+
},
|
|
133
|
+
sleep_efficiency: sleepEfficiency,
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|