@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
@@ -38,31 +38,186 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
38
38
  any,
39
39
  Name
40
40
  >;
41
- connectGarmin: FunctionReference<
41
+ disconnectGarmin: FunctionReference<
42
+ "action",
43
+ "internal",
44
+ { userId: string },
45
+ any,
46
+ Name
47
+ >;
48
+ getGarminAuthUrl: FunctionReference<
49
+ "action",
50
+ "internal",
51
+ { clientId: string; redirectUri?: string; userId: string },
52
+ any,
53
+ Name
54
+ >;
55
+ pullActivities: FunctionReference<
42
56
  "action",
43
57
  "internal",
44
58
  {
45
59
  clientId: string;
46
60
  clientSecret: string;
47
- code: string;
48
- codeVerifier: string;
49
- redirectUri?: string;
61
+ endTimeInSeconds?: number;
62
+ startTimeInSeconds?: number;
50
63
  userId: string;
51
64
  },
52
65
  any,
53
66
  Name
54
67
  >;
55
- disconnectGarmin: FunctionReference<
68
+ pullAll: FunctionReference<
56
69
  "action",
57
70
  "internal",
58
- { userId: string },
71
+ {
72
+ clientId: string;
73
+ clientSecret: string;
74
+ endTimeInSeconds?: number;
75
+ startTimeInSeconds?: number;
76
+ userId: string;
77
+ },
59
78
  any,
60
79
  Name
61
80
  >;
62
- getGarminAuthUrl: FunctionReference<
81
+ pullBloodPressures: FunctionReference<
82
+ "action",
83
+ "internal",
84
+ {
85
+ clientId: string;
86
+ clientSecret: string;
87
+ endTimeInSeconds?: number;
88
+ startTimeInSeconds?: number;
89
+ userId: string;
90
+ },
91
+ any,
92
+ Name
93
+ >;
94
+ pullBody: FunctionReference<
95
+ "action",
96
+ "internal",
97
+ {
98
+ clientId: string;
99
+ clientSecret: string;
100
+ endTimeInSeconds?: number;
101
+ startTimeInSeconds?: number;
102
+ userId: string;
103
+ },
104
+ any,
105
+ Name
106
+ >;
107
+ pullDailies: FunctionReference<
108
+ "action",
109
+ "internal",
110
+ {
111
+ clientId: string;
112
+ clientSecret: string;
113
+ endTimeInSeconds?: number;
114
+ startTimeInSeconds?: number;
115
+ userId: string;
116
+ },
117
+ any,
118
+ Name
119
+ >;
120
+ pullHRV: FunctionReference<
121
+ "action",
122
+ "internal",
123
+ {
124
+ clientId: string;
125
+ clientSecret: string;
126
+ endTimeInSeconds?: number;
127
+ startTimeInSeconds?: number;
128
+ userId: string;
129
+ },
130
+ any,
131
+ Name
132
+ >;
133
+ pullMenstruation: FunctionReference<
134
+ "action",
135
+ "internal",
136
+ {
137
+ clientId: string;
138
+ clientSecret: string;
139
+ endTimeInSeconds?: number;
140
+ startTimeInSeconds?: number;
141
+ userId: string;
142
+ },
143
+ any,
144
+ Name
145
+ >;
146
+ pullPulseOx: FunctionReference<
147
+ "action",
148
+ "internal",
149
+ {
150
+ clientId: string;
151
+ clientSecret: string;
152
+ endTimeInSeconds?: number;
153
+ startTimeInSeconds?: number;
154
+ userId: string;
155
+ },
156
+ any,
157
+ Name
158
+ >;
159
+ pullRespiration: FunctionReference<
160
+ "action",
161
+ "internal",
162
+ {
163
+ clientId: string;
164
+ clientSecret: string;
165
+ endTimeInSeconds?: number;
166
+ startTimeInSeconds?: number;
167
+ userId: string;
168
+ },
169
+ any,
170
+ Name
171
+ >;
172
+ pullSkinTemperature: FunctionReference<
173
+ "action",
174
+ "internal",
175
+ {
176
+ clientId: string;
177
+ clientSecret: string;
178
+ endTimeInSeconds?: number;
179
+ startTimeInSeconds?: number;
180
+ userId: string;
181
+ },
182
+ any,
183
+ Name
184
+ >;
185
+ pullSleep: FunctionReference<
186
+ "action",
187
+ "internal",
188
+ {
189
+ clientId: string;
190
+ clientSecret: string;
191
+ endTimeInSeconds?: number;
192
+ startTimeInSeconds?: number;
193
+ userId: string;
194
+ },
195
+ any,
196
+ Name
197
+ >;
198
+ pullStressDetails: FunctionReference<
199
+ "action",
200
+ "internal",
201
+ {
202
+ clientId: string;
203
+ clientSecret: string;
204
+ endTimeInSeconds?: number;
205
+ startTimeInSeconds?: number;
206
+ userId: string;
207
+ },
208
+ any,
209
+ Name
210
+ >;
211
+ pullUserMetrics: FunctionReference<
63
212
  "action",
64
213
  "internal",
65
- { clientId: string; redirectUri?: string; userId?: string },
214
+ {
215
+ clientId: string;
216
+ clientSecret: string;
217
+ endTimeInSeconds?: number;
218
+ startTimeInSeconds?: number;
219
+ userId: string;
220
+ },
66
221
  any,
