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.
- package/.env.example +11 -0
- package/CHANGELOG.md +675 -0
- package/LICENSE +21 -0
- package/QUICKSTART.md +182 -0
- package/README.md +322 -0
- package/dist/appDb.d.ts +83 -0
- package/dist/appDb.d.ts.map +1 -0
- package/dist/appDb.js +239 -0
- package/dist/appDb.js.map +1 -0
- package/dist/check.d.ts +12 -0
- package/dist/check.d.ts.map +1 -0
- package/dist/check.js +230 -0
- package/dist/check.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +457 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +61 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +241 -0
- package/dist/config.js.map +1 -0
- package/dist/dashboard.d.ts +8 -0
- package/dist/dashboard.d.ts.map +1 -0
- package/dist/dashboard.js +1004 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/dashboardCharts.d.ts +99 -0
- package/dist/dashboardCharts.d.ts.map +1 -0
- package/dist/dashboardCharts.js +317 -0
- package/dist/dashboardCharts.js.map +1 -0
- package/dist/dashboardData.d.ts +89 -0
- package/dist/dashboardData.d.ts.map +1 -0
- package/dist/dashboardData.js +228 -0
- package/dist/dashboardData.js.map +1 -0
- package/dist/detect/baseline.d.ts +17 -0
- package/dist/detect/baseline.d.ts.map +1 -0
- package/dist/detect/baseline.js +62 -0
- package/dist/detect/baseline.js.map +1 -0
- package/dist/detect/countdown.d.ts +30 -0
- package/dist/detect/countdown.d.ts.map +1 -0
- package/dist/detect/countdown.js +79 -0
- package/dist/detect/countdown.js.map +1 -0
- package/dist/detect/detectors.d.ts +20 -0
- package/dist/detect/detectors.d.ts.map +1 -0
- package/dist/detect/detectors.js +291 -0
- package/dist/detect/detectors.js.map +1 -0
- package/dist/detect/findings.d.ts +29 -0
- package/dist/detect/findings.d.ts.map +1 -0
- package/dist/detect/findings.js +2 -0
- package/dist/detect/findings.js.map +1 -0
- package/dist/detect/forecast.d.ts +33 -0
- package/dist/detect/forecast.d.ts.map +1 -0
- package/dist/detect/forecast.js +122 -0
- package/dist/detect/forecast.js.map +1 -0
- package/dist/detect/index.d.ts +50 -0
- package/dist/detect/index.d.ts.map +1 -0
- package/dist/detect/index.js +114 -0
- package/dist/detect/index.js.map +1 -0
- package/dist/detect/sleepQuality.d.ts +21 -0
- package/dist/detect/sleepQuality.d.ts.map +1 -0
- package/dist/detect/sleepQuality.js +164 -0
- package/dist/detect/sleepQuality.js.map +1 -0
- package/dist/detect/trimp.d.ts +22 -0
- package/dist/detect/trimp.d.ts.map +1 -0
- package/dist/detect/trimp.js +77 -0
- package/dist/detect/trimp.js.map +1 -0
- package/dist/detect/week.d.ts +31 -0
- package/dist/detect/week.d.ts.map +1 -0
- package/dist/detect/week.js +184 -0
- package/dist/detect/week.js.map +1 -0
- package/dist/garmin/auth.d.ts +10 -0
- package/dist/garmin/auth.d.ts.map +1 -0
- package/dist/garmin/auth.js +81 -0
- package/dist/garmin/auth.js.map +1 -0
- package/dist/garmin/cache.d.ts +42 -0
- package/dist/garmin/cache.d.ts.map +1 -0
- package/dist/garmin/cache.js +141 -0
- package/dist/garmin/cache.js.map +1 -0
- package/dist/garmin/client.d.ts +22 -0
- package/dist/garmin/client.d.ts.map +1 -0
- package/dist/garmin/client.js +237 -0
- package/dist/garmin/client.js.map +1 -0
- package/dist/garmin/daily.d.ts +24 -0
- package/dist/garmin/daily.d.ts.map +1 -0
- package/dist/garmin/daily.js +96 -0
- package/dist/garmin/daily.js.map +1 -0
- package/dist/garmin/garminApiTypes.d.ts +70 -0
- package/dist/garmin/garminApiTypes.d.ts.map +1 -0
- package/dist/garmin/garminApiTypes.js +3 -0
- package/dist/garmin/garminApiTypes.js.map +1 -0
- package/dist/garmin/garminConnect.d.ts +17 -0
- package/dist/garmin/garminConnect.d.ts.map +1 -0
- package/dist/garmin/garminConnect.js +5 -0
- package/dist/garmin/garminConnect.js.map +1 -0
- package/dist/garmin/partial.d.ts +32 -0
- package/dist/garmin/partial.d.ts.map +1 -0
- package/dist/garmin/partial.js +38 -0
- package/dist/garmin/partial.js.map +1 -0
- package/dist/garmin/rawApi.d.ts +59 -0
- package/dist/garmin/rawApi.d.ts.map +1 -0
- package/dist/garmin/rawApi.js +113 -0
- package/dist/garmin/rawApi.js.map +1 -0
- package/dist/garmin/types.d.ts +85 -0
- package/dist/garmin/types.d.ts.map +1 -0
- package/dist/garmin/types.js +11 -0
- package/dist/garmin/types.js.map +1 -0
- package/dist/history/capture.d.ts +12 -0
- package/dist/history/capture.d.ts.map +1 -0
- package/dist/history/capture.js +63 -0
- package/dist/history/capture.js.map +1 -0
- package/dist/history/context.d.ts +51 -0
- package/dist/history/context.d.ts.map +1 -0
- package/dist/history/context.js +122 -0
- package/dist/history/context.js.map +1 -0
- package/dist/history/fallback.d.ts +72 -0
- package/dist/history/fallback.d.ts.map +1 -0
- package/dist/history/fallback.js +185 -0
- package/dist/history/fallback.js.map +1 -0
- package/dist/history/ingest.d.ts +44 -0
- package/dist/history/ingest.d.ts.map +1 -0
- package/dist/history/ingest.js +347 -0
- package/dist/history/ingest.js.map +1 -0
- package/dist/history/scheduler.d.ts +20 -0
- package/dist/history/scheduler.d.ts.map +1 -0
- package/dist/history/scheduler.js +54 -0
- package/dist/history/scheduler.js.map +1 -0
- package/dist/history/schema.d.ts +41 -0
- package/dist/history/schema.d.ts.map +1 -0
- package/dist/history/schema.js +91 -0
- package/dist/history/schema.js.map +1 -0
- package/dist/history/store.d.ts +112 -0
- package/dist/history/store.d.ts.map +1 -0
- package/dist/history/store.js +282 -0
- package/dist/history/store.js.map +1 -0
- package/dist/httpServer.d.ts +55 -0
- package/dist/httpServer.d.ts.map +1 -0
- package/dist/httpServer.js +1027 -0
- package/dist/httpServer.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/mcpConfig.d.ts +56 -0
- package/dist/mcpConfig.d.ts.map +1 -0
- package/dist/mcpConfig.js +106 -0
- package/dist/mcpConfig.js.map +1 -0
- package/dist/pairApi.d.ts +14 -0
- package/dist/pairApi.d.ts.map +1 -0
- package/dist/pairApi.js +38 -0
- package/dist/pairApi.js.map +1 -0
- package/dist/paths.d.ts +45 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +105 -0
- package/dist/paths.js.map +1 -0
- package/dist/profile.d.ts +123 -0
- package/dist/profile.d.ts.map +1 -0
- package/dist/profile.js +327 -0
- package/dist/profile.js.map +1 -0
- package/dist/promptApi.d.ts +43 -0
- package/dist/promptApi.d.ts.map +1 -0
- package/dist/promptApi.js +328 -0
- package/dist/promptApi.js.map +1 -0
- package/dist/promptSuggestions.d.ts +16 -0
- package/dist/promptSuggestions.d.ts.map +1 -0
- package/dist/promptSuggestions.js +141 -0
- package/dist/promptSuggestions.js.map +1 -0
- package/dist/selfTest.d.ts +53 -0
- package/dist/selfTest.d.ts.map +1 -0
- package/dist/selfTest.js +225 -0
- package/dist/selfTest.js.map +1 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +67 -0
- package/dist/server.js.map +1 -0
- package/dist/setup.d.ts +2 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +210 -0
- package/dist/setup.js.map +1 -0
- package/dist/toolErrors.d.ts +2 -0
- package/dist/toolErrors.d.ts.map +1 -0
- package/dist/toolErrors.js +18 -0
- package/dist/toolErrors.js.map +1 -0
- package/dist/tools/activities.d.ts +18 -0
- package/dist/tools/activities.d.ts.map +1 -0
- package/dist/tools/activities.js +182 -0
- package/dist/tools/activities.js.map +1 -0
- package/dist/tools/bodyComposition.d.ts +10 -0
- package/dist/tools/bodyComposition.d.ts.map +1 -0
- package/dist/tools/bodyComposition.js +139 -0
- package/dist/tools/bodyComposition.js.map +1 -0
- package/dist/tools/context.d.ts +8 -0
- package/dist/tools/context.d.ts.map +1 -0
- package/dist/tools/context.js +148 -0
- package/dist/tools/context.js.map +1 -0
- package/dist/tools/findings.d.ts +7 -0
- package/dist/tools/findings.d.ts.map +1 -0
- package/dist/tools/findings.js +51 -0
- package/dist/tools/findings.js.map +1 -0
- package/dist/tools/heartRate.d.ts +10 -0
- package/dist/tools/heartRate.d.ts.map +1 -0
- package/dist/tools/heartRate.js +105 -0
- package/dist/tools/heartRate.js.map +1 -0
- package/dist/tools/index.d.ts +66 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +101 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/payloads.d.ts +148 -0
- package/dist/tools/payloads.d.ts.map +1 -0
- package/dist/tools/payloads.js +2 -0
- package/dist/tools/payloads.js.map +1 -0
- package/dist/tools/recovery.d.ts +31 -0
- package/dist/tools/recovery.d.ts.map +1 -0
- package/dist/tools/recovery.js +350 -0
- package/dist/tools/recovery.js.map +1 -0
- package/dist/tools/sleep.d.ts +10 -0
- package/dist/tools/sleep.d.ts.map +1 -0
- package/dist/tools/sleep.js +124 -0
- package/dist/tools/sleep.js.map +1 -0
- package/dist/tools/stress.d.ts +11 -0
- package/dist/tools/stress.d.ts.map +1 -0
- package/dist/tools/stress.js +108 -0
- package/dist/tools/stress.js.map +1 -0
- package/dist/tools/trainingInsights.d.ts +15 -0
- package/dist/tools/trainingInsights.d.ts.map +1 -0
- package/dist/tools/trainingInsights.js +79 -0
- package/dist/tools/trainingInsights.js.map +1 -0
- package/dist/tools/types.d.ts +12 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/vo2Max.d.ts +11 -0
- package/dist/tools/vo2Max.d.ts.map +1 -0
- package/dist/tools/vo2Max.js +110 -0
- package/dist/tools/vo2Max.js.map +1 -0
- package/dist/tools/week.d.ts +12 -0
- package/dist/tools/week.d.ts.map +1 -0
- package/dist/tools/week.js +74 -0
- package/dist/tools/week.js.map +1 -0
- package/dist/usage.d.ts +104 -0
- package/dist/usage.d.ts.map +1 -0
- package/dist/usage.js +247 -0
- package/dist/usage.js.map +1 -0
- package/dist/utils/batch.d.ts +2 -0
- package/dist/utils/batch.d.ts.map +1 -0
- package/dist/utils/batch.js +18 -0
- package/dist/utils/batch.js.map +1 -0
- package/dist/utils/helpers.d.ts +32 -0
- package/dist/utils/helpers.d.ts.map +1 -0
- package/dist/utils/helpers.js +188 -0
- package/dist/utils/helpers.js.map +1 -0
- package/dist/utils/jsonFile.d.ts +19 -0
- package/dist/utils/jsonFile.d.ts.map +1 -0
- package/dist/utils/jsonFile.js +24 -0
- package/dist/utils/jsonFile.js.map +1 -0
- package/dist/utils/logger.d.ts +4 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +97 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/secretFile.d.ts +15 -0
- package/dist/utils/secretFile.d.ts.map +1 -0
- package/dist/utils/secretFile.js +47 -0
- package/dist/utils/secretFile.js.map +1 -0
- package/dist/utils/stdio.d.ts +10 -0
- package/dist/utils/stdio.d.ts.map +1 -0
- package/dist/utils/stdio.js +70 -0
- package/dist/utils/stdio.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +7 -0
- package/dist/version.js.map +1 -0
- package/dist/watchApi.d.ts +264 -0
- package/dist/watchApi.d.ts.map +1 -0
- package/dist/watchApi.js +333 -0
- package/dist/watchApi.js.map +1 -0
- 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 @@
|
|
|
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 @@
|
|
|
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"}
|