trainbud 0.5.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 (274) hide show
  1. package/.env.example +11 -0
  2. package/CHANGELOG.md +675 -0
  3. package/LICENSE +21 -0
  4. package/QUICKSTART.md +182 -0
  5. package/README.md +322 -0
  6. package/dist/appDb.d.ts +83 -0
  7. package/dist/appDb.d.ts.map +1 -0
  8. package/dist/appDb.js +239 -0
  9. package/dist/appDb.js.map +1 -0
  10. package/dist/check.d.ts +12 -0
  11. package/dist/check.d.ts.map +1 -0
  12. package/dist/check.js +230 -0
  13. package/dist/check.js.map +1 -0
  14. package/dist/cli.d.ts +3 -0
  15. package/dist/cli.d.ts.map +1 -0
  16. package/dist/cli.js +457 -0
  17. package/dist/cli.js.map +1 -0
  18. package/dist/config.d.ts +61 -0
  19. package/dist/config.d.ts.map +1 -0
  20. package/dist/config.js +241 -0
  21. package/dist/config.js.map +1 -0
  22. package/dist/dashboard.d.ts +8 -0
  23. package/dist/dashboard.d.ts.map +1 -0
  24. package/dist/dashboard.js +1004 -0
  25. package/dist/dashboard.js.map +1 -0
  26. package/dist/dashboardCharts.d.ts +99 -0
  27. package/dist/dashboardCharts.d.ts.map +1 -0
  28. package/dist/dashboardCharts.js +317 -0
  29. package/dist/dashboardCharts.js.map +1 -0
  30. package/dist/dashboardData.d.ts +89 -0
  31. package/dist/dashboardData.d.ts.map +1 -0
  32. package/dist/dashboardData.js +228 -0
  33. package/dist/dashboardData.js.map +1 -0
  34. package/dist/detect/baseline.d.ts +17 -0
  35. package/dist/detect/baseline.d.ts.map +1 -0
  36. package/dist/detect/baseline.js +62 -0
  37. package/dist/detect/baseline.js.map +1 -0
  38. package/dist/detect/countdown.d.ts +30 -0
  39. package/dist/detect/countdown.d.ts.map +1 -0
  40. package/dist/detect/countdown.js +79 -0
  41. package/dist/detect/countdown.js.map +1 -0
  42. package/dist/detect/detectors.d.ts +20 -0
  43. package/dist/detect/detectors.d.ts.map +1 -0
  44. package/dist/detect/detectors.js +291 -0
  45. package/dist/detect/detectors.js.map +1 -0
  46. package/dist/detect/findings.d.ts +29 -0
  47. package/dist/detect/findings.d.ts.map +1 -0
  48. package/dist/detect/findings.js +2 -0
  49. package/dist/detect/findings.js.map +1 -0
  50. package/dist/detect/forecast.d.ts +33 -0
  51. package/dist/detect/forecast.d.ts.map +1 -0
  52. package/dist/detect/forecast.js +122 -0
  53. package/dist/detect/forecast.js.map +1 -0
  54. package/dist/detect/index.d.ts +50 -0
  55. package/dist/detect/index.d.ts.map +1 -0
  56. package/dist/detect/index.js +114 -0
  57. package/dist/detect/index.js.map +1 -0
  58. package/dist/detect/sleepQuality.d.ts +21 -0
  59. package/dist/detect/sleepQuality.d.ts.map +1 -0
  60. package/dist/detect/sleepQuality.js +164 -0
  61. package/dist/detect/sleepQuality.js.map +1 -0
  62. package/dist/detect/trimp.d.ts +22 -0
  63. package/dist/detect/trimp.d.ts.map +1 -0
  64. package/dist/detect/trimp.js +77 -0
  65. package/dist/detect/trimp.js.map +1 -0
  66. package/dist/detect/week.d.ts +31 -0
  67. package/dist/detect/week.d.ts.map +1 -0
  68. package/dist/detect/week.js +184 -0
  69. package/dist/detect/week.js.map +1 -0
  70. package/dist/garmin/auth.d.ts +10 -0
  71. package/dist/garmin/auth.d.ts.map +1 -0
  72. package/dist/garmin/auth.js +81 -0
  73. package/dist/garmin/auth.js.map +1 -0
  74. package/dist/garmin/cache.d.ts +42 -0
  75. package/dist/garmin/cache.d.ts.map +1 -0
  76. package/dist/garmin/cache.js +141 -0
  77. package/dist/garmin/cache.js.map +1 -0
  78. package/dist/garmin/client.d.ts +22 -0
  79. package/dist/garmin/client.d.ts.map +1 -0
  80. package/dist/garmin/client.js +237 -0
  81. package/dist/garmin/client.js.map +1 -0
  82. package/dist/garmin/daily.d.ts +24 -0
  83. package/dist/garmin/daily.d.ts.map +1 -0
  84. package/dist/garmin/daily.js +96 -0
  85. package/dist/garmin/daily.js.map +1 -0
  86. package/dist/garmin/garminApiTypes.d.ts +70 -0
  87. package/dist/garmin/garminApiTypes.d.ts.map +1 -0
  88. package/dist/garmin/garminApiTypes.js +3 -0
  89. package/dist/garmin/garminApiTypes.js.map +1 -0
  90. package/dist/garmin/garminConnect.d.ts +17 -0
  91. package/dist/garmin/garminConnect.d.ts.map +1 -0
  92. package/dist/garmin/garminConnect.js +5 -0
  93. package/dist/garmin/garminConnect.js.map +1 -0
  94. package/dist/garmin/partial.d.ts +32 -0
  95. package/dist/garmin/partial.d.ts.map +1 -0
  96. package/dist/garmin/partial.js +38 -0
  97. package/dist/garmin/partial.js.map +1 -0
  98. package/dist/garmin/rawApi.d.ts +59 -0
  99. package/dist/garmin/rawApi.d.ts.map +1 -0
  100. package/dist/garmin/rawApi.js +113 -0
  101. package/dist/garmin/rawApi.js.map +1 -0
  102. package/dist/garmin/types.d.ts +85 -0
  103. package/dist/garmin/types.d.ts.map +1 -0
  104. package/dist/garmin/types.js +11 -0
  105. package/dist/garmin/types.js.map +1 -0
  106. package/dist/history/capture.d.ts +12 -0
  107. package/dist/history/capture.d.ts.map +1 -0
  108. package/dist/history/capture.js +63 -0
  109. package/dist/history/capture.js.map +1 -0
  110. package/dist/history/context.d.ts +51 -0
  111. package/dist/history/context.d.ts.map +1 -0
  112. package/dist/history/context.js +122 -0
  113. package/dist/history/context.js.map +1 -0
  114. package/dist/history/fallback.d.ts +72 -0
  115. package/dist/history/fallback.d.ts.map +1 -0
  116. package/dist/history/fallback.js +185 -0
  117. package/dist/history/fallback.js.map +1 -0
  118. package/dist/history/ingest.d.ts +44 -0
  119. package/dist/history/ingest.d.ts.map +1 -0
  120. package/dist/history/ingest.js +347 -0
  121. package/dist/history/ingest.js.map +1 -0
  122. package/dist/history/scheduler.d.ts +20 -0
  123. package/dist/history/scheduler.d.ts.map +1 -0
  124. package/dist/history/scheduler.js +54 -0
  125. package/dist/history/scheduler.js.map +1 -0
  126. package/dist/history/schema.d.ts +41 -0
  127. package/dist/history/schema.d.ts.map +1 -0
  128. package/dist/history/schema.js +91 -0
  129. package/dist/history/schema.js.map +1 -0
  130. package/dist/history/store.d.ts +112 -0
  131. package/dist/history/store.d.ts.map +1 -0
  132. package/dist/history/store.js +282 -0
  133. package/dist/history/store.js.map +1 -0
  134. package/dist/httpServer.d.ts +55 -0
  135. package/dist/httpServer.d.ts.map +1 -0
  136. package/dist/httpServer.js +1027 -0
  137. package/dist/httpServer.js.map +1 -0
  138. package/dist/index.d.ts +3 -0
  139. package/dist/index.d.ts.map +1 -0
  140. package/dist/index.js +11 -0
  141. package/dist/index.js.map +1 -0
  142. package/dist/mcpConfig.d.ts +56 -0
  143. package/dist/mcpConfig.d.ts.map +1 -0
  144. package/dist/mcpConfig.js +106 -0
  145. package/dist/mcpConfig.js.map +1 -0
  146. package/dist/pairApi.d.ts +14 -0
  147. package/dist/pairApi.d.ts.map +1 -0
  148. package/dist/pairApi.js +38 -0
  149. package/dist/pairApi.js.map +1 -0
  150. package/dist/paths.d.ts +45 -0
  151. package/dist/paths.d.ts.map +1 -0
  152. package/dist/paths.js +105 -0
  153. package/dist/paths.js.map +1 -0
  154. package/dist/profile.d.ts +123 -0
  155. package/dist/profile.d.ts.map +1 -0
  156. package/dist/profile.js +327 -0
  157. package/dist/profile.js.map +1 -0
  158. package/dist/promptApi.d.ts +43 -0
  159. package/dist/promptApi.d.ts.map +1 -0
  160. package/dist/promptApi.js +328 -0
  161. package/dist/promptApi.js.map +1 -0
  162. package/dist/promptSuggestions.d.ts +16 -0
  163. package/dist/promptSuggestions.d.ts.map +1 -0
  164. package/dist/promptSuggestions.js +141 -0
  165. package/dist/promptSuggestions.js.map +1 -0
  166. package/dist/selfTest.d.ts +53 -0
  167. package/dist/selfTest.d.ts.map +1 -0
  168. package/dist/selfTest.js +225 -0
  169. package/dist/selfTest.js.map +1 -0
  170. package/dist/server.d.ts +10 -0
  171. package/dist/server.d.ts.map +1 -0
  172. package/dist/server.js +67 -0
  173. package/dist/server.js.map +1 -0
  174. package/dist/setup.d.ts +2 -0
  175. package/dist/setup.d.ts.map +1 -0
  176. package/dist/setup.js +210 -0
  177. package/dist/setup.js.map +1 -0
  178. package/dist/toolErrors.d.ts +2 -0
  179. package/dist/toolErrors.d.ts.map +1 -0
  180. package/dist/toolErrors.js +18 -0
  181. package/dist/toolErrors.js.map +1 -0
  182. package/dist/tools/activities.d.ts +18 -0
  183. package/dist/tools/activities.d.ts.map +1 -0
  184. package/dist/tools/activities.js +182 -0
  185. package/dist/tools/activities.js.map +1 -0
  186. package/dist/tools/bodyComposition.d.ts +10 -0
  187. package/dist/tools/bodyComposition.d.ts.map +1 -0
  188. package/dist/tools/bodyComposition.js +139 -0
  189. package/dist/tools/bodyComposition.js.map +1 -0
  190. package/dist/tools/context.d.ts +8 -0
  191. package/dist/tools/context.d.ts.map +1 -0
  192. package/dist/tools/context.js +148 -0
  193. package/dist/tools/context.js.map +1 -0
  194. package/dist/tools/findings.d.ts +7 -0
  195. package/dist/tools/findings.d.ts.map +1 -0
  196. package/dist/tools/findings.js +51 -0
  197. package/dist/tools/findings.js.map +1 -0
  198. package/dist/tools/heartRate.d.ts +10 -0
  199. package/dist/tools/heartRate.d.ts.map +1 -0
  200. package/dist/tools/heartRate.js +105 -0
  201. package/dist/tools/heartRate.js.map +1 -0
  202. package/dist/tools/index.d.ts +66 -0
  203. package/dist/tools/index.d.ts.map +1 -0
  204. package/dist/tools/index.js +101 -0
  205. package/dist/tools/index.js.map +1 -0
  206. package/dist/tools/payloads.d.ts +148 -0
  207. package/dist/tools/payloads.d.ts.map +1 -0
  208. package/dist/tools/payloads.js +2 -0
  209. package/dist/tools/payloads.js.map +1 -0
  210. package/dist/tools/recovery.d.ts +31 -0
  211. package/dist/tools/recovery.d.ts.map +1 -0
  212. package/dist/tools/recovery.js +350 -0
  213. package/dist/tools/recovery.js.map +1 -0
  214. package/dist/tools/sleep.d.ts +10 -0
  215. package/dist/tools/sleep.d.ts.map +1 -0
  216. package/dist/tools/sleep.js +124 -0
  217. package/dist/tools/sleep.js.map +1 -0
  218. package/dist/tools/stress.d.ts +11 -0
  219. package/dist/tools/stress.d.ts.map +1 -0
  220. package/dist/tools/stress.js +108 -0
  221. package/dist/tools/stress.js.map +1 -0
  222. package/dist/tools/trainingInsights.d.ts +15 -0
  223. package/dist/tools/trainingInsights.d.ts.map +1 -0
  224. package/dist/tools/trainingInsights.js +79 -0
  225. package/dist/tools/trainingInsights.js.map +1 -0
  226. package/dist/tools/types.d.ts +12 -0
  227. package/dist/tools/types.d.ts.map +1 -0
  228. package/dist/tools/types.js +2 -0
  229. package/dist/tools/types.js.map +1 -0
  230. package/dist/tools/vo2Max.d.ts +11 -0
  231. package/dist/tools/vo2Max.d.ts.map +1 -0
  232. package/dist/tools/vo2Max.js +110 -0
  233. package/dist/tools/vo2Max.js.map +1 -0
  234. package/dist/tools/week.d.ts +12 -0
  235. package/dist/tools/week.d.ts.map +1 -0
  236. package/dist/tools/week.js +74 -0
  237. package/dist/tools/week.js.map +1 -0
  238. package/dist/usage.d.ts +104 -0
  239. package/dist/usage.d.ts.map +1 -0
  240. package/dist/usage.js +247 -0
  241. package/dist/usage.js.map +1 -0
  242. package/dist/utils/batch.d.ts +2 -0
  243. package/dist/utils/batch.d.ts.map +1 -0
  244. package/dist/utils/batch.js +18 -0
  245. package/dist/utils/batch.js.map +1 -0
  246. package/dist/utils/helpers.d.ts +32 -0
  247. package/dist/utils/helpers.d.ts.map +1 -0
  248. package/dist/utils/helpers.js +188 -0
  249. package/dist/utils/helpers.js.map +1 -0
  250. package/dist/utils/jsonFile.d.ts +19 -0
  251. package/dist/utils/jsonFile.d.ts.map +1 -0
  252. package/dist/utils/jsonFile.js +24 -0
  253. package/dist/utils/jsonFile.js.map +1 -0
  254. package/dist/utils/logger.d.ts +4 -0
  255. package/dist/utils/logger.d.ts.map +1 -0
  256. package/dist/utils/logger.js +97 -0
  257. package/dist/utils/logger.js.map +1 -0
  258. package/dist/utils/secretFile.d.ts +15 -0
  259. package/dist/utils/secretFile.d.ts.map +1 -0
  260. package/dist/utils/secretFile.js +47 -0
  261. package/dist/utils/secretFile.js.map +1 -0
  262. package/dist/utils/stdio.d.ts +10 -0
  263. package/dist/utils/stdio.d.ts.map +1 -0
  264. package/dist/utils/stdio.js +70 -0
  265. package/dist/utils/stdio.js.map +1 -0
  266. package/dist/version.d.ts +2 -0
  267. package/dist/version.d.ts.map +1 -0
  268. package/dist/version.js +7 -0
  269. package/dist/version.js.map +1 -0
  270. package/dist/watchApi.d.ts +264 -0
  271. package/dist/watchApi.d.ts.map +1 -0
  272. package/dist/watchApi.js +333 -0
  273. package/dist/watchApi.js.map +1 -0
  274. package/package.json +77 -0
