@nativesquare/soma 0.12.0 → 0.13.1
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/garmin.d.ts +9 -2
- package/dist/client/garmin.d.ts.map +1 -1
- package/dist/client/garmin.js +152 -1
- package/dist/client/garmin.js.map +1 -1
- package/dist/client/index.d.ts +5 -6
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +5 -211
- package/dist/client/index.js.map +1 -1
- package/dist/client/strava.d.ts +14 -7
- package/dist/client/strava.d.ts.map +1 -1
- package/dist/client/strava.js +67 -1
- package/dist/client/strava.js.map +1 -1
- package/dist/client/types.d.ts +93 -20
- package/dist/client/types.d.ts.map +1 -1
- package/dist/component/_generated/component.d.ts +24 -5
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/garmin/private.d.ts +53 -68
- package/dist/component/garmin/private.d.ts.map +1 -1
- package/dist/component/garmin/private.js +87 -85
- package/dist/component/garmin/private.js.map +1 -1
- package/dist/component/garmin/public.d.ts +97 -43
- package/dist/component/garmin/public.d.ts.map +1 -1
- package/dist/component/garmin/public.js +75 -51
- package/dist/component/garmin/public.js.map +1 -1
- package/dist/component/garmin/webhooks.d.ts +22 -20
- package/dist/component/garmin/webhooks.d.ts.map +1 -1
- package/dist/component/garmin/webhooks.js +115 -76
- package/dist/component/garmin/webhooks.js.map +1 -1
- package/dist/component/public.d.ts +15 -15
- package/dist/component/schema.d.ts +25 -25
- package/dist/component/strava/public.d.ts +12 -8
- package/dist/component/strava/public.d.ts.map +1 -1
- package/dist/component/strava/public.js +7 -7
- package/dist/component/strava/public.js.map +1 -1
- package/dist/component/validators/activity.d.ts +4 -4
- package/dist/component/validators/athlete.d.ts +6 -0
- package/dist/component/validators/athlete.d.ts.map +1 -1
- package/dist/component/validators/athlete.js.map +1 -1
- package/dist/component/validators/body.d.ts +4 -4
- package/dist/component/validators/daily.d.ts +4 -4
- package/dist/component/validators/nutrition.d.ts +9 -3
- package/dist/component/validators/nutrition.d.ts.map +1 -1
- package/dist/component/validators/nutrition.js.map +1 -1
- package/dist/component/validators/samples.d.ts +4 -4
- package/dist/component/validators/shared.d.ts +13 -4
- package/dist/component/validators/shared.d.ts.map +1 -1
- package/dist/component/validators/shared.js +7 -0
- package/dist/component/validators/shared.js.map +1 -1
- package/dist/component/validators/sleep.d.ts +11 -5
- package/dist/component/validators/sleep.d.ts.map +1 -1
- package/dist/component/validators/sleep.js.map +1 -1
- package/dist/validators.d.ts +48 -41
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +7 -6
- package/dist/validators.js.map +1 -1
- package/package.json +1 -1
- package/src/client/garmin.ts +695 -487
- package/src/client/index.ts +10 -279
- package/src/client/strava.ts +201 -108
- package/src/client/types.ts +303 -215
- package/src/component/_generated/component.ts +19 -19
- package/src/component/garmin/private.ts +1872 -1870
- package/src/component/garmin/public.ts +104 -80
- package/src/component/garmin/webhooks.ts +122 -81
- package/src/component/strava/public.ts +393 -393
- package/src/component/validators/athlete.ts +6 -0
- package/src/component/validators/nutrition.ts +6 -0
- package/src/component/validators/shared.ts +9 -0
- package/src/component/validators/sleep.ts +6 -0
- package/src/validators.ts +35 -7
package/dist/client/strava.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { SomaComponent } from "./index.js";
|
|
2
|
-
import type { ActionCtx, SomaStravaConfig } from "./types.js";
|
|
2
|
+
import type { ActionCtx, SomaStravaConfig, RegisterRoutesOptions } from "./types.js";
|
|
3
|
+
import { type HttpRouter } from "convex/server";
|
|
4
|
+
export declare const STRAVA_CALLBACK_PATH = "/api/strava/callback";
|
|
3
5
|
export declare class SomaStrava {
|
|
4
6
|
private component;
|
|
5
7
|
private requireConfig;
|
|
@@ -13,7 +15,9 @@ export declare class SomaStrava {
|
|
|
13
15
|
*
|
|
14
16
|
* @param ctx - Action context from the host app
|
|
15
17
|
* @param opts.userId - The host app's user identifier
|
|
16
|
-
* @param opts.redirectUri - The URL Strava will redirect to after authorization
|
|
18
|
+
* @param opts.redirectUri - The URL Strava will redirect to after authorization.
|
|
19
|
+
* This should match the `registerRoutes` callback path
|
|
20
|
+
* (default: `${CONVEX_SITE_URL}/api/strava/callback`).
|
|
17
21
|
* @param opts.scope - Comma-separated Strava OAuth scopes (default: "read,activity:read_all,profile:read_all")
|
|
18
22
|
* @returns `{ authUrl, state }`
|
|
19
23
|
*
|
|
@@ -56,15 +60,17 @@ export declare class SomaStrava {
|
|
|
56
60
|
userId: string;
|
|
57
61
|
after?: number;
|
|
58
62
|
}): Promise<{
|
|
63
|
+
data: {
|
|
64
|
+
synced: {
|
|
65
|
+
activities: number;
|
|
66
|
+
athletes: number;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
59
69
|
errors: Array<{
|
|
60
|
-
error: string;
|
|
61
70
|
id: string;
|
|
71
|
+
message: string;
|
|
62
72
|
type: string;
|
|
63
73
|
}>;
|
|
64
|
-
synced: {
|
|
65
|
-
activities: number;
|
|
66
|
-
athletes: number;
|
|
67
|
-
};
|
|
68
74
|
}>;
|
|
69
75
|
/**
|
|
70
76
|
* Disconnect a user from Strava.
|
|
@@ -88,5 +94,6 @@ export declare class SomaStrava {
|
|
|
88
94
|
disconnect(ctx: ActionCtx, args: {
|
|
89
95
|
userId: string;
|
|
90
96
|
}): Promise<null>;
|
|
97
|
+
static registerRoutes(http: HttpRouter, component: SomaComponent, opts?: RegisterRoutesOptions["strava"]): void;
|
|
91
98
|
}
|
|
92
99
|
//# sourceMappingURL=strava.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"strava.d.ts","sourceRoot":"","sources":["../../src/client/strava.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"strava.d.ts","sourceRoot":"","sources":["../../src/client/strava.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACrF,OAAO,EAAqB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAEnE,eAAO,MAAM,oBAAoB,yBAAyB,CAAC;AAE3D,qBAAa,UAAU;IAEnB,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,aAAa;gBADb,SAAS,EAAE,aAAa,EACxB,aAAa,EAAE,MAAM,gBAAgB;IAG/C;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACG,UAAU,CACd,GAAG,EAAE,SAAS,EACd,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAW/D;;;;;;;;;;;;;;;;;;;;OAoBG;IACG,IAAI,CACR,GAAG,EAAE,SAAS,EACd,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;;;;;;;;;;;;;IAU1C;;;;;;;;;;;;;;;;;;OAkBG;IACG,UAAU,CACd,GAAG,EAAE,SAAS,EACd,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE;IAU1B,MAAM,CAAC,cAAc,CACnB,IAAI,EAAE,UAAU,EAChB,SAAS,EAAE,aAAa,EACxB,IAAI,CAAC,EAAE,qBAAqB,CAAC,QAAQ,CAAC;CAoFzC"}
|
package/dist/client/strava.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { httpActionGeneric } from "convex/server";
|
|
2
|
+
export const STRAVA_CALLBACK_PATH = "/api/strava/callback";
|
|
1
3
|
export class SomaStrava {
|
|
2
4
|
component;
|
|
3
5
|
requireConfig;
|
|
@@ -14,7 +16,9 @@ export class SomaStrava {
|
|
|
14
16
|
*
|
|
15
17
|
* @param ctx - Action context from the host app
|
|
16
18
|
* @param opts.userId - The host app's user identifier
|
|
17
|
-
* @param opts.redirectUri - The URL Strava will redirect to after authorization
|
|
19
|
+
* @param opts.redirectUri - The URL Strava will redirect to after authorization.
|
|
20
|
+
* This should match the `registerRoutes` callback path
|
|
21
|
+
* (default: `${CONVEX_SITE_URL}/api/strava/callback`).
|
|
18
22
|
* @param opts.scope - Comma-separated Strava OAuth scopes (default: "read,activity:read_all,profile:read_all")
|
|
19
23
|
* @returns `{ authUrl, state }`
|
|
20
24
|
*
|
|
@@ -92,5 +96,67 @@ export class SomaStrava {
|
|
|
92
96
|
clientSecret: config.clientSecret,
|
|
93
97
|
});
|
|
94
98
|
}
|
|
99
|
+
static registerRoutes(http, component, opts) {
|
|
100
|
+
const oauth = opts?.oauth ?? {};
|
|
101
|
+
const path = oauth.path ?? STRAVA_CALLBACK_PATH;
|
|
102
|
+
http.route({
|
|
103
|
+
path,
|
|
104
|
+
method: "GET",
|
|
105
|
+
handler: httpActionGeneric(async (ctx, request) => {
|
|
106
|
+
const url = new URL(request.url);
|
|
107
|
+
const code = url.searchParams.get("code");
|
|
108
|
+
const state = url.searchParams.get("state");
|
|
109
|
+
if (!code) {
|
|
110
|
+
return new Response("Missing authorization code", { status: 400 });
|
|
111
|
+
}
|
|
112
|
+
if (!state) {
|
|
113
|
+
return new Response("Missing state parameter. Ensure the state was included " +
|
|
114
|
+
"when building the Strava auth URL via getStravaAuthUrl.", { status: 400 });
|
|
115
|
+
}
|
|
116
|
+
const clientId = opts?.clientId ?? process.env.STRAVA_CLIENT_ID;
|
|
117
|
+
const clientSecret = opts?.clientSecret ?? process.env.STRAVA_CLIENT_SECRET;
|
|
118
|
+
if (!clientId || !clientSecret) {
|
|
119
|
+
return new Response("Strava credentials not configured. Set STRAVA_CLIENT_ID and " +
|
|
120
|
+
"STRAVA_CLIENT_SECRET environment variables, or pass them to registerRoutes.", { status: 500 });
|
|
121
|
+
}
|
|
122
|
+
let result;
|
|
123
|
+
try {
|
|
124
|
+
result = await ctx.runAction(component.strava.public.completeStravaOAuth, {
|
|
125
|
+
code,
|
|
126
|
+
state,
|
|
127
|
+
clientId,
|
|
128
|
+
clientSecret,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
133
|
+
return new Response(`Strava OAuth callback failed: ${message}`, {
|
|
134
|
+
status: 500,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
if (oauth.onComplete) {
|
|
138
|
+
try {
|
|
139
|
+
await oauth.onComplete(ctx, {
|
|
140
|
+
provider: "STRAVA",
|
|
141
|
+
userId: result.userId,
|
|
142
|
+
connectionId: result.connectionId,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
catch (callbackError) {
|
|
146
|
+
console.error("[soma] strava onComplete callback error:", callbackError instanceof Error ? callbackError.message : callbackError);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (oauth.redirectTo) {
|
|
150
|
+
return new Response(null, {
|
|
151
|
+
status: 302,
|
|
152
|
+
headers: { Location: oauth.redirectTo },
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
return new Response("Successfully connected to Strava!", {
|
|
156
|
+
status: 200,
|
|
157
|
+
});
|
|
158
|
+
}),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
95
161
|
}
|
|
96
162
|
//# sourceMappingURL=strava.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"strava.js","sourceRoot":"","sources":["../../src/client/strava.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"strava.js","sourceRoot":"","sources":["../../src/client/strava.ts"],"names":[],"mappings":"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"}
|
package/dist/client/types.d.ts
CHANGED
|
@@ -28,6 +28,38 @@ export interface SomaGarminConfig {
|
|
|
28
28
|
/** Your Garmin application's Client Secret. */
|
|
29
29
|
clientSecret: string;
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* A structured error from a Soma operation.
|
|
33
|
+
*
|
|
34
|
+
* Operational errors (API failures, transform errors, ingestion failures)
|
|
35
|
+
* are returned in the result — not thrown. Only configuration errors
|
|
36
|
+
* (missing connection, nonexistent document) are thrown as exceptions.
|
|
37
|
+
*/
|
|
38
|
+
export interface SomaError {
|
|
39
|
+
/** Category of the failed item (e.g. `"activity"`, `"pushWorkout"`, `"ingest"`). */
|
|
40
|
+
type: string;
|
|
41
|
+
/** Identifier of the failed item, or `"fetch"` for API-level failures. */
|
|
42
|
+
id: string;
|
|
43
|
+
/** Human-readable error description. */
|
|
44
|
+
message: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Standard result wrapper for all Soma data operations.
|
|
48
|
+
*
|
|
49
|
+
* - `data` contains the operation's success payload.
|
|
50
|
+
* - `errors` contains any operational failures that occurred.
|
|
51
|
+
*
|
|
52
|
+
* For **batch operations** (pull, sync), partial success is possible:
|
|
53
|
+
* `data` contains the counts of successfully processed items, while
|
|
54
|
+
* `errors` lists the individual failures.
|
|
55
|
+
*
|
|
56
|
+
* For **atomic operations** (push, delete), `data` is `null` when the
|
|
57
|
+
* operation fails, with a single entry in `errors` describing the cause.
|
|
58
|
+
*/
|
|
59
|
+
export type SomaResult<T> = {
|
|
60
|
+
data: T;
|
|
61
|
+
errors: SomaError[];
|
|
62
|
+
};
|
|
31
63
|
/**
|
|
32
64
|
* Common args shape for all ingestion methods.
|
|
33
65
|
*
|
|
@@ -78,20 +110,49 @@ export interface GarminConnectEvent {
|
|
|
78
110
|
userId: string;
|
|
79
111
|
connectionId: string;
|
|
80
112
|
}
|
|
81
|
-
/**
|
|
113
|
+
/** Args accepted by all Garmin webhook handler actions inside the component. */
|
|
114
|
+
export type GarminWebhookActionArgs = {
|
|
115
|
+
payload: any;
|
|
116
|
+
autoIngest?: boolean;
|
|
117
|
+
};
|
|
118
|
+
/** Result returned by all Garmin webhook handler actions inside the component. */
|
|
119
|
+
export interface GarminWebhookActionResult {
|
|
120
|
+
errors: SomaError[];
|
|
121
|
+
items: GarminWebhookItem[];
|
|
122
|
+
}
|
|
123
|
+
/** A single transformed item with its user/connection mapping. */
|
|
124
|
+
export interface GarminWebhookItem {
|
|
125
|
+
/** The Soma connection ID that this data belongs to. */
|
|
126
|
+
connectionId: string;
|
|
127
|
+
/** The host app's user ID. */
|
|
128
|
+
userId: string;
|
|
129
|
+
/** The transformed data in Soma's normalized format. */
|
|
130
|
+
data: Record<string, unknown>;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Data passed to webhook `events` handlers and `onEvent` after processing.
|
|
134
|
+
*
|
|
135
|
+
* The host app receives the full set of transformed items plus the raw Garmin
|
|
136
|
+
* payload, regardless of the `autoIngest` setting.
|
|
137
|
+
*/
|
|
82
138
|
export interface GarminWebhookEvent {
|
|
139
|
+
/** The Garmin data type that triggered this webhook (e.g. `"activities"`, `"sleeps"`). */
|
|
83
140
|
dataType: string;
|
|
84
|
-
|
|
85
|
-
errors:
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
141
|
+
/** Errors encountered during connection resolution, transformation, or ingestion. */
|
|
142
|
+
errors: SomaError[];
|
|
143
|
+
/** The raw JSON payload received from Garmin, before any transformation. */
|
|
144
|
+
rawPayload: unknown;
|
|
145
|
+
/**
|
|
146
|
+
* Transformed items in Soma's normalized format.
|
|
147
|
+
*
|
|
148
|
+
* Always contains the full set of successfully transformed items, regardless
|
|
149
|
+
* of the `autoIngest` setting. When `autoIngest` is `true`, these are the
|
|
150
|
+
* same items that were written to the database. When `autoIngest` is `false`,
|
|
151
|
+
* the host app can use them for custom ingestion or processing.
|
|
152
|
+
*
|
|
153
|
+
* Empty for ping-mode webhooks (which contain no data).
|
|
154
|
+
*/
|
|
155
|
+
items: GarminWebhookItem[];
|
|
95
156
|
}
|
|
96
157
|
/** Webhook endpoint names matching the Garmin API data types. */
|
|
97
158
|
export type GarminWebhookEventName = "activities" | "activity-details" | "manually-updated-activities" | "move-iq" | "blood-pressures" | "body-compositions" | "dailies" | "epochs" | "health-snapshot" | "sleeps" | "hrv" | "stress" | "pulse-ox" | "respiration" | "skin-temp" | "user-metrics" | "menstrual-cycle-tracking";
|
|
@@ -103,10 +164,6 @@ export type GarminWebhookHandler = (ctx: GenericActionCtx<GenericDataModel>, eve
|
|
|
103
164
|
export interface StravaOAuthOptions {
|
|
104
165
|
/** HTTP path for the OAuth callback. @default "/api/strava/callback" */
|
|
105
166
|
path?: string;
|
|
106
|
-
/** Override STRAVA_CLIENT_ID env var. */
|
|
107
|
-
clientId?: string;
|
|
108
|
-
/** Override STRAVA_CLIENT_SECRET env var. */
|
|
109
|
-
clientSecret?: string;
|
|
110
167
|
/** URL to redirect the user to after a successful connection. */
|
|
111
168
|
redirectTo?: string;
|
|
112
169
|
/** Called after Strava OAuth completes and the connection is established. */
|
|
@@ -115,10 +172,6 @@ export interface StravaOAuthOptions {
|
|
|
115
172
|
export interface GarminOAuthOptions {
|
|
116
173
|
/** HTTP path for the OAuth callback. @default "/api/garmin/callback" */
|
|
117
174
|
path?: string;
|
|
118
|
-
/** Override GARMIN_CLIENT_ID env var. */
|
|
119
|
-
clientId?: string;
|
|
120
|
-
/** Override GARMIN_CLIENT_SECRET env var. */
|
|
121
|
-
clientSecret?: string;
|
|
122
175
|
/** URL to redirect the user to after a successful connection. */
|
|
123
176
|
redirectTo?: string;
|
|
124
177
|
/** Called after Garmin OAuth completes and the connection is established. */
|
|
@@ -127,6 +180,18 @@ export interface GarminOAuthOptions {
|
|
|
127
180
|
export interface GarminWebhookOptions {
|
|
128
181
|
/** Base path prefix for all webhook routes. @default "/api/garmin/webhook" */
|
|
129
182
|
basePath?: string;
|
|
183
|
+
/**
|
|
184
|
+
* Whether to automatically ingest (upsert) transformed data into the Soma
|
|
185
|
+
* database when a webhook payload is received.
|
|
186
|
+
*
|
|
187
|
+
* When `true` (default), incoming data is validated, transformed, and written
|
|
188
|
+
* to the database automatically. When `false`, the webhook still receives and
|
|
189
|
+
* validates the payload, but skips the database write — useful when you want
|
|
190
|
+
* to handle ingestion yourself via the `onEvent` / per-type callbacks.
|
|
191
|
+
*
|
|
192
|
+
* @default true
|
|
193
|
+
*/
|
|
194
|
+
autoIngest?: boolean;
|
|
130
195
|
/** Called after every webhook payload is processed, regardless of data type. */
|
|
131
196
|
onEvent?: GarminWebhookHandler;
|
|
132
197
|
/**
|
|
@@ -151,10 +216,18 @@ export interface GarminWebhookOptions {
|
|
|
151
216
|
}
|
|
152
217
|
export interface RegisterRoutesOptions {
|
|
153
218
|
strava?: {
|
|
219
|
+
/** Override STRAVA_CLIENT_ID env var. */
|
|
220
|
+
clientId?: string;
|
|
221
|
+
/** Override STRAVA_CLIENT_SECRET env var. */
|
|
222
|
+
clientSecret?: string;
|
|
154
223
|
/** OAuth callback configuration. */
|
|
155
224
|
oauth?: StravaOAuthOptions;
|
|
156
225
|
};
|
|
157
226
|
garmin?: {
|
|
227
|
+
/** Override GARMIN_CLIENT_ID env var. */
|
|
228
|
+
clientId?: string;
|
|
229
|
+
/** Override GARMIN_CLIENT_SECRET env var. */
|
|
230
|
+
clientSecret?: string;
|
|
158
231
|
/** OAuth callback configuration. */
|
|
159
232
|
oauth?: GarminOAuthOptions;
|
|
160
233
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EACnB,MAAM,eAAe,CAAC;AAMvB,MAAM,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,gBAAgB,CAAC,EAAE,UAAU,CAAC,CAAC;AAE3E,MAAM,MAAM,WAAW,GAAG,IAAI,CAC1B,kBAAkB,CAAC,gBAAgB,CAAC,EACpC,UAAU,GAAG,aAAa,CAC7B,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,IAAI,CACxB,gBAAgB,CAAC,gBAAgB,CAAC,EAClC,UAAU,GAAG,aAAa,GAAG,WAAW,CAC3C,CAAC;AAIF;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,YAAY,EAAE,MAAM,CAAC;CACtB;AAID;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE5B;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG;IAC9C,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,aAAa,GAAG;IAClD,cAAc,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CAC7D,CAAC;AAIF,gEAAgE;AAChE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,sEAAsE;AACtE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACtB;AAID,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -156,54 +156,71 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
156
156
|
};
|
|
157
157
|
webhooks: {
|
|
158
158
|
handleGarminWebhookActivities: FunctionReference<"action", "internal", {
|
|
159
|
+
autoIngest?: boolean;
|
|
159
160
|
payload: any;
|
|
160
161
|
}, any, Name>;
|
|
161
162
|
handleGarminWebhookActivityDetails: FunctionReference<"action", "internal", {
|
|
163
|
+
autoIngest?: boolean;
|
|
162
164
|
payload: any;
|
|
163
165
|
}, any, Name>;
|
|
164
166
|
handleGarminWebhookBloodPressures: FunctionReference<"action", "internal", {
|
|
167
|
+
autoIngest?: boolean;
|
|
165
168
|
payload: any;
|
|
166
169
|
}, any, Name>;
|
|
167
170
|
handleGarminWebhookBodyCompositions: FunctionReference<"action", "internal", {
|
|
171
|
+
autoIngest?: boolean;
|
|
168
172
|
payload: any;
|
|
169
173
|
}, any, Name>;
|
|
170
174
|
handleGarminWebhookDailies: FunctionReference<"action", "internal", {
|
|
175
|
+
autoIngest?: boolean;
|
|
171
176
|
payload: any;
|
|
172
177
|
}, any, Name>;
|
|
173
178
|
handleGarminWebhookEpochs: FunctionReference<"action", "internal", {
|
|
179
|
+
autoIngest?: boolean;
|
|
174
180
|
payload: any;
|
|
175
181
|
}, any, Name>;
|
|
176
182
|
handleGarminWebhookHealthSnapshot: FunctionReference<"action", "internal", {
|
|
183
|
+
autoIngest?: boolean;
|
|
177
184
|
payload: any;
|
|
178
185
|
}, any, Name>;
|
|
179
186
|
handleGarminWebhookHRVSummary: FunctionReference<"action", "internal", {
|
|
187
|
+
autoIngest?: boolean;
|
|
180
188
|
payload: any;
|
|
181
189
|
}, any, Name>;
|
|
182
190
|
handleGarminWebhookManuallyUpdatedActivities: FunctionReference<"action", "internal", {
|
|
191
|
+
autoIngest?: boolean;
|
|
183
192
|
payload: any;
|
|
184
193
|
}, any, Name>;
|
|
185
194
|
handleGarminWebhookMenstrualCycleTracking: FunctionReference<"action", "internal", {
|
|
195
|
+
autoIngest?: boolean;
|
|
186
196
|
payload: any;
|
|
187
197
|
}, any, Name>;
|
|
188
198
|
handleGarminWebhookMoveIQ: FunctionReference<"action", "internal", {
|
|
199
|
+
autoIngest?: boolean;
|
|
189
200
|
payload: any;
|
|
190
201
|
}, any, Name>;
|
|
191
202
|
handleGarminWebhookPulseOx: FunctionReference<"action", "internal", {
|
|
203
|
+
autoIngest?: boolean;
|
|
192
204
|
payload: any;
|
|
193
205
|
}, any, Name>;
|
|
194
206
|
handleGarminWebhookRespiration: FunctionReference<"action", "internal", {
|
|
207
|
+
autoIngest?: boolean;
|
|
195
208
|
payload: any;
|
|
196
209
|
}, any, Name>;
|
|
197
210
|
handleGarminWebhookSkinTemp: FunctionReference<"action", "internal", {
|
|
211
|
+
autoIngest?: boolean;
|
|
198
212
|
payload: any;
|
|
199
213
|
}, any, Name>;
|
|
200
214
|
handleGarminWebhookSleeps: FunctionReference<"action", "internal", {
|
|
215
|
+
autoIngest?: boolean;
|
|
201
216
|
payload: any;
|
|
202
217
|
}, any, Name>;
|
|
203
218
|
handleGarminWebhookStress: FunctionReference<"action", "internal", {
|
|
219
|
+
autoIngest?: boolean;
|
|
204
220
|
payload: any;
|
|
205
221
|
}, any, Name>;
|
|
206
222
|
handleGarminWebhookUserMetrics: FunctionReference<"action", "internal", {
|
|
223
|
+
autoIngest?: boolean;
|
|
207
224
|
payload: any;
|
|
208
225
|
}, any, Name>;
|
|
209
226
|
};
|
|
@@ -1530,15 +1547,17 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
1530
1547
|
clientSecret: string;
|
|
1531
1548
|
userId: string;
|
|
1532
1549
|
}, {
|
|
1550
|
+
data: {
|
|
1551
|
+
synced: {
|
|
1552
|
+
activities: number;
|
|
1553
|
+
athletes: number;
|
|
1554
|
+
};
|
|
1555
|
+
};
|
|
1533
1556
|
errors: Array<{
|
|
1534
|
-
error: string;
|
|
1535
1557
|
id: string;
|
|
1558
|
+
message: string;
|
|
1536
1559
|
type: string;
|
|
1537
1560
|
}>;
|
|
1538
|
-
synced: {
|
|
1539
|
-
activities: number;
|
|
1540
|
-
athletes: number;
|
|
1541
|
-
};
|
|
1542
1561
|
}, Name>;
|
|
1543
1562
|
};
|
|
1544
1563
|
};
|