@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 @@
1
+ {"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../src/validators.ts"],"names":[],"mappings":"AAkEA,eAAO,MAAM,mBAAmB;;;;;CAAuB,CAAC;AACxD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;CAAwC,CAAC;AACtE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAyC,CAAC;AACxE,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAqC,CAAC;AAChE,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAsC,CAAC;AAClE,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BAAkC,CAAC;AACxD,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BAAmC,CAAC;AAC1D,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BAAmC,CAAC;AAC1D,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BAA0C,CAAC;AACxE,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BAAuC,CAAC;AAClE,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BAA4C,CAAC"}
@@ -0,0 +1,78 @@
1
+ // ─── Soma Validators ─────────────────────────────────────────────────────────
2
+ // Re-exports the Convex validators for each Soma table so host apps can use
3
+ // them for type-safe argument validation in their own mutations.
4
+ //
5
+ // Two flavors are exported for each table:
6
+ //
7
+ // *Validator — Full validator including connectionId (v.string()) and userId.
8
+ // Use when the mutation args map 1:1 to a soma.ingestX() call.
9
+ //
10
+ // *Data — Data-only validator (no connectionId/userId).
11
+ // Use when connection is resolved server-side and the client
12
+ // only sends the health data payload.
13
+ //
14
+ // ─── Examples ────────────────────────────────────────────────────────────────
15
+ //
16
+ // Direct ingest (client provides connectionId):
17
+ //
18
+ // import { activityValidator } from "@nativesquare/soma/validators";
19
+ //
20
+ // export const storeActivity = mutation({
21
+ // args: activityValidator,
22
+ // handler: async (ctx, args) => {
23
+ // await soma.ingestActivity(ctx, args);
24
+ // },
25
+ // });
26
+ //
27
+ // Batch sync (connection resolved server-side):
28
+ //
29
+ // import { activityData } from "@nativesquare/soma/validators";
30
+ //
31
+ // export const syncHealthKit = mutation({
32
+ // args: { activities: v.array(v.object(activityData)) },
33
+ // handler: async (ctx, args) => {
34
+ // const connectionId = await soma.connect(ctx, { userId, provider: "HEALTHKIT" });
35
+ // for (const activity of args.activities) {
36
+ // await soma.ingestActivity(ctx, { connectionId, userId, ...activity });
37
+ // }
38
+ // },
39
+ // });
40
+ import { v } from "convex/values";
41
+ import { connectionValidator as _connectionValidator } from "./component/validators/connection.js";
42
+ import { athleteValidator as _athleteValidator } from "./component/validators/athlete.js";
43
+ import { activityValidator as _activityValidator } from "./component/validators/activity.js";
44
+ import { bodyValidator as _bodyValidator } from "./component/validators/body.js";
45
+ import { dailyValidator as _dailyValidator } from "./component/validators/daily.js";
46
+ import { sleepValidator as _sleepValidator } from "./component/validators/sleep.js";
47
+ import { menstruationValidator as _menstruationValidator } from "./component/validators/menstruation.js";
48
+ import { nutritionValidator as _nutritionValidator } from "./component/validators/nutrition.js";
49
+ import { plannedWorkoutValidator as _plannedWorkoutValidator } from "./component/validators/plannedWorkout.js";
50
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
51
+ const asString = { connectionId: v.string() };
52
+ function stripConnection(validator) {
53
+ const { connectionId, userId, ...rest } = validator;
54
+ return rest;
55
+ }
56
+ // ─── Full validators (connectionId as v.string() + userId) ───────────────────
57
+ export const connectionValidator = _connectionValidator;
58
+ export const athleteValidator = { ..._athleteValidator, ...asString };
59
+ export const activityValidator = { ..._activityValidator, ...asString };
60
+ export const bodyValidator = { ..._bodyValidator, ...asString };
61
+ export const dailyValidator = { ..._dailyValidator, ...asString };
62
+ export const sleepValidator = { ..._sleepValidator, ...asString };
63
+ export const menstruationValidator = { ..._menstruationValidator, ...asString };
64
+ export const nutritionValidator = { ..._nutritionValidator, ...asString };
65
+ export const plannedWorkoutValidator = {
66
+ ..._plannedWorkoutValidator,
67
+ ...asString,
68
+ };
69
+ // ─── Data-only validators (no connectionId / userId) ─────────────────────────
70
+ export const athleteData = stripConnection(_athleteValidator);
71
+ export const activityData = stripConnection(_activityValidator);
72
+ export const bodyData = stripConnection(_bodyValidator);
73
+ export const dailyData = stripConnection(_dailyValidator);
74
+ export const sleepData = stripConnection(_sleepValidator);
75
+ export const menstruationData = stripConnection(_menstruationValidator);
76
+ export const nutritionData = stripConnection(_nutritionValidator);
77
+ export const plannedWorkoutData = stripConnection(_plannedWorkoutValidator);
78
+ //# sourceMappingURL=validators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validators.js","sourceRoot":"","sources":["../src/validators.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "bugs": {
7
7
  "url": "https://github.com/NativeSquare/soma/issues"
