@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.
Files changed (31) hide show
  1. package/dist/client/index.d.ts +85 -163
  2. package/dist/client/index.d.ts.map +1 -1
  3. package/dist/client/index.js +109 -130
  4. package/dist/client/index.js.map +1 -1
  5. package/dist/component/_generated/component.d.ts +92 -35
  6. package/dist/component/_generated/component.d.ts.map +1 -1
  7. package/dist/component/garmin/private.d.ts +9 -0
  8. package/dist/component/garmin/private.d.ts.map +1 -1
  9. package/dist/component/garmin/private.js +49 -0
  10. package/dist/component/garmin/private.js.map +1 -1
  11. package/dist/component/garmin/public.d.ts +237 -62
  12. package/dist/component/garmin/public.d.ts.map +1 -1
  13. package/dist/component/garmin/public.js +683 -253
  14. package/dist/component/garmin/public.js.map +1 -1
  15. package/dist/component/garmin/utils.d.ts +8 -0
  16. package/dist/component/garmin/utils.d.ts.map +1 -1
  17. package/dist/component/garmin/utils.js +9 -0
  18. package/dist/component/garmin/utils.js.map +1 -1
  19. package/dist/component/strava/public.d.ts +12 -52
  20. package/dist/component/strava/public.d.ts.map +1 -1
  21. package/dist/component/strava/public.js +16 -92
  22. package/dist/component/strava/public.js.map +1 -1
  23. package/dist/component/strava/transform/activity.js +15 -9
  24. package/dist/component/strava/transform/activity.js.map +1 -1
  25. package/package.json +1 -1
  26. package/src/client/index.ts +210 -158
  27. package/src/component/_generated/component.ts +165 -31
  28. package/src/component/garmin/private.ts +84 -0
  29. package/src/component/garmin/public.ts +804 -347
  30. package/src/component/garmin/utils.ts +17 -0
  31. 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
- * If `userId` is provided, the state parameter is stored in the component's
32
- * `pendingOAuth` table so that `completeStravaOAuth` can look it up
33
- * automatically when the callback fires. This is the recommended flow
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.optional(v.string()),
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
- if (args.userId) {
57
- await ctx.runMutation(internal.strava.private.storePendingOAuth, {
58
- provider: "STRAVA",
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
- // 5. Update lastDataUpdate timestamp
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
- * Used by `registerRoutes` — the callback handler calls this with the
150
- * `code` and `state` from the redirect. The action looks up the pending
151
- * state (userId) stored during `getStravaAuthUrl`, exchanges for tokens,
152
- * creates the connection, syncs data, and cleans up the pending entry.
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
- "Call connectStrava first.",
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 the public actions (connectStrava, completeStravaOAuth, syncStrava)
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: {