@nativesquare/soma 0.9.4 → 0.10.1
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/dist/client/index.d.ts +124 -144
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +157 -134
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +18 -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 +113 -22
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/garmin/auth.d.ts +0 -4
- package/dist/component/garmin/auth.d.ts.map +1 -1
- package/dist/component/garmin/auth.js +0 -8
- package/dist/component/garmin/auth.js.map +1 -1
- package/dist/component/garmin/private.d.ts +10 -1
- package/dist/component/garmin/private.d.ts.map +1 -1
- package/dist/component/garmin/private.js +49 -9
- package/dist/component/garmin/private.js.map +1 -1
- package/dist/component/garmin/public.d.ts +237 -62
- package/dist/component/garmin/public.d.ts.map +1 -1
- package/dist/component/garmin/public.js +689 -254
- package/dist/component/garmin/public.js.map +1 -1
- package/dist/component/garmin/utils.d.ts +8 -0
- package/dist/component/garmin/utils.d.ts.map +1 -1
- package/dist/component/garmin/utils.js +9 -0
- package/dist/component/garmin/utils.js.map +1 -1
- package/dist/component/schema.d.ts +2 -2
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +5 -3
- package/dist/component/schema.js.map +1 -1
- package/dist/{strava → component/strava}/auth.d.ts +15 -48
- package/dist/component/strava/auth.d.ts.map +1 -0
- package/dist/{strava → component/strava}/auth.js +4 -39
- package/dist/component/strava/auth.js.map +1 -0
- package/dist/component/strava/client.d.ts +8 -0
- package/dist/component/strava/client.d.ts.map +1 -0
- package/dist/component/strava/client.js +18 -0
- package/dist/component/strava/client.js.map +1 -0
- package/dist/component/strava/private.d.ts +19 -0
- package/dist/component/strava/private.d.ts.map +1 -1
- package/dist/component/strava/private.js +52 -2
- package/dist/component/strava/private.js.map +1 -1
- package/dist/component/strava/public.d.ts +54 -19
- package/dist/component/strava/public.d.ts.map +1 -1
- package/dist/component/strava/public.js +159 -109
- package/dist/component/strava/public.js.map +1 -1
- package/dist/component/strava/transform/activity.d.ts +19 -0
- package/dist/component/strava/transform/activity.d.ts.map +1 -0
- package/dist/{strava → component/strava/transform}/activity.js +31 -45
- package/dist/component/strava/transform/activity.js.map +1 -0
- package/dist/{strava → component/strava/transform}/athlete.d.ts +4 -10
- package/dist/component/strava/transform/athlete.d.ts.map +1 -0
- package/dist/{strava → component/strava/transform}/athlete.js +2 -8
- package/dist/component/strava/transform/athlete.js.map +1 -0
- package/dist/component/strava/transform/maps/sportType.d.ts +7 -0
- package/dist/component/strava/transform/maps/sportType.d.ts.map +1 -0
- package/dist/{strava/maps/sport-type.js → component/strava/transform/maps/sportType.js} +4 -2
- package/dist/component/strava/transform/maps/sportType.js.map +1 -0
- package/dist/component/strava/types/stravaApi/client/client.gen.d.ts +3 -0
- package/dist/component/strava/types/stravaApi/client/client.gen.d.ts.map +1 -0
- package/dist/component/strava/types/stravaApi/client/client.gen.js +236 -0
- package/dist/component/strava/types/stravaApi/client/client.gen.js.map +1 -0
- package/dist/component/strava/types/stravaApi/client/index.d.ts +9 -0
- package/dist/component/strava/types/stravaApi/client/index.d.ts.map +1 -0
- package/dist/component/strava/types/stravaApi/client/index.js +7 -0
- package/dist/component/strava/types/stravaApi/client/index.js.map +1 -0
- package/dist/component/strava/types/stravaApi/client/types.gen.d.ts +118 -0
- package/dist/component/strava/types/stravaApi/client/types.gen.d.ts.map +1 -0
- package/dist/component/strava/types/stravaApi/client/types.gen.js +3 -0
- package/dist/component/strava/types/stravaApi/client/types.gen.js.map +1 -0
- package/dist/component/strava/types/stravaApi/client/utils.gen.d.ts +34 -0
- package/dist/component/strava/types/stravaApi/client/utils.gen.d.ts.map +1 -0
- package/dist/component/strava/types/stravaApi/client/utils.gen.js +229 -0
- package/dist/component/strava/types/stravaApi/client/utils.gen.js.map +1 -0
- package/dist/component/strava/types/stravaApi/client.gen.d.ts +13 -0
- package/dist/component/strava/types/stravaApi/client.gen.d.ts.map +1 -0
- package/dist/component/strava/types/stravaApi/client.gen.js +4 -0
- package/dist/component/strava/types/stravaApi/client.gen.js.map +1 -0
- package/dist/component/strava/types/stravaApi/core/auth.gen.d.ts +19 -0
- package/dist/component/strava/types/stravaApi/core/auth.gen.d.ts.map +1 -0
- package/dist/component/strava/types/stravaApi/core/auth.gen.js +15 -0
- package/dist/component/strava/types/stravaApi/core/auth.gen.js.map +1 -0
- package/dist/component/strava/types/stravaApi/core/bodySerializer.gen.d.ts +26 -0
- package/dist/component/strava/types/stravaApi/core/bodySerializer.gen.d.ts.map +1 -0
- package/dist/component/strava/types/stravaApi/core/bodySerializer.gen.js +58 -0
- package/dist/component/strava/types/stravaApi/core/bodySerializer.gen.js.map +1 -0
- package/dist/component/strava/types/stravaApi/core/params.gen.d.ts +44 -0
- package/dist/component/strava/types/stravaApi/core/params.gen.d.ts.map +1 -0
- package/dist/component/strava/types/stravaApi/core/params.gen.js +101 -0
- package/dist/component/strava/types/stravaApi/core/params.gen.js.map +1 -0
- package/dist/component/strava/types/stravaApi/core/pathSerializer.gen.d.ts +34 -0
- package/dist/component/strava/types/stravaApi/core/pathSerializer.gen.d.ts.map +1 -0
- package/dist/component/strava/types/stravaApi/core/pathSerializer.gen.js +107 -0
- package/dist/component/strava/types/stravaApi/core/pathSerializer.gen.js.map +1 -0
- package/dist/component/strava/types/stravaApi/core/queryKeySerializer.gen.d.ts +19 -0
- package/dist/component/strava/types/stravaApi/core/queryKeySerializer.gen.d.ts.map +1 -0
- package/dist/component/strava/types/stravaApi/core/queryKeySerializer.gen.js +93 -0
- package/dist/component/strava/types/stravaApi/core/queryKeySerializer.gen.js.map +1 -0
- package/dist/component/strava/types/stravaApi/core/serverSentEvents.gen.d.ts +72 -0
- package/dist/component/strava/types/stravaApi/core/serverSentEvents.gen.d.ts.map +1 -0
- package/dist/component/strava/types/stravaApi/core/serverSentEvents.gen.js +134 -0
- package/dist/component/strava/types/stravaApi/core/serverSentEvents.gen.js.map +1 -0
- package/dist/component/strava/types/stravaApi/core/types.gen.d.ts +79 -0
- package/dist/component/strava/types/stravaApi/core/types.gen.d.ts.map +1 -0
- package/dist/component/strava/types/stravaApi/core/types.gen.js +3 -0
- package/dist/component/strava/types/stravaApi/core/types.gen.js.map +1 -0
- package/dist/component/strava/types/stravaApi/core/utils.gen.d.ts +20 -0
- package/dist/component/strava/types/stravaApi/core/utils.gen.d.ts.map +1 -0
- package/dist/component/strava/types/stravaApi/core/utils.gen.js +88 -0
- package/dist/component/strava/types/stravaApi/core/utils.gen.js.map +1 -0
- package/dist/component/strava/types/stravaApi/index.d.ts +3 -0
- package/dist/component/strava/types/stravaApi/index.d.ts.map +1 -0
- package/dist/component/strava/types/stravaApi/index.js +3 -0
- package/dist/component/strava/types/stravaApi/index.js.map +1 -0
- package/dist/component/strava/types/stravaApi/sdk.gen.d.ts +224 -0
- package/dist/component/strava/types/stravaApi/sdk.gen.d.ts.map +1 -0
- package/dist/component/strava/types/stravaApi/sdk.gen.js +361 -0
- package/dist/component/strava/types/stravaApi/sdk.gen.js.map +1 -0
- package/dist/component/strava/types/stravaApi/types.gen.d.ts +2209 -0
- package/dist/component/strava/types/stravaApi/types.gen.d.ts.map +1 -0
- package/dist/component/strava/types/stravaApi/types.gen.js +3 -0
- package/dist/component/strava/types/stravaApi/types.gen.js.map +1 -0
- package/dist/component/strava/types/stravaApi/zod.gen.d.ts +5332 -0
- package/dist/component/strava/types/stravaApi/zod.gen.d.ts.map +1 -0
- package/dist/component/strava/types/stravaApi/zod.gen.js +1009 -0
- package/dist/component/strava/types/stravaApi/zod.gen.js.map +1 -0
- package/dist/component/strava/utils.d.ts +15 -0
- package/dist/component/strava/utils.d.ts.map +1 -0
- package/dist/component/strava/utils.js +36 -0
- package/dist/component/strava/utils.js.map +1 -0
- package/dist/component/utils.d.ts +5 -0
- package/dist/component/utils.d.ts.map +1 -0
- package/dist/component/utils.js +11 -0
- package/dist/component/utils.js.map +1 -0
- package/package.json +131 -130
- package/src/client/index.ts +285 -164
- package/src/component/_generated/api.ts +18 -0
- package/src/component/_generated/component.ts +191 -24
- package/src/component/garmin/auth.ts +0 -9
- package/src/component/garmin/private.ts +84 -12
- package/src/component/garmin/public.ts +812 -348
- package/src/component/garmin/utils.ts +17 -0
- package/src/component/schema.ts +5 -3
- package/src/{strava → component/strava}/auth.ts +143 -185
- package/src/component/strava/client.ts +20 -0
- package/src/component/strava/private.ts +147 -89
- package/src/component/strava/public.ts +191 -139
- package/src/{strava → component/strava/transform}/activity.ts +256 -276
- package/src/{strava → component/strava/transform}/athlete.ts +41 -47
- package/src/{strava/maps/sport-type.ts → component/strava/transform/maps/sportType.ts} +100 -99
- package/src/component/strava/types/specs/strava-api.json +4796 -0
- package/src/component/strava/types/stravaApi/client/client.gen.ts +290 -0
- package/src/component/strava/types/stravaApi/client/index.ts +25 -0
- package/src/component/strava/types/stravaApi/client/types.gen.ts +214 -0
- package/src/component/strava/types/stravaApi/client/utils.gen.ts +316 -0
- package/src/component/strava/types/stravaApi/client.gen.ts +16 -0
- package/src/component/strava/types/stravaApi/core/auth.gen.ts +41 -0
- package/src/component/strava/types/stravaApi/core/bodySerializer.gen.ts +82 -0
- package/src/component/strava/types/stravaApi/core/params.gen.ts +169 -0
- package/src/component/strava/types/stravaApi/core/pathSerializer.gen.ts +171 -0
- package/src/component/strava/types/stravaApi/core/queryKeySerializer.gen.ts +117 -0
- package/src/component/strava/types/stravaApi/core/serverSentEvents.gen.ts +243 -0
- package/src/component/strava/types/stravaApi/core/types.gen.ts +104 -0
- package/src/component/strava/types/stravaApi/core/utils.gen.ts +140 -0
- package/src/component/strava/types/stravaApi/index.ts +4 -0
- package/src/component/strava/types/stravaApi/sdk.gen.ts +410 -0
- package/src/component/strava/types/stravaApi/types.gen.ts +2435 -0
- package/src/component/strava/types/stravaApi/zod.gen.ts +1132 -0
- package/src/component/strava/utils.ts +52 -0
- package/src/component/utils.ts +11 -0
- package/dist/strava/activity.d.ts +0 -121
- package/dist/strava/activity.d.ts.map +0 -1
- package/dist/strava/activity.js.map +0 -1
- package/dist/strava/athlete.d.ts.map +0 -1
- package/dist/strava/athlete.js.map +0 -1
- package/dist/strava/auth.d.ts.map +0 -1
- package/dist/strava/auth.js.map +0 -1
- package/dist/strava/client.d.ts +0 -93
- package/dist/strava/client.d.ts.map +0 -1
- package/dist/strava/client.js +0 -158
- package/dist/strava/client.js.map +0 -1
- package/dist/strava/index.d.ts +0 -13
- package/dist/strava/index.d.ts.map +0 -1
- package/dist/strava/index.js +0 -17
- package/dist/strava/index.js.map +0 -1
- package/dist/strava/maps/sport-type.d.ts +0 -7
- package/dist/strava/maps/sport-type.d.ts.map +0 -1
- package/dist/strava/maps/sport-type.js.map +0 -1
- package/dist/strava/sync.d.ts +0 -104
- package/dist/strava/sync.d.ts.map +0 -1
- package/dist/strava/sync.js +0 -87
- package/dist/strava/sync.js.map +0 -1
- package/dist/strava/types.d.ts +0 -266
- package/dist/strava/types.d.ts.map +0 -1
- package/dist/strava/types.js +0 -8
- package/dist/strava/types.js.map +0 -1
- package/src/strava/activity.test.ts +0 -415
- package/src/strava/athlete.test.ts +0 -139
- package/src/strava/auth.test.ts +0 -78
- package/src/strava/client.ts +0 -212
- package/src/strava/index.ts +0 -54
- package/src/strava/maps/sport-type.test.ts +0 -69
- package/src/strava/sync.ts +0 -168
- package/src/strava/types.ts +0 -361
package/src/client/index.ts
CHANGED
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
type GenericDataModel,
|
|
8
8
|
type HttpRouter,
|
|
9
9
|
} from "convex/server";
|
|
10
|
-
import { buildAuthUrl } from "../strava/auth.js";
|
|
11
10
|
|
|
12
11
|
export type SomaComponent = ComponentApi;
|
|
13
12
|
|
|
@@ -30,12 +29,6 @@ export interface SomaStravaConfig {
|
|
|
30
29
|
clientId: string;
|
|
31
30
|
/** Your Strava application's Client Secret. */
|
|
32
31
|
clientSecret: string;
|
|
33
|
-
/**
|
|
34
|
-
* Base URL of the Strava API (without `/api/v3` suffix).
|
|
35
|
-
* Defaults to `https://www.strava.com`.
|
|
36
|
-
* Override to point at a mock server during development.
|
|
37
|
-
*/
|
|
38
|
-
baseUrl?: string;
|
|
39
32
|
}
|
|
40
33
|
|
|
41
34
|
/**
|
|
@@ -73,7 +66,7 @@ export interface SomaGarminConfig {
|
|
|
73
66
|
*
|
|
74
67
|
* // Or with explicit Strava config:
|
|
75
68
|
* // const soma = new Soma(components.soma, {
|
|
76
|
-
* // strava: { clientId: "...", clientSecret: "..."
|
|
69
|
+
* // strava: { clientId: "...", clientSecret: "..." },
|
|
77
70
|
* // });
|
|
78
71
|
*
|
|
79
72
|
* // Connect a user to a provider:
|
|
@@ -82,8 +75,11 @@ export interface SomaGarminConfig {
|
|
|
82
75
|
* provider: "GARMIN",
|
|
83
76
|
* });
|
|
84
77
|
*
|
|
85
|
-
* //
|
|
86
|
-
* const
|
|
78
|
+
* // Start Strava OAuth (redirects user, callback handled by registerRoutes):
|
|
79
|
+
* const { authUrl } = await soma.getStravaAuthUrl(ctx, {
|
|
80
|
+
* userId: "user_123",
|
|
81
|
+
* redirectUri: "https://your-app.convex.site/api/strava/callback",
|
|
82
|
+
* });
|
|
87
83
|
* ```
|
|
88
84
|
*/
|
|
89
85
|
export class Soma {
|
|
@@ -109,7 +105,6 @@ export class Soma {
|
|
|
109
105
|
return {
|
|
110
106
|
clientId,
|
|
111
107
|
clientSecret,
|
|
112
|
-
baseUrl: process.env.STRAVA_BASE_URL,
|
|
113
108
|
};
|
|
114
109
|
}
|
|
115
110
|
|
|
@@ -885,76 +880,40 @@ export class Soma {
|
|
|
885
880
|
// environment variables or the constructor.
|
|
886
881
|
|
|
887
882
|
/**
|
|
888
|
-
*
|
|
883
|
+
* Generate a Strava OAuth authorization URL.
|
|
889
884
|
*
|
|
890
|
-
*
|
|
891
|
-
*
|
|
885
|
+
* The state parameter is stored inside the component automatically,
|
|
886
|
+
* and the callback handler registered by `registerRoutes` will
|
|
887
|
+
* complete the flow without further host-app intervention.
|
|
892
888
|
*
|
|
889
|
+
* @param ctx - Action context from the host app
|
|
890
|
+
* @param opts.userId - The host app's user identifier
|
|
893
891
|
* @param opts.redirectUri - The URL Strava will redirect to after authorization
|
|
894
892
|
* @param opts.scope - Comma-separated Strava OAuth scopes (default: "read,activity:read_all,profile:read_all")
|
|
895
|
-
* @
|
|
896
|
-
* @returns The authorization URL string
|
|
893
|
+
* @returns `{ authUrl, state }`
|
|
897
894
|
*
|
|
898
895
|
* @example
|
|
899
896
|
* ```ts
|
|
900
|
-
* const
|
|
901
|
-
*
|
|
897
|
+
* const { authUrl } = await soma.getStravaAuthUrl(ctx, {
|
|
898
|
+
* userId: "user_123",
|
|
899
|
+
* redirectUri: "https://your-app.convex.site/api/strava/callback",
|
|
902
900
|
* });
|
|
901
|
+
* // Redirect user to authUrl — the callback is handled automatically
|
|
903
902
|
* ```
|
|
904
903
|
*/
|
|
905
|
-
getStravaAuthUrl(
|
|
906
|
-
|
|
907
|
-
scope?: string
|
|
908
|
-
|
|
909
|
-
}): string {
|
|
904
|
+
async getStravaAuthUrl(
|
|
905
|
+
ctx: ActionCtx,
|
|
906
|
+
opts: { userId: string; redirectUri: string; scope?: string },
|
|
907
|
+
) {
|
|
910
908
|
const config = this.requireStravaConfig();
|
|
911
|
-
return
|
|
909
|
+
return await ctx.runAction(this.component.strava.public.getStravaAuthUrl, {
|
|
912
910
|
clientId: config.clientId,
|
|
913
911
|
redirectUri: opts.redirectUri,
|
|
914
912
|
scope: opts.scope,
|
|
915
|
-
|
|
916
|
-
baseUrl: config.baseUrl,
|
|
913
|
+
userId: opts.userId,
|
|
917
914
|
});
|
|
918
915
|
}
|
|
919
916
|
|
|
920
|
-
/**
|
|
921
|
-
* Handle the Strava OAuth callback.
|
|
922
|
-
*
|
|
923
|
-
* Exchanges the authorization code for tokens, creates/reactivates the
|
|
924
|
-
* Soma connection, stores tokens securely in the component, syncs the
|
|
925
|
-
* athlete profile, and syncs all activities.
|
|
926
|
-
*
|
|
927
|
-
* Call this from your OAuth callback endpoint after receiving the `code`
|
|
928
|
-
* query parameter from Strava.
|
|
929
|
-
*
|
|
930
|
-
* @param ctx - Action context from the host app
|
|
931
|
-
* @param args.userId - The host app's user identifier
|
|
932
|
-
* @param args.code - The authorization code from the OAuth callback
|
|
933
|
-
* @param args.includeStreams - Fetch detailed streams per activity (default: false)
|
|
934
|
-
* @returns `{ connectionId, synced, errors }`
|
|
935
|
-
*
|
|
936
|
-
* @example
|
|
937
|
-
* ```ts
|
|
938
|
-
* export const handleStravaCallback = action({
|
|
939
|
-
* args: { userId: v.string(), code: v.string() },
|
|
940
|
-
* handler: async (ctx, { userId, code }) => {
|
|
941
|
-
* return await soma.connectStrava(ctx, { userId, code });
|
|
942
|
-
* },
|
|
943
|
-
* });
|
|
944
|
-
* ```
|
|
945
|
-
*/
|
|
946
|
-
async connectStrava(
|
|
947
|
-
ctx: ActionCtx,
|
|
948
|
-
args: { userId: string; code: string; includeStreams?: boolean },
|
|
949
|
-
) {
|
|
950
|
-
const config = this.requireStravaConfig();
|
|
951
|
-
return await ctx.runAction(this.component.strava.public.connectStrava, {
|
|
952
|
-
...args,
|
|
953
|
-
clientId: config.clientId,
|
|
954
|
-
clientSecret: config.clientSecret,
|
|
955
|
-
baseUrl: config.baseUrl,
|
|
956
|
-
});
|
|
957
|
-
}
|
|
958
917
|
|
|
959
918
|
/**
|
|
960
919
|
* Sync activities from Strava for an already-connected user.
|
|
@@ -964,7 +923,6 @@ export class Soma {
|
|
|
964
923
|
*
|
|
965
924
|
* @param ctx - Action context from the host app
|
|
966
925
|
* @param args.userId - The host app's user identifier
|
|
967
|
-
* @param args.includeStreams - Fetch detailed streams per activity (default: false)
|
|
968
926
|
* @param args.after - Only sync activities after this Unix epoch timestamp (for incremental sync)
|
|
969
927
|
* @returns `{ synced, errors }`
|
|
970
928
|
*
|
|
@@ -973,21 +931,20 @@ export class Soma {
|
|
|
973
931
|
* export const syncStrava = action({
|
|
974
932
|
* args: { userId: v.string() },
|
|
975
933
|
* handler: async (ctx, { userId }) => {
|
|
976
|
-
* return await soma.syncStrava(ctx, { userId
|
|
934
|
+
* return await soma.syncStrava(ctx, { userId });
|
|
977
935
|
* },
|
|
978
936
|
* });
|
|
979
937
|
* ```
|
|
980
938
|
*/
|
|
981
939
|
async syncStrava(
|
|
982
940
|
ctx: ActionCtx,
|
|
983
|
-
args: { userId: string;
|
|
941
|
+
args: { userId: string; after?: number },
|
|
984
942
|
) {
|
|
985
943
|
const config = this.requireStravaConfig();
|
|
986
944
|
return await ctx.runAction(this.component.strava.public.syncStrava, {
|
|
987
945
|
...args,
|
|
988
946
|
clientId: config.clientId,
|
|
989
947
|
clientSecret: config.clientSecret,
|
|
990
|
-
baseUrl: config.baseUrl,
|
|
991
948
|
});
|
|
992
949
|
}
|
|
993
950
|
|
|
@@ -1019,7 +976,6 @@ export class Soma {
|
|
|
1019
976
|
...args,
|
|
1020
977
|
clientId: config.clientId,
|
|
1021
978
|
clientSecret: config.clientSecret,
|
|
1022
|
-
baseUrl: config.baseUrl,
|
|
1023
979
|
});
|
|
1024
980
|
}
|
|
1025
981
|
|
|
@@ -1031,20 +987,13 @@ export class Soma {
|
|
|
1031
987
|
/**
|
|
1032
988
|
* Generate a Garmin OAuth 2.0 authorization URL with PKCE.
|
|
1033
989
|
*
|
|
1034
|
-
*
|
|
1035
|
-
* and
|
|
1036
|
-
*
|
|
1037
|
-
* If `userId` is provided, the PKCE state is stored inside the component
|
|
1038
|
-
* automatically, and the callback handler registered by `registerRoutes`
|
|
1039
|
-
* will complete the flow without further host-app intervention. This is
|
|
1040
|
-
* the recommended approach.
|
|
1041
|
-
*
|
|
1042
|
-
* If `userId` is omitted, the host app must store the returned
|
|
1043
|
-
* `codeVerifier` itself and pass it to `connectGarmin` manually.
|
|
990
|
+
* The PKCE state is stored inside the component automatically,
|
|
991
|
+
* and the callback handler registered by `registerRoutes` will
|
|
992
|
+
* complete the flow without further host-app intervention.
|
|
1044
993
|
*
|
|
1045
994
|
* @param ctx - Action context from the host app
|
|
995
|
+
* @param opts.userId - The host app's user identifier
|
|
1046
996
|
* @param opts.redirectUri - The URL Garmin will redirect to after authorization
|
|
1047
|
-
* @param opts.userId - The host app's user identifier (required for `registerRoutes` flow)
|
|
1048
997
|
* @returns `{ authUrl, state, codeVerifier }`
|
|
1049
998
|
*
|
|
1050
999
|
* @example
|
|
@@ -1058,7 +1007,7 @@ export class Soma {
|
|
|
1058
1007
|
*/
|
|
1059
1008
|
async getGarminAuthUrl(
|
|
1060
1009
|
ctx: ActionCtx,
|
|
1061
|
-
opts: {
|
|
1010
|
+
opts: { userId: string; redirectUri?: string },
|
|
1062
1011
|
) {
|
|
1063
1012
|
const config = this.requireGarminConfig();
|
|
1064
1013
|
return await ctx.runAction(this.component.garmin.public.getGarminAuthUrl, {
|
|
@@ -1068,73 +1017,208 @@ export class Soma {
|
|
|
1068
1017
|
});
|
|
1069
1018
|
}
|
|
1070
1019
|
|
|
1071
|
-
|
|
1072
|
-
* Handle the Garmin OAuth 2.0 callback (manual flow).
|
|
1073
|
-
*
|
|
1074
|
-
* Exchanges the authorization code for tokens, creates/reactivates the
|
|
1075
|
-
* Soma connection, stores tokens securely, and syncs the last 30 days
|
|
1076
|
-
* of all data types.
|
|
1077
|
-
*
|
|
1078
|
-
* Call this from your OAuth callback endpoint after receiving the `code`
|
|
1079
|
-
* query parameter from Garmin.
|
|
1080
|
-
*
|
|
1081
|
-
* @param ctx - Action context from the host app
|
|
1082
|
-
* @param args.userId - The host app's user identifier
|
|
1083
|
-
* @param args.code - The authorization code from the callback
|
|
1084
|
-
* @param args.codeVerifier - The PKCE code verifier from Step 1
|
|
1085
|
-
* @param args.redirectUri - The redirect URI used in the authorization request
|
|
1086
|
-
* @returns `{ connectionId, synced, errors }`
|
|
1087
|
-
*
|
|
1088
|
-
* @example
|
|
1089
|
-
* ```ts
|
|
1090
|
-
* export const handleGarminCallback = action({
|
|
1091
|
-
* args: {
|
|
1092
|
-
* userId: v.string(),
|
|
1093
|
-
* code: v.string(),
|
|
1094
|
-
* codeVerifier: v.string(),
|
|
1095
|
-
* },
|
|
1096
|
-
* handler: async (ctx, args) => {
|
|
1097
|
-
* return await soma.connectGarmin(ctx, args);
|
|
1098
|
-
* },
|
|
1099
|
-
* });
|
|
1100
|
-
* ```
|
|
1101
|
-
*/
|
|
1102
|
-
async connectGarmin(
|
|
1020
|
+
async pullGarminActivities(
|
|
1103
1021
|
ctx: ActionCtx,
|
|
1104
1022
|
args: {
|
|
1105
1023
|
userId: string;
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
redirectUri?: string;
|
|
1024
|
+
startTimeInSeconds?: number;
|
|
1025
|
+
endTimeInSeconds?: number;
|
|
1109
1026
|
},
|
|
1110
1027
|
) {
|
|
1111
1028
|
const config = this.requireGarminConfig();
|
|
1112
|
-
return await ctx.runAction(this.component.garmin.public.
|
|
1029
|
+
return await ctx.runAction(this.component.garmin.public.pullActivities, {
|
|
1113
1030
|
...args,
|
|
1114
1031
|
clientId: config.clientId,
|
|
1115
1032
|
clientSecret: config.clientSecret,
|
|
1116
1033
|
});
|
|
1117
1034
|
}
|
|
1118
1035
|
|
|
1119
|
-
|
|
1120
|
-
* Complete a Garmin OAuth 2.0 flow using stored pending state.
|
|
1121
|
-
*
|
|
1122
|
-
* This is called automatically by the `registerRoutes` callback handler.
|
|
1123
|
-
* It looks up the pending OAuth state stored during `getGarminAuthUrl`,
|
|
1124
|
-
* exchanges for tokens, creates the connection, and syncs data.
|
|
1125
|
-
*
|
|
1126
|
-
* @param ctx - Action context from the host app
|
|
1127
|
-
* @param args.code - The authorization code from the callback query params
|
|
1128
|
-
* @param args.state - The state parameter from the callback query params
|
|
1129
|
-
* @param args.redirectUri - The redirect URI used in the authorization request
|
|
1130
|
-
* @returns `{ connectionId, synced, errors }`
|
|
1131
|
-
*/
|
|
1132
|
-
async completeGarminOAuth(
|
|
1036
|
+
async pullGarminDailies(
|
|
1133
1037
|
ctx: ActionCtx,
|
|
1134
|
-
args: {
|
|
1038
|
+
args: {
|
|
1039
|
+
userId: string;
|
|
1040
|
+
startTimeInSeconds?: number;
|
|
1041
|
+
endTimeInSeconds?: number;
|
|
1042
|
+
},
|
|
1043
|
+
) {
|
|
1044
|
+
const config = this.requireGarminConfig();
|
|
1045
|
+
return await ctx.runAction(this.component.garmin.public.pullDailies, {
|
|
1046
|
+
...args,
|
|
1047
|
+
clientId: config.clientId,
|
|
1048
|
+
clientSecret: config.clientSecret,
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
async pullGarminSleep(
|
|
1053
|
+
ctx: ActionCtx,
|
|
1054
|
+
args: {
|
|
1055
|
+
userId: string;
|
|
1056
|
+
startTimeInSeconds?: number;
|
|
1057
|
+
endTimeInSeconds?: number;
|
|
1058
|
+
},
|
|
1059
|
+
) {
|
|
1060
|
+
const config = this.requireGarminConfig();
|
|
1061
|
+
return await ctx.runAction(this.component.garmin.public.pullSleep, {
|
|
1062
|
+
...args,
|
|
1063
|
+
clientId: config.clientId,
|
|
1064
|
+
clientSecret: config.clientSecret,
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
async pullGarminBody(
|
|
1069
|
+
ctx: ActionCtx,
|
|
1070
|
+
args: {
|
|
1071
|
+
userId: string;
|
|
1072
|
+
startTimeInSeconds?: number;
|
|
1073
|
+
endTimeInSeconds?: number;
|
|
1074
|
+
},
|
|
1075
|
+
) {
|
|
1076
|
+
const config = this.requireGarminConfig();
|
|
1077
|
+
return await ctx.runAction(this.component.garmin.public.pullBody, {
|
|
1078
|
+
...args,
|
|
1079
|
+
clientId: config.clientId,
|
|
1080
|
+
clientSecret: config.clientSecret,
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
async pullGarminMenstruation(
|
|
1085
|
+
ctx: ActionCtx,
|
|
1086
|
+
args: {
|
|
1087
|
+
userId: string;
|
|
1088
|
+
startTimeInSeconds?: number;
|
|
1089
|
+
endTimeInSeconds?: number;
|
|
1090
|
+
},
|
|
1091
|
+
) {
|
|
1092
|
+
const config = this.requireGarminConfig();
|
|
1093
|
+
return await ctx.runAction(this.component.garmin.public.pullMenstruation, {
|
|
1094
|
+
...args,
|
|
1095
|
+
clientId: config.clientId,
|
|
1096
|
+
clientSecret: config.clientSecret,
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
async pullGarminBloodPressures(
|
|
1101
|
+
ctx: ActionCtx,
|
|
1102
|
+
args: {
|
|
1103
|
+
userId: string;
|
|
1104
|
+
startTimeInSeconds?: number;
|
|
1105
|
+
endTimeInSeconds?: number;
|
|
1106
|
+
},
|
|
1107
|
+
) {
|
|
1108
|
+
const config = this.requireGarminConfig();
|
|
1109
|
+
return await ctx.runAction(this.component.garmin.public.pullBloodPressures, {
|
|
1110
|
+
...args,
|
|
1111
|
+
clientId: config.clientId,
|
|
1112
|
+
clientSecret: config.clientSecret,
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
async pullGarminSkinTemperature(
|
|
1117
|
+
ctx: ActionCtx,
|
|
1118
|
+
args: {
|
|
1119
|
+
userId: string;
|
|
1120
|
+
startTimeInSeconds?: number;
|
|
1121
|
+
endTimeInSeconds?: number;
|
|
1122
|
+
},
|
|
1135
1123
|
) {
|
|
1136
1124
|
const config = this.requireGarminConfig();
|
|
1137
|
-
return await ctx.runAction(this.component.garmin.public.
|
|
1125
|
+
return await ctx.runAction(this.component.garmin.public.pullSkinTemperature, {
|
|
1126
|
+
...args,
|
|
1127
|
+
clientId: config.clientId,
|
|
1128
|
+
clientSecret: config.clientSecret,
|
|
1129
|
+
});
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
async pullGarminUserMetrics(
|
|
1133
|
+
ctx: ActionCtx,
|
|
1134
|
+
args: {
|
|
1135
|
+
userId: string;
|
|
1136
|
+
startTimeInSeconds?: number;
|
|
1137
|
+
endTimeInSeconds?: number;
|
|
1138
|
+
},
|
|
1139
|
+
) {
|
|
1140
|
+
const config = this.requireGarminConfig();
|
|
1141
|
+
return await ctx.runAction(this.component.garmin.public.pullUserMetrics, {
|
|
1142
|
+
...args,
|
|
1143
|
+
clientId: config.clientId,
|
|
1144
|
+
clientSecret: config.clientSecret,
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
async pullGarminHRV(
|
|
1149
|
+
ctx: ActionCtx,
|
|
1150
|
+
args: {
|
|
1151
|
+
userId: string;
|
|
1152
|
+
startTimeInSeconds?: number;
|
|
1153
|
+
endTimeInSeconds?: number;
|
|
1154
|
+
},
|
|
1155
|
+
) {
|
|
1156
|
+
const config = this.requireGarminConfig();
|
|
1157
|
+
return await ctx.runAction(this.component.garmin.public.pullHRV, {
|
|
1158
|
+
...args,
|
|
1159
|
+
clientId: config.clientId,
|
|
1160
|
+
clientSecret: config.clientSecret,
|
|
1161
|
+
});
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
async pullGarminStressDetails(
|
|
1165
|
+
ctx: ActionCtx,
|
|
1166
|
+
args: {
|
|
1167
|
+
userId: string;
|
|
1168
|
+
startTimeInSeconds?: number;
|
|
1169
|
+
endTimeInSeconds?: number;
|
|
1170
|
+
},
|
|
1171
|
+
) {
|
|
1172
|
+
const config = this.requireGarminConfig();
|
|
1173
|
+
return await ctx.runAction(this.component.garmin.public.pullStressDetails, {
|
|
1174
|
+
...args,
|
|
1175
|
+
clientId: config.clientId,
|
|
1176
|
+
clientSecret: config.clientSecret,
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
async pullGarminPulseOx(
|
|
1181
|
+
ctx: ActionCtx,
|
|
1182
|
+
args: {
|
|
1183
|
+
userId: string;
|
|
1184
|
+
startTimeInSeconds?: number;
|
|
1185
|
+
endTimeInSeconds?: number;
|
|
1186
|
+
},
|
|
1187
|
+
) {
|
|
1188
|
+
const config = this.requireGarminConfig();
|
|
1189
|
+
return await ctx.runAction(this.component.garmin.public.pullPulseOx, {
|
|
1190
|
+
...args,
|
|
1191
|
+
clientId: config.clientId,
|
|
1192
|
+
clientSecret: config.clientSecret,
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
async pullGarminRespiration(
|
|
1197
|
+
ctx: ActionCtx,
|
|
1198
|
+
args: {
|
|
1199
|
+
userId: string;
|
|
1200
|
+
startTimeInSeconds?: number;
|
|
1201
|
+
endTimeInSeconds?: number;
|
|
1202
|
+
},
|
|
1203
|
+
) {
|
|
1204
|
+
const config = this.requireGarminConfig();
|
|
1205
|
+
return await ctx.runAction(this.component.garmin.public.pullRespiration, {
|
|
1206
|
+
...args,
|
|
1207
|
+
clientId: config.clientId,
|
|
1208
|
+
clientSecret: config.clientSecret,
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
async pullGarminAll(
|
|
1213
|
+
ctx: ActionCtx,
|
|
1214
|
+
args: {
|
|
1215
|
+
userId: string;
|
|
1216
|
+
startTimeInSeconds?: number;
|
|
1217
|
+
endTimeInSeconds?: number;
|
|
1218
|
+
},
|
|
1219
|
+
) {
|
|
1220
|
+
const config = this.requireGarminConfig();
|
|
1221
|
+
return await ctx.runAction(this.component.garmin.public.pullAll, {
|
|
1138
1222
|
...args,
|
|
1139
1223
|
clientId: config.clientId,
|
|
1140
1224
|
clientSecret: config.clientSecret,
|
|
@@ -1253,28 +1337,38 @@ type PaginateTimeRangeArgs = TimeRangeArgs & {
|
|
|
1253
1337
|
/**
|
|
1254
1338
|
* Per-provider options for `registerRoutes`.
|
|
1255
1339
|
*/
|
|
1256
|
-
export interface
|
|
1340
|
+
export interface StravaOAuthOptions {
|
|
1257
1341
|
/** HTTP path for the OAuth callback. @default "/api/strava/callback" */
|
|
1258
1342
|
path?: string;
|
|
1259
1343
|
/** Override STRAVA_CLIENT_ID env var. */
|
|
1260
1344
|
clientId?: string;
|
|
1261
1345
|
/** Override STRAVA_CLIENT_SECRET env var. */
|
|
1262
1346
|
clientSecret?: string;
|
|
1263
|
-
/** Override STRAVA_BASE_URL env var. */
|
|
1264
|
-
baseUrl?: string;
|
|
1265
1347
|
/** URL to redirect the user to after a successful connection. */
|
|
1266
|
-
|
|
1348
|
+
redirectTo?: string;
|
|
1349
|
+
/** Called after Strava OAuth completes and the connection is established. */
|
|
1350
|
+
onComplete?: (
|
|
1351
|
+
ctx: GenericActionCtx<GenericDataModel>,
|
|
1352
|
+
event: StravaConnectEvent,
|
|
1353
|
+
) => Promise<void>;
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
// ─── Strava Callback Event Types ────────────────────────────────────────────
|
|
1357
|
+
|
|
1358
|
+
/** Data passed to `onComplete` after Strava OAuth completes. */
|
|
1359
|
+
export interface StravaConnectEvent {
|
|
1360
|
+
provider: "STRAVA";
|
|
1361
|
+
userId: string;
|
|
1362
|
+
connectionId: string;
|
|
1267
1363
|
}
|
|
1268
1364
|
|
|
1269
1365
|
// ─── Garmin Callback Event Types ─────────────────────────────────────────────
|
|
1270
1366
|
|
|
1271
|
-
/** Data passed to `oauth.onComplete` after Garmin OAuth
|
|
1367
|
+
/** Data passed to `oauth.onComplete` after Garmin OAuth completes. */
|
|
1272
1368
|
export interface GarminConnectEvent {
|
|
1273
1369
|
provider: "GARMIN";
|
|
1274
1370
|
userId: string;
|
|
1275
1371
|
connectionId: string;
|
|
1276
|
-
synced: Record<string, number>;
|
|
1277
|
-
errors: Array<{ type: string; id: string; error: string }>;
|
|
1278
1372
|
}
|
|
1279
1373
|
|
|
1280
1374
|
/** Data passed to webhook `events` handlers and `onEvent` after data ingestion. */
|
|
@@ -1297,7 +1391,7 @@ export interface GarminOAuthOptions {
|
|
|
1297
1391
|
clientSecret?: string;
|
|
1298
1392
|
/** URL to redirect the user to after a successful connection. */
|
|
1299
1393
|
redirectTo?: string;
|
|
1300
|
-
/** Called after Garmin OAuth completes and
|
|
1394
|
+
/** Called after Garmin OAuth completes and the connection is established. */
|
|
1301
1395
|
onComplete?: (
|
|
1302
1396
|
ctx: GenericActionCtx<GenericDataModel>,
|
|
1303
1397
|
event: GarminConnectEvent,
|
|
@@ -1327,7 +1421,10 @@ export interface GarminWebhookOptions {
|
|
|
1327
1421
|
}
|
|
1328
1422
|
|
|
1329
1423
|
export interface RegisterRoutesOptions {
|
|
1330
|
-
strava?:
|
|
1424
|
+
strava?: {
|
|
1425
|
+
/** OAuth callback configuration. */
|
|
1426
|
+
oauth?: StravaOAuthOptions;
|
|
1427
|
+
};
|
|
1331
1428
|
garmin?: {
|
|
1332
1429
|
/** OAuth callback configuration. */
|
|
1333
1430
|
oauth?: GarminOAuthOptions;
|
|
@@ -1341,8 +1438,8 @@ export interface RegisterRoutesOptions {
|
|
|
1341
1438
|
*
|
|
1342
1439
|
* Call this from your `convex/http.ts` to set up the callback endpoints
|
|
1343
1440
|
* that Strava and Garmin redirect to after user authorization. The handlers
|
|
1344
|
-
* complete the OAuth exchange, create the connection, and
|
|
1345
|
-
*
|
|
1441
|
+
* complete the OAuth exchange, create the connection, and store tokens.
|
|
1442
|
+
* The host app is responsible for calling sync separately.
|
|
1346
1443
|
*
|
|
1347
1444
|
* When called with no `opts`, registers both Strava and Garmin routes with
|
|
1348
1445
|
* default paths and credentials from environment variables. When `opts` is
|
|
@@ -1369,14 +1466,23 @@ export interface RegisterRoutesOptions {
|
|
|
1369
1466
|
* // With Garmin OAuth callbacks and per-type webhook handlers
|
|
1370
1467
|
* registerRoutes(http, components.soma, {
|
|
1371
1468
|
* strava: {
|
|
1372
|
-
*
|
|
1373
|
-
*
|
|
1469
|
+
* oauth: {
|
|
1470
|
+
* path: "/oauth/strava/callback",
|
|
1471
|
+
* redirectTo: "https://myapp.com/settings",
|
|
1472
|
+
* onComplete: async (ctx, event) => {
|
|
1473
|
+
* // Runs after OAuth completes and connection is established
|
|
1474
|
+
* await ctx.runMutation(internal.users.markConnected, {
|
|
1475
|
+
* userId: event.userId,
|
|
1476
|
+
* provider: event.provider,
|
|
1477
|
+
* });
|
|
1478
|
+
* },
|
|
1479
|
+
* },
|
|
1374
1480
|
* },
|
|
1375
1481
|
* garmin: {
|
|
1376
1482
|
* oauth: {
|
|
1377
1483
|
* redirectTo: "https://myapp.com/settings",
|
|
1378
1484
|
* onComplete: async (ctx, event) => {
|
|
1379
|
-
* // Runs after OAuth
|
|
1485
|
+
* // Runs after OAuth completes and connection is established
|
|
1380
1486
|
* await ctx.runMutation(internal.users.markConnected, {
|
|
1381
1487
|
* userId: event.userId,
|
|
1382
1488
|
* provider: event.provider,
|
|
@@ -1406,8 +1512,9 @@ export function registerRoutes(
|
|
|
1406
1512
|
const registerAll = opts === undefined;
|
|
1407
1513
|
|
|
1408
1514
|
if (registerAll || opts?.strava) {
|
|
1409
|
-
const
|
|
1410
|
-
const
|
|
1515
|
+
const stravaOpts = opts?.strava ?? {};
|
|
1516
|
+
const oauth = stravaOpts.oauth ?? {};
|
|
1517
|
+
const path = oauth.path ?? STRAVA_CALLBACK_PATH;
|
|
1411
1518
|
|
|
1412
1519
|
http.route({
|
|
1413
1520
|
path,
|
|
@@ -1415,23 +1522,23 @@ export function registerRoutes(
|
|
|
1415
1522
|
handler: httpActionGeneric(async (ctx, request) => {
|
|
1416
1523
|
const url = new URL(request.url);
|
|
1417
1524
|
const code = url.searchParams.get("code");
|
|
1418
|
-
const
|
|
1525
|
+
const state = url.searchParams.get("state");
|
|
1419
1526
|
|
|
1420
1527
|
if (!code) {
|
|
1421
1528
|
return new Response("Missing authorization code", { status: 400 });
|
|
1422
1529
|
}
|
|
1423
|
-
if (!
|
|
1530
|
+
if (!state) {
|
|
1424
1531
|
return new Response(
|
|
1425
|
-
"Missing state parameter
|
|
1426
|
-
"
|
|
1532
|
+
"Missing state parameter. Ensure the state was included " +
|
|
1533
|
+
"when building the Strava auth URL via getStravaAuthUrl.",
|
|
1427
1534
|
{ status: 400 },
|
|
1428
1535
|
);
|
|
1429
1536
|
}
|
|
1430
1537
|
|
|
1431
1538
|
const clientId =
|
|
1432
|
-
|
|
1539
|
+
oauth.clientId ?? process.env.STRAVA_CLIENT_ID;
|
|
1433
1540
|
const clientSecret =
|
|
1434
|
-
|
|
1541
|
+
oauth.clientSecret ?? process.env.STRAVA_CLIENT_SECRET;
|
|
1435
1542
|
|
|
1436
1543
|
if (!clientId || !clientSecret) {
|
|
1437
1544
|
return new Response(
|
|
@@ -1441,13 +1548,16 @@ export function registerRoutes(
|
|
|
1441
1548
|
);
|
|
1442
1549
|
}
|
|
1443
1550
|
|
|
1551
|
+
let result: {
|
|
1552
|
+
connectionId: string;
|
|
1553
|
+
userId: string;
|
|
1554
|
+
};
|
|
1444
1555
|
try {
|
|
1445
|
-
await ctx.runAction(component.strava.public.
|
|
1446
|
-
userId,
|
|
1556
|
+
result = await ctx.runAction(component.strava.public.completeStravaOAuth, {
|
|
1447
1557
|
code,
|
|
1558
|
+
state,
|
|
1448
1559
|
clientId,
|
|
1449
1560
|
clientSecret,
|
|
1450
|
-
baseUrl: strava.baseUrl ?? process.env.STRAVA_BASE_URL,
|
|
1451
1561
|
});
|
|
1452
1562
|
} catch (error) {
|
|
1453
1563
|
const message =
|
|
@@ -1457,10 +1567,25 @@ export function registerRoutes(
|
|
|
1457
1567
|
});
|
|
1458
1568
|
}
|
|
1459
1569
|
|
|
1460
|
-
if (
|
|
1570
|
+
if (oauth.onComplete) {
|
|
1571
|
+
try {
|
|
1572
|
+
await oauth.onComplete(ctx, {
|
|
1573
|
+
provider: "STRAVA",
|
|
1574
|
+
userId: result.userId,
|
|
1575
|
+
connectionId: result.connectionId,
|
|
1576
|
+
});
|
|
1577
|
+
} catch (callbackError) {
|
|
1578
|
+
console.error(
|
|
1579
|
+
"[soma] strava onComplete callback error:",
|
|
1580
|
+
callbackError instanceof Error ? callbackError.message : callbackError,
|
|
1581
|
+
);
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
if (oauth.redirectTo) {
|
|
1461
1586
|
return new Response(null, {
|
|
1462
1587
|
status: 302,
|
|
1463
|
-
headers: { Location:
|
|
1588
|
+
headers: { Location: oauth.redirectTo },
|
|
1464
1589
|
});
|
|
1465
1590
|
}
|
|
1466
1591
|
|
|
@@ -1513,8 +1638,6 @@ export function registerRoutes(
|
|
|
1513
1638
|
let result: {
|
|
1514
1639
|
connectionId: string;
|
|
1515
1640
|
userId: string;
|
|
1516
|
-
synced: Record<string, number>;
|
|
1517
|
-
errors: Array<{ type: string; id: string; error: string }>;
|
|
1518
1641
|
};
|
|
1519
1642
|
try {
|
|
1520
1643
|
result = await ctx.runAction(component.garmin.public.completeGarminOAuth, {
|
|
@@ -1537,8 +1660,6 @@ export function registerRoutes(
|
|
|
1537
1660
|
provider: "GARMIN",
|
|
1538
1661
|
userId: result.userId,
|
|
1539
1662
|
connectionId: result.connectionId,
|
|
1540
|
-
synced: result.synced,
|
|
1541
|
-
errors: result.errors,
|
|
1542
1663
|
});
|
|
1543
1664
|
} catch (callbackError) {
|
|
1544
1665
|
console.error(
|