8
8
  },
9
- "version": "0.1.2",
9
+ "version": "0.3.0",
10
10
  "license": "Apache-2.0",
11
11
  "keywords": [
12
12
  "convex",
@@ -53,6 +53,14 @@
53
53
  "types": "./dist/healthkit/index.d.ts",
54
54
  "default": "./dist/healthkit/index.js"
55
55
  },
56
+ "./strava": {
57
+ "types": "./dist/strava/index.d.ts",
58
+ "default": "./dist/strava/index.js"
59
+ },
60
+ "./validators": {
61
+ "types": "./dist/validators.d.ts",
62
+ "default": "./dist/validators.js"
63
+ },
56
64
  "./test": "./src/test.ts",
57
65
  "./_generated/component.js": {
58
66
  "types": "./dist/component/_generated/component.d.ts"
@@ -1,8 +1,29 @@
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
+ import { buildAuthUrl } from "../strava/auth.js";
3
4
 
4
5
  export type SomaComponent = ComponentApi;
5
6
 
7
+ /**
8
+ * Configuration for the Strava integration.
9
+ *
10
+ * If not provided to the constructor, the Soma class will attempt to
11
+ * read `STRAVA_CLIENT_ID`, `STRAVA_CLIENT_SECRET`, and `STRAVA_BASE_URL`
12
+ * from environment variables automatically.
13
+ */
14
+ export interface SomaStravaConfig {
15
+ /** Your Strava application's Client ID. */
16
+ clientId: string;
17
+ /** Your Strava application's Client Secret. */
18
+ clientSecret: string;
19
+ /**
20
+ * Base URL of the Strava API (without `/api/v3` suffix).
21
+ * Defaults to `https://www.strava.com`.
22
+ * Override to point at a mock server during development.
23
+ */
24
+ baseUrl?: string;
25
+ }
26
+
6
27
  /**
7
28
  * Client class for the @nativesquare/soma Convex component.
8
29
  *
@@ -18,20 +39,63 @@ export type SomaComponent = ComponentApi;
18
39
  * import { Soma } from "@nativesquare/soma";
19
40
  * import { components } from "./_generated/api";
20
41
  *
42
+ * // Zero config if env vars are set in Convex dashboard:
43
+ * // STRAVA_CLIENT_ID, STRAVA_CLIENT_SECRET, STRAVA_BASE_URL (optional)
21
44
  * const soma = new Soma(components.soma);
22
45
  *
46
+ * // Or with explicit Strava config:
47
+ * // const soma = new Soma(components.soma, {
48
+ * // strava: { clientId: "...", clientSecret: "...", baseUrl: "..." },
49
+ * // });
50
+ *
23
51
  * // Connect a user to a provider:
24
52
  * const connectionId = await soma.connect(ctx, {
25
53
  * userId: "user_123",
26
54
  * provider: "GARMIN",
27
55
  * });
28
56
  *
29
- * // List all connections:
30
- * const connections = await soma.listConnections(ctx, { userId: "user_123" });
57
+ * // Connect via Strava (handles OAuth, token storage, and initial sync):
58
+ * const result = await soma.connectStrava(ctx, { userId: "user_123", code: "..." });
31
59
  * ```
32
60
  */
33
61
  export class Soma {
34
- constructor(public component: SomaComponent) { }
62
+ private stravaConfig?: SomaStravaConfig;
63
+
64
+ constructor(
65
+ public component: SomaComponent,
66
+ options?: { strava?: SomaStravaConfig },
67
+ ) {
68
+ this.stravaConfig = options?.strava ?? this.readStravaEnv();
69
+ }
70
+
71
+ /**
72
+ * Read Strava config from environment variables.
73
+ * Returns undefined if the required vars are not set.
74
+ */
75
+ private readStravaEnv(): SomaStravaConfig | undefined {
76
+ const clientId = process.env.STRAVA_CLIENT_ID;
77
+ const clientSecret = process.env.STRAVA_CLIENT_SECRET;
78
+ if (!clientId || !clientSecret) return undefined;
79
+ return {
80
+ clientId,
81
+ clientSecret,
82
+ baseUrl: process.env.STRAVA_BASE_URL,
83
+ };
84
+ }
85
+
86
+ /**
87
+ * Get the resolved Strava config, or throw a clear error if not configured.
88
+ */
89
+ private requireStravaConfig(): SomaStravaConfig {
90
+ if (!this.stravaConfig) {
91
+ throw new Error(
92
+ "Strava is not configured. Either set STRAVA_CLIENT_ID and " +
93
+ "STRAVA_CLIENT_SECRET environment variables in the Convex dashboard, " +
94
+ "or pass { strava: { clientId, clientSecret } } to the Soma constructor.",
95
+ );
96
+ }
97
+ return this.stravaConfig;
98
+ }
35
99
 
36
100
  // ─── Connect / Disconnect ───────────────────────────────────────────────────
37
101
 
@@ -612,6 +676,150 @@ export class Soma {
612
676
  ) {
613
677
  return await ctx.runQuery(this.component.public.getAthlete, args);
614
678
  }
679
+
680
+ // ─── Strava Integration ──────────────────────────────────────────────────────
681
+ // High-level methods that handle OAuth, token storage, and data syncing
682
+ // for Strava. Requires Strava credentials to be configured either via
683
+ // environment variables or the constructor.
684
+
685
+ /**
686
+ * Build the Strava OAuth authorization URL.
687
+ *
688
+ * This is a pure computation (no DB or HTTP calls), so it doesn't need
689
+ * a Convex context. Redirect the user to this URL to begin the OAuth flow.
690
+ *
691
+ * @param opts.redirectUri - The URL Strava will redirect to after authorization
692
+ * @param opts.scope - Comma-separated Strava OAuth scopes (default: "read,activity:read_all,profile:read_all")
693
+ * @param opts.state - Optional state parameter for CSRF protection
694
+ * @returns The authorization URL string
695
+ *
696
+ * @example
697
+ * ```ts
698
+ * const url = soma.getStravaAuthUrl({
699
+ * redirectUri: "https://your-app.com/api/strava/callback",
700
+ * });
701
+ * ```
702
+ */
703
+ getStravaAuthUrl(opts: {
704
+ redirectUri: string;
705
+ scope?: string;
706
+ state?: string;
707
+ }): string {
708
+ const config = this.requireStravaConfig();
709
+ return buildAuthUrl({
710
+ clientId: config.clientId,
711
+ redirectUri: opts.redirectUri,
712
+ scope: opts.scope,
713
+ state: opts.state,
714
+ baseUrl: config.baseUrl,
715
+ });
716
+ }
717
+
718
+ /**
719
+ * Handle the Strava OAuth callback.
720
+ *
721
+ * Exchanges the authorization code for tokens, creates/reactivates the
722
+ * Soma connection, stores tokens securely in the component, syncs the
723
+ * athlete profile, and syncs all activities.
724
+ *
725
+ * Call this from your OAuth callback endpoint after receiving the `code`
726
+ * query parameter from Strava.
727
+ *
728
+ * @param ctx - Action context from the host app
729
+ * @param args.userId - The host app's user identifier
730
+ * @param args.code - The authorization code from the OAuth callback
731
+ * @param args.includeStreams - Fetch detailed streams per activity (default: false)
732
+ * @returns `{ connectionId, synced, errors }`
733
+ *
734
+ * @example
735
+ * ```ts
736
+ * export const handleStravaCallback = action({
737
+ * args: { userId: v.string(), code: v.string() },
738
+ * handler: async (ctx, { userId, code }) => {
739
+ * return await soma.connectStrava(ctx, { userId, code });
740
+ * },
741
+ * });
742
+ * ```
743
+ */
744
+ async connectStrava(
745
+ ctx: ActionCtx,
746
+ args: { userId: string; code: string; includeStreams?: boolean },
747
+ ) {
748
+ const config = this.requireStravaConfig();
749
+ return await ctx.runAction(this.component.strava.connectStrava, {
750
+ ...args,
751
+ clientId: config.clientId,
752
+ clientSecret: config.clientSecret,
753
+ baseUrl: config.baseUrl,
754
+ });
755
+ }
756
+
757
+ /**
758
+ * Sync activities from Strava for an already-connected user.
759
+ *
760
+ * Automatically refreshes the access token if expired. Fetches the
761
+ * athlete profile and activities, transforms them, and ingests into Soma.
762
+ *
763
+ * @param ctx - Action context from the host app
764
+ * @param args.userId - The host app's user identifier
765
+ * @param args.includeStreams - Fetch detailed streams per activity (default: false)
766
+ * @param args.after - Only sync activities after this Unix epoch timestamp (for incremental sync)
767
+ * @returns `{ synced, errors }`
768
+ *
769
+ * @example
770
+ * ```ts
771
+ * export const syncStrava = action({
772
+ * args: { userId: v.string() },
773
+ * handler: async (ctx, { userId }) => {
774
+ * return await soma.syncStrava(ctx, { userId, includeStreams: true });
775
+ * },
776
+ * });
777
+ * ```
778
+ */
779
+ async syncStrava(
780
+ ctx: ActionCtx,
781
+ args: { userId: string; includeStreams?: boolean; after?: number },
782
+ ) {
783
+ const config = this.requireStravaConfig();
784
+ return await ctx.runAction(this.component.strava.syncStrava, {
785
+ ...args,
786
+ clientId: config.clientId,
787
+ clientSecret: config.clientSecret,
788
+ baseUrl: config.baseUrl,
789
+ });
790
+ }
791
+
792
+ /**
793
+ * Disconnect a user from Strava.
794
+ *
795
+ * Revokes the token at Strava (best-effort), deletes stored tokens,
796
+ * and sets the connection to inactive.
797
+ *
798
+ * @param ctx - Action context from the host app
799
+ * @param args.userId - The host app's user identifier
800
+ *
801
+ * @example
802
+ * ```ts
803
+ * export const disconnectStrava = action({
804
+ * args: { userId: v.string() },
805
+ * handler: async (ctx, { userId }) => {
806
+ * await soma.disconnectStrava(ctx, { userId });
807
+ * },
808
+ * });
809
+ * ```
810
+ */
811
+ async disconnectStrava(
812
+ ctx: ActionCtx,
813
+ args: { userId: string },
814
+ ) {
815
+ const config = this.requireStravaConfig();
816
+ return await ctx.runAction(this.component.strava.disconnectStrava, {
817
+ ...args,
818
+ clientId: config.clientId,
819
+ clientSecret: config.clientSecret,
820
+ baseUrl: config.baseUrl,
821
+ });
822
+ }
615
823
  }
616
824
 
617
825
  // ─── Shared Types ────────────────────────────────────────────────────────────
@@ -10,6 +10,7 @@
10
10
 
11
11
  import type * as private_ from "../private.js";
12
12
  import type * as public_ from "../public.js";
13
+ import type * as strava from "../strava.js";
13
14
  import type * as validators_activity from "../validators/activity.js";
14
15
  import type * as validators_athlete from "../validators/athlete.js";
15
16
  import type * as validators_body from "../validators/body.js";
@@ -34,6 +35,7 @@ import { anyApi, componentsGeneric } from "convex/server";
34
35
  const fullApi: ApiFromModules<{
35
36
  private: typeof private_;
36
37
  public: typeof public_;
38
+ strava: typeof strava;
37
39
  "validators/activity": typeof validators_activity;
38
40
  "validators/athlete": typeof validators_athlete;
39
41
  "validators/body": typeof validators_body;
@@ -1293,4 +1293,53 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
1293
1293
  Name
1294
1294
  >;
1295
1295
  };
1296
+ strava: {
1297
+ connectStrava: FunctionReference<
1298
+ "action",
1299
+ "internal",
1300
+ {
1301
+ baseUrl?: string;
1302
+ clientId: string;
1303
+ clientSecret: string;
1304
+ code: string;
1305
+ includeStreams?: boolean;
1306
+ userId: string;
1307
+ },
1308
+ {
1309
+ connectionId: string;
1310
+ errors: Array<{ activityId: number; error: string }>;
1311
+ synced: number;
1312
+ },
1313
+ Name
1314
+ >;
1315
+ disconnectStrava: FunctionReference<
1316
+ "action",
1317
+ "internal",
1318
+ {
1319
+ baseUrl?: string;
1320
+ clientId: string;
1321
+ clientSecret: string;
1322
+ userId: string;
1323
+ },
1324
+ null,
1325
+ Name
1326
+ >;
1327
+ syncStrava: FunctionReference<
1328
+ "action",
1329
+ "internal",
1330
+ {
1331
+ after?: number;
1332
+ baseUrl?: string;
1333
+ clientId: string;
1334
+ clientSecret: string;
1335
+ includeStreams?: boolean;
1336
+ userId: string;
1337
+ },
1338
+ {
1339
+ errors: Array<{ activityId: number; error: string }>;
1340
+ synced: number;
1341
+ },
1342
+ Name
1343
+ >;
1344
+ };
1296
1345
  };
@@ -113,4 +113,15 @@ export default defineSchema({
113
113
  .index("by_connectionId", ["connectionId"])
114
114
  .index("by_userId", ["userId"])
115
115
  .index("by_userId_plannedDate", ["userId", "metadata.planned_date"]),
116
+
117
+ // ── Provider Tokens ────────────────────────────────────────────────────────
118
+ // OAuth tokens for cloud-based providers (Strava, Garmin, etc.).
119
+ // Stored separately from connections to keep the connection table
120
+ // provider-agnostic. One token record per connection.
121
+ providerTokens: defineTable({
122
+ connectionId: v.id("connections"),
123
+ accessToken: v.string(),
124
+ refreshToken: v.string(),
125
+ expiresAt: v.number(), // Unix epoch seconds
126
+ }).index("by_connectionId", ["connectionId"]),
116
127
  });