@clipin/convex-wearables 0.0.2 → 0.0.3
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 +9 -4
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/component.d.ts +50 -0
- package/dist/component/_generated/component.d.ts.map +1 -0
- package/dist/component/_generated/component.js +11 -0
- package/dist/component/_generated/component.js.map +1 -0
- package/dist/component/backfillJobs.d.ts +11 -11
- package/dist/component/connections.d.ts +9 -9
- package/dist/component/connections.d.ts.map +1 -1
- package/dist/component/connections.js +2 -0
- package/dist/component/connections.js.map +1 -1
- package/dist/component/dataPoints.d.ts +5 -5
- package/dist/component/events.d.ts +13 -13
- package/dist/component/garminBackfill.d.ts +2 -2
- package/dist/component/garminWebhooks.d.ts +2 -2
- package/dist/component/garminWebhooks.d.ts.map +1 -1
- package/dist/component/garminWebhooks.js +2 -0
- package/dist/component/garminWebhooks.js.map +1 -1
- package/dist/component/lifecycle.d.ts +1 -1
- package/dist/component/lifecycle.d.ts.map +1 -1
- package/dist/component/lifecycle.js +2 -0
- package/dist/component/lifecycle.js.map +1 -1
- package/dist/component/oauthStates.d.ts +3 -3
- package/dist/component/schema.d.ts +26 -26
- package/dist/component/sdkPush.d.ts +11 -11
- package/dist/component/summaries.d.ts +4 -4
- package/dist/component/syncJobs.d.ts +23 -23
- package/dist/component/syncWorkflow.d.ts +2 -2
- package/dist/test.d.ts +421 -0
- package/dist/test.d.ts.map +1 -0
- package/dist/test.js +17 -0
- package/dist/test.js.map +1 -0
- package/package.json +12 -2
- package/src/client/_generated/_ignore.ts +2 -0
- package/src/client/index.test.ts +52 -0
- package/src/client/index.ts +784 -0
- package/src/client/types.ts +533 -0
- package/src/component/_generated/_ignore.ts +2 -0
- package/src/component/_generated/api.ts +16 -0
- package/src/component/_generated/component.ts +74 -0
- package/src/component/_generated/dataModel.ts +40 -0
- package/src/component/_generated/server.ts +48 -0
- package/src/component/backfillJobs.test.ts +47 -0
- package/src/component/backfillJobs.ts +245 -0
- package/src/component/connections.test.ts +297 -0
- package/src/component/connections.ts +329 -0
- package/src/component/convex.config.ts +7 -0
- package/src/component/dataPoints.test.ts +282 -0
- package/src/component/dataPoints.ts +305 -0
- package/src/component/dataSources.test.ts +247 -0
- package/src/component/dataSources.ts +109 -0
- package/src/component/events.test.ts +380 -0
- package/src/component/events.ts +288 -0
- package/src/component/garminBackfill.ts +343 -0
- package/src/component/garminWebhooks.test.ts +609 -0
- package/src/component/garminWebhooks.ts +656 -0
- package/src/component/httpHandlers.ts +153 -0
- package/src/component/lifecycle.test.ts +179 -0
- package/src/component/lifecycle.ts +87 -0
- package/src/component/menstrualCycles.ts +124 -0
- package/src/component/oauthActions.ts +261 -0
- package/src/component/oauthStates.test.ts +170 -0
- package/src/component/oauthStates.ts +85 -0
- package/src/component/providerSettings.ts +66 -0
- package/src/component/providers/additionalProviders.test.ts +401 -0
- package/src/component/providers/garmin.ts +1169 -0
- package/src/component/providers/oauth.test.ts +174 -0
- package/src/component/providers/oauth.ts +246 -0
- package/src/component/providers/polar.ts +220 -0
- package/src/component/providers/registry.ts +37 -0
- package/src/component/providers/strava.test.ts +195 -0
- package/src/component/providers/strava.ts +253 -0
- package/src/component/providers/suunto.ts +592 -0
- package/src/component/providers/types.ts +189 -0
- package/src/component/providers/whoop.ts +600 -0
- package/src/component/schema.ts +339 -0
- package/src/component/sdkPush.test.ts +367 -0
- package/src/component/sdkPush.ts +440 -0
- package/src/component/summaries.test.ts +201 -0
- package/src/component/summaries.ts +143 -0
- package/src/component/syncJobs.test.ts +254 -0
- package/src/component/syncJobs.ts +140 -0
- package/src/component/syncWorkflow.test.ts +87 -0
- package/src/component/syncWorkflow.ts +739 -0
- package/src/component/test.setup.ts +6 -0
- package/src/component/workflowManager.ts +19 -0
- package/src/test.ts +25 -0
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for @clipin/convex-wearables.
|
|
3
|
+
*
|
|
4
|
+
* These types are used by both the component internals and the host app.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Provider types
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
export type ProviderName =
|
|
12
|
+
| "garmin"
|
|
13
|
+
| "suunto"
|
|
14
|
+
| "polar"
|
|
15
|
+
| "whoop"
|
|
16
|
+
| "strava"
|
|
17
|
+
| "apple"
|
|
18
|
+
| "samsung"
|
|
19
|
+
| "google";
|
|
20
|
+
|
|
21
|
+
export type ConnectionStatus = "active" | "inactive" | "revoked" | "expired" | "error";
|
|
22
|
+
|
|
23
|
+
export type EventCategory = "workout" | "sleep";
|
|
24
|
+
|
|
25
|
+
export type SyncJobStatus = "queued" | "running" | "completed" | "failed" | "canceled";
|
|
26
|
+
|
|
27
|
+
export type BackfillJobStatus = "queued" | "running" | "completed" | "failed" | "canceled";
|
|
28
|
+
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Provider configuration (passed by app)
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
export interface ProviderCredentials {
|
|
34
|
+
clientId: string;
|
|
35
|
+
clientSecret: string;
|
|
36
|
+
/** Suunto requires an additional subscription key. */
|
|
37
|
+
subscriptionKey?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface WearablesConfig {
|
|
41
|
+
providers: Partial<Record<ProviderName, ProviderCredentials>>;
|
|
42
|
+
/**
|
|
43
|
+
* Optional function reference called when new data is synced.
|
|
44
|
+
* The host app can use this to trigger downstream processing.
|
|
45
|
+
*/
|
|
46
|
+
onDataSynced?: unknown; // FunctionReference — typed loosely to avoid coupling
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface GarminRoutesConfig {
|
|
50
|
+
/** Route that receives Garmin push notifications. */
|
|
51
|
+
webhookPath?: string;
|
|
52
|
+
/** Optional health-check route for the Garmin webhook integration. */
|
|
53
|
+
healthPath?: string | false;
|
|
54
|
+
/** Expected Garmin client ID. Defaults to `process.env.GARMIN_CLIENT_ID`. */
|
|
55
|
+
clientId?: string;
|
|
56
|
+
/** Garmin client secret. Defaults to `process.env.GARMIN_CLIENT_SECRET`. */
|
|
57
|
+
clientSecret?: string;
|
|
58
|
+
/** Route that receives the Garmin OAuth callback. */
|
|
59
|
+
oauthCallbackPath?: string | false;
|
|
60
|
+
/** Where to redirect after a successful OAuth callback. */
|
|
61
|
+
successRedirectUrl?: string;
|
|
62
|
+
/** Query parameter set on successful OAuth redirect. */
|
|
63
|
+
successQueryParam?: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface SdkRoutesConfig {
|
|
67
|
+
/** Route that receives normalized SDK/mobile health pushes. */
|
|
68
|
+
syncPath?: string | false;
|
|
69
|
+
/**
|
|
70
|
+
* Optional shared bearer token expected on the SDK sync route.
|
|
71
|
+
* Defaults to `process.env.WEARABLES_SDK_AUTH_TOKEN` when omitted.
|
|
72
|
+
*/
|
|
73
|
+
authToken?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface RegisterRoutesConfig {
|
|
77
|
+
/**
|
|
78
|
+
* Garmin webhook routes.
|
|
79
|
+
* Pass `false` to skip registering Garmin routes.
|
|
80
|
+
*/
|
|
81
|
+
garmin?: GarminRoutesConfig | false;
|
|
82
|
+
/**
|
|
83
|
+
* Normalized SDK push route for Apple Health / Google Health Connect / Samsung Health.
|
|
84
|
+
* Omitted by default; pass a config object to register it.
|
|
85
|
+
*/
|
|
86
|
+
sdk?: SdkRoutesConfig | false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export type SdkProviderName = "apple" | "google" | "samsung";
|
|
90
|
+
|
|
91
|
+
export interface SdkDeviceMetadata {
|
|
92
|
+
model?: string;
|
|
93
|
+
softwareVersion?: string;
|
|
94
|
+
source?: string;
|
|
95
|
+
deviceType?: string;
|
|
96
|
+
originalSourceName?: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface SdkSourceMetadata {
|
|
100
|
+
deviceModel?: string;
|
|
101
|
+
softwareVersion?: string;
|
|
102
|
+
source?: string;
|
|
103
|
+
deviceType?: string;
|
|
104
|
+
originalSourceName?: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface SdkPushEvent extends SdkSourceMetadata {
|
|
108
|
+
category: EventCategory;
|
|
109
|
+
type?: string;
|
|
110
|
+
sourceName?: string;
|
|
111
|
+
durationSeconds?: number;
|
|
112
|
+
startDatetime: number;
|
|
113
|
+
endDatetime?: number;
|
|
114
|
+
externalId?: string;
|
|
115
|
+
heartRateMin?: number;
|
|
116
|
+
heartRateMax?: number;
|
|
117
|
+
heartRateAvg?: number;
|
|
118
|
+
energyBurned?: number;
|
|
119
|
+
distance?: number;
|
|
120
|
+
stepsCount?: number;
|
|
121
|
+
maxSpeed?: number;
|
|
122
|
+
maxWatts?: number;
|
|
123
|
+
movingTimeSeconds?: number;
|
|
124
|
+
totalElevationGain?: number;
|
|
125
|
+
averageSpeed?: number;
|
|
126
|
+
averageWatts?: number;
|
|
127
|
+
elevHigh?: number;
|
|
128
|
+
elevLow?: number;
|
|
129
|
+
sleepTotalDurationMinutes?: number;
|
|
130
|
+
sleepTimeInBedMinutes?: number;
|
|
131
|
+
sleepEfficiencyScore?: number;
|
|
132
|
+
sleepDeepMinutes?: number;
|
|
133
|
+
sleepRemMinutes?: number;
|
|
134
|
+
sleepLightMinutes?: number;
|
|
135
|
+
sleepAwakeMinutes?: number;
|
|
136
|
+
isNap?: boolean;
|
|
137
|
+
sleepStages?: Array<{
|
|
138
|
+
stage: string;
|
|
139
|
+
startTime: number;
|
|
140
|
+
endTime: number;
|
|
141
|
+
}>;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface SdkPushDataPoint extends SdkSourceMetadata {
|
|
145
|
+
seriesType: string;
|
|
146
|
+
recordedAt: number;
|
|
147
|
+
value: number;
|
|
148
|
+
externalId?: string;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export interface SdkPushSummary {
|
|
152
|
+
date: string;
|
|
153
|
+
category: string;
|
|
154
|
+
totalSteps?: number;
|
|
155
|
+
totalCalories?: number;
|
|
156
|
+
activeCalories?: number;
|
|
157
|
+
activeMinutes?: number;
|
|
158
|
+
totalDistance?: number;
|
|
159
|
+
floorsClimbed?: number;
|
|
160
|
+
avgHeartRate?: number;
|
|
161
|
+
maxHeartRate?: number;
|
|
162
|
+
minHeartRate?: number;
|
|
163
|
+
sleepDurationMinutes?: number;
|
|
164
|
+
sleepEfficiency?: number;
|
|
165
|
+
deepSleepMinutes?: number;
|
|
166
|
+
remSleepMinutes?: number;
|
|
167
|
+
lightSleepMinutes?: number;
|
|
168
|
+
awakeDuringMinutes?: number;
|
|
169
|
+
timeInBedMinutes?: number;
|
|
170
|
+
hrvAvg?: number;
|
|
171
|
+
hrvRmssd?: number;
|
|
172
|
+
restingHeartRate?: number;
|
|
173
|
+
recoveryScore?: number;
|
|
174
|
+
weight?: number;
|
|
175
|
+
bodyFatPercentage?: number;
|
|
176
|
+
bodyMassIndex?: number;
|
|
177
|
+
leanBodyMass?: number;
|
|
178
|
+
bodyTemperature?: number;
|
|
179
|
+
avgStressLevel?: number;
|
|
180
|
+
bodyBattery?: number;
|
|
181
|
+
spo2Avg?: number;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface SdkPushPayload {
|
|
185
|
+
userId: string;
|
|
186
|
+
provider: SdkProviderName;
|
|
187
|
+
providerUserId?: string;
|
|
188
|
+
providerUsername?: string;
|
|
189
|
+
syncTimestamp?: number;
|
|
190
|
+
/**
|
|
191
|
+
* Compatibility alias for older mobile payloads.
|
|
192
|
+
* Prefer `sourceMetadata` for new integrations.
|
|
193
|
+
*/
|
|
194
|
+
device?: SdkDeviceMetadata;
|
|
195
|
+
sourceMetadata?: SdkSourceMetadata;
|
|
196
|
+
events?: SdkPushEvent[];
|
|
197
|
+
dataPoints?: SdkPushDataPoint[];
|
|
198
|
+
summaries?: SdkPushSummary[];
|
|
199
|
+
/**
|
|
200
|
+
* Compatibility alias for older mobile payloads.
|
|
201
|
+
* Prefer `summaries` for new integrations.
|
|
202
|
+
*/
|
|
203
|
+
dailySummaries?: SdkPushSummary[];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export type SdkSyncPayload = SdkPushPayload;
|
|
207
|
+
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
// Connection types
|
|
210
|
+
// ---------------------------------------------------------------------------
|
|
211
|
+
|
|
212
|
+
export interface Connection {
|
|
213
|
+
_id: string;
|
|
214
|
+
userId: string;
|
|
215
|
+
provider: ProviderName;
|
|
216
|
+
providerUserId?: string;
|
|
217
|
+
providerUsername?: string;
|
|
218
|
+
status: ConnectionStatus;
|
|
219
|
+
lastSyncedAt?: number;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ---------------------------------------------------------------------------
|
|
223
|
+
// Event types
|
|
224
|
+
// ---------------------------------------------------------------------------
|
|
225
|
+
|
|
226
|
+
export interface WorkoutEvent {
|
|
227
|
+
_id: string;
|
|
228
|
+
userId: string;
|
|
229
|
+
category: "workout";
|
|
230
|
+
type?: string;
|
|
231
|
+
sourceName?: string;
|
|
232
|
+
durationSeconds?: number;
|
|
233
|
+
startDatetime: number;
|
|
234
|
+
endDatetime?: number;
|
|
235
|
+
externalId?: string;
|
|
236
|
+
// Workout details
|
|
237
|
+
heartRateMin?: number;
|
|
238
|
+
heartRateMax?: number;
|
|
239
|
+
heartRateAvg?: number;
|
|
240
|
+
energyBurned?: number;
|
|
241
|
+
distance?: number;
|
|
242
|
+
stepsCount?: number;
|
|
243
|
+
maxSpeed?: number;
|
|
244
|
+
maxWatts?: number;
|
|
245
|
+
movingTimeSeconds?: number;
|
|
246
|
+
totalElevationGain?: number;
|
|
247
|
+
averageSpeed?: number;
|
|
248
|
+
averageWatts?: number;
|
|
249
|
+
elevHigh?: number;
|
|
250
|
+
elevLow?: number;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export interface SleepStage {
|
|
254
|
+
stage: string;
|
|
255
|
+
startTime: number;
|
|
256
|
+
endTime: number;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export interface SleepEvent {
|
|
260
|
+
_id: string;
|
|
261
|
+
userId: string;
|
|
262
|
+
category: "sleep";
|
|
263
|
+
type?: string;
|
|
264
|
+
sourceName?: string;
|
|
265
|
+
durationSeconds?: number;
|
|
266
|
+
startDatetime: number;
|
|
267
|
+
endDatetime?: number;
|
|
268
|
+
externalId?: string;
|
|
269
|
+
// Sleep details
|
|
270
|
+
sleepTotalDurationMinutes?: number;
|
|
271
|
+
sleepTimeInBedMinutes?: number;
|
|
272
|
+
sleepEfficiencyScore?: number;
|
|
273
|
+
sleepDeepMinutes?: number;
|
|
274
|
+
sleepRemMinutes?: number;
|
|
275
|
+
sleepLightMinutes?: number;
|
|
276
|
+
sleepAwakeMinutes?: number;
|
|
277
|
+
isNap?: boolean;
|
|
278
|
+
sleepStages?: SleepStage[];
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export type HealthEvent = WorkoutEvent | SleepEvent;
|
|
282
|
+
|
|
283
|
+
// ---------------------------------------------------------------------------
|
|
284
|
+
// Data point types
|
|
285
|
+
// ---------------------------------------------------------------------------
|
|
286
|
+
|
|
287
|
+
export interface DataPoint {
|
|
288
|
+
timestamp: number;
|
|
289
|
+
value: number;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export interface TimeSeriesPage {
|
|
293
|
+
points: DataPoint[];
|
|
294
|
+
nextCursor: string | null;
|
|
295
|
+
hasMore: boolean;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ---------------------------------------------------------------------------
|
|
299
|
+
// Events page
|
|
300
|
+
// ---------------------------------------------------------------------------
|
|
301
|
+
|
|
302
|
+
export interface EventsPage {
|
|
303
|
+
events: HealthEvent[];
|
|
304
|
+
nextCursor: string | null;
|
|
305
|
+
hasMore: boolean;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// ---------------------------------------------------------------------------
|
|
309
|
+
// Summary types
|
|
310
|
+
// ---------------------------------------------------------------------------
|
|
311
|
+
|
|
312
|
+
export interface DailySummary {
|
|
313
|
+
_id: string;
|
|
314
|
+
userId: string;
|
|
315
|
+
date: string;
|
|
316
|
+
category: string;
|
|
317
|
+
// Activity
|
|
318
|
+
totalSteps?: number;
|
|
319
|
+
totalCalories?: number;
|
|
320
|
+
activeCalories?: number;
|
|
321
|
+
activeMinutes?: number;
|
|
322
|
+
totalDistance?: number;
|
|
323
|
+
floorsClimbed?: number;
|
|
324
|
+
avgHeartRate?: number;
|
|
325
|
+
maxHeartRate?: number;
|
|
326
|
+
minHeartRate?: number;
|
|
327
|
+
// Sleep
|
|
328
|
+
sleepDurationMinutes?: number;
|
|
329
|
+
sleepEfficiency?: number;
|
|
330
|
+
deepSleepMinutes?: number;
|
|
331
|
+
remSleepMinutes?: number;
|
|
332
|
+
lightSleepMinutes?: number;
|
|
333
|
+
awakeDuringMinutes?: number;
|
|
334
|
+
timeInBedMinutes?: number;
|
|
335
|
+
// Recovery
|
|
336
|
+
hrvAvg?: number;
|
|
337
|
+
hrvRmssd?: number;
|
|
338
|
+
restingHeartRate?: number;
|
|
339
|
+
recoveryScore?: number;
|
|
340
|
+
// Body
|
|
341
|
+
weight?: number;
|
|
342
|
+
bodyFatPercentage?: number;
|
|
343
|
+
bodyMassIndex?: number;
|
|
344
|
+
leanBodyMass?: number;
|
|
345
|
+
bodyTemperature?: number;
|
|
346
|
+
// Other
|
|
347
|
+
avgStressLevel?: number;
|
|
348
|
+
bodyBattery?: number;
|
|
349
|
+
spo2Avg?: number;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export interface AggregateStats {
|
|
353
|
+
sum: number;
|
|
354
|
+
count: number;
|
|
355
|
+
avg: number;
|
|
356
|
+
min: number | null;
|
|
357
|
+
max: number | null;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// ---------------------------------------------------------------------------
|
|
361
|
+
// Sync status
|
|
362
|
+
// ---------------------------------------------------------------------------
|
|
363
|
+
|
|
364
|
+
export interface SyncJob {
|
|
365
|
+
_id: string;
|
|
366
|
+
connectionId: string;
|
|
367
|
+
userId: string;
|
|
368
|
+
provider: ProviderName;
|
|
369
|
+
status: SyncJobStatus;
|
|
370
|
+
mode?: "manual" | "cron" | "webhook";
|
|
371
|
+
triggerSource?: string;
|
|
372
|
+
startedAt: number;
|
|
373
|
+
completedAt?: number;
|
|
374
|
+
error?: string;
|
|
375
|
+
recordsProcessed?: number;
|
|
376
|
+
workflowId?: string;
|
|
377
|
+
windowStart?: number;
|
|
378
|
+
windowEnd?: number;
|
|
379
|
+
attempt?: number;
|
|
380
|
+
lastHeartbeatAt?: number;
|
|
381
|
+
cursor?: string;
|
|
382
|
+
currentPhase?: "events" | "dataPoints" | "summaries";
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export interface SyncStatus {
|
|
386
|
+
provider: ProviderName;
|
|
387
|
+
connectionStatus: ConnectionStatus;
|
|
388
|
+
lastSyncedAt?: number;
|
|
389
|
+
syncJobStatus: SyncJobStatus | null;
|
|
390
|
+
syncJobError: string | null;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
export interface BackfillJob {
|
|
394
|
+
_id: string;
|
|
395
|
+
connectionId: string;
|
|
396
|
+
userId: string;
|
|
397
|
+
provider: ProviderName;
|
|
398
|
+
dataType: string;
|
|
399
|
+
status: BackfillJobStatus;
|
|
400
|
+
startedAt: number;
|
|
401
|
+
completedAt?: number;
|
|
402
|
+
error?: string;
|
|
403
|
+
workflowId?: string;
|
|
404
|
+
windowStart?: number;
|
|
405
|
+
windowEnd?: number;
|
|
406
|
+
currentDataType?: string;
|
|
407
|
+
currentAttempt?: number;
|
|
408
|
+
currentEventId?: string;
|
|
409
|
+
completedDataTypes?: string[];
|
|
410
|
+
lastHeartbeatAt?: number;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// ---------------------------------------------------------------------------
|
|
414
|
+
// Series type definitions — all 48 pre-defined metric types
|
|
415
|
+
// ---------------------------------------------------------------------------
|
|
416
|
+
|
|
417
|
+
export const SERIES_TYPES = {
|
|
418
|
+
// Heart & Cardiovascular
|
|
419
|
+
heart_rate: { id: 1, unit: "bpm" },
|
|
420
|
+
resting_heart_rate: { id: 2, unit: "bpm" },
|
|
421
|
+
heart_rate_variability_sdnn: { id: 3, unit: "ms" },
|
|
422
|
+
heart_rate_recovery_one_minute: { id: 4, unit: "bpm" },
|
|
423
|
+
walking_heart_rate_average: { id: 5, unit: "bpm" },
|
|
424
|
+
recovery_score: { id: 6, unit: "score" },
|
|
425
|
+
heart_rate_variability_rmssd: { id: 7, unit: "ms" },
|
|
426
|
+
|
|
427
|
+
// Blood & Respiratory
|
|
428
|
+
oxygen_saturation: { id: 20, unit: "percent" },
|
|
429
|
+
blood_glucose: { id: 21, unit: "mg_dl" },
|
|
430
|
+
blood_pressure_systolic: { id: 22, unit: "mmHg" },
|
|
431
|
+
blood_pressure_diastolic: { id: 23, unit: "mmHg" },
|
|
432
|
+
respiratory_rate: { id: 24, unit: "brpm" },
|
|
433
|
+
sleeping_breathing_disturbances: { id: 25, unit: "count" },
|
|
434
|
+
blood_alcohol_content: { id: 26, unit: "mg_dl" },
|
|
435
|
+
peripheral_perfusion_index: { id: 27, unit: "score" },
|
|
436
|
+
forced_vital_capacity: { id: 28, unit: "liters" },
|
|
437
|
+
forced_expiratory_volume_1: { id: 29, unit: "liters" },
|
|
438
|
+
peak_expiratory_flow_rate: { id: 30, unit: "liters" },
|
|
439
|
+
|
|
440
|
+
// Body Composition
|
|
441
|
+
height: { id: 40, unit: "cm" },
|
|
442
|
+
weight: { id: 41, unit: "kg" },
|
|
443
|
+
body_fat_percentage: { id: 42, unit: "percent" },
|
|
444
|
+
body_mass_index: { id: 43, unit: "kg_m2" },
|
|
445
|
+
lean_body_mass: { id: 44, unit: "kg" },
|
|
446
|
+
body_temperature: { id: 45, unit: "celsius" },
|
|
447
|
+
skin_temperature: { id: 46, unit: "celsius" },
|
|
448
|
+
waist_circumference: { id: 47, unit: "cm" },
|
|
449
|
+
body_fat_mass: { id: 48, unit: "kg" },
|
|
450
|
+
skeletal_muscle_mass: { id: 49, unit: "kg" },
|
|
451
|
+
|
|
452
|
+
// Fitness
|
|
453
|
+
vo2_max: { id: 60, unit: "ml_kg_min" },
|
|
454
|
+
six_minute_walk_test_distance: { id: 61, unit: "meters" },
|
|
455
|
+
|
|
456
|
+
// Activity — Basic
|
|
457
|
+
steps: { id: 80, unit: "count" },
|
|
458
|
+
energy: { id: 81, unit: "kcal" },
|
|
459
|
+
basal_energy: { id: 82, unit: "kcal" },
|
|
460
|
+
total_calories: { id: 88, unit: "kcal" },
|
|
461
|
+
active_calories: { id: 89, unit: "kcal" },
|
|
462
|
+
stand_time: { id: 83, unit: "minutes" },
|
|
463
|
+
exercise_time: { id: 84, unit: "minutes" },
|
|
464
|
+
physical_effort: { id: 85, unit: "score" },
|
|
465
|
+
flights_climbed: { id: 86, unit: "count" },
|
|
466
|
+
floors_climbed: { id: 90, unit: "count" },
|
|
467
|
+
average_met: { id: 87, unit: "met" },
|
|
468
|
+
|
|
469
|
+
// Activity — Distance
|
|
470
|
+
distance: { id: 99, unit: "meters" },
|
|
471
|
+
distance_walking_running: { id: 100, unit: "meters" },
|
|
472
|
+
distance_cycling: { id: 101, unit: "meters" },
|
|
473
|
+
distance_swimming: { id: 102, unit: "meters" },
|
|
474
|
+
distance_downhill_snow_sports: { id: 103, unit: "meters" },
|
|
475
|
+
distance_other: { id: 104, unit: "meters" },
|
|
476
|
+
elevation_gain: { id: 105, unit: "meters" },
|
|
477
|
+
|
|
478
|
+
// Activity — Walking
|
|
479
|
+
walking_step_length: { id: 120, unit: "cm" },
|
|
480
|
+
walking_speed: { id: 121, unit: "m_per_s" },
|
|
481
|
+
walking_double_support_percentage: { id: 122, unit: "percent" },
|
|
482
|
+
walking_asymmetry_percentage: { id: 123, unit: "percent" },
|
|
483
|
+
walking_steadiness: { id: 124, unit: "percent" },
|
|
484
|
+
stair_descent_speed: { id: 125, unit: "m_per_s" },
|
|
485
|
+
stair_ascent_speed: { id: 126, unit: "m_per_s" },
|
|
486
|
+
|
|
487
|
+
// Activity — Running
|
|
488
|
+
running_power: { id: 140, unit: "watts" },
|
|
489
|
+
running_speed: { id: 141, unit: "m_per_s" },
|
|
490
|
+
running_vertical_oscillation: { id: 142, unit: "cm" },
|
|
491
|
+
running_ground_contact_time: { id: 143, unit: "ms" },
|
|
492
|
+
running_stride_length: { id: 144, unit: "cm" },
|
|
493
|
+
|
|
494
|
+
// Activity — Swimming
|
|
495
|
+
swimming_stroke_count: { id: 160, unit: "count" },
|
|
496
|
+
underwater_depth: { id: 161, unit: "meters" },
|
|
497
|
+
|
|
498
|
+
// Activity — Generic
|
|
499
|
+
cadence: { id: 180, unit: "rpm" },
|
|
500
|
+
power: { id: 181, unit: "watts" },
|
|
501
|
+
speed: { id: 182, unit: "m_per_s" },
|
|
502
|
+
workout_effort_score: { id: 183, unit: "score" },
|
|
503
|
+
estimated_workout_effort_score: { id: 184, unit: "score" },
|
|
504
|
+
|
|
505
|
+
// Environmental
|
|
506
|
+
environmental_audio_exposure: { id: 200, unit: "dB" },
|
|
507
|
+
headphone_audio_exposure: { id: 201, unit: "dB" },
|
|
508
|
+
environmental_sound_reduction: { id: 202, unit: "dB" },
|
|
509
|
+
time_in_daylight: { id: 203, unit: "minutes" },
|
|
510
|
+
water_temperature: { id: 204, unit: "celsius" },
|
|
511
|
+
uv_exposure: { id: 205, unit: "count" },
|
|
512
|
+
inhaler_usage: { id: 206, unit: "count" },
|
|
513
|
+
weather_temperature: { id: 207, unit: "celsius" },
|
|
514
|
+
weather_humidity: { id: 208, unit: "percent" },
|
|
515
|
+
|
|
516
|
+
// Garmin-Specific
|
|
517
|
+
garmin_stress_level: { id: 220, unit: "score" },
|
|
518
|
+
garmin_skin_temperature: { id: 221, unit: "celsius" },
|
|
519
|
+
garmin_fitness_age: { id: 222, unit: "years" },
|
|
520
|
+
garmin_body_battery: { id: 223, unit: "percent" },
|
|
521
|
+
|
|
522
|
+
// Other
|
|
523
|
+
electrodermal_activity: { id: 500, unit: "count" },
|
|
524
|
+
push_count: { id: 501, unit: "count" },
|
|
525
|
+
atrial_fibrillation_burden: { id: 502, unit: "count" },
|
|
526
|
+
insulin_delivery: { id: 503, unit: "count" },
|
|
527
|
+
number_of_times_fallen: { id: 504, unit: "count" },
|
|
528
|
+
number_of_alcoholic_beverages: { id: 505, unit: "count" },
|
|
529
|
+
nike_fuel: { id: 506, unit: "count" },
|
|
530
|
+
hydration: { id: 507, unit: "mL" },
|
|
531
|
+
} as const;
|
|
532
|
+
|
|
533
|
+
export type SeriesType = keyof typeof SERIES_TYPES;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/**
|
|
3
|
+
* Generated API.
|
|
4
|
+
*
|
|
5
|
+
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
6
|
+
*
|
|
7
|
+
* To regenerate, run `npx convex dev`.
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { AnyApi, AnyComponents } from "convex/server";
|
|
12
|
+
import { anyApi, componentsGeneric } from "convex/server";
|
|
13
|
+
|
|
14
|
+
export const api: AnyApi = anyApi;
|
|
15
|
+
export const internal: AnyApi = anyApi;
|
|
16
|
+
export const components: AnyComponents = componentsGeneric();
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/**
|
|
3
|
+
* Generated `ComponentApi` utility.
|
|
4
|
+
*
|
|
5
|
+
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
6
|
+
*
|
|
7
|
+
* To regenerate, run `npx convex dev`.
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { ApiFromModules, FilterApi, FunctionReference } from "convex/server";
|
|
12
|
+
import type { GenericId as ConvexId } from "convex/values";
|
|
13
|
+
|
|
14
|
+
type Modules = {
|
|
15
|
+
backfillJobs: typeof import("../backfillJobs.js");
|
|
16
|
+
connections: typeof import("../connections.js");
|
|
17
|
+
dataPoints: typeof import("../dataPoints.js");
|
|
18
|
+
dataSources: typeof import("../dataSources.js");
|
|
19
|
+
events: typeof import("../events.js");
|
|
20
|
+
garminBackfill: typeof import("../garminBackfill.js");
|
|
21
|
+
garminWebhooks: typeof import("../garminWebhooks.js");
|
|
22
|
+
lifecycle: typeof import("../lifecycle.js");
|
|
23
|
+
menstrualCycles: typeof import("../menstrualCycles.js");
|
|
24
|
+
oauthActions: typeof import("../oauthActions.js");
|
|
25
|
+
sdkPush: typeof import("../sdkPush.js");
|
|
26
|
+
summaries: typeof import("../summaries.js");
|
|
27
|
+
syncJobs: typeof import("../syncJobs.js");
|
|
28
|
+
syncWorkflow: typeof import("../syncWorkflow.js");
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
type PublicApi = FilterApi<
|
|
32
|
+
ApiFromModules<Modules>,
|
|
33
|
+
FunctionReference<any, "public", any, any>
|
|
34
|
+
>;
|
|
35
|
+
|
|
36
|
+
type ConvertComponentBoundary<T> =
|
|
37
|
+
T extends ConvexId<string>
|
|
38
|
+
? string
|
|
39
|
+
: T extends readonly (infer Item)[]
|
|
40
|
+
? Array<ConvertComponentBoundary<Item>>
|
|
41
|
+
: T extends object
|
|
42
|
+
? { [Key in keyof T]: ConvertComponentBoundary<T[Key]> }
|
|
43
|
+
: T;
|
|
44
|
+
|
|
45
|
+
type ConvertComponentArgs<Args extends Record<string, any>> = {
|
|
46
|
+
[Key in keyof Args]: ConvertComponentBoundary<Args[Key]>;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
type ToComponentApi<API, Name extends string | undefined> =
|
|
50
|
+
API extends FunctionReference<infer Type, any, infer Args, infer ReturnType>
|
|
51
|
+
? FunctionReference<
|
|
52
|
+
Type,
|
|
53
|
+
"internal",
|
|
54
|
+
ConvertComponentArgs<Args>,
|
|
55
|
+
ConvertComponentBoundary<ReturnType>,
|
|
56
|
+
Name
|
|
57
|
+
>
|
|
58
|
+
: API extends object
|
|
59
|
+
? { [Key in keyof API]: ToComponentApi<API[Key], Name> }
|
|
60
|
+
: never;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* A utility for referencing a Convex component's exposed API.
|
|
64
|
+
*
|
|
65
|
+
* Useful when expecting a parameter like `components.myComponent`.
|
|
66
|
+
* Usage:
|
|
67
|
+
* ```ts
|
|
68
|
+
* async function myFunction(ctx: QueryCtx, component: ComponentApi) {
|
|
69
|
+
* return ctx.runQuery(component.someFile.someQuery, { ...args });
|
|
70
|
+
* }
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
74
|
+
ToComponentApi<PublicApi, Name>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/**
|
|
3
|
+
* Generated data model types.
|
|
4
|
+
*
|
|
5
|
+
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
6
|
+
*
|
|
7
|
+
* To regenerate, run `npx convex dev`.
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type {
|
|
12
|
+
DataModelFromSchemaDefinition,
|
|
13
|
+
DocumentByName,
|
|
14
|
+
TableNamesInDataModel,
|
|
15
|
+
} from "convex/server";
|
|
16
|
+
import type { GenericId } from "convex/values";
|
|
17
|
+
import schema from "../schema.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The names of all of your Convex tables.
|
|
21
|
+
*/
|
|
22
|
+
export type TableNames = TableNamesInDataModel<DataModel>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The type of a document stored in Convex.
|
|
26
|
+
*/
|
|
27
|
+
export type Doc<TableName extends TableNames> = DocumentByName<
|
|
28
|
+
DataModel,
|
|
29
|
+
TableName
|
|
30
|
+
>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* An identifier for a document in Convex.
|
|
34
|
+
*/
|
|
35
|
+
export type Id<TableName extends TableNames> = GenericId<TableName>;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A type describing your Convex data model.
|
|
39
|
+
*/
|
|
40
|
+
export type DataModel = DataModelFromSchemaDefinition<typeof schema>;
|