@nativesquare/soma 0.10.0 → 0.10.2
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 +85 -163
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +109 -130
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/component.d.ts +92 -35
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/garmin/private.d.ts +9 -0
- package/dist/component/garmin/private.d.ts.map +1 -1
- package/dist/component/garmin/private.js +49 -0
- 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 +683 -253
- 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/strava/public.d.ts +12 -52
- package/dist/component/strava/public.d.ts.map +1 -1
- package/dist/component/strava/public.js +16 -92
- package/dist/component/strava/public.js.map +1 -1
- package/dist/component/strava/transform/activity.js +15 -9
- package/dist/component/strava/transform/activity.js.map +1 -1
- package/package.json +1 -1
- package/src/client/index.ts +210 -158
- package/src/component/_generated/component.ts +165 -31
- package/src/component/garmin/private.ts +84 -0
- package/src/component/garmin/public.ts +804 -347
- package/src/component/garmin/utils.ts +17 -0
- package/src/component/strava/public.ts +17 -123
|
@@ -23,3 +23,20 @@ export function timeRangeQuery(params: TimeRangeParams, accessToken: string) {
|
|
|
23
23
|
token: accessToken,
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
export function buildTimeRangeQuery(
|
|
28
|
+
input: { startTimeInSeconds?: number; endTimeInSeconds?: number },
|
|
29
|
+
accessToken: string,
|
|
30
|
+
nowSeconds = Math.floor(Date.now() / 1000),
|
|
31
|
+
defaultSyncDays = 30,
|
|
32
|
+
) {
|
|
33
|
+
const uploadStartTimeInSeconds =
|
|
34
|
+
input.startTimeInSeconds ?? nowSeconds - defaultSyncDays * 86400;
|
|
35
|
+
const uploadEndTimeInSeconds = input.endTimeInSeconds ?? nowSeconds;
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
uploadStartTimeInSeconds: String(uploadStartTimeInSeconds),
|
|
39
|
+
uploadEndTimeInSeconds: String(uploadEndTimeInSeconds),
|
|
40
|
+
token: accessToken,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -28,20 +28,16 @@ import { transformAthlete } from "./transform/athlete.js";
|
|
|
28
28
|
/**
|
|
29
29
|
* Generate a Strava OAuth authorization URL.
|
|
30
30
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* when using `registerRoutes`.
|
|
35
|
-
*
|
|
36
|
-
* If `userId` is omitted, the host app must store the returned `state`
|
|
37
|
-
* itself and pass the userId to `connectStrava` manually.
|
|
31
|
+
* The state parameter is stored in the component's `pendingOAuth` table
|
|
32
|
+
* so that `completeStravaOAuth` can look it up automatically when the
|
|
33
|
+
* callback fires via `registerRoutes`.
|
|
38
34
|
*/
|
|
39
35
|
export const getStravaAuthUrl = action({
|
|
40
36
|
args: {
|
|
41
37
|
clientId: v.string(),
|
|
42
38
|
redirectUri: v.string(),
|
|
43
39
|
scope: v.optional(v.string()),
|
|
44
|
-
userId: v.
|
|
40
|
+
userId: v.string(),
|
|
45
41
|
},
|
|
46
42
|
handler: async (ctx, args) => {
|
|
47
43
|
const state = generateState();
|
|
@@ -53,103 +49,26 @@ export const getStravaAuthUrl = action({
|
|
|
53
49
|
state,
|
|
54
50
|
});
|
|
55
51
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
state,
|
|
60
|
-
userId: args.userId,
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return { authUrl, state };
|
|
65
|
-
},
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Full Strava OAuth callback handler (manual flow).
|
|
70
|
-
*
|
|
71
|
-
* Exchanges the authorization code for tokens, creates/reactivates the
|
|
72
|
-
* Soma connection, stores tokens securely, syncs the athlete profile,
|
|
73
|
-
* and syncs all activities.
|
|
74
|
-
*
|
|
75
|
-
* Used when the host app handles the callback itself and passes the
|
|
76
|
-
* userId directly (analogous to `connectGarmin`).
|
|
77
|
-
*
|
|
78
|
-
* Returns `{ connectionId, synced, errors }`.
|
|
79
|
-
*/
|
|
80
|
-
export const connectStrava = action({
|
|
81
|
-
args: {
|
|
82
|
-
userId: v.string(),
|
|
83
|
-
clientId: v.string(),
|
|
84
|
-
clientSecret: v.string(),
|
|
85
|
-
code: v.string(),
|
|
86
|
-
},
|
|
87
|
-
returns: v.object({
|
|
88
|
-
connectionId: v.string(),
|
|
89
|
-
synced: v.object({ athletes: v.number(), activities: v.number() }),
|
|
90
|
-
errors: v.array(
|
|
91
|
-
v.object({ type: v.string(), id: v.string(), error: v.string() }),
|
|
92
|
-
),
|
|
93
|
-
}),
|
|
94
|
-
handler: async (ctx, args): Promise<{
|
|
95
|
-
connectionId: Id<"connections">;
|
|
96
|
-
synced: { athletes: number; activities: number };
|
|
97
|
-
errors: Array<{ type: string; id: string; error: string }>;
|
|
98
|
-
}> => {
|
|
99
|
-
// 1. Exchange authorization code for tokens
|
|
100
|
-
const tokens = await exchangeCode({
|
|
101
|
-
clientId: args.clientId,
|
|
102
|
-
clientSecret: args.clientSecret,
|
|
103
|
-
code: args.code,
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
// 2. Create/reactivate the Soma connection
|
|
107
|
-
const connectionId: Id<"connections"> = await ctx.runMutation(
|
|
108
|
-
api.public.connect,
|
|
109
|
-
{
|
|
110
|
-
userId: args.userId,
|
|
111
|
-
provider: "STRAVA",
|
|
112
|
-
},
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
// 3. Store OAuth tokens in providerTokens table
|
|
116
|
-
await ctx.runMutation(
|
|
117
|
-
internal.strava.private.storeTokens,
|
|
118
|
-
{
|
|
119
|
-
connectionId,
|
|
120
|
-
accessToken: tokens.access_token,
|
|
121
|
-
refreshToken: tokens.refresh_token,
|
|
122
|
-
expiresAt: tokens.expires_at,
|
|
123
|
-
},
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
// 4. Sync all data types
|
|
127
|
-
const result = await ctx.runAction(api.strava.public.syncAllTypes, {
|
|
128
|
-
accessToken: tokens.access_token,
|
|
129
|
-
connectionId,
|
|
52
|
+
await ctx.runMutation(internal.strava.private.storePendingOAuth, {
|
|
53
|
+
provider: "STRAVA",
|
|
54
|
+
state,
|
|
130
55
|
userId: args.userId,
|
|
131
56
|
});
|
|
132
57
|
|
|
133
|
-
|
|
134
|
-
await ctx.runMutation(
|
|
135
|
-
api.public.updateConnection,
|
|
136
|
-
{
|
|
137
|
-
connectionId,
|
|
138
|
-
lastDataUpdate: new Date().toISOString(),
|
|
139
|
-
},
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
return { connectionId, synced: result.synced, errors: result.errors };
|
|
58
|
+
return { authUrl, state };
|
|
143
59
|
},
|
|
144
60
|
});
|
|
145
61
|
|
|
146
62
|
/**
|
|
147
63
|
* Complete a Strava OAuth flow using stored pending state.
|
|
148
64
|
*
|
|
149
|
-
*
|
|
150
|
-
* `code` and `state` from the redirect. The action looks
|
|
151
|
-
* state (userId) stored during `getStravaAuthUrl`,
|
|
152
|
-
* creates the connection,
|
|
65
|
+
* Called internally by `registerRoutes` — the callback handler calls
|
|
66
|
+
* this with the `code` and `state` from the redirect. The action looks
|
|
67
|
+
* up the pending state (userId) stored during `getStravaAuthUrl`,
|
|
68
|
+
* exchanges for tokens, creates the connection, stores tokens, and
|
|
69
|
+
* cleans up the pending entry.
|
|
70
|
+
*
|
|
71
|
+
* The host app is responsible for calling `syncStrava` afterwards.
|
|
153
72
|
*/
|
|
154
73
|
export const completeStravaOAuth = action({
|
|
155
74
|
args: {
|
|
@@ -161,16 +80,10 @@ export const completeStravaOAuth = action({
|
|
|
161
80
|
returns: v.object({
|
|
162
81
|
connectionId: v.string(),
|
|
163
82
|
userId: v.string(),
|
|
164
|
-
synced: v.object({ athletes: v.number(), activities: v.number() }),
|
|
165
|
-
errors: v.array(
|
|
166
|
-
v.object({ type: v.string(), id: v.string(), error: v.string() }),
|
|
167
|
-
),
|
|
168
83
|
}),
|
|
169
84
|
handler: async (ctx, args): Promise<{
|
|
170
85
|
connectionId: Id<"connections">;
|
|
171
86
|
userId: string;
|
|
172
|
-
synced: { athletes: number; activities: number };
|
|
173
|
-
errors: Array<{ type: string; id: string; error: string }>;
|
|
174
87
|
}> => {
|
|
175
88
|
// 1. Look up pending state
|
|
176
89
|
const pending: Doc<"pendingOAuth"> | null = await ctx.runQuery(
|
|
@@ -217,27 +130,9 @@ export const completeStravaOAuth = action({
|
|
|
217
130
|
},
|
|
218
131
|
);
|
|
219
132
|
|
|
220
|
-
// 6. Sync all data types
|
|
221
|
-
const result = await ctx.runAction(api.strava.public.syncAllTypes, {
|
|
222
|
-
accessToken: tokens.access_token,
|
|
223
|
-
connectionId,
|
|
224
|
-
userId: pending.userId,
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
// 7. Update lastDataUpdate timestamp
|
|
228
|
-
await ctx.runMutation(
|
|
229
|
-
api.public.updateConnection,
|
|
230
|
-
{
|
|
231
|
-
connectionId,
|
|
232
|
-
lastDataUpdate: new Date().toISOString(),
|
|
233
|
-
},
|
|
234
|
-
);
|
|
235
|
-
|
|
236
133
|
return {
|
|
237
134
|
connectionId,
|
|
238
135
|
userId: pending.userId,
|
|
239
|
-
synced: result.synced,
|
|
240
|
-
errors: result.errors,
|
|
241
136
|
};
|
|
242
137
|
},
|
|
243
138
|
});
|
|
@@ -275,7 +170,7 @@ export const syncStrava = action({
|
|
|
275
170
|
if (!connection) {
|
|
276
171
|
throw new Error(
|
|
277
172
|
`No Strava connection found for user "${args.userId}". ` +
|
|
278
|
-
"
|
|
173
|
+
"Connect to Strava first via getStravaAuthUrl.",
|
|
279
174
|
);
|
|
280
175
|
}
|
|
281
176
|
if (!connection.active) {
|
|
@@ -351,8 +246,7 @@ export const syncStrava = action({
|
|
|
351
246
|
/**
|
|
352
247
|
* Fetch and ingest all Strava data types for a connected user.
|
|
353
248
|
*
|
|
354
|
-
* Called by
|
|
355
|
-
* after obtaining a valid access token.
|
|
249
|
+
* Called by syncStrava after obtaining a valid access token.
|
|
356
250
|
*/
|
|
357
251
|
export const syncAllTypes = action({
|
|
358
252
|
args: {
|