@nativesquare/soma 0.1.2 → 0.3.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 (76) hide show
  1. package/README.md +260 -19
  2. package/dist/client/index.d.ts +158 -4
  3. package/dist/client/index.d.ts.map +1 -1
  4. package/dist/client/index.js +165 -3
  5. package/dist/client/index.js.map +1 -1
  6. package/dist/component/_generated/api.d.ts +2 -0
  7. package/dist/component/_generated/api.d.ts.map +1 -1
  8. package/dist/component/_generated/api.js.map +1 -1
  9. package/dist/component/_generated/component.d.ts +37 -0
  10. package/dist/component/_generated/component.d.ts.map +1 -1
  11. package/dist/component/public.d.ts +3 -3
  12. package/dist/component/schema.d.ts +18 -5
  13. package/dist/component/schema.d.ts.map +1 -1
  14. package/dist/component/schema.js +10 -0
  15. package/dist/component/schema.js.map +1 -1
  16. package/dist/component/strava.d.ts +88 -0
  17. package/dist/component/strava.d.ts.map +1 -0
  18. package/dist/component/strava.js +318 -0
  19. package/dist/component/strava.js.map +1 -0
  20. package/dist/component/validators/activity.d.ts +4 -4
  21. package/dist/component/validators/samples.d.ts +2 -2
  22. package/dist/strava/activity.d.ts +121 -0
  23. package/dist/strava/activity.d.ts.map +1 -0
  24. package/dist/strava/activity.js +201 -0
  25. package/dist/strava/activity.js.map +1 -0
  26. package/dist/strava/athlete.d.ts +34 -0
  27. package/dist/strava/athlete.d.ts.map +1 -0
  28. package/dist/strava/athlete.js +39 -0
  29. package/dist/strava/athlete.js.map +1 -0
  30. package/dist/strava/auth.d.ts +103 -0
  31. package/dist/strava/auth.d.ts.map +1 -0
  32. package/dist/strava/auth.js +111 -0
  33. package/dist/strava/auth.js.map +1 -0
  34. package/dist/strava/client.d.ts +93 -0
  35. package/dist/strava/client.d.ts.map +1 -0
  36. package/dist/strava/client.js +158 -0
  37. package/dist/strava/client.js.map +1 -0
  38. package/dist/strava/index.d.ts +13 -0
  39. package/dist/strava/index.d.ts.map +1 -0
  40. package/dist/strava/index.js +17 -0
  41. package/dist/strava/index.js.map +1 -0
  42. package/dist/strava/maps/sport-type.d.ts +7 -0
  43. package/dist/strava/maps/sport-type.d.ts.map +1 -0
  44. package/dist/strava/maps/sport-type.js +84 -0
  45. package/dist/strava/maps/sport-type.js.map +1 -0
  46. package/dist/strava/sync.d.ts +104 -0
  47. package/dist/strava/sync.d.ts.map +1 -0
  48. package/dist/strava/sync.js +87 -0
  49. package/dist/strava/sync.js.map +1 -0
  50. package/dist/strava/types.d.ts +266 -0
  51. package/dist/strava/types.d.ts.map +1 -0
  52. package/dist/strava/types.js +8 -0
  53. package/dist/strava/types.js.map +1 -0
  54. package/dist/validators.d.ts +6617 -0
  55. package/dist/validators.d.ts.map +1 -0
  56. package/dist/validators.js +78 -0
  57. package/dist/validators.js.map +1 -0
  58. package/package.json +9 -1
  59. package/src/client/index.ts +212 -4
  60. package/src/component/_generated/api.ts +2 -0
  61. package/src/component/_generated/component.ts +49 -0
  62. package/src/component/schema.ts +11 -0
  63. package/src/component/strava.ts +383 -0
  64. package/src/strava/activity.test.ts +415 -0
  65. package/src/strava/activity.ts +276 -0
  66. package/src/strava/athlete.test.ts +139 -0
  67. package/src/strava/athlete.ts +47 -0
  68. package/src/strava/auth.test.ts +78 -0
  69. package/src/strava/auth.ts +185 -0
  70. package/src/strava/client.ts +212 -0
  71. package/src/strava/index.ts +54 -0
  72. package/src/strava/maps/sport-type.test.ts +69 -0
  73. package/src/strava/maps/sport-type.ts +99 -0
  74. package/src/strava/sync.ts +168 -0
  75. package/src/strava/types.ts +361 -0
  76. package/src/validators.ts +89 -0
