@nativesquare/soma 0.12.0 → 0.13.0

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 (60) hide show
  1. package/dist/client/garmin.d.ts +5 -1
  2. package/dist/client/garmin.d.ts.map +1 -1
  3. package/dist/client/garmin.js +148 -0
  4. package/dist/client/garmin.js.map +1 -1
  5. package/dist/client/index.d.ts +5 -6
  6. package/dist/client/index.d.ts.map +1 -1
  7. package/dist/client/index.js +5 -211
  8. package/dist/client/index.js.map +1 -1
  9. package/dist/client/strava.d.ts +11 -6
  10. package/dist/client/strava.d.ts.map +1 -1
  11. package/dist/client/strava.js +64 -0
  12. package/dist/client/strava.js.map +1 -1
  13. package/dist/client/types.d.ts +93 -20
  14. package/dist/client/types.d.ts.map +1 -1
  15. package/dist/component/_generated/component.d.ts +24 -5
  16. package/dist/component/_generated/component.d.ts.map +1 -1
  17. package/dist/component/garmin/private.d.ts +53 -68
  18. package/dist/component/garmin/private.d.ts.map +1 -1
  19. package/dist/component/garmin/private.js +87 -85
  20. package/dist/component/garmin/private.js.map +1 -1
  21. package/dist/component/garmin/public.d.ts +97 -43
  22. package/dist/component/garmin/public.d.ts.map +1 -1
  23. package/dist/component/garmin/public.js +75 -51
  24. package/dist/component/garmin/public.js.map +1 -1
  25. package/dist/component/garmin/webhooks.d.ts +22 -20
  26. package/dist/component/garmin/webhooks.d.ts.map +1 -1
  27. package/dist/component/garmin/webhooks.js +115 -76
  28. package/dist/component/garmin/webhooks.js.map +1 -1
  29. package/dist/component/public.d.ts +15 -15
  30. package/dist/component/schema.d.ts +25 -25
  31. package/dist/component/strava/public.d.ts +12 -8
  32. package/dist/component/strava/public.d.ts.map +1 -1
  33. package/dist/component/strava/public.js +7 -7
  34. package/dist/component/strava/public.js.map +1 -1
  35. package/dist/component/validators/activity.d.ts +4 -4
  36. package/dist/component/validators/body.d.ts +4 -4
  37. package/dist/component/validators/daily.d.ts +4 -4
  38. package/dist/component/validators/nutrition.d.ts +3 -3
  39. package/dist/component/validators/samples.d.ts +4 -4
  40. package/dist/component/validators/shared.d.ts +13 -4
  41. package/dist/component/validators/shared.d.ts.map +1 -1
  42. package/dist/component/validators/shared.js +7 -0
  43. package/dist/component/validators/shared.js.map +1 -1
  44. package/dist/component/validators/sleep.d.ts +5 -5
  45. package/dist/validators.d.ts +41 -40
  46. package/dist/validators.d.ts.map +1 -1
  47. package/dist/validators.js +1 -0
  48. package/dist/validators.js.map +1 -1
  49. package/package.json +1 -1
  50. package/src/client/garmin.ts +692 -487
  51. package/src/client/index.ts +10 -279
  52. package/src/client/strava.ts +199 -108
  53. package/src/client/types.ts +303 -215
  54. package/src/component/_generated/component.ts +19 -19
  55. package/src/component/garmin/private.ts +1872 -1870
  56. package/src/component/garmin/public.ts +104 -80
  57. package/src/component/garmin/webhooks.ts +122 -81
  58. package/src/component/strava/public.ts +393 -393
  59. package/src/component/validators/shared.ts +9 -0
  60. package/src/validators.ts +1 -0
