@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.
- package/dist/client/garmin.d.ts +5 -1
- package/dist/client/garmin.d.ts.map +1 -1
- package/dist/client/garmin.js +148 -0
- 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 +11 -6
- package/dist/client/strava.d.ts.map +1 -1
- package/dist/client/strava.js +64 -0
- 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/body.d.ts +4 -4
- package/dist/component/validators/daily.d.ts +4 -4
- package/dist/component/validators/nutrition.d.ts +3 -3
- 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 +5 -5
- package/dist/validators.d.ts +41 -40
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +1 -0
- package/dist/validators.js.map +1 -1
- package/package.json +1 -1
- package/src/client/garmin.ts +692 -487
- package/src/client/index.ts +10 -279
- package/src/client/strava.ts +199 -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/shared.ts +9 -0
- package/src/validators.ts +1 -0
|
@@ -103,17 +103,30 @@ function isWebhookPushMode(payload: unknown): boolean {
|
|
|
103
103
|
return !("callbackURL" in firstItem);
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
/** Shape returned by every public webhook handler action to the HTTP layer. */
|
|
106
107
|
type WebhookResult = {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
affectedUsers: Array<{ userId: string; connectionId: string }>;
|
|
108
|
+
errors: Array<{ type: string; id: string; message: string }>;
|
|
109
|
+
items: Array<{ connectionId: string; userId: string; data: Record<string, unknown> }>;
|
|
110
110
|
};
|
|
111
111
|
|
|
112
|
+
/** Shape returned by internal push-processing actions (transform only, no DB writes). */
|
|
112
113
|
type ProcessResult = {
|
|
113
114
|
items: Array<{ connectionId: string; userId: string; data: Record<string, unknown> }>;
|
|
114
|
-
errors: Array<{ type: string; id: string;
|
|
115
|
+
errors: Array<{ type: string; id: string; message: string }>;
|
|
115
116
|
};
|
|
116
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Build a WebhookResult from a ProcessResult without writing to the database.
|
|
120
|
+
* Used when `autoIngest` is disabled — the host app still gets the full set of
|
|
121
|
+
* transformed items in its callbacks, but no data is persisted.
|
|
122
|
+
*/
|
|
123
|
+
function toWebhookResult(result: ProcessResult): WebhookResult {
|
|
124
|
+
return {
|
|
125
|
+
errors: result.errors,
|
|
126
|
+
items: result.items,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
117
130
|
/**
|
|
118
131
|
* Ingest transformed items from a private handler and update connections.
|
|
119
132
|
* Shared orchestration logic for all push-mode webhook handlers.
|
|
@@ -124,10 +137,8 @@ async function ingestAndUpdate(
|
|
|
124
137
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
125
138
|
ingestMutation: any,
|
|
126
139
|
): Promise<WebhookResult> {
|
|
127
|
-
let processed = 0;
|
|
128
140
|
const errors = [...result.errors];
|
|
129
141
|
const connectionsToUpdate = new Set<string>();
|
|
130
|
-
const userMap = new Map<string, { userId: string; connectionId: string }>();
|
|
131
142
|
|
|
132
143
|
for (const item of result.items) {
|
|
133
144
|
try {
|
|
@@ -135,18 +146,13 @@ async function ingestAndUpdate(
|
|
|
135
146
|
connectionId: item.connectionId,
|
|
136
147
|
userId: item.userId,
|
|
137
148
|
...item.data,
|
|
138
|
-
} as never);
|
|
139
|
-
processed++;
|
|
140
|
-
connectionsToUpdate.add(item.connectionId);
|
|
141
|
-
userMap.set(`${item.userId}:${item.connectionId}`, {
|
|
142
|
-
userId: item.userId,
|
|
143
|
-
connectionId: item.connectionId,
|
|
144
149
|
});
|
|
150
|
+
connectionsToUpdate.add(item.connectionId);
|
|
145
151
|
} catch (err) {
|
|
146
152
|
errors.push({
|
|
147
153
|
type: "ingest",
|
|
148
154
|
id: "unknown",
|
|
149
|
-
|
|
155
|
+
message: err instanceof Error ? err.message : String(err),
|
|
150
156
|
});
|
|
151
157
|
}
|
|
152
158
|
}
|
|
@@ -158,7 +164,7 @@ async function ingestAndUpdate(
|
|
|
158
164
|
} as never);
|
|
159
165
|
}
|
|
160
166
|
|
|
161
|
-
return {
|
|
167
|
+
return { errors, items: result.items };
|
|
162
168
|
}
|
|
163
169
|
|
|
164
170
|
// ─── Webhook Handlers ────────────────────────────────────────────────────────
|
|
@@ -167,8 +173,9 @@ async function ingestAndUpdate(
|
|
|
167
173
|
* Handle a webhook for Garmin activities (push or ping mode).
|
|
168
174
|
*/
|
|
169
175
|
export const handleGarminWebhookActivities = action({
|
|
170
|
-
args: { payload: v.any() },
|
|
176
|
+
args: { payload: v.any(), autoIngest: v.optional(v.boolean()) },
|
|
171
177
|
handler: async (ctx, args): Promise<WebhookResult> => {
|
|
178
|
+
const shouldIngest = args.autoIngest !== false;
|
|
172
179
|
if (isWebhookPushMode(args.payload)) {
|
|
173
180
|
const pushResult = garminActivityPushPayloadSchema.safeParse(args.payload);
|
|
174
181
|
if (pushResult.success && pushResult.data.activities.length > 0) {
|
|
@@ -176,7 +183,9 @@ export const handleGarminWebhookActivities = action({
|
|
|
176
183
|
internal.garmin.private.processActivityPushPayload,
|
|
177
184
|
{ payload: args.payload },
|
|
178
185
|
);
|
|
179
|
-
return
|
|
186
|
+
return shouldIngest
|
|
187
|
+
? await ingestAndUpdate(ctx, result, api.public.ingestActivity)
|
|
188
|
+
: toWebhookResult(result);
|
|
180
189
|
}
|
|
181
190
|
} else {
|
|
182
191
|
const pingResult = garminActivityPingPayloadSchema.safeParse(args.payload);
|
|
@@ -191,7 +200,7 @@ export const handleGarminWebhookActivities = action({
|
|
|
191
200
|
console.warn(
|
|
192
201
|
`[garmin:webhook:activities] Payload matched neither ping nor push schema`,
|
|
193
202
|
);
|
|
194
|
-
return {
|
|
203
|
+
return { errors: [], items: [] };
|
|
195
204
|
},
|
|
196
205
|
});
|
|
197
206
|
|
|
@@ -199,8 +208,9 @@ export const handleGarminWebhookActivities = action({
|
|
|
199
208
|
* Handle a webhook for Garmin activity details (push or ping mode).
|
|
200
209
|
*/
|
|
201
210
|
export const handleGarminWebhookActivityDetails = action({
|
|
202
|
-
args: { payload: v.any() },
|
|
211
|
+
args: { payload: v.any(), autoIngest: v.optional(v.boolean()) },
|
|
203
212
|
handler: async (ctx, args): Promise<WebhookResult> => {
|
|
213
|
+
const shouldIngest = args.autoIngest !== false;
|
|
204
214
|
if (isWebhookPushMode(args.payload)) {
|
|
205
215
|
const pushResult =
|
|
206
216
|
garminActivityDetailsPushPayloadSchema.safeParse(args.payload);
|
|
@@ -212,7 +222,9 @@ export const handleGarminWebhookActivityDetails = action({
|
|
|
212
222
|
internal.garmin.private.processActivityDetailsPushPayload,
|
|
213
223
|
{ payload: args.payload },
|
|
214
224
|
);
|
|
215
|
-
return
|
|
225
|
+
return shouldIngest
|
|
226
|
+
? await ingestAndUpdate(ctx, result, api.public.ingestActivity)
|
|
227
|
+
: toWebhookResult(result);
|
|
216
228
|
}
|
|
217
229
|
} else {
|
|
218
230
|
const pingResult =
|
|
@@ -231,7 +243,7 @@ export const handleGarminWebhookActivityDetails = action({
|
|
|
231
243
|
console.warn(
|
|
232
244
|
`[garmin:webhook:activityDetails] Payload matched neither ping nor push schema`,
|
|
233
245
|
);
|
|
234
|
-
return {
|
|
246
|
+
return { errors: [], items: [] };
|
|
235
247
|
},
|
|
236
248
|
});
|
|
237
249
|
|
|
@@ -239,8 +251,9 @@ export const handleGarminWebhookActivityDetails = action({
|
|
|
239
251
|
* Handle a webhook for Garmin manually updated activities (push or ping mode).
|
|
240
252
|
*/
|
|
241
253
|
export const handleGarminWebhookManuallyUpdatedActivities = action({
|
|
242
|
-
args: { payload: v.any() },
|
|
254
|
+
args: { payload: v.any(), autoIngest: v.optional(v.boolean()) },
|
|
243
255
|
handler: async (ctx, args): Promise<WebhookResult> => {
|
|
256
|
+
const shouldIngest = args.autoIngest !== false;
|
|
244
257
|
if (isWebhookPushMode(args.payload)) {
|
|
245
258
|
const pushResult =
|
|
246
259
|
garminManuallyUpdatedActivitiesPushPayloadSchema.safeParse(
|
|
@@ -255,7 +268,9 @@ export const handleGarminWebhookManuallyUpdatedActivities = action({
|
|
|
255
268
|
.processManuallyUpdatedActivitiesPushPayload,
|
|
256
269
|
{ payload: args.payload },
|
|
257
270
|
);
|
|
258
|
-
return
|
|
271
|
+
return shouldIngest
|
|
272
|
+
? await ingestAndUpdate(ctx, result, api.public.ingestActivity)
|
|
273
|
+
: toWebhookResult(result);
|
|
259
274
|
}
|
|
260
275
|
} else {
|
|
261
276
|
const pingResult =
|
|
@@ -277,7 +292,7 @@ export const handleGarminWebhookManuallyUpdatedActivities = action({
|
|
|
277
292
|
console.warn(
|
|
278
293
|
`[garmin:webhook:manuallyUpdatedActivities] Payload matched neither ping nor push schema`,
|
|
279
294
|
);
|
|
280
|
-
return {
|
|
295
|
+
return { errors: [], items: [] };
|
|
281
296
|
},
|
|
282
297
|
});
|
|
283
298
|
|
|
@@ -285,8 +300,9 @@ export const handleGarminWebhookManuallyUpdatedActivities = action({
|
|
|
285
300
|
* Handle a webhook for Garmin Move IQ auto-detected activities (push or ping mode).
|
|
286
301
|
*/
|
|
287
302
|
export const handleGarminWebhookMoveIQ = action({
|
|
288
|
-
args: { payload: v.any() },
|
|
303
|
+
args: { payload: v.any(), autoIngest: v.optional(v.boolean()) },
|
|
289
304
|
handler: async (ctx, args): Promise<WebhookResult> => {
|
|
305
|
+
const shouldIngest = args.autoIngest !== false;
|
|
290
306
|
if (isWebhookPushMode(args.payload)) {
|
|
291
307
|
const pushResult =
|
|
292
308
|
garminMoveIQPushPayloadSchema.safeParse(args.payload);
|
|
@@ -298,7 +314,9 @@ export const handleGarminWebhookMoveIQ = action({
|
|
|
298
314
|
internal.garmin.private.processMoveIQPushPayload,
|
|
299
315
|
{ payload: args.payload },
|
|
300
316
|
);
|
|
301
|
-
return
|
|
317
|
+
return shouldIngest
|
|
318
|
+
? await ingestAndUpdate(ctx, result, api.public.ingestActivity)
|
|
319
|
+
: toWebhookResult(result);
|
|
302
320
|
}
|
|
303
321
|
} else {
|
|
304
322
|
const pingResult =
|
|
@@ -317,7 +335,7 @@ export const handleGarminWebhookMoveIQ = action({
|
|
|
317
335
|
console.warn(
|
|
318
336
|
`[garmin:webhook:moveIQ] Payload matched neither ping nor push schema`,
|
|
319
337
|
);
|
|
320
|
-
return {
|
|
338
|
+
return { errors: [], items: [] };
|
|
321
339
|
},
|
|
322
340
|
});
|
|
323
341
|
|
|
@@ -325,8 +343,9 @@ export const handleGarminWebhookMoveIQ = action({
|
|
|
325
343
|
* Handle a webhook for Garmin blood pressure summaries (push or ping mode).
|
|
326
344
|
*/
|
|
327
345
|
export const handleGarminWebhookBloodPressures = action({
|
|
328
|
-
args: { payload: v.any() },
|
|
346
|
+
args: { payload: v.any(), autoIngest: v.optional(v.boolean()) },
|
|
329
347
|
handler: async (ctx, args): Promise<WebhookResult> => {
|
|
348
|
+
const shouldIngest = args.autoIngest !== false;
|
|
330
349
|
if (isWebhookPushMode(args.payload)) {
|
|
331
350
|
const pushResult =
|
|
332
351
|
garminBloodPressurePushPayloadSchema.safeParse(args.payload);
|
|
@@ -338,7 +357,9 @@ export const handleGarminWebhookBloodPressures = action({
|
|
|
338
357
|
internal.garmin.private.processBloodPressurePushPayload,
|
|
339
358
|
{ payload: args.payload },
|
|
340
359
|
);
|
|
341
|
-
return
|
|
360
|
+
return shouldIngest
|
|
361
|
+
? await ingestAndUpdate(ctx, result, api.public.ingestBody)
|
|
362
|
+
: toWebhookResult(result);
|
|
342
363
|
}
|
|
343
364
|
} else {
|
|
344
365
|
const pingResult =
|
|
@@ -357,7 +378,7 @@ export const handleGarminWebhookBloodPressures = action({
|
|
|
357
378
|
console.warn(
|
|
358
379
|
`[garmin:webhook:bloodPressures] Payload matched neither ping nor push schema`,
|
|
359
380
|
);
|
|
360
|
-
return {
|
|
381
|
+
return { errors: [], items: [] };
|
|
361
382
|
},
|
|
362
383
|
});
|
|
363
384
|
|
|
@@ -365,8 +386,9 @@ export const handleGarminWebhookBloodPressures = action({
|
|
|
365
386
|
* Handle a webhook for Garmin body composition summaries (push or ping mode).
|
|
366
387
|
*/
|
|
367
388
|
export const handleGarminWebhookBodyCompositions = action({
|
|
368
|
-
args: { payload: v.any() },
|
|
389
|
+
args: { payload: v.any(), autoIngest: v.optional(v.boolean()) },
|
|
369
390
|
handler: async (ctx, args): Promise<WebhookResult> => {
|
|
391
|
+
const shouldIngest = args.autoIngest !== false;
|
|
370
392
|
if (isWebhookPushMode(args.payload)) {
|
|
371
393
|
const pushResult =
|
|
372
394
|
garminBodyCompositionsPushPayloadSchema.safeParse(args.payload);
|
|
@@ -378,7 +400,9 @@ export const handleGarminWebhookBodyCompositions = action({
|
|
|
378
400
|
internal.garmin.private.processBodyCompositionsPushPayload,
|
|
379
401
|
{ payload: args.payload },
|
|
380
402
|
);
|
|
381
|
-
return
|
|
403
|
+
return shouldIngest
|
|
404
|
+
? await ingestAndUpdate(ctx, result, api.public.ingestBody)
|
|
405
|
+
: toWebhookResult(result);
|
|
382
406
|
}
|
|
383
407
|
} else {
|
|
384
408
|
const pingResult =
|
|
@@ -397,7 +421,7 @@ export const handleGarminWebhookBodyCompositions = action({
|
|
|
397
421
|
console.warn(
|
|
398
422
|
`[garmin:webhook:bodyCompositions] Payload matched neither ping nor push schema`,
|
|
399
423
|
);
|
|
400
|
-
return {
|
|
424
|
+
return { errors: [], items: [] };
|
|
401
425
|
},
|
|
402
426
|
});
|
|
403
427
|
|
|
@@ -405,8 +429,9 @@ export const handleGarminWebhookBodyCompositions = action({
|
|
|
405
429
|
* Handle a webhook for Garmin daily summaries (push or ping mode).
|
|
406
430
|
*/
|
|
407
431
|
export const handleGarminWebhookDailies = action({
|
|
408
|
-
args: { payload: v.any() },
|
|
432
|
+
args: { payload: v.any(), autoIngest: v.optional(v.boolean()) },
|
|
409
433
|
handler: async (ctx, args): Promise<WebhookResult> => {
|
|
434
|
+
const shouldIngest = args.autoIngest !== false;
|
|
410
435
|
if (isWebhookPushMode(args.payload)) {
|
|
411
436
|
const pushResult =
|
|
412
437
|
garminDailiesPushPayloadSchema.safeParse(args.payload);
|
|
@@ -418,7 +443,9 @@ export const handleGarminWebhookDailies = action({
|
|
|
418
443
|
internal.garmin.private.processDailiesPushPayload,
|
|
419
444
|
{ payload: args.payload },
|
|
420
445
|
);
|
|
421
|
-
return
|
|
446
|
+
return shouldIngest
|
|
447
|
+
? await ingestAndUpdate(ctx, result, api.public.ingestDaily)
|
|
448
|
+
: toWebhookResult(result);
|
|
422
449
|
}
|
|
423
450
|
} else {
|
|
424
451
|
const pingResult =
|
|
@@ -437,7 +464,7 @@ export const handleGarminWebhookDailies = action({
|
|
|
437
464
|
console.warn(
|
|
438
465
|
`[garmin:webhook:dailies] Payload matched neither ping nor push schema`,
|
|
439
466
|
);
|
|
440
|
-
return {
|
|
467
|
+
return { errors: [], items: [] };
|
|
441
468
|
},
|
|
442
469
|
});
|
|
443
470
|
|
|
@@ -445,8 +472,9 @@ export const handleGarminWebhookDailies = action({
|
|
|
445
472
|
* Handle a webhook for Garmin epoch summaries (push or ping mode).
|
|
446
473
|
*/
|
|
447
474
|
export const handleGarminWebhookEpochs = action({
|
|
448
|
-
args: { payload: v.any() },
|
|
475
|
+
args: { payload: v.any(), autoIngest: v.optional(v.boolean()) },
|
|
449
476
|
handler: async (ctx, args): Promise<WebhookResult> => {
|
|
477
|
+
const shouldIngest = args.autoIngest !== false;
|
|
450
478
|
if (isWebhookPushMode(args.payload)) {
|
|
451
479
|
const pushResult =
|
|
452
480
|
garminEpochPushPayloadSchema.safeParse(args.payload);
|
|
@@ -458,7 +486,9 @@ export const handleGarminWebhookEpochs = action({
|
|
|
458
486
|
internal.garmin.private.processEpochPushPayload,
|
|
459
487
|
{ payload: args.payload },
|
|
460
488
|
);
|
|
461
|
-
return
|
|
489
|
+
return shouldIngest
|
|
490
|
+
? await ingestAndUpdate(ctx, result, api.public.ingestDaily)
|
|
491
|
+
: toWebhookResult(result);
|
|
462
492
|
}
|
|
463
493
|
} else {
|
|
464
494
|
const pingResult =
|
|
@@ -477,7 +507,7 @@ export const handleGarminWebhookEpochs = action({
|
|
|
477
507
|
console.warn(
|
|
478
508
|
`[garmin:webhook:epochs] Payload matched neither ping nor push schema`,
|
|
479
509
|
);
|
|
480
|
-
return {
|
|
510
|
+
return { errors: [], items: [] };
|
|
481
511
|
},
|
|
482
512
|
});
|
|
483
513
|
|
|
@@ -485,8 +515,9 @@ export const handleGarminWebhookEpochs = action({
|
|
|
485
515
|
* Handle a webhook for Garmin health snapshot summaries (push or ping mode).
|
|
486
516
|
*/
|
|
487
517
|
export const handleGarminWebhookHealthSnapshot = action({
|
|
488
|
-
args: { payload: v.any() },
|
|
518
|
+
args: { payload: v.any(), autoIngest: v.optional(v.boolean()) },
|
|
489
519
|
handler: async (ctx, args): Promise<WebhookResult> => {
|
|
520
|
+
const shouldIngest = args.autoIngest !== false;
|
|
490
521
|
if (isWebhookPushMode(args.payload)) {
|
|
491
522
|
const pushResult =
|
|
492
523
|
garminHealthSnapshotPushPayloadSchema.safeParse(args.payload);
|
|
@@ -498,7 +529,9 @@ export const handleGarminWebhookHealthSnapshot = action({
|
|
|
498
529
|
internal.garmin.private.processHealthSnapshotPushPayload,
|
|
499
530
|
{ payload: args.payload },
|
|
500
531
|
);
|
|
501
|
-
return
|
|
532
|
+
return shouldIngest
|
|
533
|
+
? await ingestAndUpdate(ctx, result, api.public.ingestDaily)
|
|
534
|
+
: toWebhookResult(result);
|
|
502
535
|
}
|
|
503
536
|
} else {
|
|
504
537
|
const pingResult =
|
|
@@ -517,18 +550,17 @@ export const handleGarminWebhookHealthSnapshot = action({
|
|
|
517
550
|
console.warn(
|
|
518
551
|
`[garmin:webhook:healthSnapshot] Payload matched neither ping nor push schema`,
|
|
519
552
|
);
|
|
520
|
-
return {
|
|
553
|
+
return { errors: [], items: [] };
|
|
521
554
|
},
|
|
522
555
|
});
|
|
523
556
|
|
|
524
557
|
/**
|
|
525
558
|
* Handle a webhook for Garmin sleep summaries (push or ping mode).
|
|
526
|
-
* Follows the structured dispatch pattern: validates with Zod, then delegates
|
|
527
|
-
* to internal actions for processing.
|
|
528
559
|
*/
|
|
529
560
|
export const handleGarminWebhookSleeps = action({
|
|
530
|
-
args: { payload: v.any() },
|
|
561
|
+
args: { payload: v.any(), autoIngest: v.optional(v.boolean()) },
|
|
531
562
|
handler: async (ctx, args): Promise<WebhookResult> => {
|
|
563
|
+
const shouldIngest = args.autoIngest !== false;
|
|
532
564
|
if (isWebhookPushMode(args.payload)) {
|
|
533
565
|
const pushResult =
|
|
534
566
|
garminSleepsPushPayloadSchema.safeParse(args.payload);
|
|
@@ -540,7 +572,9 @@ export const handleGarminWebhookSleeps = action({
|
|
|
540
572
|
internal.garmin.private.processSleepsPushPayload,
|
|
541
573
|
{ payload: args.payload },
|
|
542
574
|
);
|
|
543
|
-
return
|
|
575
|
+
return shouldIngest
|
|
576
|
+
? await ingestAndUpdate(ctx, result, api.public.ingestSleep)
|
|
577
|
+
: toWebhookResult(result);
|
|
544
578
|
}
|
|
545
579
|
} else {
|
|
546
580
|
const pingResult =
|
|
@@ -559,18 +593,17 @@ export const handleGarminWebhookSleeps = action({
|
|
|
559
593
|
console.warn(
|
|
560
594
|
`[garmin:webhook:sleeps] Payload matched neither ping nor push schema`,
|
|
561
595
|
);
|
|
562
|
-
return {
|
|
596
|
+
return { errors: [], items: [] };
|
|
563
597
|
},
|
|
564
598
|
});
|
|
565
599
|
|
|
566
600
|
/**
|
|
567
601
|
* Handle a webhook for Garmin skin temperature summaries (push or ping mode).
|
|
568
|
-
* Follows the structured dispatch pattern: validates with Zod, then delegates
|
|
569
|
-
* to internal actions for processing.
|
|
570
602
|
*/
|
|
571
603
|
export const handleGarminWebhookSkinTemp = action({
|
|
572
|
-
args: { payload: v.any() },
|
|
604
|
+
args: { payload: v.any(), autoIngest: v.optional(v.boolean()) },
|
|
573
605
|
handler: async (ctx, args): Promise<WebhookResult> => {
|
|
606
|
+
const shouldIngest = args.autoIngest !== false;
|
|
574
607
|
if (isWebhookPushMode(args.payload)) {
|
|
575
608
|
const pushResult =
|
|
576
609
|
garminSkinTemperaturePushPayloadSchema.safeParse(args.payload);
|
|
@@ -582,7 +615,9 @@ export const handleGarminWebhookSkinTemp = action({
|
|
|
582
615
|
internal.garmin.private.processSkinTemperaturePushPayload,
|
|
583
616
|
{ payload: args.payload },
|
|
584
617
|
);
|
|
585
|
-
return
|
|
618
|
+
return shouldIngest
|
|
619
|
+
? await ingestAndUpdate(ctx, result, api.public.ingestBody)
|
|
620
|
+
: toWebhookResult(result);
|
|
586
621
|
}
|
|
587
622
|
} else {
|
|
588
623
|
const pingResult =
|
|
@@ -601,18 +636,17 @@ export const handleGarminWebhookSkinTemp = action({
|
|
|
601
636
|
console.warn(
|
|
602
637
|
`[garmin:webhook:skinTemperature] Payload matched neither ping nor push schema`,
|
|
603
638
|
);
|
|
604
|
-
return {
|
|
639
|
+
return { errors: [], items: [] };
|
|
605
640
|
},
|
|
606
641
|
});
|
|
607
642
|
|
|
608
643
|
/**
|
|
609
644
|
* Handle a webhook for Garmin user metrics (push or ping mode).
|
|
610
|
-
* Follows the structured dispatch pattern: validates with Zod, then delegates
|
|
611
|
-
* to internal actions for processing.
|
|
612
645
|
*/
|
|
613
646
|
export const handleGarminWebhookUserMetrics = action({
|
|
614
|
-
args: { payload: v.any() },
|
|
647
|
+
args: { payload: v.any(), autoIngest: v.optional(v.boolean()) },
|
|
615
648
|
handler: async (ctx, args): Promise<WebhookResult> => {
|
|
649
|
+
const shouldIngest = args.autoIngest !== false;
|
|
616
650
|
if (isWebhookPushMode(args.payload)) {
|
|
617
651
|
const pushResult =
|
|
618
652
|
garminUserMetricsPushPayloadSchema.safeParse(args.payload);
|
|
@@ -624,7 +658,9 @@ export const handleGarminWebhookUserMetrics = action({
|
|
|
624
658
|
internal.garmin.private.processUserMetricsPushPayload,
|
|
625
659
|
{ payload: args.payload },
|
|
626
660
|
);
|
|
627
|
-
return
|
|
661
|
+
return shouldIngest
|
|
662
|
+
? await ingestAndUpdate(ctx, result, api.public.ingestBody)
|
|
663
|
+
: toWebhookResult(result);
|
|
628
664
|
}
|
|
629
665
|
} else {
|
|
630
666
|
const pingResult =
|
|
@@ -643,18 +679,17 @@ export const handleGarminWebhookUserMetrics = action({
|
|
|
643
679
|
console.warn(
|
|
644
680
|
`[garmin:webhook:userMetrics] Payload matched neither ping nor push schema`,
|
|
645
681
|
);
|
|
646
|
-
return {
|
|
682
|
+
return { errors: [], items: [] };
|
|
647
683
|
},
|
|
648
684
|
});
|
|
649
685
|
|
|
650
686
|
/**
|
|
651
687
|
* Handle a webhook for Garmin menstrual cycle tracking (push or ping mode).
|
|
652
|
-
* Follows the structured dispatch pattern: validates with Zod, then delegates
|
|
653
|
-
* to internal actions for processing.
|
|
654
688
|
*/
|
|
655
689
|
export const handleGarminWebhookMenstrualCycleTracking = action({
|
|
656
|
-
args: { payload: v.any() },
|
|
690
|
+
args: { payload: v.any(), autoIngest: v.optional(v.boolean()) },
|
|
657
691
|
handler: async (ctx, args): Promise<WebhookResult> => {
|
|
692
|
+
const shouldIngest = args.autoIngest !== false;
|
|
658
693
|
if (isWebhookPushMode(args.payload)) {
|
|
659
694
|
const pushResult =
|
|
660
695
|
garminMenstrualCycleTrackingPushPayloadSchema.safeParse(args.payload);
|
|
@@ -666,7 +701,9 @@ export const handleGarminWebhookMenstrualCycleTracking = action({
|
|
|
666
701
|
internal.garmin.private.processMenstrualCycleTrackingPushPayload,
|
|
667
702
|
{ payload: args.payload },
|
|
668
703
|
);
|
|
669
|
-
return
|
|
704
|
+
return shouldIngest
|
|
705
|
+
? await ingestAndUpdate(ctx, result, api.public.ingestMenstruation)
|
|
706
|
+
: toWebhookResult(result);
|
|
670
707
|
}
|
|
671
708
|
} else {
|
|
672
709
|
const pingResult =
|
|
@@ -685,18 +722,17 @@ export const handleGarminWebhookMenstrualCycleTracking = action({
|
|
|
685
722
|
console.warn(
|
|
686
723
|
`[garmin:webhook:menstrualCycleTracking] Payload matched neither ping nor push schema`,
|
|
687
724
|
);
|
|
688
|
-
return {
|
|
725
|
+
return { errors: [], items: [] };
|
|
689
726
|
},
|
|
690
727
|
});
|
|
691
728
|
|
|
692
729
|
/**
|
|
693
730
|
* Handle a webhook for Garmin HRV summaries (push or ping mode).
|
|
694
|
-
* Follows the structured dispatch pattern: validates with Zod, then delegates
|
|
695
|
-
* to internal actions for processing.
|
|
696
731
|
*/
|
|
697
732
|
export const handleGarminWebhookHRVSummary = action({
|
|
698
|
-
args: { payload: v.any() },
|
|
733
|
+
args: { payload: v.any(), autoIngest: v.optional(v.boolean()) },
|
|
699
734
|
handler: async (ctx, args): Promise<WebhookResult> => {
|
|
735
|
+
const shouldIngest = args.autoIngest !== false;
|
|
700
736
|
if (isWebhookPushMode(args.payload)) {
|
|
701
737
|
const pushResult =
|
|
702
738
|
garminHRVSummaryPushPayloadSchema.safeParse(args.payload);
|
|
@@ -708,7 +744,9 @@ export const handleGarminWebhookHRVSummary = action({
|
|
|
708
744
|
internal.garmin.private.processHRVSummaryPushPayload,
|
|
709
745
|
{ payload: args.payload },
|
|
710
746
|
);
|
|
711
|
-
return
|
|
747
|
+
return shouldIngest
|
|
748
|
+
? await ingestAndUpdate(ctx, result, api.public.ingestDaily)
|
|
749
|
+
: toWebhookResult(result);
|
|
712
750
|
}
|
|
713
751
|
} else {
|
|
714
752
|
const pingResult =
|
|
@@ -727,18 +765,17 @@ export const handleGarminWebhookHRVSummary = action({
|
|
|
727
765
|
console.warn(
|
|
728
766
|
`[garmin:webhook:hrvSummary] Payload matched neither ping nor push schema`,
|
|
729
767
|
);
|
|
730
|
-
return {
|
|
768
|
+
return { errors: [], items: [] };
|
|
731
769
|
},
|
|
732
770
|
});
|
|
733
771
|
|
|
734
772
|
/**
|
|
735
773
|
* Handle a webhook for Garmin stress detail summaries (push or ping mode).
|
|
736
|
-
* Follows the structured dispatch pattern: validates with Zod, then delegates
|
|
737
|
-
* to internal actions for processing.
|
|
738
774
|
*/
|
|
739
775
|
export const handleGarminWebhookStress = action({
|
|
740
|
-
args: { payload: v.any() },
|
|
776
|
+
args: { payload: v.any(), autoIngest: v.optional(v.boolean()) },
|
|
741
777
|
handler: async (ctx, args): Promise<WebhookResult> => {
|
|
778
|
+
const shouldIngest = args.autoIngest !== false;
|
|
742
779
|
if (isWebhookPushMode(args.payload)) {
|
|
743
780
|
const pushResult =
|
|
744
781
|
garminStressPushPayloadSchema.safeParse(args.payload);
|
|
@@ -750,7 +787,9 @@ export const handleGarminWebhookStress = action({
|
|
|
750
787
|
internal.garmin.private.processStressPushPayload,
|
|
751
788
|
{ payload: args.payload },
|
|
752
789
|
);
|
|
753
|
-
return
|
|
790
|
+
return shouldIngest
|
|
791
|
+
? await ingestAndUpdate(ctx, result, api.public.ingestDaily)
|
|
792
|
+
: toWebhookResult(result);
|
|
754
793
|
}
|
|
755
794
|
} else {
|
|
756
795
|
const pingResult =
|
|
@@ -769,18 +808,17 @@ export const handleGarminWebhookStress = action({
|
|
|
769
808
|
console.warn(
|
|
770
809
|
`[garmin:webhook:stressDetails] Payload matched neither ping nor push schema`,
|
|
771
810
|
);
|
|
772
|
-
return {
|
|
811
|
+
return { errors: [], items: [] };
|
|
773
812
|
},
|
|
774
813
|
});
|
|
775
814
|
|
|
776
815
|
/**
|
|
777
816
|
* Handle a webhook for Garmin pulse oximetry (SpO2) summaries (push or ping mode).
|
|
778
|
-
* Follows the structured dispatch pattern: validates with Zod, then delegates
|
|
779
|
-
* to internal actions for processing.
|
|
780
817
|
*/
|
|
781
818
|
export const handleGarminWebhookPulseOx = action({
|
|
782
|
-
args: { payload: v.any() },
|
|
819
|
+
args: { payload: v.any(), autoIngest: v.optional(v.boolean()) },
|
|
783
820
|
handler: async (ctx, args): Promise<WebhookResult> => {
|
|
821
|
+
const shouldIngest = args.autoIngest !== false;
|
|
784
822
|
if (isWebhookPushMode(args.payload)) {
|
|
785
823
|
const pushResult =
|
|
786
824
|
garminPulseOxPushPayloadSchema.safeParse(args.payload);
|
|
@@ -792,7 +830,9 @@ export const handleGarminWebhookPulseOx = action({
|
|
|
792
830
|
internal.garmin.private.processPulseOxPushPayload,
|
|
793
831
|
{ payload: args.payload },
|
|
794
832
|
);
|
|
795
|
-
return
|
|
833
|
+
return shouldIngest
|
|
834
|
+
? await ingestAndUpdate(ctx, result, api.public.ingestDaily)
|
|
835
|
+
: toWebhookResult(result);
|
|
796
836
|
}
|
|
797
837
|
} else {
|
|
798
838
|
const pingResult =
|
|
@@ -811,18 +851,17 @@ export const handleGarminWebhookPulseOx = action({
|
|
|
811
851
|
console.warn(
|
|
812
852
|
`[garmin:webhook:pulseOx] Payload matched neither ping nor push schema`,
|
|
813
853
|
);
|
|
814
|
-
return {
|
|
854
|
+
return { errors: [], items: [] };
|
|
815
855
|
},
|
|
816
856
|
});
|
|
817
857
|
|
|
818
858
|
/**
|
|
819
859
|
* Handle a webhook for Garmin respiration summaries (push or ping mode).
|
|
820
|
-
* Follows the structured dispatch pattern: validates with Zod, then delegates
|
|
821
|
-
* to internal actions for processing.
|
|
822
860
|
*/
|
|
823
861
|
export const handleGarminWebhookRespiration = action({
|
|
824
|
-
args: { payload: v.any() },
|
|
862
|
+
args: { payload: v.any(), autoIngest: v.optional(v.boolean()) },
|
|
825
863
|
handler: async (ctx, args): Promise<WebhookResult> => {
|
|
864
|
+
const shouldIngest = args.autoIngest !== false;
|
|
826
865
|
if (isWebhookPushMode(args.payload)) {
|
|
827
866
|
const pushResult =
|
|
828
867
|
garminRespirationPushPayloadSchema.safeParse(args.payload);
|
|
@@ -834,7 +873,9 @@ export const handleGarminWebhookRespiration = action({
|
|
|
834
873
|
internal.garmin.private.processRespirationPushPayload,
|
|
835
874
|
{ payload: args.payload },
|
|
836
875
|
);
|
|
837
|
-
return
|
|
876
|
+
return shouldIngest
|
|
877
|
+
? await ingestAndUpdate(ctx, result, api.public.ingestDaily)
|
|
878
|
+
: toWebhookResult(result);
|
|
838
879
|
}
|
|
839
880
|
} else {
|
|
840
881
|
const pingResult =
|
|
@@ -853,6 +894,6 @@ export const handleGarminWebhookRespiration = action({
|
|
|
853
894
|
console.warn(
|
|
854
895
|
`[garmin:webhook:respiration] Payload matched neither ping nor push schema`,
|
|
855
896
|
);
|
|
856
|
-
return {
|
|
897
|
+
return { errors: [], items: [] };
|
|
857
898
|
},
|
|
858
899
|
});
|