@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
|
@@ -38,31 +38,186 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
38
38
|
any,
|
|
39
39
|
Name
|
|
40
40
|
>;
|
|
41
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
redirectUri?: string;
|
|
61
|
+
endTimeInSeconds?: number;
|
|
62
|
+
startTimeInSeconds?: number;
|
|
50
63
|
userId: string;
|
|
51
64
|
},
|
|
52
65
|
any,
|
|
53
66
|
Name
|
|
54
67
|
>;
|
|
55
|
-
|
|
68
|
+
pullAll: FunctionReference<
|
|
56
69
|
"action",
|
|
57
70
|
"internal",
|
|
58
|
-
{
|
|
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
|
-
|
|
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
|
-
{
|
|
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
|
|
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
|
/**
|