@nativesquare/soma 0.9.3 → 0.10.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 (202) hide show
  1. package/dist/client/index.d.ts +96 -33
  2. package/dist/client/index.d.ts.map +1 -1
  3. package/dist/client/index.js +80 -35
  4. package/dist/client/index.js.map +1 -1
  5. package/dist/component/_generated/api.d.ts +18 -0
  6. package/dist/component/_generated/api.d.ts.map +1 -1
  7. package/dist/component/_generated/api.js.map +1 -1
  8. package/dist/component/_generated/component.d.ts +43 -9
  9. package/dist/component/_generated/component.d.ts.map +1 -1
  10. package/dist/component/garmin/auth.d.ts +0 -4
  11. package/dist/component/garmin/auth.d.ts.map +1 -1
  12. package/dist/component/garmin/auth.js +0 -8
  13. package/dist/component/garmin/auth.js.map +1 -1
  14. package/dist/component/garmin/private.d.ts +20 -3
  15. package/dist/component/garmin/private.d.ts.map +1 -1
  16. package/dist/component/garmin/private.js +17 -26
  17. package/dist/component/garmin/private.js.map +1 -1
  18. package/dist/component/garmin/public.d.ts +4 -4
  19. package/dist/component/garmin/public.d.ts.map +1 -1
  20. package/dist/component/garmin/public.js +6 -1
  21. package/dist/component/garmin/public.js.map +1 -1
  22. package/dist/component/garmin/webhooks.d.ts +4 -0
  23. package/dist/component/garmin/webhooks.d.ts.map +1 -1
  24. package/dist/component/garmin/webhooks.js +23 -18
  25. package/dist/component/garmin/webhooks.js.map +1 -1
  26. package/dist/component/schema.d.ts +2 -2
  27. package/dist/component/schema.d.ts.map +1 -1
  28. package/dist/component/schema.js +5 -3
  29. package/dist/component/schema.js.map +1 -1
  30. package/dist/{strava → component/strava}/auth.d.ts +15 -48
  31. package/dist/component/strava/auth.d.ts.map +1 -0
  32. package/dist/{strava → component/strava}/auth.js +4 -39
  33. package/dist/component/strava/auth.js.map +1 -0
  34. package/dist/component/strava/client.d.ts +8 -0
  35. package/dist/component/strava/client.d.ts.map +1 -0
  36. package/dist/component/strava/client.js +18 -0
  37. package/dist/component/strava/client.js.map +1 -0
  38. package/dist/component/strava/private.d.ts +19 -0
  39. package/dist/component/strava/private.d.ts.map +1 -1
  40. package/dist/component/strava/private.js +52 -2
  41. package/dist/component/strava/private.js.map +1 -1
  42. package/dist/component/strava/public.d.ts +87 -12
  43. package/dist/component/strava/public.d.ts.map +1 -1
  44. package/dist/component/strava/public.js +218 -92
  45. package/dist/component/strava/public.js.map +1 -1
  46. package/dist/component/strava/transform/activity.d.ts +19 -0
  47. package/dist/component/strava/transform/activity.d.ts.map +1 -0
  48. package/dist/{strava → component/strava/transform}/activity.js +21 -41
  49. package/dist/component/strava/transform/activity.js.map +1 -0
  50. package/dist/{strava → component/strava/transform}/athlete.d.ts +4 -10
  51. package/dist/component/strava/transform/athlete.d.ts.map +1 -0
  52. package/dist/{strava → component/strava/transform}/athlete.js +2 -8
  53. package/dist/component/strava/transform/athlete.js.map +1 -0
  54. package/dist/component/strava/transform/maps/sportType.d.ts +7 -0
  55. package/dist/component/strava/transform/maps/sportType.d.ts.map +1 -0
  56. package/dist/{strava/maps/sport-type.js → component/strava/transform/maps/sportType.js} +4 -2
  57. package/dist/component/strava/transform/maps/sportType.js.map +1 -0
  58. package/dist/component/strava/types/stravaApi/client/client.gen.d.ts +3 -0
  59. package/dist/component/strava/types/stravaApi/client/client.gen.d.ts.map +1 -0
  60. package/dist/component/strava/types/stravaApi/client/client.gen.js +236 -0
  61. package/dist/component/strava/types/stravaApi/client/client.gen.js.map +1 -0
  62. package/dist/component/strava/types/stravaApi/client/index.d.ts +9 -0
  63. package/dist/component/strava/types/stravaApi/client/index.d.ts.map +1 -0
  64. package/dist/component/strava/types/stravaApi/client/index.js +7 -0
  65. package/dist/component/strava/types/stravaApi/client/index.js.map +1 -0
  66. package/dist/component/strava/types/stravaApi/client/types.gen.d.ts +118 -0
  67. package/dist/component/strava/types/stravaApi/client/types.gen.d.ts.map +1 -0
  68. package/dist/component/strava/types/stravaApi/client/types.gen.js +3 -0
  69. package/dist/component/strava/types/stravaApi/client/types.gen.js.map +1 -0
  70. package/dist/component/strava/types/stravaApi/client/utils.gen.d.ts +34 -0
  71. package/dist/component/strava/types/stravaApi/client/utils.gen.d.ts.map +1 -0
  72. package/dist/component/strava/types/stravaApi/client/utils.gen.js +229 -0
  73. package/dist/component/strava/types/stravaApi/client/utils.gen.js.map +1 -0
  74. package/dist/component/strava/types/stravaApi/client.gen.d.ts +13 -0
  75. package/dist/component/strava/types/stravaApi/client.gen.d.ts.map +1 -0
  76. package/dist/component/strava/types/stravaApi/client.gen.js +4 -0
  77. package/dist/component/strava/types/stravaApi/client.gen.js.map +1 -0
  78. package/dist/component/strava/types/stravaApi/core/auth.gen.d.ts +19 -0
  79. package/dist/component/strava/types/stravaApi/core/auth.gen.d.ts.map +1 -0
  80. package/dist/component/strava/types/stravaApi/core/auth.gen.js +15 -0
  81. package/dist/component/strava/types/stravaApi/core/auth.gen.js.map +1 -0
  82. package/dist/component/strava/types/stravaApi/core/bodySerializer.gen.d.ts +26 -0
  83. package/dist/component/strava/types/stravaApi/core/bodySerializer.gen.d.ts.map +1 -0
  84. package/dist/component/strava/types/stravaApi/core/bodySerializer.gen.js +58 -0
  85. package/dist/component/strava/types/stravaApi/core/bodySerializer.gen.js.map +1 -0
  86. package/dist/component/strava/types/stravaApi/core/params.gen.d.ts +44 -0
  87. package/dist/component/strava/types/stravaApi/core/params.gen.d.ts.map +1 -0
  88. package/dist/component/strava/types/stravaApi/core/params.gen.js +101 -0
  89. package/dist/component/strava/types/stravaApi/core/params.gen.js.map +1 -0
  90. package/dist/component/strava/types/stravaApi/core/pathSerializer.gen.d.ts +34 -0
  91. package/dist/component/strava/types/stravaApi/core/pathSerializer.gen.d.ts.map +1 -0
  92. package/dist/component/strava/types/stravaApi/core/pathSerializer.gen.js +107 -0
  93. package/dist/component/strava/types/stravaApi/core/pathSerializer.gen.js.map +1 -0
  94. package/dist/component/strava/types/stravaApi/core/queryKeySerializer.gen.d.ts +19 -0
  95. package/dist/component/strava/types/stravaApi/core/queryKeySerializer.gen.d.ts.map +1 -0
  96. package/dist/component/strava/types/stravaApi/core/queryKeySerializer.gen.js +93 -0
  97. package/dist/component/strava/types/stravaApi/core/queryKeySerializer.gen.js.map +1 -0
  98. package/dist/component/strava/types/stravaApi/core/serverSentEvents.gen.d.ts +72 -0
  99. package/dist/component/strava/types/stravaApi/core/serverSentEvents.gen.d.ts.map +1 -0
  100. package/dist/component/strava/types/stravaApi/core/serverSentEvents.gen.js +134 -0
  101. package/dist/component/strava/types/stravaApi/core/serverSentEvents.gen.js.map +1 -0
  102. package/dist/component/strava/types/stravaApi/core/types.gen.d.ts +79 -0
  103. package/dist/component/strava/types/stravaApi/core/types.gen.d.ts.map +1 -0
  104. package/dist/component/strava/types/stravaApi/core/types.gen.js +3 -0
  105. package/dist/component/strava/types/stravaApi/core/types.gen.js.map +1 -0
  106. package/dist/component/strava/types/stravaApi/core/utils.gen.d.ts +20 -0
  107. package/dist/component/strava/types/stravaApi/core/utils.gen.d.ts.map +1 -0
  108. package/dist/component/strava/types/stravaApi/core/utils.gen.js +88 -0
  109. package/dist/component/strava/types/stravaApi/core/utils.gen.js.map +1 -0
  110. package/dist/component/strava/types/stravaApi/index.d.ts +3 -0
  111. package/dist/component/strava/types/stravaApi/index.d.ts.map +1 -0
  112. package/dist/component/strava/types/stravaApi/index.js +3 -0
  113. package/dist/component/strava/types/stravaApi/index.js.map +1 -0
  114. package/dist/component/strava/types/stravaApi/sdk.gen.d.ts +224 -0
  115. package/dist/component/strava/types/stravaApi/sdk.gen.d.ts.map +1 -0
  116. package/dist/component/strava/types/stravaApi/sdk.gen.js +361 -0
  117. package/dist/component/strava/types/stravaApi/sdk.gen.js.map +1 -0
  118. package/dist/component/strava/types/stravaApi/types.gen.d.ts +2209 -0
  119. package/dist/component/strava/types/stravaApi/types.gen.d.ts.map +1 -0
  120. package/dist/component/strava/types/stravaApi/types.gen.js +3 -0
  121. package/dist/component/strava/types/stravaApi/types.gen.js.map +1 -0
  122. package/dist/component/strava/types/stravaApi/zod.gen.d.ts +5332 -0
  123. package/dist/component/strava/types/stravaApi/zod.gen.d.ts.map +1 -0
  124. package/dist/component/strava/types/stravaApi/zod.gen.js +1009 -0
  125. package/dist/component/strava/types/stravaApi/zod.gen.js.map +1 -0
  126. package/dist/component/strava/utils.d.ts +15 -0
  127. package/dist/component/strava/utils.d.ts.map +1 -0
  128. package/dist/component/strava/utils.js +36 -0
  129. package/dist/component/strava/utils.js.map +1 -0
  130. package/dist/component/utils.d.ts +5 -0
  131. package/dist/component/utils.d.ts.map +1 -0
  132. package/dist/component/utils.js +11 -0
  133. package/dist/component/utils.js.map +1 -0
  134. package/package.json +131 -130
  135. package/src/client/index.ts +121 -52
  136. package/src/component/_generated/api.ts +18 -0
  137. package/src/component/_generated/component.ts +44 -11
  138. package/src/component/garmin/auth.ts +0 -9
  139. package/src/component/garmin/private.ts +0 -12
  140. package/src/component/garmin/public.ts +8 -1
  141. package/src/component/schema.ts +5 -3
  142. package/src/{strava → component/strava}/auth.ts +143 -185
  143. package/src/component/strava/client.ts +20 -0
  144. package/src/component/strava/private.ts +147 -89
  145. package/src/component/strava/public.ts +268 -110
  146. package/src/{strava → component/strava/transform}/activity.ts +256 -276
  147. package/src/{strava → component/strava/transform}/athlete.ts +41 -47
  148. package/src/{strava/maps/sport-type.ts → component/strava/transform/maps/sportType.ts} +100 -99
  149. package/src/component/strava/types/specs/strava-api.json +4796 -0
  150. package/src/component/strava/types/stravaApi/client/client.gen.ts +290 -0
  151. package/src/component/strava/types/stravaApi/client/index.ts +25 -0
  152. package/src/component/strava/types/stravaApi/client/types.gen.ts +214 -0
  153. package/src/component/strava/types/stravaApi/client/utils.gen.ts +316 -0
  154. package/src/component/strava/types/stravaApi/client.gen.ts +16 -0
  155. package/src/component/strava/types/stravaApi/core/auth.gen.ts +41 -0
  156. package/src/component/strava/types/stravaApi/core/bodySerializer.gen.ts +82 -0
  157. package/src/component/strava/types/stravaApi/core/params.gen.ts +169 -0
  158. package/src/component/strava/types/stravaApi/core/pathSerializer.gen.ts +171 -0
  159. package/src/component/strava/types/stravaApi/core/queryKeySerializer.gen.ts +117 -0
  160. package/src/component/strava/types/stravaApi/core/serverSentEvents.gen.ts +243 -0
  161. package/src/component/strava/types/stravaApi/core/types.gen.ts +104 -0
  162. package/src/component/strava/types/stravaApi/core/utils.gen.ts +140 -0
  163. package/src/component/strava/types/stravaApi/index.ts +4 -0
  164. package/src/component/strava/types/stravaApi/sdk.gen.ts +410 -0
  165. package/src/component/strava/types/stravaApi/types.gen.ts +2435 -0
  166. package/src/component/strava/types/stravaApi/zod.gen.ts +1132 -0
  167. package/src/component/strava/utils.ts +52 -0
  168. package/src/component/utils.ts +11 -0
  169. package/dist/strava/activity.d.ts +0 -121
  170. package/dist/strava/activity.d.ts.map +0 -1
  171. package/dist/strava/activity.js.map +0 -1
  172. package/dist/strava/athlete.d.ts.map +0 -1
  173. package/dist/strava/athlete.js.map +0 -1
  174. package/dist/strava/auth.d.ts.map +0 -1
  175. package/dist/strava/auth.js.map +0 -1
  176. package/dist/strava/client.d.ts +0 -93
  177. package/dist/strava/client.d.ts.map +0 -1
  178. package/dist/strava/client.js +0 -158
  179. package/dist/strava/client.js.map +0 -1
  180. package/dist/strava/index.d.ts +0 -13
  181. package/dist/strava/index.d.ts.map +0 -1
  182. package/dist/strava/index.js +0 -17
  183. package/dist/strava/index.js.map +0 -1
  184. package/dist/strava/maps/sport-type.d.ts +0 -7
  185. package/dist/strava/maps/sport-type.d.ts.map +0 -1
  186. package/dist/strava/maps/sport-type.js.map +0 -1
  187. package/dist/strava/sync.d.ts +0 -104
  188. package/dist/strava/sync.d.ts.map +0 -1
  189. package/dist/strava/sync.js +0 -87
  190. package/dist/strava/sync.js.map +0 -1
  191. package/dist/strava/types.d.ts +0 -266
  192. package/dist/strava/types.d.ts.map +0 -1
  193. package/dist/strava/types.js +0 -8
  194. package/dist/strava/types.js.map +0 -1
  195. package/src/strava/activity.test.ts +0 -415
  196. package/src/strava/athlete.test.ts +0 -139
  197. package/src/strava/auth.test.ts +0 -78
  198. package/src/strava/client.ts +0 -212
  199. package/src/strava/index.ts +0 -54
  200. package/src/strava/maps/sport-type.test.ts +0 -69
  201. package/src/strava/sync.ts +0 -168
  202. package/src/strava/types.ts +0 -361