67
222
  Name
68
223
  >;
@@ -1656,28 +1811,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
1656
1811
  code: string;
1657
1812
  state: string;
1658
1813
  },
1659
- {
1660
- connectionId: string;
1661
- errors: Array<{ error: string; id: string; type: string }>;
1662
- synced: { activities: number; athletes: number };
1663
- userId: string;
1664
- },
1665
- Name
1666
- >;
1667
- connectStrava: FunctionReference<
1668
- "action",
1669
- "internal",
1670
- {
1671
- clientId: string;
1672
- clientSecret: string;
1673
- code: string;
1674
- userId: string;
1675
- },
1676
- {
1677
- connectionId: string;
1678
- errors: Array<{ error: string; id: string; type: string }>;
1679
- synced: { activities: number; athletes: number };
1680
- },
1814
+ { connectionId: string; userId: string },
1681
1815
  Name
1682
1816
  >;
1683
1817
  disconnectStrava: FunctionReference<
@@ -1694,7 +1828,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
1694
1828
  clientId: string;
1695
1829
  redirectUri: string;
1696
1830
  scope?: string;
1697
- userId?: string;
1831
+ userId: string;
1698
1832
  },
1699
1833
  any,
1700
1834
  Name
@@ -94,6 +94,10 @@ import {
94
94
  garminMenstrualCycleTrackingPushPayloadSchema,
95
95
  } from "./schemas/menstrualCycleTracking.js";
96
96
  import { transformMenstrualCycleTracking } from "./transform/menstrualCycleTracking.js";
97
+ import { refreshToken } from "./auth";
98
+ import type { Doc, Id } from "../_generated/dataModel";
99
+
100
+ const REFRESH_BUFFER_SECONDS = 600;
97
101
  // ─── Internal Pending OAuth CRUD ─────────────────────────────────────────────
98
102
  // Temporary storage for in-progress Garmin OAuth 2.0 PKCE flows.
99
103
  // Bridges getGarminAuthUrl and completeGarminOAuth.
@@ -140,6 +144,84 @@ export const deletePendingOAuth = internalMutation({
140
144
  },
141
145
  });
142
146
 
147
+ export const resolveConnectionAndAccessToken = internalAction({
148
+ args: {
149
+ userId: v.string(),
150
+ clientId: v.string(),
151
+ clientSecret: v.string(),
152
+ },
153
+ handler: async (ctx, args): Promise<{
154
+ connectionId: Id<"connections">;
155
+ accessToken: string;
156
+ }> => {
157
+ const connection: Doc<"connections"> | null = await ctx.runQuery(
158
+ internal.private.getConnectionByProvider,
159
+ { userId: args.userId, provider: "GARMIN" },
160
+ );
161
+
162
+ if (!connection) {
163
+ throw new Error(
164
+ `No Garmin connection found for user "${args.userId}". ` +
165
+ "Connect to Garmin first via getGarminAuthUrl.",
166
+ );
167
+ }
168
+
169
+ if (!connection.active) {
170
+ throw new Error(
171
+ `Garmin connection for user "${args.userId}" is inactive. Reconnect first.`,
172
+ );
173
+ }
174
+
175
+ const connectionId = connection._id;
176
+
177
+ const tokenDoc: Doc<"providerTokens"> | null = await ctx.runQuery(
178
+ internal.garmin.private.getTokens,
179
+ { connectionId },
180
+ );
181
+
182
+ if (!tokenDoc) {
183
+ throw new Error(
184
+ "No Garmin tokens found for this connection. " +
185
+ "The connection may have been created before token storage was available.",
186
+ );
187
+ }
188
+
189
+ let accessToken = tokenDoc.accessToken;
190
+
191
+ // Refresh the token if it's expired or about to expire
192
+ const nowSeconds = Math.floor(Date.now() / 1000);
193
+ if (
194
+ tokenDoc.expiresAt &&
195
+ tokenDoc.refreshToken &&
196
+ nowSeconds >= tokenDoc.expiresAt - REFRESH_BUFFER_SECONDS
197
+ ) {
198
+ const refreshed = await refreshToken({
199
+ clientId: args.clientId,
200
+ clientSecret: args.clientSecret,
201
+ refreshToken: tokenDoc.refreshToken,
202
+ });
203
+
204
+ accessToken = refreshed.access_token;
205
+ const newExpiresAt = nowSeconds + refreshed.expires_in;
206
+
207
+ const _refreshed: null = await ctx.runMutation(
208
+ internal.garmin.private.storeTokens,
209
+ {
210
+ connectionId,
211
+ accessToken: refreshed.access_token,
212
+ refreshToken: refreshed.refresh_token,
213
+ expiresAt: newExpiresAt,
214
+ },
215
+ );
216
+ }
217
+
218
+ return {
219
+ connectionId,
220
+ accessToken,
221
+ };
222
+ },
223
+ });
224
+
143
225
  // ─── Internal Token CRUD ─────────────────────────────────────────────────────
144
226
 
145
227
  /**
@@ -228,6 +310,8 @@ export const deleteTokens = internalMutation({
228
310
  },
229
311
  });
230
312
 
313
+
314
+
231
315
  // ─── Activity Push Processing ───────────────────────────────────────────────
232
316
 
233
317
  /**