trainingpeaks-mcp 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/LICENSE +21 -0
- package/README.md +224 -0
- package/dist/api/files.d.ts +11 -0
- package/dist/api/files.d.ts.map +1 -0
- package/dist/api/files.js +33 -0
- package/dist/api/files.js.map +1 -0
- package/dist/api/fitness.d.ts +12 -0
- package/dist/api/fitness.d.ts.map +1 -0
- package/dist/api/fitness.js +55 -0
- package/dist/api/fitness.js.map +1 -0
- package/dist/api/peaks.d.ts +15 -0
- package/dist/api/peaks.d.ts.map +1 -0
- package/dist/api/peaks.js +85 -0
- package/dist/api/peaks.js.map +1 -0
- package/dist/api/user.d.ts +12 -0
- package/dist/api/user.d.ts.map +1 -0
- package/dist/api/user.js +43 -0
- package/dist/api/user.js.map +1 -0
- package/dist/api/workouts.d.ts +20 -0
- package/dist/api/workouts.d.ts.map +1 -0
- package/dist/api/workouts.js +156 -0
- package/dist/api/workouts.js.map +1 -0
- package/dist/auth.d.ts +17 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +120 -0
- package/dist/auth.js.map +1 -0
- package/dist/client.d.ts +22 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +135 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +91 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/http.d.ts +3 -0
- package/dist/mcp/http.d.ts.map +1 -0
- package/dist/mcp/http.js +102 -0
- package/dist/mcp/http.js.map +1 -0
- package/dist/mcp/logger.d.ts +7 -0
- package/dist/mcp/logger.d.ts.map +1 -0
- package/dist/mcp/logger.js +99 -0
- package/dist/mcp/logger.js.map +1 -0
- package/dist/mcp/server.d.ts +4 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +57 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/stdio-wrapper.cjs +30 -0
- package/dist/mcp/stdio-wrapper.cjs.map +1 -0
- package/dist/mcp/stdio-wrapper.d.cts +3 -0
- package/dist/mcp/stdio-wrapper.d.cts.map +1 -0
- package/dist/mcp/stdio.d.ts +3 -0
- package/dist/mcp/stdio.d.ts.map +1 -0
- package/dist/mcp/stdio.js +31 -0
- package/dist/mcp/stdio.js.map +1 -0
- package/dist/mcp/tools/datetime.d.ts +41 -0
- package/dist/mcp/tools/datetime.d.ts.map +1 -0
- package/dist/mcp/tools/datetime.js +132 -0
- package/dist/mcp/tools/datetime.js.map +1 -0
- package/dist/mcp/tools/files.d.ts +22 -0
- package/dist/mcp/tools/files.d.ts.map +1 -0
- package/dist/mcp/tools/files.js +92 -0
- package/dist/mcp/tools/files.js.map +1 -0
- package/dist/mcp/tools/fitness.d.ts +16 -0
- package/dist/mcp/tools/fitness.d.ts.map +1 -0
- package/dist/mcp/tools/fitness.js +15 -0
- package/dist/mcp/tools/fitness.js.map +1 -0
- package/dist/mcp/tools/peaks.d.ts +76 -0
- package/dist/mcp/tools/peaks.d.ts.map +1 -0
- package/dist/mcp/tools/peaks.js +93 -0
- package/dist/mcp/tools/peaks.js.map +1 -0
- package/dist/mcp/tools/user.d.ts +7 -0
- package/dist/mcp/tools/user.d.ts.map +1 -0
- package/dist/mcp/tools/user.js +12 -0
- package/dist/mcp/tools/user.js.map +1 -0
- package/dist/mcp/tools/workouts.d.ts +74 -0
- package/dist/mcp/tools/workouts.d.ts.map +1 -0
- package/dist/mcp/tools/workouts.js +169 -0
- package/dist/mcp/tools/workouts.js.map +1 -0
- package/dist/types.d.ts +182 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +85 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rob West
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# trainingpeaks-mcp
|
|
2
|
+
|
|
3
|
+
An MCP (Model Context Protocol) server for accessing your TrainingPeaks training data. Works with Claude Desktop, ChatGPT, and other MCP-compatible clients.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **20 tools** for accessing workouts, strength workouts, fitness metrics, peaks/PRs, files, and datetime
|
|
8
|
+
- **Dual transport**: stdio for Claude Desktop, HTTP for ChatGPT
|
|
9
|
+
- **FIT file parsing**: Extract structured data from downloaded FIT files
|
|
10
|
+
- Also usable as a standalone TypeScript library
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
### One-Click Install (Claude Desktop)
|
|
15
|
+
|
|
16
|
+
1. Download the latest `.mcpb` file from [Releases](https://github.com/robertgregorywest/trainingpeaks-mcp/releases)
|
|
17
|
+
2. Double-click the `.mcpb` file — Claude Desktop will open and prompt you to install
|
|
18
|
+
3. Enter your TrainingPeaks credentials when prompted
|
|
19
|
+
|
|
20
|
+
That's it — no Node.js installation required. Claude Desktop bundles its own runtime.
|
|
21
|
+
|
|
22
|
+
### Manual Configuration (Claude Desktop)
|
|
23
|
+
|
|
24
|
+
Requires **Node.js 20+**. Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"mcpServers": {
|
|
29
|
+
"trainingpeaks": {
|
|
30
|
+
"command": "npx",
|
|
31
|
+
"args": ["trainingpeaks-mcp"],
|
|
32
|
+
"env": {
|
|
33
|
+
"TP_USERNAME": "your-email@example.com",
|
|
34
|
+
"TP_PASSWORD": "your-password"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Restart Claude Desktop. You can now ask Claude about your training data!
|
|
42
|
+
|
|
43
|
+
### ChatGPT (via HTTP)
|
|
44
|
+
|
|
45
|
+
Requires **Node.js 20+**.
|
|
46
|
+
|
|
47
|
+
1. Clone and install:
|
|
48
|
+
```bash
|
|
49
|
+
git clone https://github.com/robertgregorywest/trainingpeaks-mcp.git
|
|
50
|
+
cd trainingpeaks-mcp
|
|
51
|
+
npm install
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
2. Create `.env` file with your credentials:
|
|
55
|
+
```bash
|
|
56
|
+
cp .env.example .env
|
|
57
|
+
# Edit .env with your TrainingPeaks credentials
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
3. Build and start the HTTP server:
|
|
61
|
+
```bash
|
|
62
|
+
npm run build
|
|
63
|
+
npm run start:http
|
|
64
|
+
```
|
|
65
|
+
You should see: `TrainingPeaks MCP HTTP server running on port 3000`
|
|
66
|
+
|
|
67
|
+
4. Install ngrok (if not already installed):
|
|
68
|
+
|
|
69
|
+
**macOS (Homebrew):**
|
|
70
|
+
```bash
|
|
71
|
+
brew install ngrok
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Windows (Chocolatey):**
|
|
75
|
+
```bash
|
|
76
|
+
choco install ngrok
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Or download directly from:** https://ngrok.com/download
|
|
80
|
+
|
|
81
|
+
5. Create a free ngrok account and get your auth token:
|
|
82
|
+
- Sign up at https://dashboard.ngrok.com/signup
|
|
83
|
+
- Copy your auth token from https://dashboard.ngrok.com/get-started/your-authtoken
|
|
84
|
+
- Configure ngrok with your token:
|
|
85
|
+
```bash
|
|
86
|
+
ngrok config add-authtoken YOUR_AUTH_TOKEN
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
6. Start ngrok to expose your local server (in a new terminal):
|
|
90
|
+
```bash
|
|
91
|
+
ngrok http 3000
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
ngrok will display output like:
|
|
95
|
+
```
|
|
96
|
+
Forwarding https://abc123.ngrok-free.app -> http://localhost:3000
|
|
97
|
+
```
|
|
98
|
+
Copy the `https://...ngrok-free.app` URL.
|
|
99
|
+
|
|
100
|
+
7. Add the MCP connector in ChatGPT:
|
|
101
|
+
- Go to ChatGPT Settings → Connectors → Add Connector
|
|
102
|
+
- Enter your ngrok URL with `/mcp` path: `https://abc123.ngrok-free.app/mcp`
|
|
103
|
+
- Save the connector
|
|
104
|
+
|
|
105
|
+
8. Test by asking ChatGPT about your TrainingPeaks data!
|
|
106
|
+
|
|
107
|
+
## Available Tools
|
|
108
|
+
|
|
109
|
+
| Tool | Description |
|
|
110
|
+
|------|-------------|
|
|
111
|
+
| `get_user` | Get user profile including athlete ID |
|
|
112
|
+
| `get_athlete_id` | Get just the athlete ID |
|
|
113
|
+
| `get_workouts` | List workouts in a date range |
|
|
114
|
+
| `get_workout` | Get single workout summary |
|
|
115
|
+
| `get_workout_details` | Get workout with full metrics, intervals, laps, zones |
|
|
116
|
+
| `search_workouts` | Search workouts by title (case-insensitive) within a number of days |
|
|
117
|
+
| `compare_intervals` | Compare laps/intervals side-by-side across workouts with power/duration filters |
|
|
118
|
+
| `get_strength_workouts` | Get strength workouts in a date range (sets, blocks, exercises, compliance) |
|
|
119
|
+
| `download_attachment` | Download workout attachment |
|
|
120
|
+
| `parse_fit_file` | Parse FIT file and extract structured data |
|
|
121
|
+
| `get_fitness_data` | Get CTL/ATL/TSB for date range |
|
|
122
|
+
| `get_current_fitness` | Get today's fitness metrics |
|
|
123
|
+
| `get_peaks` | Get peaks for specific sport and type |
|
|
124
|
+
| `get_all_peaks` | Get all peaks for a sport |
|
|
125
|
+
| `get_workout_peaks` | Get PRs from specific workout |
|
|
126
|
+
| `get_power_peaks` | Get cycling power PRs |
|
|
127
|
+
| `get_running_peaks` | Get running pace PRs |
|
|
128
|
+
| `get_current_datetime` | Get current date and time with optional timezone |
|
|
129
|
+
| `get_current_date` | Get current date in ISO, US, EU, or custom format |
|
|
130
|
+
| `get_current_time` | Get current time in 24h, 12h, or custom format |
|
|
131
|
+
|
|
132
|
+
## Example Prompts
|
|
133
|
+
|
|
134
|
+
- "What workouts did I do last week?"
|
|
135
|
+
- "Show me my current fitness (CTL, ATL, TSB)"
|
|
136
|
+
- "What are my best 5-minute power efforts?"
|
|
137
|
+
- "Get details for my most recent ride including heart rate zones"
|
|
138
|
+
- "Download and parse the FIT file from yesterday's ride"
|
|
139
|
+
- "Search for all my tempo workouts in the last 30 days"
|
|
140
|
+
- "Compare the intervals across my last 3 threshold rides"
|
|
141
|
+
- "What is the current date and time?"
|
|
142
|
+
|
|
143
|
+
## Environment Variables
|
|
144
|
+
|
|
145
|
+
Create a `.env` file from the example:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
cp .env.example .env
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Then edit `.env` with your TrainingPeaks credentials:
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
TP_USERNAME=your-email@example.com
|
|
155
|
+
TP_PASSWORD=your-password
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
| Variable | Description |
|
|
159
|
+
|----------|-------------|
|
|
160
|
+
| `TP_USERNAME` | TrainingPeaks email address |
|
|
161
|
+
| `TP_PASSWORD` | TrainingPeaks password |
|
|
162
|
+
| `PORT` | HTTP server port (default: 3000) |
|
|
163
|
+
|
|
164
|
+
## Library Usage
|
|
165
|
+
|
|
166
|
+
You can also use this package as a standalone TypeScript library:
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
import { createClient } from 'trainingpeaks-mcp';
|
|
170
|
+
|
|
171
|
+
const client = createClient();
|
|
172
|
+
|
|
173
|
+
// Get workouts for a date range
|
|
174
|
+
const workouts = await client.getWorkouts('2024-01-01', '2024-12-31');
|
|
175
|
+
|
|
176
|
+
// Get workout details with metrics
|
|
177
|
+
const details = await client.getWorkoutDetails(workouts[0].workoutId);
|
|
178
|
+
console.log(details.metrics);
|
|
179
|
+
|
|
180
|
+
// Get current fitness
|
|
181
|
+
const fitness = await client.getCurrentFitness();
|
|
182
|
+
console.log(`CTL: ${fitness.ctl}, ATL: ${fitness.atl}, TSB: ${fitness.tsb}`);
|
|
183
|
+
|
|
184
|
+
// Clean up when done
|
|
185
|
+
await client.close();
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Development
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
npm run build # Compile TypeScript
|
|
192
|
+
npm run lint # Run ESLint
|
|
193
|
+
npm run test # Run unit tests
|
|
194
|
+
npm run typecheck # Type-check without emitting
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Integration Tests
|
|
198
|
+
|
|
199
|
+
Integration tests make real API calls to TrainingPeaks to verify all endpoints work correctly.
|
|
200
|
+
|
|
201
|
+
**Prerequisites:**
|
|
202
|
+
1. Create `.env` file with your TrainingPeaks credentials (see [Environment Variables](#environment-variables))
|
|
203
|
+
|
|
204
|
+
**Run integration tests:**
|
|
205
|
+
```bash
|
|
206
|
+
npm run test:integration
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**What's tested:**
|
|
210
|
+
- User API: `getUser`, `getAthleteId`
|
|
211
|
+
- Workouts API: `getWorkouts`, `getWorkout`, `getWorkoutDetails`
|
|
212
|
+
- Fitness API: `getCurrentFitness`, `getFitnessData`
|
|
213
|
+
- Peaks API: `getAllPeaks`, `getPeaks`, `getWorkoutPeaks`, `getPowerPeaks`, `getRunningPeaks`
|
|
214
|
+
- Files API: `downloadActivityFile`
|
|
215
|
+
|
|
216
|
+
Tests are skipped automatically if credentials are not available.
|
|
217
|
+
|
|
218
|
+
## How It Works
|
|
219
|
+
|
|
220
|
+
This library uses Playwright to automate browser login to TrainingPeaks, capturing the authentication token from API requests. The token is then used for subsequent API calls.
|
|
221
|
+
|
|
222
|
+
## License
|
|
223
|
+
|
|
224
|
+
MIT
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { HttpClient } from '../client.js';
|
|
2
|
+
import type { UserApi } from './user.js';
|
|
3
|
+
export declare class FilesApi {
|
|
4
|
+
private client;
|
|
5
|
+
private userApi;
|
|
6
|
+
constructor(client: HttpClient, userApi: UserApi);
|
|
7
|
+
downloadActivityFile(workoutId: number): Promise<Buffer | null>;
|
|
8
|
+
downloadAttachment(workoutId: number, attachmentId: number): Promise<Buffer>;
|
|
9
|
+
}
|
|
10
|
+
export declare function createFilesApi(client: HttpClient, userApi: UserApi): FilesApi;
|
|
11
|
+
//# sourceMappingURL=files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/api/files.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWzC,qBAAa,QAAQ;IACnB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,OAAO,CAAU;gBAEb,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO;IAK1C,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAoB/D,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAKnF;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,QAAQ,CAE7E"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { gunzipSync } from 'node:zlib';
|
|
2
|
+
export class FilesApi {
|
|
3
|
+
client;
|
|
4
|
+
userApi;
|
|
5
|
+
constructor(client, userApi) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
this.userApi = userApi;
|
|
8
|
+
}
|
|
9
|
+
async downloadActivityFile(workoutId) {
|
|
10
|
+
const athleteId = await this.userApi.getAthleteId();
|
|
11
|
+
const detailsEndpoint = `/fitness/v6/athletes/${athleteId}/workouts/${workoutId}/details`;
|
|
12
|
+
const details = await this.client.request(detailsEndpoint);
|
|
13
|
+
const fileInfo = details.workoutDeviceFileInfos?.[0];
|
|
14
|
+
if (!fileInfo) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const rawEndpoint = `/fitness/v6/athletes/${athleteId}/workouts/${workoutId}/rawfiledata/${fileInfo.fileId}`;
|
|
18
|
+
const buffer = await this.client.requestRaw(rawEndpoint);
|
|
19
|
+
if (fileInfo.fileName.endsWith('.gz')) {
|
|
20
|
+
return Buffer.from(gunzipSync(buffer));
|
|
21
|
+
}
|
|
22
|
+
return buffer;
|
|
23
|
+
}
|
|
24
|
+
async downloadAttachment(workoutId, attachmentId) {
|
|
25
|
+
const athleteId = await this.userApi.getAthleteId();
|
|
26
|
+
const endpoint = `/fitness/v6/athletes/${athleteId}/workouts/${workoutId}/attachments/${attachmentId}/raw`;
|
|
27
|
+
return this.client.requestRaw(endpoint);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export function createFilesApi(client, userApi) {
|
|
31
|
+
return new FilesApi(client, userApi);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/api/files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAavC,MAAM,OAAO,QAAQ;IACX,MAAM,CAAa;IACnB,OAAO,CAAU;IAEzB,YAAY,MAAkB,EAAE,OAAgB;QAC9C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,SAAiB;QAC1C,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;QACpD,MAAM,eAAe,GAAG,wBAAwB,SAAS,aAAa,SAAS,UAAU,CAAC;QAC1F,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAyB,eAAe,CAAC,CAAC;QAEnF,MAAM,QAAQ,GAAG,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAAC,CAAC,CAAC;QACrD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,WAAW,GAAG,wBAAwB,SAAS,aAAa,SAAS,gBAAgB,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC7G,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;QAEzD,IAAI,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACtC,OAAO,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QACzC,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,SAAiB,EAAE,YAAoB;QAC9D,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;QACpD,MAAM,QAAQ,GAAG,wBAAwB,SAAS,aAAa,SAAS,gBAAgB,YAAY,MAAM,CAAC;QAC3G,OAAO,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC1C,CAAC;CACF;AAED,MAAM,UAAU,cAAc,CAAC,MAAkB,EAAE,OAAgB;IACjE,OAAO,IAAI,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { HttpClient } from '../client.js';
|
|
2
|
+
import type { UserApi } from './user.js';
|
|
3
|
+
import type { FitnessMetrics } from '../types.js';
|
|
4
|
+
export declare class FitnessApi {
|
|
5
|
+
private client;
|
|
6
|
+
private userApi;
|
|
7
|
+
constructor(client: HttpClient, userApi: UserApi);
|
|
8
|
+
getFitnessData(startDate: string, endDate: string): Promise<FitnessMetrics[]>;
|
|
9
|
+
getCurrentFitness(): Promise<FitnessMetrics>;
|
|
10
|
+
}
|
|
11
|
+
export declare function createFitnessApi(client: HttpClient, userApi: UserApi): FitnessApi;
|
|
12
|
+
//# sourceMappingURL=fitness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fitness.d.ts","sourceRoot":"","sources":["../../src/api/fitness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAelD,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,OAAO,CAAU;gBAEb,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO;IAK1C,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAqC7E,iBAAiB,IAAI,OAAO,CAAC,cAAc,CAAC;CAYnD;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,UAAU,CAEjF"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export class FitnessApi {
|
|
2
|
+
client;
|
|
3
|
+
userApi;
|
|
4
|
+
constructor(client, userApi) {
|
|
5
|
+
this.client = client;
|
|
6
|
+
this.userApi = userApi;
|
|
7
|
+
}
|
|
8
|
+
async getFitnessData(startDate, endDate) {
|
|
9
|
+
const athleteId = await this.userApi.getAthleteId();
|
|
10
|
+
const endpoint = `/fitness/v1/athletes/${athleteId}/reporting/performancedata/${startDate}/${endDate}`;
|
|
11
|
+
const response = await this.client.request(endpoint, {
|
|
12
|
+
method: 'POST',
|
|
13
|
+
body: { atlConstant: 7, atlStart: 0, ctlConstant: 42, ctlStart: 0, workoutTypes: [] },
|
|
14
|
+
});
|
|
15
|
+
// Handle different response structures
|
|
16
|
+
const data = Array.isArray(response) ? response : response.data || [];
|
|
17
|
+
if (!Array.isArray(data)) {
|
|
18
|
+
console.error('Unexpected fitness API response structure');
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
return data.map((d) => {
|
|
22
|
+
// Parse CTL/ATL/TSB - they may be strings like "NaN" or numbers
|
|
23
|
+
const parseMetric = (val) => {
|
|
24
|
+
if (val === undefined || val === null)
|
|
25
|
+
return 0;
|
|
26
|
+
if (typeof val === 'string') {
|
|
27
|
+
const parsed = parseFloat(val);
|
|
28
|
+
return isNaN(parsed) ? 0 : parsed;
|
|
29
|
+
}
|
|
30
|
+
return isNaN(val) ? 0 : val;
|
|
31
|
+
};
|
|
32
|
+
return {
|
|
33
|
+
date: d.workoutDay?.split('T')[0] || '',
|
|
34
|
+
ctl: parseMetric(d.ctl),
|
|
35
|
+
atl: parseMetric(d.atl),
|
|
36
|
+
tsb: parseMetric(d.tsb),
|
|
37
|
+
dailyTss: d.tssActual,
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
async getCurrentFitness() {
|
|
42
|
+
const today = new Date().toISOString().split('T')[0];
|
|
43
|
+
const data = await this.getFitnessData(today, today);
|
|
44
|
+
return (data[0] ?? {
|
|
45
|
+
date: today,
|
|
46
|
+
ctl: 0,
|
|
47
|
+
atl: 0,
|
|
48
|
+
tsb: 0,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function createFitnessApi(client, userApi) {
|
|
53
|
+
return new FitnessApi(client, userApi);
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=fitness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fitness.js","sourceRoot":"","sources":["../../src/api/fitness.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { HttpClient } from '../client.js';
|
|
2
|
+
import type { UserApi } from './user.js';
|
|
3
|
+
import type { PeakSport, PeakType, PeakData, PeaksResponse, WorkoutPeaks, GetPeaksOptions } from '../types.js';
|
|
4
|
+
export declare class PeaksApi {
|
|
5
|
+
private client;
|
|
6
|
+
private userApi;
|
|
7
|
+
constructor(client: HttpClient, userApi: UserApi);
|
|
8
|
+
getPeaks(sport: PeakSport, type: PeakType, options?: GetPeaksOptions): Promise<PeaksResponse>;
|
|
9
|
+
getAllPeaks(sport: PeakSport, options?: GetPeaksOptions): Promise<PeaksResponse>;
|
|
10
|
+
getWorkoutPeaks(workoutId: number): Promise<WorkoutPeaks>;
|
|
11
|
+
getPowerPeaks(options?: GetPeaksOptions): Promise<PeakData[]>;
|
|
12
|
+
getRunningPeaks(options?: GetPeaksOptions): Promise<PeakData[]>;
|
|
13
|
+
}
|
|
14
|
+
export declare function createPeaksApi(client: HttpClient, userApi: UserApi): PeaksApi;
|
|
15
|
+
//# sourceMappingURL=peaks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"peaks.d.ts","sourceRoot":"","sources":["../../src/api/peaks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EACV,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,eAAe,EAChB,MAAM,aAAa,CAAC;AAqBrB,qBAAa,QAAQ;IACnB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,OAAO,CAAU;gBAEb,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO;IAK1C,QAAQ,CACZ,KAAK,EAAE,SAAS,EAChB,IAAI,EAAE,QAAQ,EACd,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,aAAa,CAAC;IAiCnB,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,aAAa,CAAC;IA6BpF,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAezD,aAAa,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAKjE,eAAe,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;CAI1E;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,QAAQ,CAE7E"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export class PeaksApi {
|
|
2
|
+
client;
|
|
3
|
+
userApi;
|
|
4
|
+
constructor(client, userApi) {
|
|
5
|
+
this.client = client;
|
|
6
|
+
this.userApi = userApi;
|
|
7
|
+
}
|
|
8
|
+
async getPeaks(sport, type, options = {}) {
|
|
9
|
+
const athleteId = await this.userApi.getAthleteId();
|
|
10
|
+
const params = new URLSearchParams();
|
|
11
|
+
params.set('sport', sport);
|
|
12
|
+
params.set('type', type);
|
|
13
|
+
if (options.startDate) {
|
|
14
|
+
params.set('startDate', options.startDate);
|
|
15
|
+
}
|
|
16
|
+
if (options.endDate) {
|
|
17
|
+
params.set('endDate', options.endDate);
|
|
18
|
+
}
|
|
19
|
+
if (options.limit) {
|
|
20
|
+
params.set('limit', options.limit.toString());
|
|
21
|
+
}
|
|
22
|
+
const endpoint = `/fitness/v1/athletes/${athleteId}/peaks?${params.toString()}`;
|
|
23
|
+
const response = await this.client.request(endpoint);
|
|
24
|
+
return {
|
|
25
|
+
sport,
|
|
26
|
+
peaks: response.peaks.map((p) => ({
|
|
27
|
+
type: p.type,
|
|
28
|
+
value: p.value,
|
|
29
|
+
unit: p.unit,
|
|
30
|
+
workoutId: p.workoutId,
|
|
31
|
+
workoutDate: p.workoutDate,
|
|
32
|
+
workoutTitle: p.workoutTitle,
|
|
33
|
+
})),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
async getAllPeaks(sport, options = {}) {
|
|
37
|
+
const athleteId = await this.userApi.getAthleteId();
|
|
38
|
+
const params = new URLSearchParams();
|
|
39
|
+
params.set('sport', sport);
|
|
40
|
+
if (options.startDate) {
|
|
41
|
+
params.set('startDate', options.startDate);
|
|
42
|
+
}
|
|
43
|
+
if (options.endDate) {
|
|
44
|
+
params.set('endDate', options.endDate);
|
|
45
|
+
}
|
|
46
|
+
const endpoint = `/fitness/v1/athletes/${athleteId}/peaks/all?${params.toString()}`;
|
|
47
|
+
const response = await this.client.request(endpoint);
|
|
48
|
+
return {
|
|
49
|
+
sport,
|
|
50
|
+
peaks: response.peaks.map((p) => ({
|
|
51
|
+
type: p.type,
|
|
52
|
+
value: p.value,
|
|
53
|
+
unit: p.unit,
|
|
54
|
+
workoutId: p.workoutId,
|
|
55
|
+
workoutDate: p.workoutDate,
|
|
56
|
+
workoutTitle: p.workoutTitle,
|
|
57
|
+
})),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async getWorkoutPeaks(workoutId) {
|
|
61
|
+
const athleteId = await this.userApi.getAthleteId();
|
|
62
|
+
const endpoint = `/fitness/v1/athletes/${athleteId}/workouts/${workoutId}/peaks`;
|
|
63
|
+
const response = await this.client.request(endpoint);
|
|
64
|
+
return {
|
|
65
|
+
workoutId,
|
|
66
|
+
peaks: response.peaks.map((p) => ({
|
|
67
|
+
type: p.type,
|
|
68
|
+
value: p.value,
|
|
69
|
+
unit: p.unit,
|
|
70
|
+
})),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
async getPowerPeaks(options = {}) {
|
|
74
|
+
const response = await this.getAllPeaks('Bike', options);
|
|
75
|
+
return response.peaks.filter((p) => p.type.startsWith('power'));
|
|
76
|
+
}
|
|
77
|
+
async getRunningPeaks(options = {}) {
|
|
78
|
+
const response = await this.getAllPeaks('Run', options);
|
|
79
|
+
return response.peaks.filter((p) => p.type.startsWith('speed'));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export function createPeaksApi(client, userApi) {
|
|
83
|
+
return new PeaksApi(client, userApi);
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=peaks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"peaks.js","sourceRoot":"","sources":["../../src/api/peaks.ts"],"names":[],"mappings":"AA8BA,MAAM,OAAO,QAAQ;IACX,MAAM,CAAa;IACnB,OAAO,CAAU;IAEzB,YAAY,MAAkB,EAAE,OAAgB;QAC9C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,QAAQ,CACZ,KAAgB,EAChB,IAAc,EACd,UAA2B,EAAE;QAE7B,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;QAEpD,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC3B,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAEzB,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACtB,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QAC7C,CAAC;QACD,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QACzC,CAAC;QACD,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;QAChD,CAAC;QAED,MAAM,QAAQ,GAAG,wBAAwB,SAAS,UAAU,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;QAChF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAmB,QAAQ,CAAC,CAAC;QAEvE,OAAO;YACL,KAAK;YACL,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAChC,IAAI,EAAE,CAAC,CAAC,IAAgB;gBACxB,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,WAAW,EAAE,CAAC,CAAC,WAAW;gBAC1B,YAAY,EAAE,CAAC,CAAC,YAAY;aAC7B,CAAC,CAAC;SACJ,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,KAAgB,EAAE,UAA2B,EAAE;QAC/D,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;QAEpD,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAE3B,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACtB,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QAC7C,CAAC;QACD,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QACzC,CAAC;QAED,MAAM,QAAQ,GAAG,wBAAwB,SAAS,cAAc,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;QACpF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAmB,QAAQ,CAAC,CAAC;QAEvE,OAAO;YACL,KAAK;YACL,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAChC,IAAI,EAAE,CAAC,CAAC,IAAgB;gBACxB,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,WAAW,EAAE,CAAC,CAAC,WAAW;gBAC1B,YAAY,EAAE,CAAC,CAAC,YAAY;aAC7B,CAAC,CAAC;SACJ,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,SAAiB;QACrC,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;QACpD,MAAM,QAAQ,GAAG,wBAAwB,SAAS,aAAa,SAAS,QAAQ,CAAC;QACjF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAA0B,QAAQ,CAAC,CAAC;QAE9E,OAAO;YACL,SAAS;YACT,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAChC,IAAI,EAAE,CAAC,CAAC,IAAgB;gBACxB,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,IAAI,EAAE,CAAC,CAAC,IAAI;aACb,CAAC,CAAC;SACJ,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,UAA2B,EAAE;QAC/C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACzD,OAAO,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,UAA2B,EAAE;QACjD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACxD,OAAO,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IAClE,CAAC;CACF;AAED,MAAM,UAAU,cAAc,CAAC,MAAkB,EAAE,OAAgB;IACjE,OAAO,IAAI,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { HttpClient } from '../client.js';
|
|
2
|
+
import type { User } from '../types.js';
|
|
3
|
+
export declare class UserApi {
|
|
4
|
+
private client;
|
|
5
|
+
private cachedUser;
|
|
6
|
+
constructor(client: HttpClient);
|
|
7
|
+
getUser(): Promise<User>;
|
|
8
|
+
getAthleteId(): Promise<number>;
|
|
9
|
+
clearCache(): void;
|
|
10
|
+
}
|
|
11
|
+
export declare function createUserApi(client: HttpClient): UserApi;
|
|
12
|
+
//# sourceMappingURL=user.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../src/api/user.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAuBxC,qBAAa,OAAO;IAClB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,UAAU,CAAqB;gBAE3B,MAAM,EAAE,UAAU;IAIxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA8BxB,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAKrC,UAAU,IAAI,IAAI;CAGnB;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAEzD"}
|
package/dist/api/user.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export class UserApi {
|
|
2
|
+
client;
|
|
3
|
+
cachedUser = null;
|
|
4
|
+
constructor(client) {
|
|
5
|
+
this.client = client;
|
|
6
|
+
}
|
|
7
|
+
async getUser() {
|
|
8
|
+
if (this.cachedUser) {
|
|
9
|
+
return this.cachedUser;
|
|
10
|
+
}
|
|
11
|
+
const response = await this.client.request('/users/v3/user');
|
|
12
|
+
if (!response || !response.user) {
|
|
13
|
+
console.error('Unexpected API response:', JSON.stringify(response, null, 2));
|
|
14
|
+
throw new Error('Invalid response from user API');
|
|
15
|
+
}
|
|
16
|
+
const athletes = response.user.athletes || [];
|
|
17
|
+
const user = {
|
|
18
|
+
id: response.user.userId,
|
|
19
|
+
athleteId: athletes[0]?.athleteId ?? response.user.userId,
|
|
20
|
+
email: response.user.email,
|
|
21
|
+
firstName: response.user.firstName,
|
|
22
|
+
lastName: response.user.lastName,
|
|
23
|
+
dateOfBirth: response.user.birthday,
|
|
24
|
+
gender: response.user.gender,
|
|
25
|
+
countryCode: response.user.country,
|
|
26
|
+
timezone: response.user.timeZone,
|
|
27
|
+
isPremium: response.user.settings?.account?.isPremium,
|
|
28
|
+
};
|
|
29
|
+
this.cachedUser = user;
|
|
30
|
+
return user;
|
|
31
|
+
}
|
|
32
|
+
async getAthleteId() {
|
|
33
|
+
const user = await this.getUser();
|
|
34
|
+
return user.athleteId;
|
|
35
|
+
}
|
|
36
|
+
clearCache() {
|
|
37
|
+
this.cachedUser = null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function createUserApi(client) {
|
|
41
|
+
return new UserApi(client);
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=user.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user.js","sourceRoot":"","sources":["../../src/api/user.ts"],"names":[],"mappings":"AAwBA,MAAM,OAAO,OAAO;IACV,MAAM,CAAa;IACnB,UAAU,GAAgB,IAAI,CAAC;IAEvC,YAAY,MAAkB;QAC5B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,OAAO,IAAI,CAAC,UAAU,CAAC;QACzB,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAkB,gBAAgB,CAAC,CAAC;QAE9E,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;YAChC,OAAO,CAAC,KAAK,CAAC,0BAA0B,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAC7E,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QACpD,CAAC;QAED,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;QAC9C,MAAM,IAAI,GAAS;YACjB,EAAE,EAAE,QAAQ,CAAC,IAAI,CAAC,MAAM;YACxB,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,SAAS,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM;YACzD,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK;YAC1B,SAAS,EAAE,QAAQ,CAAC,IAAI,CAAC,SAAS;YAClC,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,QAAQ;YAChC,WAAW,EAAE,QAAQ,CAAC,IAAI,CAAC,QAAQ;YACnC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,MAAM;YAC5B,WAAW,EAAE,QAAQ,CAAC,IAAI,CAAC,OAAO;YAClC,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,QAAQ;YAChC,SAAS,EAAE,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS;SACtD,CAAC;QAEF,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,UAAU;QACR,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;IACzB,CAAC;CACF;AAED,MAAM,UAAU,aAAa,CAAC,MAAkB;IAC9C,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { HttpClient } from '../client.js';
|
|
2
|
+
import type { UserApi } from './user.js';
|
|
3
|
+
import type { WorkoutSummary, WorkoutDetail, GetWorkoutsOptions, StrengthWorkoutSummary } from '../types.js';
|
|
4
|
+
export declare class WorkoutsApi {
|
|
5
|
+
private client;
|
|
6
|
+
private userApi;
|
|
7
|
+
constructor(client: HttpClient, userApi: UserApi);
|
|
8
|
+
getWorkouts(startDate: string, endDate: string, options?: GetWorkoutsOptions): Promise<WorkoutSummary[]>;
|
|
9
|
+
private fetchWorkoutsForRange;
|
|
10
|
+
getWorkout(workoutId: number): Promise<WorkoutSummary>;
|
|
11
|
+
getWorkoutDetails(workoutId: number): Promise<WorkoutDetail>;
|
|
12
|
+
getStrengthWorkouts(startDate: string, endDate: string): Promise<StrengthWorkoutSummary[]>;
|
|
13
|
+
private mapStrengthWorkoutResponse;
|
|
14
|
+
private mapWorkoutResponse;
|
|
15
|
+
private mapWorkoutDetailResponse;
|
|
16
|
+
private chunkDateRange;
|
|
17
|
+
private formatDate;
|
|
18
|
+
}
|
|
19
|
+
export declare function createWorkoutsApi(client: HttpClient, userApi: UserApi): WorkoutsApi;
|
|
20
|
+
//# sourceMappingURL=workouts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workouts.d.ts","sourceRoot":"","sources":["../../src/api/workouts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,sBAAsB,EACvB,MAAM,aAAa,CAAC;AAMrB,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,OAAO,CAAU;gBAEb,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO;IAK1C,WAAW,CACf,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,cAAc,EAAE,CAAC;YAmBd,qBAAqB;IAc7B,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAOtD,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAQ5D,mBAAmB,CACvB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,sBAAsB,EAAE,CAAC;IAUpC,OAAO,CAAC,0BAA0B;IAgClC,OAAO,CAAC,kBAAkB;IAsC1B,OAAO,CAAC,wBAAwB;IAwBhC,OAAO,CAAC,cAAc;IA+BtB,OAAO,CAAC,UAAU;CAGnB;AAoFD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,WAAW,CAEnF"}
|