@@ -1,215 +1,303 @@
1
- import type {
2
- GenericActionCtx,
3
- GenericMutationCtx,
4
- GenericQueryCtx,
5
- GenericDataModel,
6
- } from "convex/server";
7
-
8
- // ─── Context Types ──────────────────────────────────────────────────────────
9
- // Narrowed Convex context types that only expose the runner methods each
10
- // operation actually needs.
11
-
12
- export type QueryCtx = Pick<GenericQueryCtx<GenericDataModel>, "runQuery">;
13
-
14
- export type MutationCtx = Pick<
15
- GenericMutationCtx<GenericDataModel>,
16
- "runQuery" | "runMutation"
17
- >;
18
-
19
- export type ActionCtx = Pick<
20
- GenericActionCtx<GenericDataModel>,
21
- "runQuery" | "runMutation" | "runAction"
22
- >;
23
-
24
- // ─── Provider Configuration ─────────────────────────────────────────────────
25
-
26
- /**
27
- * Configuration for the Strava integration.
28
- *
29
- * If not provided to the Soma constructor, the class will attempt to
30
- * read `STRAVA_CLIENT_ID` and `STRAVA_CLIENT_SECRET`
31
- * from environment variables automatically.
32
- */
33
- export interface SomaStravaConfig {
34
- /** Your Strava application's Client ID. */
35
- clientId: string;
36
- /** Your Strava application's Client Secret. */
37
- clientSecret: string;
38
- }
39
-
40
- /**
41
- * Configuration for the Garmin integration.
42
- *
43
- * If not provided to the Soma constructor, the class will attempt to
44
- * read `GARMIN_CLIENT_ID` and `GARMIN_CLIENT_SECRET` from
45
- * environment variables automatically.
46
- */
47
- export interface SomaGarminConfig {
48
- /** Your Garmin application's Client ID. */
49
- clientId: string;
50
- /** Your Garmin application's Client Secret. */
51
- clientSecret: string;
52
- }
53
-
54
- // ─── Data Query & Ingestion Args ────────────────────────────────────────────
55
-
56
- /**
57
- * Common args shape for all ingestion methods.
58
- *
59
- * Requires `connectionId` and `userId` at minimum additional fields
60
- * come from the transformer output (e.g., `metadata`, `calories_data`, etc.)
61
- * and are validated server-side by Convex validators.
62
- */
63
- export type IngestArgs = {
64
- connectionId: string;
65
- userId: string;
66
- } & Record<string, unknown>;
67
-
68
- /**
69
- * Base args for time-range filtered queries.
70
- *
71
- * - `userId` is required for all health data queries.
72
- * - `startTime` / `endTime` are optional ISO-8601 bounds on `metadata.start_time`.
73
- */
74
- export type TimeRangeArgs = {
75
- userId: string;
76
- startTime?: string;
77
- endTime?: string;
78
- };
79
-
80
- /**
81
- * Args for list (collect-all) queries with optional ordering and limit.
82
- */
83
- export type ListTimeRangeArgs = TimeRangeArgs & {
84
- order?: "asc" | "desc";
85
- limit?: number;
86
- };
87
-
88
- /**
89
- * Args for paginated queries with Convex pagination options.
90
- */
91
- export type PaginateTimeRangeArgs = TimeRangeArgs & {
92
- paginationOpts: { numItems: number; cursor: string | null };
93
- };
94
-
95
- // ─── OAuth Callback Events ──────────────────────────────────────────────────
96
-
97
- /** Data passed to `onComplete` after Strava OAuth completes. */
98
- export interface StravaConnectEvent {
99
- provider: "STRAVA";
100
- userId: string;
101
- connectionId: string;
102
- }
103
-
104
- /** Data passed to `oauth.onComplete` after Garmin OAuth completes. */
105
- export interface GarminConnectEvent {
106
- provider: "GARMIN";
107
- userId: string;
108
- connectionId: string;
109
- }
110
-
111
- // ─── Garmin Webhook Types ───────────────────────────────────────────────────
112
-
113
- /** Data passed to webhook `events` handlers and `onEvent` after data ingestion. */
114
- export interface GarminWebhookEvent {
115
- dataType: string;
116
- processed: number;
117
- errors: Array<{ type: string; id: string; error: string }>;
118
- /** Users whose data was affected by this webhook. */
119
- affectedUsers: Array<{ userId: string; connectionId: string }>;
120
- }
121
-
122
- /** Webhook endpoint names matching the Garmin API data types. */
123
- export type GarminWebhookEventName =
124
- | "activities" | "activity-details" | "manually-updated-activities" | "move-iq"
125
- | "blood-pressures" | "body-compositions" | "dailies" | "epochs"
126
- | "health-snapshot" | "sleeps" | "hrv" | "stress" | "pulse-ox"
127
- | "respiration" | "skin-temp" | "user-metrics" | "menstrual-cycle-tracking";
128
-
129
- /** Handler for a specific webhook event or the catch-all `onEvent`. */
130
- export type GarminWebhookHandler = (
131
- ctx: GenericActionCtx<GenericDataModel>,
132
- event: GarminWebhookEvent,
133
- ) => Promise<void>;
134
-
135
- // ─── Route Registration Options ─────────────────────────────────────────────
136
-
137
- /**
138
- * Per-provider options for `registerRoutes`.
139
- */
140
- export interface StravaOAuthOptions {
141
- /** HTTP path for the OAuth callback. @default "/api/strava/callback" */
142
- path?: string;
143
- /** Override STRAVA_CLIENT_ID env var. */
144
- clientId?: string;
145
- /** Override STRAVA_CLIENT_SECRET env var. */
146
- clientSecret?: string;
147
- /** URL to redirect the user to after a successful connection. */
148
- redirectTo?: string;
149
- /** Called after Strava OAuth completes and the connection is established. */
150
- onComplete?: (
151
- ctx: GenericActionCtx<GenericDataModel>,
152
- event: StravaConnectEvent,
153
- ) => Promise<void>;
154
- }
155
-
156
- export interface GarminOAuthOptions {
157
- /** HTTP path for the OAuth callback. @default "/api/garmin/callback" */
158
- path?: string;
159
- /** Override GARMIN_CLIENT_ID env var. */
160
- clientId?: string;
161
- /** Override GARMIN_CLIENT_SECRET env var. */
162
- clientSecret?: string;
163
- /** URL to redirect the user to after a successful connection. */
164
- redirectTo?: string;
165
- /** Called after Garmin OAuth completes and the connection is established. */
166
- onComplete?: (
167
- ctx: GenericActionCtx<GenericDataModel>,
168
- event: GarminConnectEvent,
169
- ) => Promise<void>;
170
- }
171
-
172
- export interface GarminWebhookOptions {
173
- /** Base path prefix for all webhook routes. @default "/api/garmin/webhook" */
174
- basePath?: string;
175
- /** Called after every webhook payload is processed, regardless of data type. */
176
- onEvent?: GarminWebhookHandler;
177
- /**
178
- * Per-data-type webhook registration.
179
- *
180
- * **Only data types listed here get an HTTP route registered.**
181
- * Unlisted types are ignored Garmin receives a 404 if it POSTs to them.
182
- *
183
- * Pass a handler function to run custom logic after ingestion,
184
- * or `true` to register the route with default processing only.
185
- *
186
- * @example
187
- * ```ts
188
- * events: {
189
- * "activities": async (ctx, event) => { // custom side-effect },
190
- * "sleeps": true, // register route, default processing only
191
- * "dailies": true,
192
- * }
193
- * ```
194
- */
195
- events?: Partial<Record<GarminWebhookEventName, GarminWebhookHandler | true>>;
196
- }
197
-
198
- export interface RegisterRoutesOptions {
199
- strava?: {
200
- /** OAuth callback configuration. */
201
- oauth?: StravaOAuthOptions;
202
- };
203
- garmin?: {
204
- /** OAuth callback configuration. */
205
- oauth?: GarminOAuthOptions;
206
- /**
207
- * Webhook route configuration.
208
- *
209
- * Routes are **disabled by default**. Only data types listed in `events`
210
- * get an HTTP endpoint registered. Omit entirely or set to `false` to
211
- * skip all webhook routes.
212
- */
213
- webhook?: GarminWebhookOptions | false;
214
- };
215
- }
1
+ import type {
2
+ GenericActionCtx,
3
+ GenericMutationCtx,
4
+ GenericQueryCtx,
5
+ GenericDataModel,
6
+ } from "convex/server";
7
+
8
+ // ─── Context Types ──────────────────────────────────────────────────────────
9
+ // Narrowed Convex context types that only expose the runner methods each
10
+ // operation actually needs.
11
+
12
+ export type QueryCtx = Pick<GenericQueryCtx<GenericDataModel>, "runQuery">;
13
+
14
+ export type MutationCtx = Pick<
15
+ GenericMutationCtx<GenericDataModel>,
16
+ "runQuery" | "runMutation"
17
+ >;
18
+
19
+ export type ActionCtx = Pick<
20
+ GenericActionCtx<GenericDataModel>,
21
+ "runQuery" | "runMutation" | "runAction"
22
+ >;
23
+
24
+ // ─── Provider Configuration ─────────────────────────────────────────────────
25
+
26
+ /**
27
+ * Configuration for the Strava integration.
28
+ *
29
+ * If not provided to the Soma constructor, the class will attempt to
30
+ * read `STRAVA_CLIENT_ID` and `STRAVA_CLIENT_SECRET`
31
+ * from environment variables automatically.
32
+ */
33
+ export interface SomaStravaConfig {
34
+ /** Your Strava application's Client ID. */
35
+ clientId: string;
36
+ /** Your Strava application's Client Secret. */
37
+ clientSecret: string;
38
+ }
39
+
40
+ /**
41
+ * Configuration for the Garmin integration.
42
+ *
43
+ * If not provided to the Soma constructor, the class will attempt to
44
+ * read `GARMIN_CLIENT_ID` and `GARMIN_CLIENT_SECRET` from
45
+ * environment variables automatically.
46
+ */
47
+ export interface SomaGarminConfig {
48
+ /** Your Garmin application's Client ID. */
49
+ clientId: string;
50
+ /** Your Garmin application's Client Secret. */
51
+ clientSecret: string;
52
+ }
53
+
54
+ // ─── Shared Error & Result Types ───────────────────────────────────────────
55
+
56
+ /**
57
+ * A structured error from a Soma operation.
58
+ *
59
+ * Operational errors (API failures, transform errors, ingestion failures)
60
+ * are returned in the result not thrown. Only configuration errors
61
+ * (missing connection, nonexistent document) are thrown as exceptions.
62
+ */
63
+ export interface SomaError {
64
+ /** Category of the failed item (e.g. `"activity"`, `"pushWorkout"`, `"ingest"`). */
65
+ type: string;
66
+ /** Identifier of the failed item, or `"fetch"` for API-level failures. */
67
+ id: string;
68
+ /** Human-readable error description. */
69
+ message: string;
70
+ }
71
+
72
+ /**
73
+ * Standard result wrapper for all Soma data operations.
74
+ *
75
+ * - `data` contains the operation's success payload.
76
+ * - `errors` contains any operational failures that occurred.
77
+ *
78
+ * For **batch operations** (pull, sync), partial success is possible:
79
+ * `data` contains the counts of successfully processed items, while
80
+ * `errors` lists the individual failures.
81
+ *
82
+ * For **atomic operations** (push, delete), `data` is `null` when the
83
+ * operation fails, with a single entry in `errors` describing the cause.
84
+ */
85
+ export type SomaResult<T> = {
86
+ data: T;
87
+ errors: SomaError[];
88
+ };
89
+
90
+ // ─── Data Query & Ingestion Args ────────────────────────────────────────────
91
+
92
+ /**
93
+ * Common args shape for all ingestion methods.
94
+ *
95
+ * Requires `connectionId` and `userId` at minimum — additional fields
96
+ * come from the transformer output (e.g., `metadata`, `calories_data`, etc.)
97
+ * and are validated server-side by Convex validators.
98
+ */
99
+ export type IngestArgs = {
100
+ connectionId: string;
101
+ userId: string;
102
+ } & Record<string, unknown>;
103
+
104
+ /**
105
+ * Base args for time-range filtered queries.
106
+ *
107
+ * - `userId` is required for all health data queries.
108
+ * - `startTime` / `endTime` are optional ISO-8601 bounds on `metadata.start_time`.
109
+ */
110
+ export type TimeRangeArgs = {
111
+ userId: string;
112
+ startTime?: string;
113
+ endTime?: string;
114
+ };
115
+
116
+ /**
117
+ * Args for list (collect-all) queries with optional ordering and limit.
118
+ */
119
+ export type ListTimeRangeArgs = TimeRangeArgs & {
120
+ order?: "asc" | "desc";
121
+ limit?: number;
122
+ };
123
+
124
+ /**
125
+ * Args for paginated queries with Convex pagination options.
126
+ */
127
+ export type PaginateTimeRangeArgs = TimeRangeArgs & {
128
+ paginationOpts: { numItems: number; cursor: string | null };
129
+ };
130
+
131
+ // ─── OAuth Callback Events ──────────────────────────────────────────────────
132
+
133
+ /** Data passed to `onComplete` after Strava OAuth completes. */
134
+ export interface StravaConnectEvent {
135
+ provider: "STRAVA";
136
+ userId: string;
137
+ connectionId: string;
138
+ }
139
+
140
+ /** Data passed to `oauth.onComplete` after Garmin OAuth completes. */
141
+ export interface GarminConnectEvent {
142
+ provider: "GARMIN";
143
+ userId: string;
144
+ connectionId: string;
145
+ }
146
+
147
+ // ─── Garmin Webhook Types ───────────────────────────────────────────────────
148
+
149
+ /** Args accepted by all Garmin webhook handler actions inside the component. */
150
+ export type GarminWebhookActionArgs = {
151
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
152
+ payload: any;
153
+ autoIngest?: boolean;
154
+ };
155
+
156
+ /** Result returned by all Garmin webhook handler actions inside the component. */
157
+ export interface GarminWebhookActionResult {
158
+ errors: SomaError[];
159
+ items: GarminWebhookItem[];
160
+ }
161
+
162
+ /** A single transformed item with its user/connection mapping. */
163
+ export interface GarminWebhookItem {
164
+ /** The Soma connection ID that this data belongs to. */
165
+ connectionId: string;
166
+ /** The host app's user ID. */
167
+ userId: string;
168
+ /** The transformed data in Soma's normalized format. */
169
+ data: Record<string, unknown>;
170
+ }
171
+
172
+ /**
173
+ * Data passed to webhook `events` handlers and `onEvent` after processing.
174
+ *
175
+ * The host app receives the full set of transformed items plus the raw Garmin
176
+ * payload, regardless of the `autoIngest` setting.
177
+ */
178
+ export interface GarminWebhookEvent {
179
+ /** The Garmin data type that triggered this webhook (e.g. `"activities"`, `"sleeps"`). */
180
+ dataType: string;
181
+ /** Errors encountered during connection resolution, transformation, or ingestion. */
182
+ errors: SomaError[];
183
+ /** The raw JSON payload received from Garmin, before any transformation. */
184
+ rawPayload: unknown;
185
+ /**
186
+ * Transformed items in Soma's normalized format.
187
+ *
188
+ * Always contains the full set of successfully transformed items, regardless
189
+ * of the `autoIngest` setting. When `autoIngest` is `true`, these are the
190
+ * same items that were written to the database. When `autoIngest` is `false`,
191
+ * the host app can use them for custom ingestion or processing.
192
+ *
193
+ * Empty for ping-mode webhooks (which contain no data).
194
+ */
195
+ items: GarminWebhookItem[];
196
+ }
197
+
198
+ /** Webhook endpoint names matching the Garmin API data types. */
199
+ export type GarminWebhookEventName =
200
+ | "activities" | "activity-details" | "manually-updated-activities" | "move-iq"
201
+ | "blood-pressures" | "body-compositions" | "dailies" | "epochs"
202
+ | "health-snapshot" | "sleeps" | "hrv" | "stress" | "pulse-ox"
203
+ | "respiration" | "skin-temp" | "user-metrics" | "menstrual-cycle-tracking";
204
+
205
+ /** Handler for a specific webhook event or the catch-all `onEvent`. */
206
+ export type GarminWebhookHandler = (
207
+ ctx: GenericActionCtx<GenericDataModel>,
208
+ event: GarminWebhookEvent,
209
+ ) => Promise<void>;
210
+
211
+ // ─── Route Registration Options ─────────────────────────────────────────────
212
+
213
+ /**
214
+ * Per-provider options for `registerRoutes`.
215
+ */
216
+ export interface StravaOAuthOptions {
217
+ /** HTTP path for the OAuth callback. @default "/api/strava/callback" */
218
+ path?: string;
219
+ /** URL to redirect the user to after a successful connection. */
220
+ redirectTo?: string;
221
+ /** Called after Strava OAuth completes and the connection is established. */
222
+ onComplete?: (
223
+ ctx: GenericActionCtx<GenericDataModel>,
224
+ event: StravaConnectEvent,
225
+ ) => Promise<void>;
226
+ }
227
+
228
+ export interface GarminOAuthOptions {
229
+ /** HTTP path for the OAuth callback. @default "/api/garmin/callback" */
230
+ path?: string;
231
+ /** URL to redirect the user to after a successful connection. */
232
+ redirectTo?: string;
233
+ /** Called after Garmin OAuth completes and the connection is established. */
234
+ onComplete?: (
235
+ ctx: GenericActionCtx<GenericDataModel>,
236
+ event: GarminConnectEvent,
237
+ ) => Promise<void>;
238
+ }
239
+
240
+ export interface GarminWebhookOptions {
241
+ /** Base path prefix for all webhook routes. @default "/api/garmin/webhook" */
242
+ basePath?: string;
243
+ /**
244
+ * Whether to automatically ingest (upsert) transformed data into the Soma
245
+ * database when a webhook payload is received.
246
+ *
247
+ * When `true` (default), incoming data is validated, transformed, and written
248
+ * to the database automatically. When `false`, the webhook still receives and
249
+ * validates the payload, but skips the database write — useful when you want
250
+ * to handle ingestion yourself via the `onEvent` / per-type callbacks.
251
+ *
252
+ * @default true
253
+ */
254
+ autoIngest?: boolean;
255
+ /** Called after every webhook payload is processed, regardless of data type. */
256
+ onEvent?: GarminWebhookHandler;
257
+ /**
258
+ * Per-data-type webhook registration.
259
+ *
260
+ * **Only data types listed here get an HTTP route registered.**
261
+ * Unlisted types are ignored — Garmin receives a 404 if it POSTs to them.
262
+ *
263
+ * Pass a handler function to run custom logic after ingestion,
264
+ * or `true` to register the route with default processing only.
265
+ *
266
+ * @example
267
+ * ```ts
268
+ * events: {
269
+ * "activities": async (ctx, event) => { // custom side-effect },
270
+ * "sleeps": true, // register route, default processing only
271
+ * "dailies": true,
272
+ * }
273
+ * ```
274
+ */
275
+ events?: Partial<Record<GarminWebhookEventName, GarminWebhookHandler | true>>;
276
+ }
277
+
278
+ export interface RegisterRoutesOptions {
279
+ strava?: {
280
+ /** Override STRAVA_CLIENT_ID env var. */
281
+ clientId?: string;
282
+ /** Override STRAVA_CLIENT_SECRET env var. */
283
+ clientSecret?: string;
284
+ /** OAuth callback configuration. */
285
+ oauth?: StravaOAuthOptions;
286
+ };
287
+ garmin?: {
288
+ /** Override GARMIN_CLIENT_ID env var. */
289
+ clientId?: string;
290
+ /** Override GARMIN_CLIENT_SECRET env var. */
291
+ clientSecret?: string;
292
+ /** OAuth callback configuration. */
293
+ oauth?: GarminOAuthOptions;
294
+ /**
295
+ * Webhook route configuration.
296
+ *
297
+ * Routes are **disabled by default**. Only data types listed in `events`
298
+ * get an HTTP endpoint registered. Omit entirely or set to `false` to
299
+ * skip all webhook routes.
300
+ */
301
+ webhook?: GarminWebhookOptions | false;
302
+ };
303
+ }