@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.
- package/README.md +260 -19
- package/dist/client/index.d.ts +158 -4
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +165 -3
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +2 -0
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +37 -0
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/public.d.ts +3 -3
- package/dist/component/schema.d.ts +18 -5
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +10 -0
- package/dist/component/schema.js.map +1 -1
- package/dist/component/strava.d.ts +88 -0
- package/dist/component/strava.d.ts.map +1 -0
- package/dist/component/strava.js +318 -0
- package/dist/component/strava.js.map +1 -0
- package/dist/component/validators/activity.d.ts +4 -4
- package/dist/component/validators/samples.d.ts +2 -2
- package/dist/strava/activity.d.ts +121 -0
- package/dist/strava/activity.d.ts.map +1 -0
- package/dist/strava/activity.js +201 -0
- package/dist/strava/activity.js.map +1 -0
- package/dist/strava/athlete.d.ts +34 -0
- package/dist/strava/athlete.d.ts.map +1 -0
- package/dist/strava/athlete.js +39 -0
- package/dist/strava/athlete.js.map +1 -0
- package/dist/strava/auth.d.ts +103 -0
- package/dist/strava/auth.d.ts.map +1 -0
- package/dist/strava/auth.js +111 -0
- package/dist/strava/auth.js.map +1 -0
- package/dist/strava/client.d.ts +93 -0
- package/dist/strava/client.d.ts.map +1 -0
- package/dist/strava/client.js +158 -0
- package/dist/strava/client.js.map +1 -0
- package/dist/strava/index.d.ts +13 -0
- package/dist/strava/index.d.ts.map +1 -0
- package/dist/strava/index.js +17 -0
- package/dist/strava/index.js.map +1 -0
- package/dist/strava/maps/sport-type.d.ts +7 -0
- package/dist/strava/maps/sport-type.d.ts.map +1 -0
- package/dist/strava/maps/sport-type.js +84 -0
- package/dist/strava/maps/sport-type.js.map +1 -0
- package/dist/strava/sync.d.ts +104 -0
- package/dist/strava/sync.d.ts.map +1 -0
- package/dist/strava/sync.js +87 -0
- package/dist/strava/sync.js.map +1 -0
- package/dist/strava/types.d.ts +266 -0
- package/dist/strava/types.d.ts.map +1 -0
- package/dist/strava/types.js +8 -0
- package/dist/strava/types.js.map +1 -0
- package/dist/validators.d.ts +6617 -0
- package/dist/validators.d.ts.map +1 -0
- package/dist/validators.js +78 -0
- package/dist/validators.js.map +1 -0
- package/package.json +9 -1
- package/src/client/index.ts +212 -4
- package/src/component/_generated/api.ts +2 -0
- package/src/component/_generated/component.ts +49 -0
- package/src/component/schema.ts +11 -0
- package/src/component/strava.ts +383 -0
- package/src/strava/activity.test.ts +415 -0
- package/src/strava/activity.ts +276 -0
- package/src/strava/athlete.test.ts +139 -0
- package/src/strava/athlete.ts +47 -0
- package/src/strava/auth.test.ts +78 -0
- package/src/strava/auth.ts +185 -0
- package/src/strava/client.ts +212 -0
- package/src/strava/index.ts +54 -0
- package/src/strava/maps/sport-type.test.ts +69 -0
- package/src/strava/maps/sport-type.ts +99 -0
- package/src/strava/sync.ts +168 -0
- package/src/strava/types.ts +361 -0
- package/src/validators.ts +89 -0
package/README.md
CHANGED
|
@@ -205,6 +205,231 @@ Available transformers from `@nativesquare/soma/healthkit`:
|
|
|
205
205
|
Also exports enum mapping utilities: `mapActivityType`, `mapSleepLevel`,
|
|
206
206
|
`isAsleepCategory`, and `mapMenstruationFlow`.
|
|
207
207
|
|
|
208
|
+
## Strava Integration
|
|
209
|
+
|
|
210
|
+
Soma includes a built-in Strava integration that handles the full OAuth
|
|
211
|
+
lifecycle — authorization URL generation, token exchange, secure token storage,
|
|
212
|
+
automatic token refresh, and data syncing. No manual API calls or token
|
|
213
|
+
management required.
|
|
214
|
+
|
|
215
|
+
There are two ways to use the Strava integration:
|
|
216
|
+
|
|
217
|
+
- **Managed** — use `soma.connectStrava()`, `soma.syncStrava()`, and
|
|
218
|
+
`soma.disconnectStrava()` which handle everything end-to-end including OAuth
|
|
219
|
+
token storage inside the component.
|
|
220
|
+
- **Manual** — use the `StravaClient`, `syncActivities`, and `syncAthlete`
|
|
221
|
+
utilities from `@nativesquare/soma/strava` for full control over the flow
|
|
222
|
+
(e.g., custom token storage, webhook-driven sync).
|
|
223
|
+
|
|
224
|
+
### Prerequisites
|
|
225
|
+
|
|
226
|
+
1. **Create a Strava API Application** at
|
|
227
|
+
[strava.com/settings/api](https://www.strava.com/settings/api) to obtain your
|
|
228
|
+
Client ID and Client Secret.
|
|
229
|
+
|
|
230
|
+
2. **Set environment variables** in the
|
|
231
|
+
[Convex dashboard](https://dashboard.convex.dev):
|
|
232
|
+
|
|
233
|
+
| Variable | Required | Description |
|
|
234
|
+
| --------------------- | -------- | --------------------------------------------- |
|
|
235
|
+
| `STRAVA_CLIENT_ID` | Yes | Your Strava application's Client ID |
|
|
236
|
+
| `STRAVA_CLIENT_SECRET`| Yes | Your Strava application's Client Secret |
|
|
237
|
+
| `STRAVA_BASE_URL` | No | Override for testing with a mock server |
|
|
238
|
+
|
|
239
|
+
Alternatively, pass credentials directly to the `Soma` constructor:
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
const soma = new Soma(components.soma, {
|
|
243
|
+
strava: {
|
|
244
|
+
clientId: "your-client-id",
|
|
245
|
+
clientSecret: "your-client-secret",
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Managed flow (recommended)
|
|
251
|
+
|
|
252
|
+
The managed flow handles OAuth, token storage, and syncing in a single call.
|
|
253
|
+
|
|
254
|
+
#### Step 1: Generate the authorization URL
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
import { Soma } from "@nativesquare/soma";
|
|
258
|
+
import { components } from "./_generated/api";
|
|
259
|
+
import { query } from "./_generated/server";
|
|
260
|
+
import { v } from "convex/values";
|
|
261
|
+
|
|
262
|
+
const soma = new Soma(components.soma);
|
|
263
|
+
|
|
264
|
+
export const getStravaAuthUrl = query({
|
|
265
|
+
args: { redirectUri: v.string() },
|
|
266
|
+
handler: async (_ctx, { redirectUri }) => {
|
|
267
|
+
return soma.getStravaAuthUrl({ redirectUri });
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Redirect the user to this URL. After they authorize, Strava redirects back to
|
|
273
|
+
your `redirectUri` with a `code` query parameter.
|
|
274
|
+
|
|
275
|
+
#### Step 2: Handle the OAuth callback
|
|
276
|
+
|
|
277
|
+
```ts
|
|
278
|
+
import { action } from "./_generated/server";
|
|
279
|
+
|
|
280
|
+
export const handleStravaCallback = action({
|
|
281
|
+
args: { userId: v.string(), code: v.string() },
|
|
282
|
+
handler: async (ctx, { userId, code }) => {
|
|
283
|
+
// Exchanges the code, stores tokens, syncs athlete + activities
|
|
284
|
+
return await soma.connectStrava(ctx, { userId, code });
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
`connectStrava` returns `{ connectionId, synced, errors }` — the number of
|
|
290
|
+
activities synced and any per-activity errors.
|
|
291
|
+
|
|
292
|
+
#### Step 3: Incremental sync
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
export const syncStravaActivities = action({
|
|
296
|
+
args: { userId: v.string() },
|
|
297
|
+
handler: async (ctx, { userId }) => {
|
|
298
|
+
// Auto-refreshes token if expired, then syncs
|
|
299
|
+
return await soma.syncStrava(ctx, { userId, includeStreams: true });
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Pass `after` (Unix epoch seconds) to only sync activities after a given
|
|
305
|
+
timestamp, useful for incremental updates.
|
|
306
|
+
|
|
307
|
+
#### Step 4: Disconnect
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
export const disconnectStrava = action({
|
|
311
|
+
args: { userId: v.string() },
|
|
312
|
+
handler: async (ctx, { userId }) => {
|
|
313
|
+
// Revokes token at Strava, deletes stored tokens, sets connection inactive
|
|
314
|
+
await soma.disconnectStrava(ctx, { userId });
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
#### Step 5: Query Strava data
|
|
320
|
+
|
|
321
|
+
Once connected and synced, use the standard Soma query methods — Strava data is
|
|
322
|
+
stored in the same normalized tables as any other provider:
|
|
323
|
+
|
|
324
|
+
```ts
|
|
325
|
+
export const getStravaConnection = query({
|
|
326
|
+
args: { userId: v.string() },
|
|
327
|
+
handler: async (ctx, { userId }) => {
|
|
328
|
+
return await soma.getConnectionByProvider(ctx, {
|
|
329
|
+
userId,
|
|
330
|
+
provider: "STRAVA",
|
|
331
|
+
});
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
export const listStravaActivities = query({
|
|
336
|
+
args: { userId: v.string() },
|
|
337
|
+
handler: async (ctx, { userId }) => {
|
|
338
|
+
return await soma.listActivities(ctx, { userId, order: "desc" });
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
export const getStravaAthlete = query({
|
|
343
|
+
args: { userId: v.string() },
|
|
344
|
+
handler: async (ctx, { userId }) => {
|
|
345
|
+
const athletes = await soma.listAthletes(ctx, { userId });
|
|
346
|
+
return athletes[0] ?? null;
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Manual flow (advanced)
|
|
352
|
+
|
|
353
|
+
For full control over the OAuth flow, token storage, and sync timing, use the
|
|
354
|
+
low-level utilities from `@nativesquare/soma/strava`. This is useful when you
|
|
355
|
+
want to manage tokens yourself, use webhooks for sync triggers, or integrate
|
|
356
|
+
with a custom auth system.
|
|
357
|
+
|
|
358
|
+
```ts
|
|
359
|
+
import { Soma } from "@nativesquare/soma";
|
|
360
|
+
import {
|
|
361
|
+
StravaClient,
|
|
362
|
+
syncActivities,
|
|
363
|
+
syncAthlete,
|
|
364
|
+
} from "@nativesquare/soma/strava";
|
|
365
|
+
import { components } from "./_generated/api";
|
|
366
|
+
import { internalAction } from "./_generated/server";
|
|
367
|
+
import { v } from "convex/values";
|
|
368
|
+
|
|
369
|
+
const soma = new Soma(components.soma);
|
|
370
|
+
|
|
371
|
+
export const syncStrava = internalAction({
|
|
372
|
+
args: {
|
|
373
|
+
userId: v.string(),
|
|
374
|
+
connectionId: v.string(),
|
|
375
|
+
accessToken: v.string(),
|
|
376
|
+
},
|
|
377
|
+
handler: async (ctx, { userId, connectionId, accessToken }) => {
|
|
378
|
+
const client = new StravaClient({ accessToken });
|
|
379
|
+
|
|
380
|
+
// Sync the athlete profile
|
|
381
|
+
await syncAthlete({ client, soma, ctx, connectionId, userId });
|
|
382
|
+
|
|
383
|
+
// Sync activities (with streams and lap data)
|
|
384
|
+
const result = await syncActivities({
|
|
385
|
+
client,
|
|
386
|
+
soma,
|
|
387
|
+
ctx,
|
|
388
|
+
connectionId,
|
|
389
|
+
userId,
|
|
390
|
+
includeStreams: true,
|
|
391
|
+
includeLaps: true,
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
// Update the connection timestamp
|
|
395
|
+
await soma.updateConnection(ctx, {
|
|
396
|
+
connectionId,
|
|
397
|
+
lastDataUpdate: new Date().toISOString(),
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
return result; // { synced: number, errors: [...] }
|
|
401
|
+
},
|
|
402
|
+
});
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
You can also use the transformers and client independently:
|
|
406
|
+
|
|
407
|
+
```ts
|
|
408
|
+
import {
|
|
409
|
+
StravaClient,
|
|
410
|
+
transformActivity,
|
|
411
|
+
transformAthlete,
|
|
412
|
+
buildAuthUrl,
|
|
413
|
+
exchangeCode,
|
|
414
|
+
refreshToken,
|
|
415
|
+
} from "@nativesquare/soma/strava";
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Available Strava exports (`@nativesquare/soma/strava`)
|
|
419
|
+
|
|
420
|
+
| Export | Type | Description |
|
|
421
|
+
| -------------------- | ----------- | ------------------------------------------------ |
|
|
422
|
+
| `StravaClient` | Class | Typed Strava API client (uses global `fetch`) |
|
|
423
|
+
| `StravaApiError` | Class | Error thrown for non-OK Strava API responses |
|
|
424
|
+
| `syncActivities` | Function | Sync activities from Strava into Soma |
|
|
425
|
+
| `syncAthlete` | Function | Sync athlete profile from Strava into Soma |
|
|
426
|
+
| `transformActivity` | Function | Transform Strava activity → Soma schema |
|
|
427
|
+
| `transformAthlete` | Function | Transform Strava athlete → Soma schema |
|
|
428
|
+
| `mapSportType` | Function | Map Strava sport type → Soma activity type enum |
|
|
429
|
+
| `buildAuthUrl` | Function | Build the OAuth authorization URL |
|
|
430
|
+
| `exchangeCode` | Function | Exchange OAuth code for tokens |
|
|
431
|
+
| `refreshToken` | Function | Refresh an expired access token |
|
|
432
|
+
|
|
208
433
|
## Data Queries
|
|
209
434
|
|
|
210
435
|
Soma provides query methods for reading back health data with optional
|
|
@@ -295,17 +520,18 @@ long as it conforms to the validators.
|
|
|
295
520
|
|
|
296
521
|
### Tables
|
|
297
522
|
|
|
298
|
-
| Table | Description
|
|
299
|
-
| ---------------- |
|
|
300
|
-
| `connections` | User ↔ provider links
|
|
301
|
-
| `athletes` | User profile data from providers
|
|
302
|
-
| `activities` | Workouts and exercise sessions
|
|
303
|
-
| `body` | Body metrics (heart rate, blood pressure, etc.)
|
|
304
|
-
| `daily` | Daily activity summaries (steps, calories)
|
|
305
|
-
| `sleep` | Sleep session data
|
|
306
|
-
| `menstruation` | Menstruation and fertility data
|
|
307
|
-
| `nutrition` | Food, drink, macro/micronutrient data
|
|
308
|
-
| `plannedWorkouts`| Scheduled/planned workouts
|
|
523
|
+
| Table | Description |
|
|
524
|
+
| ---------------- | ------------------------------------------------------- |
|
|
525
|
+
| `connections` | User ↔ provider links |
|
|
526
|
+
| `athletes` | User profile data from providers |
|
|
527
|
+
| `activities` | Workouts and exercise sessions |
|
|
528
|
+
| `body` | Body metrics (heart rate, blood pressure, etc.) |
|
|
529
|
+
| `daily` | Daily activity summaries (steps, calories) |
|
|
530
|
+
| `sleep` | Sleep session data |
|
|
531
|
+
| `menstruation` | Menstruation and fertility data |
|
|
532
|
+
| `nutrition` | Food, drink, macro/micronutrient data |
|
|
533
|
+
| `plannedWorkouts`| Scheduled/planned workouts |
|
|
534
|
+
| `providerTokens` | OAuth tokens for cloud-based providers (Strava, etc.) |
|
|
309
535
|
|
|
310
536
|
All data fields are optional (`v.optional`) unless explicitly required. This
|
|
311
537
|
accommodates providers that support different subsets of health data.
|
|
@@ -358,6 +584,19 @@ All `paginate*` methods accept `{ userId, startTime?, endTime?, paginationOpts }
|
|
|
358
584
|
| `listAthletes(ctx, { userId })` | Query | List athlete profiles for a user |
|
|
359
585
|
| `getAthlete(ctx, { connectionId })` | Query | Get athlete profile for a connection |
|
|
360
586
|
|
|
587
|
+
### Strava Methods
|
|
588
|
+
|
|
589
|
+
These methods require Strava credentials to be configured (via environment
|
|
590
|
+
variables or the `Soma` constructor). They handle OAuth, token storage, and data
|
|
591
|
+
syncing end-to-end.
|
|
592
|
+
|
|
593
|
+
| Method | Context | Description |
|
|
594
|
+
| ------------------------------------------------------------------- | ------- | ---------------------------------------------------- |
|
|
595
|
+
| `getStravaAuthUrl({ redirectUri, scope?, state? })` | Pure | Build the Strava OAuth authorization URL |
|
|
596
|
+
| `connectStrava(ctx, { userId, code, includeStreams? })` | Action | Handle OAuth callback: exchange code, store tokens, sync all data |
|
|
597
|
+
| `syncStrava(ctx, { userId, includeStreams?, after? })` | Action | Incremental sync with auto token refresh |
|
|
598
|
+
| `disconnectStrava(ctx, { userId })` | Action | Revoke token, delete stored tokens, deactivate connection |
|
|
599
|
+
|
|
361
600
|
## Direct Component Access
|
|
362
601
|
|
|
363
602
|
For advanced use cases, you can call component functions directly instead of
|
|
@@ -387,17 +626,19 @@ export const connect = mutation({
|
|
|
387
626
|
});
|
|
388
627
|
```
|
|
389
628
|
|
|
390
|
-
All public functions are available under `components.soma.public.*`.
|
|
391
|
-
`
|
|
629
|
+
All public functions are available under `components.soma.public.*`. Strava
|
|
630
|
+
actions are under `components.soma.strava.*`. Use `ctx.runQuery` for queries,
|
|
631
|
+
`ctx.runMutation` for mutations, and `ctx.runAction` for actions.
|
|
392
632
|
|
|
393
633
|
## Exports
|
|
394
634
|
|
|
395
|
-
| Import Path | Contents
|
|
396
|
-
| -------------------------------- |
|
|
397
|
-
| `@nativesquare/soma` | `Soma` client class
|
|
398
|
-
| `@nativesquare/soma/healthkit` | Apple HealthKit → Soma transformers & types
|
|
399
|
-
| `@nativesquare/soma/
|
|
400
|
-
| `@nativesquare/soma/
|
|
635
|
+
| Import Path | Contents |
|
|
636
|
+
| -------------------------------- | ------------------------------------------------------------- |
|
|
637
|
+
| `@nativesquare/soma` | `Soma` client class |
|
|
638
|
+
| `@nativesquare/soma/healthkit` | Apple HealthKit → Soma transformers & types |
|
|
639
|
+
| `@nativesquare/soma/strava` | Strava API client, transformers, OAuth helpers, sync utilities |
|
|
640
|
+
| `@nativesquare/soma/react` | React hooks (coming soon) |
|
|
641
|
+
| `@nativesquare/soma/convex.config.js` | Component config for `convex.config.ts` |
|
|
401
642
|
|
|
402
643
|
## License
|
|
403
644
|
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
import type { ComponentApi } from "../component/_generated/component.js";
|
|
2
|
-
import type { MutationCtx, QueryCtx } from "./types.js";
|
|
2
|
+
import type { ActionCtx, MutationCtx, QueryCtx } from "./types.js";
|
|
3
3
|
export type SomaComponent = ComponentApi;
|
|
4
|
+
/**
|
|
5
|
+
* Configuration for the Strava integration.
|
|
6
|
+
*
|
|
7
|
+
* If not provided to the constructor, the Soma class will attempt to
|
|
8
|
+
* read `STRAVA_CLIENT_ID`, `STRAVA_CLIENT_SECRET`, and `STRAVA_BASE_URL`
|
|
9
|
+
* from environment variables automatically.
|
|
10
|
+
*/
|
|
11
|
+
export interface SomaStravaConfig {
|
|
12
|
+
/** Your Strava application's Client ID. */
|
|
13
|
+
clientId: string;
|
|
14
|
+
/** Your Strava application's Client Secret. */
|
|
15
|
+
clientSecret: string;
|
|
16
|
+
/**
|
|
17
|
+
* Base URL of the Strava API (without `/api/v3` suffix).
|
|
18
|
+
* Defaults to `https://www.strava.com`.
|
|
19
|
+
* Override to point at a mock server during development.
|
|
20
|
+
*/
|
|
21
|
+
baseUrl?: string;
|
|
22
|
+
}
|
|
4
23
|
/**
|
|
5
24
|
* Client class for the @nativesquare/soma Convex component.
|
|
6
25
|
*
|
|
@@ -16,21 +35,40 @@ export type SomaComponent = ComponentApi;
|
|
|
16
35
|
* import { Soma } from "@nativesquare/soma";
|
|
17
36
|
* import { components } from "./_generated/api";
|
|
18
37
|
*
|
|
38
|
+
* // Zero config if env vars are set in Convex dashboard:
|
|
39
|
+
* // STRAVA_CLIENT_ID, STRAVA_CLIENT_SECRET, STRAVA_BASE_URL (optional)
|
|
19
40
|
* const soma = new Soma(components.soma);
|
|
20
41
|
*
|
|
42
|
+
* // Or with explicit Strava config:
|
|
43
|
+
* // const soma = new Soma(components.soma, {
|
|
44
|
+
* // strava: { clientId: "...", clientSecret: "...", baseUrl: "..." },
|
|
45
|
+
* // });
|
|
46
|
+
*
|
|
21
47
|
* // Connect a user to a provider:
|
|
22
48
|
* const connectionId = await soma.connect(ctx, {
|
|
23
49
|
* userId: "user_123",
|
|
24
50
|
* provider: "GARMIN",
|
|
25
51
|
* });
|
|
26
52
|
*
|
|
27
|
-
* //
|
|
28
|
-
* const
|
|
53
|
+
* // Connect via Strava (handles OAuth, token storage, and initial sync):
|
|
54
|
+
* const result = await soma.connectStrava(ctx, { userId: "user_123", code: "..." });
|
|
29
55
|
* ```
|
|
30
56
|
*/
|
|
31
57
|
export declare class Soma {
|
|
32
58
|
component: SomaComponent;
|
|
33
|
-
|
|
59
|
+
private stravaConfig?;
|
|
60
|
+
constructor(component: SomaComponent, options?: {
|
|
61
|
+
strava?: SomaStravaConfig;
|
|
62
|
+
});
|
|
63
|
+
/**
|
|
64
|
+
* Read Strava config from environment variables.
|
|
65
|
+
* Returns undefined if the required vars are not set.
|
|
66
|
+
*/
|
|
67
|
+
private readStravaEnv;
|
|
68
|
+
/**
|
|
69
|
+
* Get the resolved Strava config, or throw a clear error if not configured.
|
|
70
|
+
*/
|
|
71
|
+
private requireStravaConfig;
|
|
34
72
|
/**
|
|
35
73
|
* Connect a user to a wearable provider.
|
|
36
74
|
*
|
|
@@ -406,6 +444,122 @@ export declare class Soma {
|
|
|
406
444
|
getAthlete(ctx: QueryCtx, args: {
|
|
407
445
|
connectionId: string;
|
|
408
446
|
}): Promise<any>;
|
|
447
|
+
/**
|
|
448
|
+
* Build the Strava OAuth authorization URL.
|
|
449
|
+
*
|
|
450
|
+
* This is a pure computation (no DB or HTTP calls), so it doesn't need
|
|
451
|
+
* a Convex context. Redirect the user to this URL to begin the OAuth flow.
|
|
452
|
+
*
|
|
453
|
+
* @param opts.redirectUri - The URL Strava will redirect to after authorization
|
|
454
|
+
* @param opts.scope - Comma-separated Strava OAuth scopes (default: "read,activity:read_all,profile:read_all")
|
|
455
|
+
* @param opts.state - Optional state parameter for CSRF protection
|
|
456
|
+
* @returns The authorization URL string
|
|
457
|
+
*
|
|
458
|
+
* @example
|
|
459
|
+
* ```ts
|
|
460
|
+
* const url = soma.getStravaAuthUrl({
|
|
461
|
+
* redirectUri: "https://your-app.com/api/strava/callback",
|
|
462
|
+
* });
|
|
463
|
+
* ```
|
|
464
|
+
*/
|
|
465
|
+
getStravaAuthUrl(opts: {
|
|
466
|
+
redirectUri: string;
|
|
467
|
+
scope?: string;
|
|
468
|
+
state?: string;
|
|
469
|
+
}): string;
|
|
470
|
+
/**
|
|
471
|
+
* Handle the Strava OAuth callback.
|
|
472
|
+
*
|
|
473
|
+
* Exchanges the authorization code for tokens, creates/reactivates the
|
|
474
|
+
* Soma connection, stores tokens securely in the component, syncs the
|
|
475
|
+
* athlete profile, and syncs all activities.
|
|
476
|
+
*
|
|
477
|
+
* Call this from your OAuth callback endpoint after receiving the `code`
|
|
478
|
+
* query parameter from Strava.
|
|
479
|
+
*
|
|
480
|
+
* @param ctx - Action context from the host app
|
|
481
|
+
* @param args.userId - The host app's user identifier
|
|
482
|
+
* @param args.code - The authorization code from the OAuth callback
|
|
483
|
+
* @param args.includeStreams - Fetch detailed streams per activity (default: false)
|
|
484
|
+
* @returns `{ connectionId, synced, errors }`
|
|
485
|
+
*
|
|
486
|
+
* @example
|
|
487
|
+
* ```ts
|
|
488
|
+
* export const handleStravaCallback = action({
|
|
489
|
+
* args: { userId: v.string(), code: v.string() },
|
|
490
|
+
* handler: async (ctx, { userId, code }) => {
|
|
491
|
+
* return await soma.connectStrava(ctx, { userId, code });
|
|
492
|
+
* },
|
|
493
|
+
* });
|
|
494
|
+
* ```
|
|
495
|
+
*/
|
|
496
|
+
connectStrava(ctx: ActionCtx, args: {
|
|
497
|
+
userId: string;
|
|
498
|
+
code: string;
|
|
499
|
+
includeStreams?: boolean;
|
|
500
|
+
}): Promise<{
|
|
501
|
+
connectionId: string;
|
|
502
|
+
errors: Array<{
|
|
503
|
+
activityId: number;
|
|
504
|
+
error: string;
|
|
505
|
+
}>;
|
|
506
|
+
synced: number;
|
|
507
|
+
}>;
|
|
508
|
+
/**
|
|
509
|
+
* Sync activities from Strava for an already-connected user.
|
|
510
|
+
*
|
|
511
|
+
* Automatically refreshes the access token if expired. Fetches the
|
|
512
|
+
* athlete profile and activities, transforms them, and ingests into Soma.
|
|
513
|
+
*
|
|
514
|
+
* @param ctx - Action context from the host app
|
|
515
|
+
* @param args.userId - The host app's user identifier
|
|
516
|
+
* @param args.includeStreams - Fetch detailed streams per activity (default: false)
|
|
517
|
+
* @param args.after - Only sync activities after this Unix epoch timestamp (for incremental sync)
|
|
518
|
+
* @returns `{ synced, errors }`
|
|
519
|
+
*
|
|
520
|
+
* @example
|
|
521
|
+
* ```ts
|
|
522
|
+
* export const syncStrava = action({
|
|
523
|
+
* args: { userId: v.string() },
|
|
524
|
+
* handler: async (ctx, { userId }) => {
|
|
525
|
+
* return await soma.syncStrava(ctx, { userId, includeStreams: true });
|
|
526
|
+
* },
|
|
527
|
+
* });
|
|
528
|
+
* ```
|
|
529
|
+
*/
|
|
530
|
+
syncStrava(ctx: ActionCtx, args: {
|
|
531
|
+
userId: string;
|
|
532
|
+
includeStreams?: boolean;
|
|
533
|
+
after?: number;
|
|
534
|
+
}): Promise<{
|
|
535
|
+
errors: Array<{
|
|
536
|
+
activityId: number;
|
|
537
|
+
error: string;
|
|
538
|
+
}>;
|
|
539
|
+
synced: number;
|
|
540
|
+
}>;
|
|
541
|
+
/**
|
|
542
|
+
* Disconnect a user from Strava.
|
|
543
|
+
*
|
|
544
|
+
* Revokes the token at Strava (best-effort), deletes stored tokens,
|
|
545
|
+
* and sets the connection to inactive.
|
|
546
|
+
*
|
|
547
|
+
* @param ctx - Action context from the host app
|
|
548
|
+
* @param args.userId - The host app's user identifier
|
|
549
|
+
*
|
|
550
|
+
* @example
|
|
551
|
+
* ```ts
|
|
552
|
+
* export const disconnectStrava = action({
|
|
553
|
+
* args: { userId: v.string() },
|
|
554
|
+
* handler: async (ctx, { userId }) => {
|
|
555
|
+
* await soma.disconnectStrava(ctx, { userId });
|
|
556
|
+
* },
|
|
557
|
+
* });
|
|
558
|
+
* ```
|
|
559
|
+
*/
|
|
560
|
+
disconnectStrava(ctx: ActionCtx, args: {
|
|
561
|
+
userId: string;
|
|
562
|
+
}): Promise<null>;
|
|
409
563
|
}
|
|
410
564
|
/**
|
|
411
565
|
* Common args shape for all ingestion methods.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
|