@@ -1,415 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { transformActivity } from "./activity.js";
3
- import type { DetailedActivity, SummaryActivity, StreamSet, Lap } from "./types.js";
4
-
5
- // ─── Fixtures ────────────────────────────────────────────────────────────────
6
-
7
- const baseSummaryActivity: SummaryActivity = {
8
- resource_state: 2,
9
- athlete: { id: 134815, resource_state: 1 },
10
- name: "Happy Friday",
11
- distance: 24931.4,
12
- moving_time: 4500,
13
- elapsed_time: 4500,
14
- total_elevation_gain: 0,
15
- type: "Ride",
16
- sport_type: "MountainBikeRide",
17
- workout_type: null,
18
- id: 154504250376823,
19
- external_id: "garmin_push_12345678987654321",
20
- upload_id: 987654321234567900000,
21
- start_date: "2018-05-02T12:15:09Z",
22
- start_date_local: "2018-05-02T05:15:09Z",
23
- timezone: "(GMT-08:00) America/Los_Angeles",
24
- utc_offset: -25200,
25
- start_latlng: null,
26
- end_latlng: null,
27
- location_city: null,
28
- location_state: null,
29
- location_country: "United States",
30
- achievement_count: 0,
31
- kudos_count: 3,
32
- comment_count: 1,
33
- athlete_count: 1,
34
- photo_count: 0,
35
- map: {
36
- id: "a12345678987654321",
37
- summary_polyline: null,
38
- resource_state: 2,
39
- polyline: null,
40
- },
41
- trainer: true,
42
- commute: false,
43
- manual: false,
44
- private: false,
45
- flagged: false,
46
- gear_id: "b12345678987654321",
47
- from_accepted_tag: false,
48
- average_speed: 5.54,
49
- max_speed: 11,
50
- average_cadence: 67.1,
51
- average_watts: 175.3,
52
- weighted_average_watts: 210,
53
- kilojoules: 788.7,
54
- device_watts: true,
55
- has_heartrate: true,
56
- average_heartrate: 140.3,
57
- max_heartrate: 178,
58
- max_watts: 406,
59
- pr_count: 0,
60
- total_photo_count: 1,
61
- has_kudoed: false,
62
- suffer_score: 82,
63
- device_name: "Garmin Edge 1030",
64
- };
65
-
66
- const baseDetailedActivity: DetailedActivity = {
67
- ...baseSummaryActivity,
68
- resource_state: 3,
69
- id: 12345678987654320,
70
- distance: 28099,
71
- moving_time: 4207,
72
- elapsed_time: 4410,
73
- total_elevation_gain: 516,
74
- start_date: "2018-02-16T14:52:54Z",
75
- start_latlng: [37.83, -122.26],
76
- end_latlng: [37.83, -122.26],
77
- calories: 870.2,
78
- elev_high: 446.6,
79
- elev_low: 17.2,
80
- average_speed: 6.679,
81
- max_speed: 18.5,
82
- average_cadence: 78.5,
83
- average_watts: 185.5,
84
- max_watts: 743,
85
- kilojoules: 780.5,
86
- has_heartrate: false,
87
- average_heartrate: undefined,
88
- max_heartrate: undefined,
89
- description: "",
90
- gear: {
91
- id: "b12345678987654321",
92
- primary: true,
93
- name: "Tarmac",
94
- resource_state: 2,
95
- distance: 32547610,
96
- },
97
- segment_efforts: [],
98
- splits_metric: [],
99
- laps: [],
100
- embed_token: "abc123",
101
- partner_brand_tag: null,
102
- map: {
103
- id: "a1410355832",
104
- polyline: "encoded_polyline_data",
105
- resource_state: 3,
106
- summary_polyline: "summary_encoded_data",
107
- },
108
- };
109
-
110
- // ─── Tests ───────────────────────────────────────────────────────────────────
111
-
112
- describe("transformActivity", () => {
113
- describe("metadata", () => {
114
- it("sets summary_id from activity id", () => {
115
- const result = transformActivity(baseDetailedActivity);
116
- expect(result.metadata.summary_id).toBe(
117
- String(baseDetailedActivity.id),
118
- );
119
- });
120
-
121
- it("sets start_time from start_date", () => {
122
- const result = transformActivity(baseDetailedActivity);
123
- expect(result.metadata.start_time).toBe("2018-02-16T14:52:54Z");
124
- });
125
-
126
- it("computes end_time from start_date + elapsed_time", () => {
127
- const result = transformActivity(baseDetailedActivity);
128
- const expected = new Date(
129
- new Date("2018-02-16T14:52:54Z").getTime() + 4410 * 1000,
130
- ).toISOString();
131
- expect(result.metadata.end_time).toBe(expected);
132
- });
133
-
134
- it("maps sport_type to Terra ActivityType", () => {
135
- const result = transformActivity(baseDetailedActivity);
136
- expect(result.metadata.type).toBe(1); // Biking for MountainBikeRide
137
- });
138
-
139
- it("sets upload_type to 1 (Automatic) for non-manual activities", () => {
140
- const result = transformActivity(baseDetailedActivity);
141
- expect(result.metadata.upload_type).toBe(1);
142
- });
143
-
144
- it("sets upload_type to 2 (Manual) for manual activities", () => {
145
- const manual = { ...baseDetailedActivity, manual: true };
146
- const result = transformActivity(manual);
147
- expect(result.metadata.upload_type).toBe(2);
148
- });
149
-
150
- it("sets name from activity name", () => {
151
- const result = transformActivity(baseDetailedActivity);
152
- expect(result.metadata.name).toBe("Happy Friday");
153
- });
154
-
155
- it("maps location fields", () => {
156
- const result = transformActivity(baseDetailedActivity);
157
- expect(result.metadata.country).toBe("United States");
158
- });
159
- });
160
-
161
- describe("active_durations_data", () => {
162
- it("sets activity_seconds from moving_time", () => {
163
- const result = transformActivity(baseDetailedActivity);
164
- expect(result.active_durations_data.activity_seconds).toBe(4207);
165
- });
166
- });
167
-
168
- describe("calories_data", () => {
169
- it("sets total_burned_calories for detailed activities", () => {
170
- const result = transformActivity(baseDetailedActivity);
171
- expect(result.calories_data?.total_burned_calories).toBe(870.2);
172
- });
173
-
174
- it("returns undefined for summary activities", () => {
175
- const result = transformActivity(baseSummaryActivity);
176
- expect(result.calories_data).toBeUndefined();
177
- });
178
- });
179
-
180
- describe("distance_data", () => {
181
- it("sets distance_meters from activity distance", () => {
182
- const result = transformActivity(baseDetailedActivity);
183
- expect(result.distance_data?.summary?.distance_meters).toBe(28099);
184
- });
185
-
186
- it("includes elevation for detailed activities", () => {
187
- const result = transformActivity(baseDetailedActivity);
188
- expect(
189
- result.distance_data?.summary?.elevation?.gain_actual_meters,
190
- ).toBe(516);
191
- expect(result.distance_data?.summary?.elevation?.max_meters).toBe(446.6);
192
- expect(result.distance_data?.summary?.elevation?.min_meters).toBe(17.2);
193
- });
194
- });
195
-
196
- describe("heart_rate_data", () => {
197
- it("maps summary HR from activity fields", () => {
198
- const withHr = {
199
- ...baseDetailedActivity,
200
- has_heartrate: true,
201
- average_heartrate: 145,
202
- max_heartrate: 180,
203
- };
204
- const result = transformActivity(withHr);
205
- expect(result.heart_rate_data?.summary?.avg_hr_bpm).toBe(145);
206
- expect(result.heart_rate_data?.summary?.max_hr_bpm).toBe(180);
207
- });
208
-
209
- it("maps detailed HR from streams", () => {
210
- const streams: StreamSet = {
211
- time: {
212
- type: "time",
213
- data: [0, 5, 10],
214
- series_type: "distance",
215
- original_size: 3,
216
- resolution: "high",
217
- },
218
- heartrate: {
219
- type: "heartrate",
220
- data: [120, 135, 150],
221
- series_type: "distance",
222
- original_size: 3,
223
- resolution: "high",
224
- },
225
- };
226
- const result = transformActivity(baseSummaryActivity, { streams });
227
- expect(result.heart_rate_data?.detailed?.hr_samples).toHaveLength(3);
228
- expect(result.heart_rate_data?.detailed?.hr_samples?.[0].bpm).toBe(120);
229
- });
230
-
231
- it("returns undefined when no HR data", () => {
232
- const result = transformActivity(baseDetailedActivity);
233
- expect(result.heart_rate_data).toBeUndefined();
234
- });
235
- });
236
-
237
- describe("movement_data", () => {
238
- it("maps speed and cadence from activity fields", () => {
239
- const result = transformActivity(baseDetailedActivity);
240
- expect(result.movement_data?.avg_speed_meters_per_second).toBe(6.679);
241
- expect(result.movement_data?.max_speed_meters_per_second).toBe(18.5);
242
- expect(result.movement_data?.avg_cadence_rpm).toBe(78.5);
243
- });
244
-
245
- it("maps speed samples from velocity_smooth stream", () => {
246
- const streams: StreamSet = {
247
- time: {
248
- type: "time",
249
- data: [0, 5],
250
- series_type: "distance",
251
- original_size: 2,
252
- resolution: "high",
253
- },
254
- velocity_smooth: {
255
- type: "velocity_smooth",
256
- data: [3.5, 4.2],
257
- series_type: "distance",
258
- original_size: 2,
259
- resolution: "high",
260
- },
261
- };
262
- const result = transformActivity(baseSummaryActivity, { streams });
263
- expect(result.movement_data?.speed_samples).toHaveLength(2);
264
- expect(
265
- result.movement_data?.speed_samples?.[0].speed_meters_per_second,
266
- ).toBe(3.5);
267
- });
268
- });
269
-
270
- describe("power_data", () => {
271
- it("maps power summary from activity fields", () => {
272
- const result = transformActivity(baseDetailedActivity);
273
- expect(result.power_data?.avg_watts).toBe(185.5);
274
- expect(result.power_data?.max_watts).toBe(743);
275
- });
276
-
277
- it("maps power samples from watts stream", () => {
278
- const streams: StreamSet = {
279
- time: {
280
- type: "time",
281
- data: [0, 5, 10],
282
- series_type: "distance",
283
- original_size: 3,
284
- resolution: "high",
285
- },
286
- watts: {
287
- type: "watts",
288
- data: [200, 250, 180],
289
- series_type: "distance",
290
- original_size: 3,
291
- resolution: "high",
292
- },
293
- };
294
- const result = transformActivity(baseSummaryActivity, { streams });
295
- expect(result.power_data?.power_samples).toHaveLength(3);
296
- expect(result.power_data?.power_samples?.[1].watts).toBe(250);
297
- });
298
- });
299
-
300
- describe("position_data", () => {
301
- it("maps start/end positions from activity", () => {
302
- const result = transformActivity(baseDetailedActivity);
303
- expect(result.position_data?.start_pos_lat_lng_deg).toEqual([
304
- 37.83, -122.26,
305
- ]);
306
- expect(result.position_data?.end_pos_lat_lng_deg).toEqual([
307
- 37.83, -122.26,
308
- ]);
309
- });
310
-
311
- it("maps position samples from latlng stream", () => {
312
- const streams: StreamSet = {
313
- time: {
314
- type: "time",
315
- data: [0, 5],
316
- series_type: "distance",
317
- original_size: 2,
318
- resolution: "high",
319
- },
320
- latlng: {
321
- type: "latlng",
322
- data: [
323
- [37.83, -122.26],
324
- [37.84, -122.25],
325
- ],
326
- series_type: "distance",
327
- original_size: 2,
328
- resolution: "high",
329
- },
330
- };
331
- const result = transformActivity(baseDetailedActivity, { streams });
332
- expect(result.position_data?.position_samples).toHaveLength(2);
333
- expect(
334
- result.position_data?.position_samples?.[0].coords_lat_lng_deg,
335
- ).toEqual([37.83, -122.26]);
336
- });
337
-
338
- it("returns undefined when no position data", () => {
339
- const noPos = {
340
- ...baseSummaryActivity,
341
- start_latlng: null,
342
- end_latlng: null,
343
- };
344
- const result = transformActivity(noPos);
345
- expect(result.position_data).toBeUndefined();
346
- });
347
- });
348
-
349
- describe("polyline_map_data", () => {
350
- it("maps summary_polyline from activity map", () => {
351
- const result = transformActivity(baseDetailedActivity);
352
- expect(result.polyline_map_data?.summary_polyline).toBe(
353
- "summary_encoded_data",
354
- );
355
- });
356
-
357
- it("returns undefined when no summary_polyline", () => {
358
- const result = transformActivity(baseSummaryActivity);
359
- expect(result.polyline_map_data).toBeUndefined();
360
- });
361
- });
362
-
363
- describe("energy_data", () => {
364
- it("maps kilojoules", () => {
365
- const result = transformActivity(baseDetailedActivity);
366
- expect(result.energy_data?.energy_kilojoules).toBe(780.5);
367
- });
368
- });
369
-
370
- describe("device_data", () => {
371
- it("maps device_name", () => {
372
- const result = transformActivity(baseDetailedActivity);
373
- expect(result.device_data?.name).toBe("Garmin Edge 1030");
374
- });
375
- });
376
-
377
- describe("lap_data", () => {
378
- it("maps laps from explicit laps array", () => {
379
- const laps: Lap[] = [
380
- {
381
- id: 4479306946,
382
- resource_state: 2,
383
- name: "Lap 1",
384
- activity: { id: 1410355832, resource_state: 1 },
385
- athlete: { id: 134815, resource_state: 1 },
386
- elapsed_time: 1573,
387
- moving_time: 1569,
388
- start_date: "2018-02-16T14:52:54Z",
389
- start_date_local: "2018-02-16T06:52:54Z",
390
- distance: 8046.72,
391
- start_index: 0,
392
- end_index: 1570,
393
- total_elevation_gain: 276,
394
- average_speed: 5.12,
395
- max_speed: 9.5,
396
- average_cadence: 78.6,
397
- device_watts: true,
398
- average_watts: 233.1,
399
- lap_index: 1,
400
- split: 1,
401
- },
402
- ];
403
-
404
- const result = transformActivity(baseSummaryActivity, { laps });
405
- expect(result.lap_data?.laps).toHaveLength(1);
406
- expect(result.lap_data?.laps?.[0].distance_meters).toBe(8046.72);
407
- expect(result.lap_data?.laps?.[0].avg_speed_meters_per_second).toBe(
408
- 5.12,
409
- );
410
- expect(result.lap_data?.laps?.[0].start_time).toBe(
411
- "2018-02-16T14:52:54Z",
412
- );
413
- });
414
- });
415
- });
@@ -1,139 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { transformAthlete } from "./athlete.js";
3
- import type { DetailedAthlete } from "./types.js";
4
-
5
- const baseAthlete: DetailedAthlete = {
6
- id: 10001,
7
- username: "carlos_v",
8
- resource_state: 3,
9
- firstname: "Carlos",
10
- lastname: "Velasquez",
11
- city: "Boulder",
12
- state: "Colorado",
13
- country: "US",
14
- sex: "M",
15
- premium: true,
16
- summit: true,
17
- created_at: "2019-03-12T08:15:00Z",
18
- updated_at: "2025-12-01T14:22:30Z",
19
- badge_type_id: 6,
20
- profile_medium:
21
- "https://dgalywyr863hv.cloudfront.net/pictures/athletes/10001/10001/2/medium.jpg",
22
- profile:
23
- "https://dgalywyr863hv.cloudfront.net/pictures/athletes/10001/10001/2/large.jpg",
24
- friend: null,
25
- follower: null,
26
- follower_count: 342,
27
- friend_count: 128,
28
- mutual_friend_count: 0,
29
- athlete_type: 0,
30
- date_preference: "%m/%d/%Y",
31
- measurement_preference: "feet",
32
- ftp: 310,
33
- weight: 72.5,
34
- clubs: [
35
- {
36
- id: 5001,
37
- resource_state: 2,
38
- name: "Boulder Cycling Club",
39
- profile_medium:
40
- "https://dgalywyr863hv.cloudfront.net/pictures/clubs/5001/medium.jpg",
41
- sport_type: "cycling",
42
- city: "Boulder",
43
- state: "Colorado",
44
- country: "US",
45
- member_count: 215,
46
- featured: false,
47
- verified: true,
48
- url: "boulder-cycling-club",
49
- },
50
- ],
51
- bikes: [
52
- {
53
- id: "b1000001",
54
- primary: true,
55
- name: "Specialized Tarmac SL7",
56
- resource_state: 2,
57
- distance: 12450600,
58
- },
59
- {
60
- id: "b1000002",
61
- primary: false,
62
- name: "Canyon Endurace CF",
63
- resource_state: 2,
64
- distance: 5820300,
65
- },
66
- ],
67
- shoes: [
68
- {
69
- id: "g1000001",
70
- primary: true,
71
- name: "Shimano S-Phyre RC9",
72
- resource_state: 2,
73
- distance: 8200000,
74
- },
75
- ],
76
- };
77
-
78
- describe("transformAthlete", () => {
79
- it("maps first and last name", () => {
80
- const result = transformAthlete(baseAthlete);
81
- expect(result.first_name).toBe("Carlos");
82
- expect(result.last_name).toBe("Velasquez");
83
- });
84
-
85
- it("maps location fields", () => {
86
- const result = transformAthlete(baseAthlete);
87
- expect(result.city).toBe("Boulder");
88
- expect(result.state).toBe("Colorado");
89
- expect(result.country).toBe("US");
90
- });
91
-
92
- it("maps sex M to male", () => {
93
- const result = transformAthlete(baseAthlete);
94
- expect(result.sex).toBe("male");
95
- });
96
-
97
- it("maps sex F to female", () => {
98
- const female = { ...baseAthlete, sex: "F" as const };
99
- const result = transformAthlete(female);
100
- expect(result.sex).toBe("female");
101
- });
102
-
103
- it("maps null sex to undefined", () => {
104
- const noSex = { ...baseAthlete, sex: null };
105
- const result = transformAthlete(noSex);
106
- expect(result.sex).toBeUndefined();
107
- });
108
-
109
- it("maps created_at to joined_provider", () => {
110
- const result = transformAthlete(baseAthlete);
111
- expect(result.joined_provider).toBe("2019-03-12T08:15:00Z");
112
- });
113
-
114
- it("maps bikes and shoes to devices", () => {
115
- const result = transformAthlete(baseAthlete);
116
- expect(result.devices).toHaveLength(3);
117
- expect(result.devices?.[0]).toEqual({
118
- name: "Specialized Tarmac SL7",
119
- id: "b1000001",
120
- });
121
- expect(result.devices?.[2]).toEqual({
122
- name: "Shimano S-Phyre RC9",
123
- id: "g1000001",
124
- });
125
- });
126
-
127
- it("handles athlete with null location", () => {
128
- const noLocation = {
129
- ...baseAthlete,
130
- city: null,
131
- state: null,
132
- country: null,
133
- };
134
- const result = transformAthlete(noLocation);
135
- expect(result.city).toBeUndefined();
136
- expect(result.state).toBeUndefined();
137
- expect(result.country).toBeUndefined();
138
- });
139
- });
@@ -1,78 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { buildAuthUrl } from "./auth.js";
3
-
4
- describe("buildAuthUrl", () => {
5
- it("builds a valid Strava authorization URL", () => {
6
- const url = buildAuthUrl({
7
- clientId: "12345",
8
- redirectUri: "https://example.com/callback",
9
- });
10
-
11
- const parsed = new URL(url);
12
- expect(parsed.origin).toBe("https://www.strava.com");
13
- expect(parsed.pathname).toBe("/oauth/authorize");
14
- expect(parsed.searchParams.get("client_id")).toBe("12345");
15
- expect(parsed.searchParams.get("redirect_uri")).toBe(
16
- "https://example.com/callback",
17
- );
18
- expect(parsed.searchParams.get("response_type")).toBe("code");
19
- expect(parsed.searchParams.get("approval_prompt")).toBe("auto");
20
- expect(parsed.searchParams.get("scope")).toBe(
21
- "read,activity:read_all,profile:read_all",
22
- );
23
- });
24
-
25
- it("uses custom scope", () => {
26
- const url = buildAuthUrl({
27
- clientId: "12345",
28
- redirectUri: "https://example.com/callback",
29
- scope: "read,activity:read",
30
- });
31
-
32
- const parsed = new URL(url);
33
- expect(parsed.searchParams.get("scope")).toBe("read,activity:read");
34
- });
35
-
36
- it("includes state parameter when provided", () => {
37
- const url = buildAuthUrl({
38
- clientId: "12345",
39
- redirectUri: "https://example.com/callback",
40
- state: "csrf-token-123",
41
- });
42
-
43
- const parsed = new URL(url);
44
- expect(parsed.searchParams.get("state")).toBe("csrf-token-123");
45
- });
46
-
47
- it("omits state parameter when not provided", () => {
48
- const url = buildAuthUrl({
49
- clientId: "12345",
50
- redirectUri: "https://example.com/callback",
51
- });
52
-
53
- const parsed = new URL(url);
54
- expect(parsed.searchParams.has("state")).toBe(false);
55
- });
56
-
57
- it("uses custom baseUrl", () => {
58
- const url = buildAuthUrl({
59
- clientId: "12345",
60
- redirectUri: "https://example.com/callback",
61
- baseUrl: "https://strava-mock-server.onrender.com",
62
- });
63
-
64
- const parsed = new URL(url);
65
- expect(parsed.origin).toBe("https://strava-mock-server.onrender.com");
66
- });
67
-
68
- it("strips trailing slashes from baseUrl", () => {
69
- const url = buildAuthUrl({
70
- clientId: "12345",
71
- redirectUri: "https://example.com/callback",
72
- baseUrl: "https://mock.example.com///",
73
- });
74
-
75
- expect(url).toMatch(/^https:\/\/mock\.example\.com\/oauth/);
76
- expect(url).not.toMatch(/\/\/oauth/);
77
- });
78
- });