@@ -0,0 +1,201 @@
1
+ // ─── Activity Transformer ────────────────────────────────────────────────────
2
+ // Transforms a Strava DetailedActivity (+ optional streams/laps) into the
3
+ // Soma Activity schema shape.
4
+ import { mapSportType } from "./maps/sport-type.js";
5
+ /**
6
+ * Transform a Strava activity into a Soma Activity document shape.
7
+ *
8
+ * The returned object is ready to be spread into an `ingestActivity` call
9
+ * alongside `connectionId` and `userId`.
10
+ *
11
+ * Accepts either a DetailedActivity (from `GET /activities/{id}`) or a
12
+ * SummaryActivity (from `GET /athlete/activities`). When a DetailedActivity
13
+ * is provided, additional fields like `calories`, `segment_efforts`, and
14
+ * embedded `laps` are mapped. Optional streams and laps can also be supplied
15
+ * for time-series data (heart rate, power, position, etc.).
16
+ *
17
+ * @param activity - The Strava activity (summary or detailed)
18
+ * @param opts - Optional streams and laps data
19
+ * @returns Soma Activity fields (without connectionId/userId)
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const data = transformActivity(stravaActivity, { streams, laps });
24
+ * await soma.ingestActivity(ctx, { connectionId, userId, ...data });
25
+ * ```
26
+ */
27
+ export function transformActivity(activity, opts) {
28
+ const streams = opts?.streams;
29
+ const laps = opts?.laps ?? (isDetailed(activity) ? activity.laps : undefined);
30
+ const timeStream = streams?.time?.data;
31
+ const startDate = activity.start_date;
32
+ return {
33
+ metadata: {
34
+ summary_id: String(activity.id),
35
+ start_time: activity.start_date,
36
+ end_time: computeEndTime(activity.start_date, activity.elapsed_time),
37
+ type: mapSportType(activity.sport_type),
38
+ upload_type: activity.manual ? 2 : 1, // 2=Manual, 1=Automatic
39
+ name: activity.name,
40
+ city: activity.location_city ?? undefined,
41
+ state: activity.location_state ?? undefined,
42
+ country: activity.location_country ?? undefined,
43
+ },
44
+ active_durations_data: {
45
+ activity_seconds: activity.moving_time,
46
+ },
47
+ calories_data: isDetailed(activity) && activity.calories != null
48
+ ? { total_burned_calories: activity.calories }
49
+ : undefined,
50
+ device_data: activity.device_name
51
+ ? { name: activity.device_name }
52
+ : undefined,
53
+ distance_data: buildDistanceData(activity),
54
+ energy_data: activity.kilojoules != null
55
+ ? { energy_kilojoules: activity.kilojoules }
56
+ : undefined,
57
+ heart_rate_data: buildHeartRateData(activity, streams, timeStream, startDate),
58
+ lap_data: buildLapData(laps),
59
+ movement_data: buildMovementData(activity, streams, timeStream, startDate),
60
+ polyline_map_data: activity.map?.summary_polyline
61
+ ? { summary_polyline: activity.map.summary_polyline }
62
+ : undefined,
63
+ position_data: buildPositionData(activity, streams, timeStream, startDate),
64
+ power_data: buildPowerData(activity, streams, timeStream, startDate),
65
+ };
66
+ }
67
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
68
+ function isDetailed(activity) {
69
+ return activity.resource_state === 3;
70
+ }
71
+ function computeEndTime(startDate, elapsedTimeSeconds) {
72
+ const start = new Date(startDate);
73
+ return new Date(start.getTime() + elapsedTimeSeconds * 1000).toISOString();
74
+ }
75
+ /**
76
+ * Compute an ISO-8601 timestamp for a stream data point, given the
77
+ * activity start time and the time stream offset in seconds.
78
+ */
79
+ function streamTimestamp(startDate, timeOffsetSeconds) {
80
+ const start = new Date(startDate);
81
+ return new Date(start.getTime() + timeOffsetSeconds * 1000).toISOString();
82
+ }
83
+ function buildDistanceData(activity) {
84
+ if (activity.distance == null && activity.total_elevation_gain == null) {
85
+ return undefined;
86
+ }
87
+ const detailed = isDetailed(activity) ? activity : undefined;
88
+ return {
89
+ summary: {
90
+ distance_meters: activity.distance ?? undefined,
91
+ elevation: activity.total_elevation_gain != null
92
+ ? {
93
+ gain_actual_meters: activity.total_elevation_gain,
94
+ max_meters: detailed?.elev_high ?? undefined,
95
+ min_meters: detailed?.elev_low ?? undefined,
96
+ }
97
+ : undefined,
98
+ },
99
+ };
100
+ }
101
+ function buildHeartRateData(activity, streams, timeStream, startDate) {
102
+ const hasHrSummary = activity.average_heartrate != null || activity.max_heartrate != null;
103
+ const hrStream = streams?.heartrate?.data;
104
+ const hasHrStream = hrStream && hrStream.length > 0 && timeStream;
105
+ if (!hasHrSummary && !hasHrStream)
106
+ return undefined;
107
+ return {
108
+ summary: hasHrSummary
109
+ ? {
110
+ avg_hr_bpm: activity.average_heartrate,
111
+ max_hr_bpm: activity.max_heartrate,
112
+ }
113
+ : undefined,
114
+ detailed: hasHrStream && timeStream
115
+ ? {
116
+ hr_samples: hrStream.map((bpm, i) => ({
117
+ timestamp: streamTimestamp(startDate, timeStream[i]),
118
+ bpm,
119
+ })),
120
+ }
121
+ : undefined,
122
+ };
123
+ }
124
+ function buildLapData(laps) {
125
+ if (!laps || laps.length === 0)
126
+ return undefined;
127
+ return {
128
+ laps: laps.map((lap) => ({
129
+ start_time: lap.start_date,
130
+ end_time: computeEndTime(lap.start_date, lap.elapsed_time),
131
+ distance_meters: lap.distance,
132
+ calories: undefined,
133
+ avg_speed_meters_per_second: lap.average_speed,
134
+ avg_hr_bpm: lap.average_heartrate,
135
+ })),
136
+ };
137
+ }
138
+ function buildMovementData(activity, streams, timeStream, startDate) {
139
+ const hasMovement = activity.average_speed != null ||
140
+ activity.max_speed != null ||
141
+ activity.average_cadence != null;
142
+ const speedStream = streams?.velocity_smooth?.data;
143
+ const cadenceStream = streams?.cadence?.data;
144
+ const hasStreams = ((speedStream && speedStream.length > 0) ||
145
+ (cadenceStream && cadenceStream.length > 0)) &&
146
+ timeStream;
147
+ if (!hasMovement && !hasStreams)
148
+ return undefined;
149
+ return {
150
+ avg_speed_meters_per_second: activity.average_speed ?? undefined,
151
+ max_speed_meters_per_second: activity.max_speed ?? undefined,
152
+ avg_cadence_rpm: activity.average_cadence ?? undefined,
153
+ speed_samples: speedStream && timeStream
154
+ ? speedStream.map((speed, i) => ({
155
+ timestamp: streamTimestamp(startDate, timeStream[i]),
156
+ speed_meters_per_second: speed,
157
+ }))
158
+ : undefined,
159
+ cadence_samples: cadenceStream && timeStream
160
+ ? cadenceStream.map((cadence, i) => ({
161
+ timestamp: streamTimestamp(startDate, timeStream[i]),
162
+ cadence_rpm: cadence,
163
+ }))
164
+ : undefined,
165
+ };
166
+ }
167
+ function buildPositionData(activity, streams, timeStream, startDate) {
168
+ const latlngStream = streams?.latlng?.data;
169
+ const hasPositionStream = latlngStream && latlngStream.length > 0 && timeStream;
170
+ const hasStartEnd = activity.start_latlng != null || activity.end_latlng != null;
171
+ if (!hasPositionStream && !hasStartEnd)
172
+ return undefined;
173
+ return {
174
+ start_pos_lat_lng_deg: activity.start_latlng ?? undefined,
175
+ end_pos_lat_lng_deg: activity.end_latlng ?? undefined,
176
+ position_samples: hasPositionStream && timeStream
177
+ ? latlngStream.map((coords, i) => ({
178
+ timestamp: streamTimestamp(startDate, timeStream[i]),
179
+ coords_lat_lng_deg: coords,
180
+ }))
181
+ : undefined,
182
+ };
183
+ }
184
+ function buildPowerData(activity, streams, timeStream, startDate) {
185
+ const hasPowerSummary = activity.average_watts != null || activity.max_watts != null;
186
+ const wattsStream = streams?.watts?.data;
187
+ const hasWattsStream = wattsStream && wattsStream.length > 0 && timeStream;
188
+ if (!hasPowerSummary && !hasWattsStream)
189
+ return undefined;
190
+ return {
191
+ avg_watts: activity.average_watts ?? undefined,
192
+ max_watts: activity.max_watts ?? undefined,
193
+ power_samples: hasWattsStream && timeStream
194
+ ? wattsStream.map((watts, i) => ({
195
+ timestamp: streamTimestamp(startDate, timeStream[i]),
196
+ watts,
197
+ }))
198
+ : undefined,
199
+ };
200
+ }
201
+ //# sourceMappingURL=activity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activity.js","sourceRoot":"","sources":["../../src/strava/activity.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,0EAA0E;AAC1E,8BAA8B;AAG9B,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAQpD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,iBAAiB,CAC/B,QAA4C,EAC5C,IAA4C;IAE5C,MAAM,OAAO,GAAG,IAAI,EAAE,OAAO,CAAC;IAC9B,MAAM,IAAI,GAAG,IAAI,EAAE,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC9E,MAAM,UAAU,GAAG,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC;IACvC,MAAM,SAAS,GAAG,QAAQ,CAAC,UAAU,CAAC;IAEtC,OAAO;QACL,QAAQ,EAAE;YACR,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC/B,UAAU,EAAE,QAAQ,CAAC,UAAU;YAC/B,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,YAAY,CAAC;YACpE,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC,UAAU,CAAC;YACvC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,wBAAwB;YAC9D,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,IAAI,EAAE,QAAQ,CAAC,aAAa,IAAI,SAAS;YACzC,KAAK,EAAE,QAAQ,CAAC,cAAc,IAAI,SAAS;YAC3C,OAAO,EAAE,QAAQ,CAAC,gBAAgB,IAAI,SAAS;SAChD;QAED,qBAAqB,EAAE;YACrB,gBAAgB,EAAE,QAAQ,CAAC,WAAW;SACvC;QAED,aAAa,EAAE,UAAU,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,QAAQ,IAAI,IAAI;YAC9D,CAAC,CAAC,EAAE,qBAAqB,EAAE,QAAQ,CAAC,QAAQ,EAAE;YAC9C,CAAC,CAAC,SAAS;QAEb,WAAW,EAAE,QAAQ,CAAC,WAAW;YAC/B,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,WAAW,EAAE;YAChC,CAAC,CAAC,SAAS;QAEb,aAAa,EAAE,iBAAiB,CAAC,QAAQ,CAAC;QAE1C,WAAW,EAAE,QAAQ,CAAC,UAAU,IAAI,IAAI;YACtC,CAAC,CAAC,EAAE,iBAAiB,EAAE,QAAQ,CAAC,UAAU,EAAE;YAC5C,CAAC,CAAC,SAAS;QAEb,eAAe,EAAE,kBAAkB,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,CAAC;QAE7E,QAAQ,EAAE,YAAY,CAAC,IAAI,CAAC;QAE5B,aAAa,EAAE,iBAAiB,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,CAAC;QAE1E,iBAAiB,EAAE,QAAQ,CAAC,GAAG,EAAE,gBAAgB;YAC/C,CAAC,CAAC,EAAE,gBAAgB,EAAE,QAAQ,CAAC,GAAG,CAAC,gBAAgB,EAAE;YACrD,CAAC,CAAC,SAAS;QAEb,aAAa,EAAE,iBAAiB,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,CAAC;QAE1E,UAAU,EAAE,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,CAAC;KACrE,CAAC;AACJ,CAAC;AAED,gFAAgF;AAEhF,SAAS,UAAU,CACjB,QAA4C;IAE5C,OAAO,QAAQ,CAAC,cAAc,KAAK,CAAC,CAAC;AACvC,CAAC;AAED,SAAS,cAAc,CAAC,SAAiB,EAAE,kBAA0B;IACnE,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC;IAClC,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,kBAAkB,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;AAC7E,CAAC;AAED;;;GAGG;AACH,SAAS,eAAe,CACtB,SAAiB,EACjB,iBAAyB;IAEzB,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC;IAClC,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,iBAAiB,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;AAC5E,CAAC;AAED,SAAS,iBAAiB,CAAC,QAA4C;IACrE,IAAI,QAAQ,CAAC,QAAQ,IAAI,IAAI,IAAI,QAAQ,CAAC,oBAAoB,IAAI,IAAI,EAAE,CAAC;QACvE,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IAE7D,OAAO;QACL,OAAO,EAAE;YACP,eAAe,EAAE,QAAQ,CAAC,QAAQ,IAAI,SAAS;YAC/C,SAAS,EACP,QAAQ,CAAC,oBAAoB,IAAI,IAAI;gBACnC,CAAC,CAAC;oBACA,kBAAkB,EAAE,QAAQ,CAAC,oBAAoB;oBACjD,UAAU,EAAE,QAAQ,EAAE,SAAS,IAAI,SAAS;oBAC5C,UAAU,EAAE,QAAQ,EAAE,QAAQ,IAAI,SAAS;iBAC5C;gBACD,CAAC,CAAC,SAAS;SAChB;KACF,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB,CACzB,QAA4C,EAC5C,OAA8B,EAC9B,UAAgC,EAChC,SAAiB;IAEjB,MAAM,YAAY,GAChB,QAAQ,CAAC,iBAAiB,IAAI,IAAI,IAAI,QAAQ,CAAC,aAAa,IAAI,IAAI,CAAC;IACvE,MAAM,QAAQ,GAAG,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC;IAC1C,MAAM,WAAW,GAAG,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC;IAElE,IAAI,CAAC,YAAY,IAAI,CAAC,WAAW;QAAE,OAAO,SAAS,CAAC;IAEpD,OAAO;QACL,OAAO,EAAE,YAAY;YACnB,CAAC,CAAC;gBACA,UAAU,EAAE,QAAQ,CAAC,iBAAiB;gBACtC,UAAU,EAAE,QAAQ,CAAC,aAAa;aACnC;YACD,CAAC,CAAC,SAAS;QACb,QAAQ,EACN,WAAW,IAAI,UAAU;YACvB,CAAC,CAAC;gBACA,UAAU,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;oBACpC,SAAS,EAAE,eAAe,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;oBACpD,GAAG;iBACJ,CAAC,CAAC;aACJ;YACD,CAAC,CAAC,SAAS;KAChB,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,IAAuB;IAC3C,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAEjD,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YACvB,UAAU,EAAE,GAAG,CAAC,UAAU;YAC1B,QAAQ,EAAE,cAAc,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,YAAY,CAAC;YAC1D,eAAe,EAAE,GAAG,CAAC,QAAQ;YAC7B,QAAQ,EAAE,SAA+B;YACzC,2BAA2B,EAAE,GAAG,CAAC,aAAa;YAC9C,UAAU,EAAE,GAAG,CAAC,iBAAiB;SAClC,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CACxB,QAA4C,EAC5C,OAA8B,EAC9B,UAAgC,EAChC,SAAiB;IAEjB,MAAM,WAAW,GACf,QAAQ,CAAC,aAAa,IAAI,IAAI;QAC9B,QAAQ,CAAC,SAAS,IAAI,IAAI;QAC1B,QAAQ,CAAC,eAAe,IAAI,IAAI,CAAC;IACnC,MAAM,WAAW,GAAG,OAAO,EAAE,eAAe,EAAE,IAAI,CAAC;IACnD,MAAM,aAAa,GAAG,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;IAC7C,MAAM,UAAU,GACd,CAAC,CAAC,WAAW,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC;QACtC,CAAC,aAAa,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC9C,UAAU,CAAC;IAEb,IAAI,CAAC,WAAW,IAAI,CAAC,UAAU;QAAE,OAAO,SAAS,CAAC;IAElD,OAAO;QACL,2BAA2B,EAAE,QAAQ,CAAC,aAAa,IAAI,SAAS;QAChE,2BAA2B,EAAE,QAAQ,CAAC,SAAS,IAAI,SAAS;QAC5D,eAAe,EAAE,QAAQ,CAAC,eAAe,IAAI,SAAS;QACtD,aAAa,EACX,WAAW,IAAI,UAAU;YACvB,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC/B,SAAS,EAAE,eAAe,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;gBACpD,uBAAuB,EAAE,KAAK;aAC/B,CAAC,CAAC;YACH,CAAC,CAAC,SAAS;QACf,eAAe,EACb,aAAa,IAAI,UAAU;YACzB,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;gBACnC,SAAS,EAAE,eAAe,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;gBACpD,WAAW,EAAE,OAAO;aACrB,CAAC,CAAC;YACH,CAAC,CAAC,SAAS;KAChB,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CACxB,QAA4C,EAC5C,OAA8B,EAC9B,UAAgC,EAChC,SAAiB;IAEjB,MAAM,YAAY,GAAG,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC;IAC3C,MAAM,iBAAiB,GAAG,YAAY,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC;IAChF,MAAM,WAAW,GACf,QAAQ,CAAC,YAAY,IAAI,IAAI,IAAI,QAAQ,CAAC,UAAU,IAAI,IAAI,CAAC;IAE/D,IAAI,CAAC,iBAAiB,IAAI,CAAC,WAAW;QAAE,OAAO,SAAS,CAAC;IAEzD,OAAO;QACL,qBAAqB,EAAE,QAAQ,CAAC,YAAY,IAAI,SAAS;QACzD,mBAAmB,EAAE,QAAQ,CAAC,UAAU,IAAI,SAAS;QACrD,gBAAgB,EACd,iBAAiB,IAAI,UAAU;YAC7B,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;gBACjC,SAAS,EAAE,eAAe,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;gBACpD,kBAAkB,EAAE,MAAM;aAC3B,CAAC,CAAC;YACH,CAAC,CAAC,SAAS;KAChB,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CACrB,QAA4C,EAC5C,OAA8B,EAC9B,UAAgC,EAChC,SAAiB;IAEjB,MAAM,eAAe,GACnB,QAAQ,CAAC,aAAa,IAAI,IAAI,IAAI,QAAQ,CAAC,SAAS,IAAI,IAAI,CAAC;IAC/D,MAAM,WAAW,GAAG,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC;IACzC,MAAM,cAAc,GAAG,WAAW,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC;IAE3E,IAAI,CAAC,eAAe,IAAI,CAAC,cAAc;QAAE,OAAO,SAAS,CAAC;IAE1D,OAAO;QACL,SAAS,EAAE,QAAQ,CAAC,aAAa,IAAI,SAAS;QAC9C,SAAS,EAAE,QAAQ,CAAC,SAAS,IAAI,SAAS;QAC1C,aAAa,EACX,cAAc,IAAI,UAAU;YAC1B,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC/B,SAAS,EAAE,eAAe,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;gBACpD,KAAK;aACN,CAAC,CAAC;YACH,CAAC,CAAC,SAAS;KAChB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,34 @@
1
+ import type { DetailedAthlete } from "./types.js";
2
+ /**
3
+ * The output shape of {@link transformAthlete}.
4
+ */
5
+ export type AthleteData = ReturnType<typeof transformAthlete>;
6
+ /**
7
+ * Transform a Strava athlete profile into a Soma Athlete document shape.
8
+ *
9
+ * Strava provides a relatively rich profile compared to HealthKit, including
10
+ * name, location, sex, and the date the athlete joined Strava.
11
+ *
12
+ * @param athlete - The Strava DetailedAthlete from `GET /athlete`
13
+ * @returns Soma Athlete fields (without connectionId/userId)
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const data = transformAthlete(stravaAthlete);
18
+ * await soma.ingestAthlete(ctx, { connectionId, userId, ...data });
19
+ * ```
20
+ */
21
+ export declare function transformAthlete(athlete: DetailedAthlete): {
22
+ first_name: string;
23
+ last_name: string;
24
+ city: string | undefined;
25
+ state: string | undefined;
26
+ country: string | undefined;
27
+ sex: string | undefined;
28
+ joined_provider: string;
29
+ devices: {
30
+ name: string;
31
+ id: string;
32
+ }[] | undefined;
33
+ };
34
+ //# sourceMappingURL=athlete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"athlete.d.ts","sourceRoot":"","sources":["../../src/strava/athlete.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE9D;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,eAAe;;;;;;;;;;;;EAqBxD"}
@@ -0,0 +1,39 @@
1
+ // ─── Athlete Transformer ─────────────────────────────────────────────────────
2
+ // Transforms a Strava DetailedAthlete into the Soma Athlete schema shape.
3
+ /**
4
+ * Transform a Strava athlete profile into a Soma Athlete document shape.
5
+ *
6
+ * Strava provides a relatively rich profile compared to HealthKit, including
7
+ * name, location, sex, and the date the athlete joined Strava.
8
+ *
9
+ * @param athlete - The Strava DetailedAthlete from `GET /athlete`
10
+ * @returns Soma Athlete fields (without connectionId/userId)
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const data = transformAthlete(stravaAthlete);
15
+ * await soma.ingestAthlete(ctx, { connectionId, userId, ...data });
16
+ * ```
17
+ */
18
+ export function transformAthlete(athlete) {
19
+ const sexMap = {
20
+ M: "male",
21
+ F: "female",
22
+ };
23
+ return {
24
+ first_name: athlete.firstname ?? undefined,
25
+ last_name: athlete.lastname ?? undefined,
26
+ city: athlete.city ?? undefined,
27
+ state: athlete.state ?? undefined,
28
+ country: athlete.country ?? undefined,
29
+ sex: athlete.sex ? sexMap[athlete.sex] : undefined,
30
+ joined_provider: athlete.created_at ?? undefined,
31
+ devices: athlete.bikes && athlete.shoes
32
+ ? [
33
+ ...athlete.bikes.map((b) => ({ name: b.name, id: b.id })),
34
+ ...athlete.shoes.map((s) => ({ name: s.name, id: s.id })),
35
+ ]
36
+ : undefined,
37
+ };
38
+ }
39
+ //# sourceMappingURL=athlete.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"athlete.js","sourceRoot":"","sources":["../../src/strava/athlete.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,0EAA0E;AAS1E;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAwB;IACvD,MAAM,MAAM,GAA2B;QACrC,CAAC,EAAE,MAAM;QACT,CAAC,EAAE,QAAQ;KACZ,CAAC;IAEF,OAAO;QACL,UAAU,EAAE,OAAO,CAAC,SAAS,IAAI,SAAS;QAC1C,SAAS,EAAE,OAAO,CAAC,QAAQ,IAAI,SAAS;QACxC,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,SAAS;QAC/B,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,SAAS;QACjC,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,SAAS;QACrC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS;QAClD,eAAe,EAAE,OAAO,CAAC,UAAU,IAAI,SAAS;QAChD,OAAO,EAAE,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK;YACrC,CAAC,CAAC;gBACA,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACzD,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;aAC1D;YACD,CAAC,CAAC,SAAS;KACd,CAAC;AACJ,CAAC"}
@@ -0,0 +1,103 @@
1
+ import type { OAuthTokenResponse } from "./types.js";
2
+ export interface BuildAuthUrlOptions {
3
+ /** Your Strava application's Client ID. */
4
+ clientId: string;
5
+ /** The URL Strava will redirect to after authorization. */
6
+ redirectUri: string;
7
+ /**
8
+ * Comma-separated Strava OAuth scopes.
9
+ * @default "read,activity:read_all,profile:read_all"
10
+ */
11
+ scope?: string;
12
+ /** Optional state parameter for CSRF protection. */
13
+ state?: string;
14
+ /**
15
+ * Base URL of the Strava site.
16
+ * @default "https://www.strava.com"
17
+ */
18
+ baseUrl?: string;
19
+ }
20
+ /**
21
+ * Build the Strava OAuth authorization URL.
22
+ *
23
+ * Redirect the user to this URL to begin the OAuth flow. After the user
24
+ * grants access, Strava will redirect back to `redirectUri` with a `code`
25
+ * query parameter.
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * const url = buildAuthUrl({
30
+ * clientId: process.env.STRAVA_CLIENT_ID!,
31
+ * redirectUri: "https://your-app.com/api/strava/callback",
32
+ * });
33
+ * // Redirect user to `url`
34
+ * ```
35
+ */
36
+ export declare function buildAuthUrl(opts: BuildAuthUrlOptions): string;
37
+ export interface ExchangeCodeOptions {
38
+ /** Your Strava application's Client ID. */
39
+ clientId: string;
40
+ /** Your Strava application's Client Secret. */
41
+ clientSecret: string;
42
+ /** The authorization code from the OAuth callback. */
43
+ code: string;
44
+ /**
45
+ * Base URL of the Strava site.
46
+ * @default "https://www.strava.com"
47
+ */
48
+ baseUrl?: string;
49
+ }
50
+ /**
51
+ * Exchange an authorization code for access and refresh tokens.
52
+ *
53
+ * Call this from your OAuth callback endpoint after receiving the `code`
54
+ * query parameter from Strava.
55
+ *
56
+ * @returns The token response including `access_token`, `refresh_token`,
57
+ * `expires_at`, and the authenticated `athlete` profile.
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * const tokens = await exchangeCode({
62
+ * clientId: process.env.STRAVA_CLIENT_ID!,
63
+ * clientSecret: process.env.STRAVA_CLIENT_SECRET!,
64
+ * code: request.query.code,
65
+ * });
66
+ * // Store tokens.access_token, tokens.refresh_token, tokens.expires_at
67
+ * ```
68
+ */
69
+ export declare function exchangeCode(opts: ExchangeCodeOptions): Promise<OAuthTokenResponse>;
70
+ export interface RefreshTokenOptions {
71
+ /** Your Strava application's Client ID. */
72
+ clientId: string;
73
+ /** Your Strava application's Client Secret. */
74
+ clientSecret: string;
75
+ /** The refresh token from a previous token exchange or refresh. */
76
+ refreshToken: string;
77
+ /**
78
+ * Base URL of the Strava site.
79
+ * @default "https://www.strava.com"
80
+ */
81
+ baseUrl?: string;
82
+ }
83
+ /**
84
+ * Refresh an expired access token using a refresh token.
85
+ *
86
+ * Strava access tokens expire after ~6 hours. Call this when the
87
+ * `expires_at` timestamp has passed to obtain a fresh access token.
88
+ *
89
+ * @returns A new token response with a fresh `access_token` and
90
+ * possibly a new `refresh_token`.
91
+ *
92
+ * @example
93
+ * ```ts
94
+ * const tokens = await refreshToken({
95
+ * clientId: process.env.STRAVA_CLIENT_ID!,
96
+ * clientSecret: process.env.STRAVA_CLIENT_SECRET!,
97
+ * refreshToken: storedRefreshToken,
98
+ * });
99
+ * // Update stored tokens
100
+ * ```
101
+ */
102
+ export declare function refreshToken(opts: RefreshTokenOptions): Promise<OAuthTokenResponse>;
103
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/strava/auth.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAMrD,MAAM,WAAW,mBAAmB;IAClC,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAe9D;AAID,MAAM,WAAW,mBAAmB;IAClC,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,YAAY,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,kBAAkB,CAAC,CAuB7B;AAID,MAAM,WAAW,mBAAmB;IAClC,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,YAAY,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,YAAY,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,kBAAkB,CAAC,CAuB7B"}
@@ -0,0 +1,111 @@
1
+ // ─── Strava OAuth Helpers ────────────────────────────────────────────────────
2
+ // Pure helper functions for the Strava OAuth 2.0 Authorization Code flow.
3
+ // No external dependencies — uses the global `fetch`.
4
+ const DEFAULT_BASE_URL = "https://www.strava.com";
5
+ /**
6
+ * Build the Strava OAuth authorization URL.
7
+ *
8
+ * Redirect the user to this URL to begin the OAuth flow. After the user
9
+ * grants access, Strava will redirect back to `redirectUri` with a `code`
10
+ * query parameter.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const url = buildAuthUrl({
15
+ * clientId: process.env.STRAVA_CLIENT_ID!,
16
+ * redirectUri: "https://your-app.com/api/strava/callback",
17
+ * });
18
+ * // Redirect user to `url`
19
+ * ```
20
+ */
21
+ export function buildAuthUrl(opts) {
22
+ const base = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
23
+ const params = new URLSearchParams({
24
+ client_id: opts.clientId,
25
+ redirect_uri: opts.redirectUri,
26
+ response_type: "code",
27
+ approval_prompt: "auto",
28
+ scope: opts.scope ?? "read,activity:read_all,profile:read_all",
29
+ });
30
+ if (opts.state) {
31
+ params.set("state", opts.state);
32
+ }
33
+ return `${base}/oauth/authorize?${params.toString()}`;
34
+ }
35
+ /**
36
+ * Exchange an authorization code for access and refresh tokens.
37
+ *
38
+ * Call this from your OAuth callback endpoint after receiving the `code`
39
+ * query parameter from Strava.
40
+ *
41
+ * @returns The token response including `access_token`, `refresh_token`,
42
+ * `expires_at`, and the authenticated `athlete` profile.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * const tokens = await exchangeCode({
47
+ * clientId: process.env.STRAVA_CLIENT_ID!,
48
+ * clientSecret: process.env.STRAVA_CLIENT_SECRET!,
49
+ * code: request.query.code,
50
+ * });
51
+ * // Store tokens.access_token, tokens.refresh_token, tokens.expires_at
52
+ * ```
53
+ */
54
+ export async function exchangeCode(opts) {
55
+ const base = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
56
+ const url = `${base}/oauth/token`;
57
+ const response = await fetch(url, {
58
+ method: "POST",
59
+ headers: { "Content-Type": "application/json" },
60
+ body: JSON.stringify({
61
+ client_id: opts.clientId,
62
+ client_secret: opts.clientSecret,
63
+ code: opts.code,
64
+ grant_type: "authorization_code",
65
+ }),
66
+ });
67
+ if (!response.ok) {
68
+ const body = await response.text().catch(() => "");
69
+ throw new Error(`Strava OAuth error (exchangeCode): ${response.status} ${response.statusText} — ${body}`);
70
+ }
71
+ return (await response.json());
72
+ }
73
+ /**
74
+ * Refresh an expired access token using a refresh token.
75
+ *
76
+ * Strava access tokens expire after ~6 hours. Call this when the
77
+ * `expires_at` timestamp has passed to obtain a fresh access token.
78
+ *
79
+ * @returns A new token response with a fresh `access_token` and
80
+ * possibly a new `refresh_token`.
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * const tokens = await refreshToken({
85
+ * clientId: process.env.STRAVA_CLIENT_ID!,
86
+ * clientSecret: process.env.STRAVA_CLIENT_SECRET!,
87
+ * refreshToken: storedRefreshToken,
88
+ * });
89
+ * // Update stored tokens
90
+ * ```
91
+ */
92
+ export async function refreshToken(opts) {
93
+ const base = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
94
+ const url = `${base}/oauth/token`;
95
+ const response = await fetch(url, {
96
+ method: "POST",
97
+ headers: { "Content-Type": "application/json" },
98
+ body: JSON.stringify({
99
+ client_id: opts.clientId,
100
+ client_secret: opts.clientSecret,
101
+ refresh_token: opts.refreshToken,
102
+ grant_type: "refresh_token",
103
+ }),
104
+ });
105
+ if (!response.ok) {
106
+ const body = await response.text().catch(() => "");
107
+ throw new Error(`Strava OAuth error (refreshToken): ${response.status} ${response.statusText} — ${body}`);
108
+ }
109
+ return (await response.json());
110
+ }
111
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/strava/auth.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,0EAA0E;AAC1E,sDAAsD;AAItD,MAAM,gBAAgB,GAAG,wBAAwB,CAAC;AAuBlD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,YAAY,CAAC,IAAyB;IACpD,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,gBAAgB,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACpE,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;QACjC,SAAS,EAAE,IAAI,CAAC,QAAQ;QACxB,YAAY,EAAE,IAAI,CAAC,WAAW;QAC9B,aAAa,EAAE,MAAM;QACrB,eAAe,EAAE,MAAM;QACvB,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,yCAAyC;KAC/D,CAAC,CAAC;IAEH,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;IAED,OAAO,GAAG,IAAI,oBAAoB,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;AACxD,CAAC;AAkBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,IAAyB;IAEzB,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,gBAAgB,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACpE,MAAM,GAAG,GAAG,GAAG,IAAI,cAAc,CAAC;IAElC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAChC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,SAAS,EAAE,IAAI,CAAC,QAAQ;YACxB,aAAa,EAAE,IAAI,CAAC,YAAY;YAChC,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,UAAU,EAAE,oBAAoB;SACjC,CAAC;KACH,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACnD,MAAM,IAAI,KAAK,CACb,sCAAsC,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,MAAM,IAAI,EAAE,CACzF,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAuB,CAAC;AACvD,CAAC;AAkBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,IAAyB;IAEzB,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,gBAAgB,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACpE,MAAM,GAAG,GAAG,GAAG,IAAI,cAAc,CAAC;IAElC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAChC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,SAAS,EAAE,IAAI,CAAC,QAAQ;YACxB,aAAa,EAAE,IAAI,CAAC,YAAY;YAChC,aAAa,EAAE,IAAI,CAAC,YAAY;YAChC,UAAU,EAAE,eAAe;SAC5B,CAAC;KACH,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACnD,MAAM,IAAI,KAAK,CACb,sCAAsC,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,MAAM,IAAI,EAAE,CACzF,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAuB,CAAC;AACvD,CAAC"}
@@ -0,0 +1,93 @@
1
+ import type { DetailedActivity, DetailedAthlete, Lap, StreamSet, SummaryActivity } from "./types.js";
2
+ export interface StravaClientOptions {
3
+ /** OAuth access token for the authenticated athlete. */
4
+ accessToken: string;
5
+ /**
6
+ * Base URL of the Strava API (without `/api/v3` suffix).
7
+ * Defaults to `https://www.strava.com`.
8
+ * Override to point at a mock server during development.
9
+ */
10
+ baseUrl?: string;
11
+ }
12
+ /**
13
+ * A lightweight client for the Strava API v3.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const client = new StravaClient({
18
+ * accessToken: "tok_xxx",
19
+ * baseUrl: "https://strava-mock-server.onrender.com", // optional
20
+ * });
21
+ *
22
+ * const athlete = await client.getAthlete();
23
+ * const activities = await client.listActivities({ per_page: 50 });
24
+ * ```
25
+ */
26
+ export declare class StravaClient {
27
+ private readonly accessToken;
28
+ private readonly baseUrl;
29
+ constructor(opts: StravaClientOptions);
30
+ /**
31
+ * Get the authenticated athlete's profile.
32
+ *
33
+ * Strava API: `GET /athlete`
34
+ */
35
+ getAthlete(): Promise<DetailedAthlete>;
36
+ /**
37
+ * List the authenticated athlete's activities.
38
+ *
39
+ * Strava API: `GET /athlete/activities`
40
+ *
41
+ * @param params.before - Only return activities before this Unix epoch timestamp
42
+ * @param params.after - Only return activities after this Unix epoch timestamp
43
+ * @param params.page - Page number (defaults to 1)
44
+ * @param params.per_page - Items per page (defaults to 30, max 200)
45
+ */
46
+ listActivities(params?: {
47
+ before?: number;
48
+ after?: number;
49
+ page?: number;
50
+ per_page?: number;
51
+ }): Promise<SummaryActivity[]>;
52
+ /**
53
+ * List ALL activities for the authenticated athlete, automatically
54
+ * paginating through all pages.
55
+ *
56
+ * @param params.after - Only return activities after this Unix epoch timestamp
57
+ * @param params.before - Only return activities before this Unix epoch timestamp
58
+ * @param params.per_page - Items per page (defaults to 200)
59
+ */
60
+ listAllActivities(params?: {
61
+ after?: number;
62
+ before?: number;
63
+ per_page?: number;
64
+ }): Promise<SummaryActivity[]>;
65
+ /**
66
+ * Get a detailed activity by ID.
67
+ *
68
+ * Strava API: `GET /activities/{id}`
69
+ */
70
+ getActivity(id: number): Promise<DetailedActivity>;
71
+ /**
72
+ * Get time-series streams for an activity.
73
+ *
74
+ * Strava API: `GET /activities/{id}/streams`
75
+ *
76
+ * @param id - Activity ID
77
+ * @param keys - Stream types to request (e.g. `["heartrate", "watts", "latlng", "altitude", "time"]`)
78
+ */
79
+ getActivityStreams(id: number, keys?: string[]): Promise<StreamSet>;
80
+ /**
81
+ * Get laps for an activity.
82
+ *
83
+ * Strava API: `GET /activities/{id}/laps`
84
+ */
85
+ getActivityLaps(id: number): Promise<Lap[]>;
86
+ private get;
87
+ }
88
+ export declare class StravaApiError extends Error {
89
+ readonly status: number;
90
+ readonly body: string;
91
+ constructor(message: string, status: number, body: string);
92
+ }
93
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/strava/client.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,GAAG,EACH,SAAS,EAET,eAAe,EAChB,MAAM,YAAY,CAAC;AAKpB,MAAM,WAAW,mBAAmB;IAClC,wDAAwD;IACxD,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,IAAI,EAAE,mBAAmB;IAOrC;;;;OAIG;IACG,UAAU,IAAI,OAAO,CAAC,eAAe,CAAC;IAM5C;;;;;;;;;OASG;IACG,cAAc,CAAC,MAAM,CAAC,EAAE;QAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAa9B;;;;;;;OAOG;IACG,iBAAiB,CAAC,MAAM,CAAC,EAAE;QAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAoB9B;;;;OAIG;IACG,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAIxD;;;;;;;OAOG;IACG,kBAAkB,CACtB,EAAE,EAAE,MAAM,EACV,IAAI,GAAE,MAAM,EAWX,GACA,OAAO,CAAC,SAAS,CAAC;IAWrB;;;;OAIG;IACG,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;YAMnC,GAAG;CAqBlB;AAID,qBAAa,cAAe,SAAQ,KAAK;aAGrB,MAAM,EAAE,MAAM;aACd,IAAI,EAAE,MAAM;gBAF5B,OAAO,EAAE,MAAM,EACC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM;CAK/B"}