@@ -0,0 +1,96 @@
1
+ import { fetchDailyStress, fetchMaxMetrics, mapDailyStress, mapMaxMetrics, } from "./rawApi.js";
2
+ import { average, formatIsoDate } from "../utils/helpers.js";
3
+ // SECTION: Mappers
4
+ export function mapSleepData(date, sleepData) {
5
+ const dailySleep = sleepData.dailySleepDTO;
6
+ if (!dailySleep) {
7
+ return null;
8
+ }
9
+ return {
10
+ date: formatIsoDate(date),
11
+ totalSleepSeconds: dailySleep.sleepTimeSeconds,
12
+ deepSleepSeconds: dailySleep.deepSleepSeconds,
13
+ lightSleepSeconds: dailySleep.lightSleepSeconds,
14
+ remSleepSeconds: dailySleep.remSleepSeconds,
15
+ awakeCount: dailySleep.awakeCount,
16
+ sleepScore: dailySleep.sleepScores?.overall?.value ?? null,
17
+ avgSleepStress: dailySleep.avgSleepStress ?? null,
18
+ avgOvernightHrv: sleepData.avgOvernightHrv ?? null,
19
+ hrvStatus: sleepData.hrvStatus ?? null,
20
+ };
21
+ }
22
+ export function mapHeartRateData(date, heartRate) {
23
+ if (!heartRate) {
24
+ return null;
25
+ }
26
+ // Connect returns null entries inside heartRateValues, and null rows in place
27
+ // of whole buckets. Both crashed this on a real account before the guard.
28
+ const samples = (heartRate.heartRateValues ?? [])
29
+ .flat()
30
+ .filter((entry) => entry != null)
31
+ .map((entry) => entry.heartrate);
32
+ const averageHeartRate = samples.length > 0 ? average(samples) : null;
33
+ if (heartRate.restingHeartRate == null && averageHeartRate == null) {
34
+ return null;
35
+ }
36
+ return {
37
+ date: formatIsoDate(date),
38
+ restingHeartRate: heartRate.restingHeartRate ?? null,
39
+ maxHeartRate: heartRate.maxHeartRate ?? null,
40
+ minHeartRate: heartRate.minHeartRate ?? null,
41
+ averageHeartRate,
42
+ };
43
+ }
44
+ /**
45
+ * Connect omits these for activities they do not apply to -- strength training
46
+ * reports no distance or pace, an indoor ride no elevation. They were once
47
+ * typed as plain numbers, which type-checked while crashing on
48
+ * `undefined.toFixed`.
49
+ */
50
+ export function mapActivity(activity) {
51
+ return {
52
+ activityId: activity.activityId,
53
+ name: activity.activityName,
54
+ type: activity.activityType.typeKey,
55
+ startTimeLocal: activity.startTimeLocal,
56
+ distanceMeters: activity.distance ?? null,
57
+ durationSeconds: activity.duration ?? null,
58
+ averageHeartRate: activity.averageHR ?? null,
59
+ maxHeartRate: activity.maxHR ?? null,
60
+ elevationGainMeters: activity.elevationGain ?? null,
61
+ calories: activity.calories ?? null,
62
+ averageSpeedMps: activity.averageSpeed ?? null,
63
+ };
64
+ }
65
+ export function mapWeightData(date, weightData) {
66
+ return weightData.dateWeightList.map((entry) => ({
67
+ date: entry.calendarDate || formatIsoDate(date),
68
+ weightKg: entry.weight ?? null,
69
+ bodyFatPercent: entry.bodyFat ?? null,
70
+ muscleMassKg: entry.muscleMass ?? null,
71
+ bmi: entry.bmi ?? null,
72
+ }));
73
+ }
74
+ // SECTION: Fetchers
75
+ export async function fetchSleepDay(client, date) {
76
+ const raw = await client.getSleepData(date);
77
+ return { raw, mapped: mapSleepData(date, raw) };
78
+ }
79
+ export async function fetchHeartRateDay(client, date) {
80
+ const raw = await client.getHeartRate(date);
81
+ return { raw, mapped: mapHeartRateData(date, raw) };
82
+ }
83
+ export async function fetchStressDay(client, date) {
84
+ const raw = await fetchDailyStress(client, date);
85
+ return { raw, mapped: mapDailyStress(date, raw) };
86
+ }
87
+ export async function fetchVo2MaxDay(client, date) {
88
+ const raw = await fetchMaxMetrics(client, date);
89
+ return { raw, mapped: mapMaxMetrics(date, raw) };
90
+ }
91
+ export async function fetchBodyCompositionDay(client, date) {
92
+ const raw = await client.getDailyWeightData(date);
93
+ const mapped = mapWeightData(date, raw);
94
+ return { raw, mapped: mapped.length > 0 ? mapped : null };
95
+ }
96
+ //# sourceMappingURL=daily.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daily.js","sourceRoot":"","sources":["../../src/garmin/daily.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ export interface GarminOAuth1 {
2
+ oauth_token: string;
3
+ oauth_token_secret: string;
4
+ }
5
+ export interface GarminOAuth2 {
6
+ scope: string;
7
+ jti: string;
8
+ access_token: string;
9
+ token_type: string;
10
+ refresh_token: string;
11
+ expires_in: number;
12
+ expires_at: number;
13
+ refresh_token_expires_in: number;
14
+ refresh_token_expires_at: number;
15
+ }
16
+ export interface StoredSessionTokens {
17
+ oauth1: GarminOAuth1;
18
+ oauth2: GarminOAuth2;
19
+ }
20
+ export interface IActivity {
21
+ activityId: number;
22
+ activityName: string;
23
+ startTimeLocal: string;
24
+ distance: number;
25
+ duration: number;
26
+ averageHR?: number;
27
+ maxHR?: number;
28
+ elevationGain: number;
29
+ calories: number;
30
+ averageSpeed: number;
31
+ activityType: {
32
+ typeKey: string;
33
+ };
34
+ }
35
+ export interface SleepData {
36
+ avgOvernightHrv?: number | null;
37
+ hrvStatus?: string | null;
38
+ dailySleepDTO?: {
39
+ sleepTimeSeconds: number;
40
+ deepSleepSeconds: number;
41
+ lightSleepSeconds: number;
42
+ remSleepSeconds: number;
43
+ awakeCount: number;
44
+ avgSleepStress?: number | null;
45
+ sleepScores?: {
46
+ overall?: {
47
+ value: number;
48
+ };
49
+ };
50
+ };
51
+ }
52
+ export interface HeartRateData {
53
+ restingHeartRate?: number;
54
+ maxHeartRate?: number;
55
+ minHeartRate?: number;
56
+ lastSevenDaysAvgRestingHeartRate?: number;
57
+ heartRateValues?: Array<Array<{
58
+ heartrate: number;
59
+ } | null> | null> | null;
60
+ }
61
+ export interface WeightDataResponse {
62
+ dateWeightList: Array<{
63
+ calendarDate: string;
64
+ weight: number;
65
+ bodyFat: number | null;
66
+ muscleMass: number | null;
67
+ bmi: number | null;
68
+ }>;
69
+ }
70
+ //# sourceMappingURL=garminApiTypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"garminApiTypes.d.ts","sourceRoot":"","sources":["../../src/garmin/garminApiTypes.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,wBAAwB,EAAE,MAAM,CAAC;IACjC,wBAAwB,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE;QACZ,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,MAAM,WAAW,SAAS;IACxB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,aAAa,CAAC,EAAE;QACd,gBAAgB,EAAE,MAAM,CAAC;QACzB,gBAAgB,EAAE,MAAM,CAAC;QACzB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,eAAe,EAAE,MAAM,CAAC;QACxB,UAAU,EAAE,MAAM,CAAC;QACnB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,WAAW,CAAC,EAAE;YACZ,OAAO,CAAC,EAAE;gBACR,KAAK,EAAE,MAAM,CAAC;aACf,CAAC;SACH,CAAC;KACH,CAAC;CACH;AAED,MAAM,WAAW,aAAa;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gCAAgC,CAAC,EAAE,MAAM,CAAC;IAC1C,eAAe,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;CAC5E;AAED,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,KAAK,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;QACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;KACpB,CAAC,CAAC;CACJ"}
@@ -0,0 +1,3 @@
1
+ // Minimal Garmin Connect API shapes used by TrainBud (avoids importing garmin-connect/dist internals).
2
+ export {};
3
+ //# sourceMappingURL=garminApiTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"garminApiTypes.js","sourceRoot":"","sources":["../../src/garmin/garminApiTypes.ts"],"names":[],"mappings":"AAAA,uGAAuG"}
@@ -0,0 +1,17 @@
1
+ import type { HeartRateData, IActivity, SleepData, StoredSessionTokens, WeightDataResponse } from "./garminApiTypes.js";
2
+ export interface GarminConnectInstance {
3
+ login: (username?: string, password?: string) => Promise<GarminConnectInstance>;
4
+ loadToken: (oauth1: StoredSessionTokens["oauth1"], oauth2: StoredSessionTokens["oauth2"]) => void;
5
+ exportToken: () => StoredSessionTokens;
6
+ getUserProfile: () => Promise<unknown>;
7
+ getActivities: (start?: number, limit?: number) => Promise<IActivity[]>;
8
+ getSleepData: (date?: Date) => Promise<SleepData>;
9
+ getHeartRate: (date?: Date) => Promise<HeartRateData>;
10
+ getDailyWeightData: (date?: Date) => Promise<WeightDataResponse>;
11
+ get: (url: string, data?: Record<string, unknown>) => Promise<unknown>;
12
+ }
13
+ export declare const GarminConnect: new (credentials?: {
14
+ username: string;
15
+ password: string;
16
+ }) => GarminConnectInstance;
17
+ //# sourceMappingURL=garminConnect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"garminConnect.d.ts","sourceRoot":"","sources":["../../src/garmin/garminConnect.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,aAAa,EACb,SAAS,EACT,SAAS,EACT,mBAAmB,EACnB,kBAAkB,EACnB,MAAM,qBAAqB,CAAC;AAQ7B,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAChF,SAAS,EAAE,CAAC,MAAM,EAAE,mBAAmB,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,mBAAmB,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IAClG,WAAW,EAAE,MAAM,mBAAmB,CAAC;IACvC,cAAc,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACvC,aAAa,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IACxE,YAAY,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IAClD,YAAY,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IACtD,kBAAkB,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACjE,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACxE;AAID,eAAO,MAAM,aAAa,qBAjBU;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,KAAK,qBAiB3B,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { createRequire } from "node:module";
2
+ const require = createRequire(import.meta.url);
3
+ const moduleRef = require("garmin-connect");
4
+ export const GarminConnect = moduleRef.GarminConnect;
5
+ //# sourceMappingURL=garminConnect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"garminConnect.js","sourceRoot":"","sources":["../../src/garmin/garminConnect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAS5C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAkB/C,MAAM,SAAS,GAAG,OAAO,CAAC,gBAAgB,CAAwB,CAAC;AAEnE,MAAM,CAAC,MAAM,aAAa,GAAG,SAAS,CAAC,aAAa,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Every per-day tool used to write `catch { return null }` around its fetch and
3
+ * then filter the nulls away, which collapsed two different facts into one
4
+ * empty array: "the user did not wear the watch" and "Garmin would not answer".
5
+ * `withCache` then persisted that array for the full success TTL, so a single
6
+ * rate-limited minute made the tool report "No sleep data found for the last 7
7
+ * nights" for the next two hours -- an assertion about the user's life, made
8
+ * from a failed request.
9
+ *
10
+ * A day that could not be fetched is now counted rather than discarded, so the
11
+ * renderer can say which of the two happened and the cache can refuse to keep a
12
+ * degraded answer for long.
13
+ */
14
+ export interface DayFetchResult<T> {
15
+ /** Days that answered with a real measurement. */
16
+ values: T[];
17
+ /** Days whose request failed. NOT days with nothing recorded. */
18
+ unreachableDays: number;
19
+ /** Days asked for, so a caller can say "3 of 30". */
20
+ requestedDays: number;
21
+ }
22
+ export declare function fetchEachDay<T, D>(dates: D[], fetchOne: (date: D) => Promise<T | null | undefined>, source: string): Promise<DayFetchResult<T>>;
23
+ /** True when part of the answer is missing because a request failed. */
24
+ export declare function isPartial(result: {
25
+ unreachableDays: number;
26
+ }): boolean;
27
+ /**
28
+ * The one sentence that keeps "nothing was recorded" and "nothing could be
29
+ * fetched" from rendering the same. Returns "" when the fetch was complete.
30
+ */
31
+ export declare function partialFetchNote(result: DayFetchResult<unknown>, noun: string): string;
32
+ //# sourceMappingURL=partial.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"partial.d.ts","sourceRoot":"","sources":["../../src/garmin/partial.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,kDAAkD;IAClD,MAAM,EAAE,CAAC,EAAE,CAAC;IACZ,iEAAiE;IACjE,eAAe,EAAE,MAAM,CAAC;IACxB,qDAAqD;IACrD,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,wBAAsB,YAAY,CAAC,CAAC,EAAE,CAAC,EACrC,KAAK,EAAE,CAAC,EAAE,EACV,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,EACpD,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAkB5B;AAED,wEAAwE;AACxE,wBAAgB,SAAS,CAAC,MAAM,EAAE;IAAE,eAAe,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAEtE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAUtF"}
@@ -0,0 +1,38 @@
1
+ import { mapInBatches } from "../utils/batch.js";
2
+ import { logger } from "../utils/logger.js";
3
+ export async function fetchEachDay(dates, fetchOne, source) {
4
+ let unreachableDays = 0;
5
+ const mapped = await mapInBatches(dates, async (date) => {
6
+ try {
7
+ return await fetchOne(date);
8
+ }
9
+ catch (error) {
10
+ unreachableDays += 1;
11
+ logger.debug({ err: error, date: String(date), source }, "Day fetch failed");
12
+ return null;
13
+ }
14
+ });
15
+ return {
16
+ values: mapped.filter((value) => value !== null && value !== undefined),
17
+ unreachableDays,
18
+ requestedDays: dates.length,
19
+ };
20
+ }
21
+ /** True when part of the answer is missing because a request failed. */
22
+ export function isPartial(result) {
23
+ return result.unreachableDays > 0;
24
+ }
25
+ /**
26
+ * The one sentence that keeps "nothing was recorded" and "nothing could be
27
+ * fetched" from rendering the same. Returns "" when the fetch was complete.
28
+ */
29
+ export function partialFetchNote(result, noun) {
30
+ if (result.unreachableDays === 0) {
31
+ return "";
32
+ }
33
+ if (result.values.length === 0) {
34
+ return `Could not reach Garmin for any of the last ${result.requestedDays} ${noun}. This is not the same as having no ${noun} recorded — try again shortly.`;
35
+ }
36
+ return `Note: ${result.unreachableDays} of ${result.requestedDays} ${noun} could not be fetched, so these figures cover only what was retrieved.`;
37
+ }
38
+ //# sourceMappingURL=partial.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"partial.js","sourceRoot":"","sources":["../../src/garmin/partial.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAwB5C,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAU,EACV,QAAoD,EACpD,MAAc;IAEd,IAAI,eAAe,GAAG,CAAC,CAAC;IAExB,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACtD,IAAI,CAAC;YACH,OAAO,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,eAAe,IAAI,CAAC,CAAC;YACrB,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,kBAAkB,CAAC,CAAC;YAC7E,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAc,EAAE,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAC;QACnF,eAAe;QACf,aAAa,EAAE,KAAK,CAAC,MAAM;KAC5B,CAAC;AACJ,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,SAAS,CAAC,MAAmC;IAC3D,OAAO,MAAM,CAAC,eAAe,GAAG,CAAC,CAAC;AACpC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAA+B,EAAE,IAAY;IAC5E,IAAI,MAAM,CAAC,eAAe,KAAK,CAAC,EAAE,CAAC;QACjC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,8CAA8C,MAAM,CAAC,aAAa,IAAI,IAAI,uCAAuC,IAAI,gCAAgC,CAAC;IAC/J,CAAC;IAED,OAAO,SAAS,MAAM,CAAC,eAAe,OAAO,MAAM,CAAC,aAAa,IAAI,IAAI,wEAAwE,CAAC;AACpJ,CAAC"}
@@ -0,0 +1,59 @@
1
+ import type { GarminConnectInstance } from "./garminConnect.js";
2
+ /**
3
+ * The calendar day this Date stands for, read in the zone it was built in.
4
+ *
5
+ * Every Date reaching here is LOCAL midnight -- `getDateRange` and
6
+ * `getDatesBetween` both go through `DateTime.local().startOf("day")`. Reading
7
+ * one of those `{ zone: "utc" }` does not convert it, it asks which UTC day
8
+ * that instant falls on, and for any zone east of UTC that is the day before.
9
+ * On this machine, Asia/Baku at UTC+4, local midnight on 2026-09-03 is
10
+ * 2026-09-02T20:00Z, so every stress and VO2 max request asked Connect about
11
+ * 2026-09-02 and the answer was then stamped 2026-09-02 as well -- plausible
12
+ * numbers filed under a day the user did not live, feeding the recovery score.
13
+ * Auckland is off by one the same way; Los Angeles happens to land right,
14
+ * which is the mirror image of the garmin-connect bug fixed in d64a78a.
15
+ *
16
+ * Asserting `toISOString()` is what let that one survive: it is a property of
17
+ * which midnight the Date sits on, not of the day Garmin is asked about. The
18
+ * test beside this one pins the day in the URL, in four zones.
19
+ */
20
+ declare function toGarminDate(date: Date): string;
21
+ /** Exported for the timezone test: the bug is invisible from outside this module. */
22
+ export declare const __toGarminDateForTest: typeof toGarminDate;
23
+ /**
24
+ * Connect takes the date as a path segment here. Sent as a query parameter --
25
+ * which is how this was written -- it answers 404 for every day, so stress was
26
+ * always empty and the recovery score, which is partly built from stress, was
27
+ * always degraded. The failure was invisible because the 404s were swallowed
28
+ * per day and the tool simply reported "no stress data".
29
+ */
30
+ export declare function fetchDailyStress(client: GarminConnectInstance, date: Date): Promise<unknown>;
31
+ /**
32
+ * `maxmet/daily/<date>` answers 404 -- it is `latest/<date>`. VO2 max is only
33
+ * recomputed after a qualifying activity, so "latest" is the right question
34
+ * anyway: a daily route would come back empty on most days even if it existed.
35
+ *
36
+ * The catch is that this endpoint **ignores the date it is given**. Asked about
37
+ * 2026-04-05 it returns the current measurement with its own
38
+ * `generic.calendarDate` of 2026-08-12. Stamping the response with the
39
+ * requested date -- which is what this did -- writes one real reading across
40
+ * every day in the range as if it had been measured on each of them, which is
41
+ * invented history, and a trend over it is flat by construction.
42
+ */
43
+ export declare function fetchMaxMetrics(client: GarminConnectInstance, date: Date): Promise<unknown>;
44
+ export interface DailyStressSummary {
45
+ date: string;
46
+ averageStress: number | null;
47
+ maxStress: number | null;
48
+ restStress: number | null;
49
+ stressDurationSeconds: number | null;
50
+ }
51
+ export interface Vo2MaxEntry {
52
+ date: string;
53
+ vo2Max: number | null;
54
+ vo2MaxCycling: number | null;
55
+ }
56
+ export declare function mapDailyStress(date: Date, payload: unknown): DailyStressSummary | null;
57
+ export declare function mapMaxMetrics(date: Date, payload: unknown): Vo2MaxEntry | null;
58
+ export {};
59
+ //# sourceMappingURL=rawApi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rawApi.d.ts","sourceRoot":"","sources":["../../src/garmin/rawApi.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAIhE;;;;;;;;;;;;;;;;;GAiBG;AACH,iBAAS,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAExC;AAED,qFAAqF;AACrF,eAAO,MAAM,qBAAqB,qBAAe,CAAC;AAElD;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,qBAAqB,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAElG;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,qBAAqB,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAEjG;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAYD,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,GAAG,kBAAkB,GAAG,IAAI,CA0BtF;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,GAAG,WAAW,GAAG,IAAI,CAuC9E"}
@@ -0,0 +1,113 @@
1
+ import { DateTime } from "luxon";
2
+ const GC_API = "https://connectapi.garmin.com";
3
+ /**
4
+ * The calendar day this Date stands for, read in the zone it was built in.
5
+ *
6
+ * Every Date reaching here is LOCAL midnight -- `getDateRange` and
7
+ * `getDatesBetween` both go through `DateTime.local().startOf("day")`. Reading
8
+ * one of those `{ zone: "utc" }` does not convert it, it asks which UTC day
9
+ * that instant falls on, and for any zone east of UTC that is the day before.
10
+ * On this machine, Asia/Baku at UTC+4, local midnight on 2026-09-03 is
11
+ * 2026-09-02T20:00Z, so every stress and VO2 max request asked Connect about
12
+ * 2026-09-02 and the answer was then stamped 2026-09-02 as well -- plausible
13
+ * numbers filed under a day the user did not live, feeding the recovery score.
14
+ * Auckland is off by one the same way; Los Angeles happens to land right,
15
+ * which is the mirror image of the garmin-connect bug fixed in d64a78a.
16
+ *
17
+ * Asserting `toISOString()` is what let that one survive: it is a property of
18
+ * which midnight the Date sits on, not of the day Garmin is asked about. The
19
+ * test beside this one pins the day in the URL, in four zones.
20
+ */
21
+ function toGarminDate(date) {
22
+ return DateTime.fromJSDate(date).toFormat("yyyy-MM-dd");
23
+ }
24
+ /** Exported for the timezone test: the bug is invisible from outside this module. */
25
+ export const __toGarminDateForTest = toGarminDate;
26
+ /**
27
+ * Connect takes the date as a path segment here. Sent as a query parameter --
28
+ * which is how this was written -- it answers 404 for every day, so stress was
29
+ * always empty and the recovery score, which is partly built from stress, was
30
+ * always degraded. The failure was invisible because the 404s were swallowed
31
+ * per day and the tool simply reported "no stress data".
32
+ */
33
+ export async function fetchDailyStress(client, date) {
34
+ return client.get(`${GC_API}/wellness-service/wellness/dailyStress/${toGarminDate(date)}`);
35
+ }
36
+ /**
37
+ * `maxmet/daily/<date>` answers 404 -- it is `latest/<date>`. VO2 max is only
38
+ * recomputed after a qualifying activity, so "latest" is the right question
39
+ * anyway: a daily route would come back empty on most days even if it existed.
40
+ *
41
+ * The catch is that this endpoint **ignores the date it is given**. Asked about
42
+ * 2026-04-05 it returns the current measurement with its own
43
+ * `generic.calendarDate` of 2026-08-12. Stamping the response with the
44
+ * requested date -- which is what this did -- writes one real reading across
45
+ * every day in the range as if it had been measured on each of them, which is
46
+ * invented history, and a trend over it is flat by construction.
47
+ */
48
+ export async function fetchMaxMetrics(client, date) {
49
+ return client.get(`${GC_API}/metrics-service/metrics/maxmet/latest/${toGarminDate(date)}`);
50
+ }
51
+ /**
52
+ * Connect reports a negative stress level for a day it has no measurement for:
53
+ * -1 when the watch was not worn, -2 while the day is still in progress. They
54
+ * are sentinels, not readings, and averaging them in pulls the weekly figure
55
+ * below anything the scale can produce.
56
+ */
57
+ function measured(value) {
58
+ return value === null || value < 0 ? null : value;
59
+ }
60
+ export function mapDailyStress(date, payload) {
61
+ if (!payload || typeof payload !== "object") {
62
+ return null;
63
+ }
64
+ const data = payload;
65
+ // The real response carries avgStressLevel and maxStressLevel and nothing
66
+ // else of use: there is no overallStressLevel, restStressLevel or
67
+ // stressDuration in it. overallStressLevel is kept as a fallback because
68
+ // other wellness endpoints do use that name.
69
+ const averageStress = typeof data.avgStressLevel === "number"
70
+ ? data.avgStressLevel
71
+ : typeof data.overallStressLevel === "number"
72
+ ? data.overallStressLevel
73
+ : null;
74
+ return {
75
+ date: toGarminDate(date),
76
+ averageStress: measured(averageStress),
77
+ maxStress: measured(typeof data.maxStressLevel === "number" ? data.maxStressLevel : null),
78
+ restStress: typeof data.restStressLevel === "number" ? data.restStressLevel : null,
79
+ stressDurationSeconds: typeof data.stressDuration === "number" ? data.stressDuration : null,
80
+ };
81
+ }
82
+ export function mapMaxMetrics(date, payload) {
83
+ if (!payload || typeof payload !== "object") {
84
+ return null;
85
+ }
86
+ const data = payload;
87
+ const generic = data.generic;
88
+ const cycling = data.cycling;
89
+ const vo2Max = typeof generic?.vo2MaxValue === "number"
90
+ ? generic.vo2MaxValue
91
+ : typeof data.vo2MaxValue === "number"
92
+ ? data.vo2MaxValue
93
+ : null;
94
+ const vo2MaxCycling = typeof cycling?.vo2MaxValue === "number"
95
+ ? cycling.vo2MaxValue
96
+ : typeof data.vo2MaxCyclingValue === "number"
97
+ ? data.vo2MaxCyclingValue
98
+ : null;
99
+ if (vo2Max === null && vo2MaxCycling === null) {
100
+ return null;
101
+ }
102
+ // The day the measurement was actually taken, which is rarely the day asked
103
+ // about. Falling back to the requested date only when Connect omits its own.
104
+ const measuredOn = typeof generic?.calendarDate === "string" && generic.calendarDate.length > 0
105
+ ? generic.calendarDate
106
+ : toGarminDate(date);
107
+ return {
108
+ date: measuredOn,
109
+ vo2Max,
110
+ vo2MaxCycling,
111
+ };
112
+ }
113
+ //# sourceMappingURL=rawApi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rawApi.js","sourceRoot":"","sources":["../../src/garmin/rawApi.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ import type { StoredSessionTokens } from "./garminApiTypes.js";
2
+ export type StoredSession = StoredSessionTokens;
3
+ export interface ActivitySummary {
4
+ activityId: number;
5
+ name: string;
6
+ type: string;
7
+ startTimeLocal: string;
8
+ distanceMeters: number | null;
9
+ durationSeconds: number | null;
10
+ averageHeartRate: number | null;
11
+ maxHeartRate: number | null;
12
+ elevationGainMeters: number | null;
13
+ calories: number | null;
14
+ averageSpeedMps: number | null;
15
+ }
16
+ export interface SleepNightSummary {
17
+ date: string;
18
+ totalSleepSeconds: number;
19
+ deepSleepSeconds: number | null;
20
+ lightSleepSeconds: number | null;
21
+ remSleepSeconds: number | null;
22
+ awakeCount: number | null;
23
+ sleepScore: number | null;
24
+ avgSleepStress: number | null;
25
+ avgOvernightHrv: number | null;
26
+ hrvStatus: string | null;
27
+ }
28
+ export interface HeartRateDaySummary {
29
+ date: string;
30
+ restingHeartRate: number | null;
31
+ maxHeartRate: number | null;
32
+ minHeartRate: number | null;
33
+ averageHeartRate: number | null;
34
+ }
35
+ export interface BodyCompositionEntry {
36
+ date: string;
37
+ weightKg: number | null;
38
+ bodyFatPercent: number | null;
39
+ muscleMassKg: number | null;
40
+ bmi: number | null;
41
+ }
42
+ export interface RecoveryWeights {
43
+ hrv: number;
44
+ sleep: number;
45
+ stress: number;
46
+ restingHr: number;
47
+ }
48
+ export interface RecoveryStatusResult {
49
+ score: number;
50
+ status: "recovered" | "good" | "fatigued";
51
+ recommendation: string;
52
+ /**
53
+ * Null means the signal was not measured, which is a different thing from
54
+ * measuring badly. Sleep defaulted to 0 seconds when the watch was not worn
55
+ * and scored 35 out of 100 for it, so an unworn night produced a confident
56
+ * "fatigued" verdict out of no data at all. A null component is dropped and
57
+ * the remaining weights are renormalised.
58
+ */
59
+ components: {
60
+ hrvScore: number;
61
+ sleepScore: number | null;
62
+ stressScore: number;
63
+ restingHrScore: number;
64
+ };
65
+ }
66
+ export interface ToolTextResult {
67
+ type: "text";
68
+ text: string;
69
+ }
70
+ /**
71
+ * Tools return rendered text for MCP clients and the same information as typed
72
+ * data for everything else -- the history store, the detectors and the watch.
73
+ * Before this existed, watchApi.ts recovered numbers by matching regexes
74
+ * against the formatted prose, so a wording change in a renderer silently broke
75
+ * the watch.
76
+ */
77
+ export interface ToolResult<T> extends ToolTextResult {
78
+ data: T;
79
+ }
80
+ export declare class GarminApiError extends Error {
81
+ readonly statusCode?: number;
82
+ readonly retryAfterSeconds?: number;
83
+ constructor(message: string, statusCode?: number, retryAfterSeconds?: number);
84
+ }
85
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/garmin/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export class GarminApiError extends Error {
2
+ statusCode;
3
+ retryAfterSeconds;
4
+ constructor(message, statusCode, retryAfterSeconds) {
5
+ super(message);
6
+ this.name = "GarminApiError";
7
+ this.statusCode = statusCode;
8
+ this.retryAfterSeconds = retryAfterSeconds;
9
+ }
10
+ }
11
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/garmin/types.ts"],"names":[],"mappings":"AAkGA,MAAM,OAAO,cAAe,SAAQ,KAAK;IAC9B,UAAU,CAAU;IACpB,iBAAiB,CAAU;IAEpC,YAAY,OAAe,EAAE,UAAmB,EAAE,iBAA0B;QAC1E,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;IAC7C,CAAC;CACF"}
@@ -0,0 +1,12 @@
1
+ import type { IngestSource } from "./schema.js";
2
+ /**
3
+ * Matched case-insensitively against the key name. Identity and credentials
4
+ * only -- never a measurement, never a date. A fixture with its numbers
5
+ * redacted would be worse than the invented payload it replaces.
6
+ */
7
+ export declare const REDACTED_KEYS: string[];
8
+ /** Deep clone with identity fields replaced. Never mutates its input. */
9
+ export declare function redactPayload(payload: unknown): unknown;
10
+ /** Writes one redacted response to `<dir>/<source>-<date>.json`. */
11
+ export declare function writeFixture(directory: string, source: IngestSource, date: string, payload: unknown): string;
12
+ //# sourceMappingURL=capture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture.d.ts","sourceRoot":"","sources":["../../src/history/capture.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAgBhD;;;;GAIG;AACH,eAAO,MAAM,aAAa,UAczB,CAAC;AAYF,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAgBvD;AAED,oEAAoE;AACpE,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,GACf,MAAM,CAOR"}