@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,212 +0,0 @@
1
- // ─── Strava API Client ───────────────────────────────────────────────────────
2
- // Lightweight, fetch-based client for the Strava API v3.
3
- // No external dependencies — uses the global `fetch` available in Convex
4
- // actions and modern runtimes.
5
-
6
- import type {
7
- DetailedActivity,
8
- DetailedAthlete,
9
- Lap,
10
- StreamSet,
11
- Stream,
12
- SummaryActivity,
13
- } from "./types.js";
14
-
15
- const DEFAULT_BASE_URL = "https://www.strava.com";
16
- const API_PREFIX = "/api/v3";
17
-
18
- export interface StravaClientOptions {
19
- /** OAuth access token for the authenticated athlete. */
20
- accessToken: string;
21
- /**
22
- * Base URL of the Strava API (without `/api/v3` suffix).
23
- * Defaults to `https://www.strava.com`.
24
- * Override to point at a mock server during development.
25
- */
26
- baseUrl?: string;
27
- }
28
-
29
- /**
30
- * A lightweight client for the Strava API v3.
31
- *
32
- * @example
33
- * ```ts
34
- * const client = new StravaClient({
35
- * accessToken: "tok_xxx",
36
- * baseUrl: "https://strava-mock-server.onrender.com", // optional
37
- * });
38
- *
39
- * const athlete = await client.getAthlete();
40
- * const activities = await client.listActivities({ per_page: 50 });
41
- * ```
42
- */
43
- export class StravaClient {
44
- private readonly accessToken: string;
45
- private readonly baseUrl: string;
46
-
47
- constructor(opts: StravaClientOptions) {
48
- this.accessToken = opts.accessToken;
49
- this.baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
50
- }
51
-
52
- // ─── Athlete ─────────────────────────────────────────────────────────────
53
-
54
- /**
55
- * Get the authenticated athlete's profile.
56
- *
57
- * Strava API: `GET /athlete`
58
- */
59
- async getAthlete(): Promise<DetailedAthlete> {
60
- return this.get<DetailedAthlete>("/athlete");
61
- }
62
-
63
- // ─── Activities ──────────────────────────────────────────────────────────
64
-
65
- /**
66
- * List the authenticated athlete's activities.
67
- *
68
- * Strava API: `GET /athlete/activities`
69
- *
70
- * @param params.before - Only return activities before this Unix epoch timestamp
71
- * @param params.after - Only return activities after this Unix epoch timestamp
72
- * @param params.page - Page number (defaults to 1)
73
- * @param params.per_page - Items per page (defaults to 30, max 200)
74
- */
75
- async listActivities(params?: {
76
- before?: number;
77
- after?: number;
78
- page?: number;
79
- per_page?: number;
80
- }): Promise<SummaryActivity[]> {
81
- const query = new URLSearchParams();
82
- if (params?.before != null) query.set("before", String(params.before));
83
- if (params?.after != null) query.set("after", String(params.after));
84
- if (params?.page != null) query.set("page", String(params.page));
85
- if (params?.per_page != null) query.set("per_page", String(params.per_page));
86
-
87
- const qs = query.toString();
88
- return this.get<SummaryActivity[]>(
89
- `/athlete/activities${qs ? `?${qs}` : ""}`,
90
- );
91
- }
92
-
93
- /**
94
- * List ALL activities for the authenticated athlete, automatically
95
- * paginating through all pages.
96
- *
97
- * @param params.after - Only return activities after this Unix epoch timestamp
98
- * @param params.before - Only return activities before this Unix epoch timestamp
99
- * @param params.per_page - Items per page (defaults to 200)
100
- */
101
- async listAllActivities(params?: {
102
- after?: number;
103
- before?: number;
104
- per_page?: number;
105
- }): Promise<SummaryActivity[]> {
106
- const perPage = params?.per_page ?? 200;
107
- const all: SummaryActivity[] = [];
108
- let page = 1;
109
-
110
- while (true) {
111
- const batch = await this.listActivities({
112
- after: params?.after,
113
- before: params?.before,
114
- page,
115
- per_page: perPage,
116
- });
117
- all.push(...batch);
118
- if (batch.length < perPage) break;
119
- page++;
120
- }
121
-
122
- return all;
123
- }
124
-
125
- /**
126
- * Get a detailed activity by ID.
127
- *
128
- * Strava API: `GET /activities/{id}`
129
- */
130
- async getActivity(id: number): Promise<DetailedActivity> {
131
- return this.get<DetailedActivity>(`/activities/${id}`);
132
- }
133
-
134
- /**
135
- * Get time-series streams for an activity.
136
- *
137
- * Strava API: `GET /activities/{id}/streams`
138
- *
139
- * @param id - Activity ID
140
- * @param keys - Stream types to request (e.g. `["heartrate", "watts", "latlng", "altitude", "time"]`)
141
- */
142
- async getActivityStreams(
143
- id: number,
144
- keys: string[] = [
145
- "time",
146
- "heartrate",
147
- "watts",
148
- "cadence",
149
- "latlng",
150
- "altitude",
151
- "velocity_smooth",
152
- "grade_smooth",
153
- "distance",
154
- "temp",
155
- ],
156
- ): Promise<StreamSet> {
157
- const query = new URLSearchParams({
158
- keys: keys.join(","),
159
- key_by_type: "true",
160
- });
161
- const streams = await this.get<Record<string, Stream>>(
162
- `/activities/${id}/streams?${query.toString()}`,
163
- );
164
- return streams as StreamSet;
165
- }
166
-
167
- /**
168
- * Get laps for an activity.
169
- *
170
- * Strava API: `GET /activities/{id}/laps`
171
- */
172
- async getActivityLaps(id: number): Promise<Lap[]> {
173
- return this.get<Lap[]>(`/activities/${id}/laps`);
174
- }
175
-
176
- // ─── Internal ────────────────────────────────────────────────────────────
177
-
178
- private async get<T>(path: string): Promise<T> {
179
- const url = `${this.baseUrl}${API_PREFIX}${path}`;
180
- const response = await fetch(url, {
181
- method: "GET",
182
- headers: {
183
- Authorization: `Bearer ${this.accessToken}`,
184
- Accept: "application/json",
185
- },
186
- });
187
-
188
- if (!response.ok) {
189
- const body = await response.text().catch(() => "");
190
- throw new StravaApiError(
191
- `Strava API error: ${response.status} ${response.statusText}`,
192
- response.status,
193
- body,
194
- );
195
- }
196
-
197
- return (await response.json()) as T;
198
- }
199
- }
200
-
201
- // ─── Error ───────────────────────────────────────────────────────────────────
202
-
203
- export class StravaApiError extends Error {
204
- constructor(
205
- message: string,
206
- public readonly status: number,
207
- public readonly body: string,
208
- ) {
209
- super(message);
210
- this.name = "StravaApiError";
211
- }
212
- }
@@ -1,54 +0,0 @@
1
- // ─── @nativesquare/soma/strava ───────────────────────────────────────────────
2
- // Strava API → Soma schema transformers, API client, OAuth helpers, and sync.
3
- //
4
- // Pure TypeScript with zero runtime dependencies (uses global `fetch`).
5
- // Compatible with Convex actions and any modern runtime.
6
-
7
- // ── Transformers ─────────────────────────────────────────────────────────────
8
- export { transformActivity } from "./activity.js";
9
- export type { ActivityData } from "./activity.js";
10
-
11
- export { transformAthlete } from "./athlete.js";
12
- export type { AthleteData } from "./athlete.js";
13
-
14
- // ── Enum Maps ────────────────────────────────────────────────────────────────
15
- export { mapSportType } from "./maps/sport-type.js";
16
-
17
- // ── API Client ───────────────────────────────────────────────────────────────
18
- export { StravaClient, StravaApiError } from "./client.js";
19
- export type { StravaClientOptions } from "./client.js";
20
-
21
- // ── OAuth Helpers ────────────────────────────────────────────────────────────
22
- export { buildAuthUrl, exchangeCode, refreshToken } from "./auth.js";
23
- export type {
24
- BuildAuthUrlOptions,
25
- ExchangeCodeOptions,
26
- RefreshTokenOptions,
27
- } from "./auth.js";
28
-
29
- // ── Sync Helpers ─────────────────────────────────────────────────────────────
30
- export { syncActivities, syncAthlete } from "./sync.js";
31
- export type {
32
- SyncActivitiesOptions,
33
- SyncActivitiesResult,
34
- SyncAthleteOptions,
35
- } from "./sync.js";
36
-
37
- // ── Types ────────────────────────────────────────────────────────────────────
38
- export type {
39
- DetailedActivity,
40
- DetailedAthlete,
41
- Lap,
42
- OAuthTokenResponse,
43
- PolylineMap,
44
- SegmentEffort,
45
- Split,
46
- Stream,
47
- StreamSet,
48
- StravaSportType,
49
- SummaryActivity,
50
- SummaryAthlete,
51
- SummaryClub,
52
- SummaryGear,
53
- SummarySegment,
54
- } from "./types.js";
@@ -1,69 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { mapSportType } from "./sport-type.js";
3
-
4
- describe("mapSportType", () => {
5
- it("maps cycling sport types to Terra Biking (1)", () => {
6
- expect(mapSportType("Ride")).toBe(1);
7
- expect(mapSportType("MountainBikeRide")).toBe(1);
8
- expect(mapSportType("GravelRide")).toBe(1);
9
- expect(mapSportType("EBikeRide")).toBe(1);
10
- expect(mapSportType("EMountainBikeRide")).toBe(1);
11
- expect(mapSportType("VirtualRide")).toBe(1);
12
- expect(mapSportType("Velomobile")).toBe(1);
13
- });
14
-
15
- it("maps running sport types to Terra Running (8)", () => {
16
- expect(mapSportType("Run")).toBe(8);
17
- expect(mapSportType("TrailRun")).toBe(8);
18
- expect(mapSportType("VirtualRun")).toBe(8);
19
- });
20
-
21
- it("maps Walk to Terra Walking (7)", () => {
22
- expect(mapSportType("Walk")).toBe(7);
23
- });
24
-
25
- it("maps Swim to Terra Swimming (82)", () => {
26
- expect(mapSportType("Swim")).toBe(82);
27
- });
28
-
29
- it("maps Hike to Terra Hiking (35)", () => {
30
- expect(mapSportType("Hike")).toBe(35);
31
- });
32
-
33
- it("maps snow sports correctly", () => {
34
- expect(mapSportType("AlpineSki")).toBe(66);
35
- expect(mapSportType("NordicSki")).toBe(67);
36
- expect(mapSportType("Snowboard")).toBe(73);
37
- expect(mapSportType("Snowshoe")).toBe(74);
38
- });
39
-
40
- it("maps water sports correctly", () => {
41
- expect(mapSportType("Rowing")).toBe(53);
42
- expect(mapSportType("Kayaking")).toBe(40);
43
- expect(mapSportType("Sail")).toBe(59);
44
- expect(mapSportType("Surfing")).toBe(81);
45
- expect(mapSportType("Kitesurf")).toBe(41);
46
- });
47
-
48
- it("maps racket sports correctly", () => {
49
- expect(mapSportType("Tennis")).toBe(87);
50
- expect(mapSportType("TableTennis")).toBe(85);
51
- expect(mapSportType("Badminton")).toBe(10);
52
- expect(mapSportType("Squash")).toBe(76);
53
- });
54
-
55
- it("maps gym/fitness activities correctly", () => {
56
- expect(mapSportType("WeightTraining")).toBe(80);
57
- expect(mapSportType("Crossfit")).toBe(113);
58
- expect(mapSportType("Yoga")).toBe(100);
59
- expect(mapSportType("Pilates")).toBe(49);
60
- expect(mapSportType("HighIntensityIntervalTraining")).toBe(114);
61
- expect(mapSportType("Elliptical")).toBe(25);
62
- expect(mapSportType("StairStepper")).toBe(78);
63
- });
64
-
65
- it("returns Terra Other (108) for unknown types", () => {
66
- expect(mapSportType("UnknownSport" as never)).toBe(108);
67
- expect(mapSportType("" as never)).toBe(108);
68
- });
69
- });
@@ -1,168 +0,0 @@
1
- // ─── Strava Sync Helper ──────────────────────────────────────────────────────
2
- // High-level function that combines the Strava client, transformers,
3
- // and Soma ingestion into a single call. Designed to be used inside a
4
- // Convex action.
5
-
6
- import type { Soma } from "../client/index.js";
7
- import type { ActionCtx } from "../client/types.js";
8
- import type { StravaClient } from "./client.js";
9
- import { transformActivity } from "./activity.js";
10
- import { transformAthlete } from "./athlete.js";
11
-
12
- export interface SyncActivitiesOptions {
13
- /** Authenticated Strava API client. */
14
- client: StravaClient;
15
- /** Soma component instance. */
16
- soma: Soma;
17
- /** Convex action context (has runMutation for ingestion). */
18
- ctx: ActionCtx;
19
- /** The Soma connection ID for this user–Strava link. */
20
- connectionId: string;
21
- /** The host app's user identifier (e.g., Clerk user ID). */
22
- userId: string;
23
- /**
24
- * Only sync activities after this Unix epoch timestamp (seconds).
25
- * Useful for incremental sync — pass the `lastDataUpdate` timestamp.
26
- */
27
- after?: number;
28
- /**
29
- * Only sync activities before this Unix epoch timestamp (seconds).
30
- */
31
- before?: number;
32
- /**
33
- * Fetch detailed streams (heart rate, power, position, etc.) for each
34
- * activity. Adds one API call per activity.
35
- * @default false
36
- */
37
- includeStreams?: boolean;
38
- /**
39
- * Fetch lap data for each activity. Adds one API call per activity
40
- * (unless the detailed activity already contains laps).
41
- * @default false
42
- */
43
- includeLaps?: boolean;
44
- }
45
-
46
- export interface SyncActivitiesResult {
47
- /** Number of activities successfully synced. */
48
- synced: number;
49
- /** Activity IDs that failed to sync (non-fatal). */
50
- errors: Array<{ activityId: number; error: string }>;
51
- }
52
-
53
- /**
54
- * Sync activities from Strava into Soma.
55
- *
56
- * This function handles the full flow:
57
- * 1. Lists activities from the Strava API (with auto-pagination)
58
- * 2. Optionally fetches detailed data, streams, and laps per activity
59
- * 3. Transforms each activity into the Soma schema
60
- * 4. Ingests each activity into Soma (with automatic deduplication)
61
- *
62
- * Designed to be called from a Convex action.
63
- *
64
- * @example
65
- * ```ts
66
- * import { StravaClient, syncActivities } from "@nativesquare/soma/strava";
67
- * import { Soma } from "@nativesquare/soma";
68
- *
69
- * export const syncStrava = internalAction({
70
- * args: { userId: v.string(), connectionId: v.string(), accessToken: v.string() },
71
- * handler: async (ctx, { userId, connectionId, accessToken }) => {
72
- * const client = new StravaClient({ accessToken });
73
- * const soma = new Soma(components.soma);
74
- *
75
- * const result = await syncActivities({
76
- * client,
77
- * soma,
78
- * ctx,
79
- * connectionId,
80
- * userId,
81
- * includeStreams: true,
82
- * });
83
- *
84
- * console.log(`Synced ${result.synced} activities`);
85
- * },
86
- * });
87
- * ```
88
- */
89
- export async function syncActivities(
90
- opts: SyncActivitiesOptions,
91
- ): Promise<SyncActivitiesResult> {
92
- const {
93
- client,
94
- soma,
95
- ctx,
96
- connectionId,
97
- userId,
98
- after,
99
- before,
100
- includeStreams = false,
101
- includeLaps = false,
102
- } = opts;
103
-
104
- const summaries = await client.listAllActivities({
105
- after,
106
- before,
107
- });
108
-
109
- let synced = 0;
110
- const errors: SyncActivitiesResult["errors"] = [];
111
-
112
- for (const summary of summaries) {
113
- try {
114
- const detailed = await client.getActivity(summary.id);
115
-
116
- const streams = includeStreams
117
- ? await client.getActivityStreams(summary.id)
118
- : undefined;
119
-
120
- const laps =
121
- includeLaps && (!detailed.laps || detailed.laps.length === 0)
122
- ? await client.getActivityLaps(summary.id)
123
- : undefined;
124
-
125
- const data = transformActivity(detailed, { streams, laps });
126
-
127
- await soma.ingestActivity(ctx, { connectionId, userId, ...data });
128
- synced++;
129
- } catch (err) {
130
- errors.push({
131
- activityId: summary.id,
132
- error: err instanceof Error ? err.message : String(err),
133
- });
134
- }
135
- }
136
-
137
- return { synced, errors };
138
- }
139
-
140
- export interface SyncAthleteOptions {
141
- /** Authenticated Strava API client. */
142
- client: StravaClient;
143
- /** Soma component instance. */
144
- soma: Soma;
145
- /** Convex action context. */
146
- ctx: ActionCtx;
147
- /** The Soma connection ID for this user–Strava link. */
148
- connectionId: string;
149
- /** The host app's user identifier. */
150
- userId: string;
151
- }
152
-
153
- /**
154
- * Sync the authenticated athlete's profile from Strava into Soma.
155
- *
156
- * @example
157
- * ```ts
158
- * await syncAthlete({ client, soma, ctx, connectionId, userId });
159
- * ```
160
- */
161
- export async function syncAthlete(
162
- opts: SyncAthleteOptions,
163
- ): Promise<void> {
164
- const { client, soma, ctx, connectionId, userId } = opts;
165
- const athlete = await client.getAthlete();
166
- const data = transformAthlete(athlete);
167
- await soma.ingestAthlete(ctx, { connectionId, userId, ...data });
168
- }