@nativesquare/soma 0.16.1 → 0.16.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.
Files changed (37) hide show
  1. package/dist/client/garmin.d.ts.map +1 -1
  2. package/dist/client/garmin.js +8 -0
  3. package/dist/client/garmin.js.map +1 -1
  4. package/dist/client/healthkit.d.ts +63 -97
  5. package/dist/client/healthkit.d.ts.map +1 -1
  6. package/dist/client/healthkit.js +62 -492
  7. package/dist/client/healthkit.js.map +1 -1
  8. package/dist/component/_generated/api.d.ts +4 -0
  9. package/dist/component/_generated/api.d.ts.map +1 -1
  10. package/dist/component/_generated/api.js.map +1 -1
  11. package/dist/component/_generated/component.d.ts +472 -0
  12. package/dist/component/_generated/component.d.ts.map +1 -1
  13. package/dist/component/healthkit/index.d.ts +1 -0
  14. package/dist/component/healthkit/index.d.ts.map +1 -1
  15. package/dist/component/healthkit/index.js +5 -0
  16. package/dist/component/healthkit/index.js.map +1 -1
  17. package/dist/component/healthkit/public.d.ts +451 -0
  18. package/dist/component/healthkit/public.d.ts.map +1 -0
  19. package/dist/component/healthkit/public.js +386 -0
  20. package/dist/component/healthkit/public.js.map +1 -0
  21. package/dist/component/healthkit/types.d.ts +13 -76
  22. package/dist/component/healthkit/types.d.ts.map +1 -1
  23. package/dist/component/healthkit/types.js +9 -6
  24. package/dist/component/healthkit/types.js.map +1 -1
  25. package/dist/component/healthkit/validators.d.ts +1933 -0
  26. package/dist/component/healthkit/validators.d.ts.map +1 -0
  27. package/dist/component/healthkit/validators.js +199 -0
  28. package/dist/component/healthkit/validators.js.map +1 -0
  29. package/dist/component/strava/types/stravaApi/zod.gen.d.ts +3 -3
  30. package/package.json +1 -1
  31. package/src/client/healthkit.ts +283 -791
  32. package/src/component/_generated/api.ts +4 -0
  33. package/src/component/_generated/component.ts +412 -0
  34. package/src/component/healthkit/index.ts +74 -46
  35. package/src/component/healthkit/public.ts +597 -0
  36. package/src/component/healthkit/types.ts +45 -114
  37. package/src/component/healthkit/validators.ts +253 -0
@@ -1,30 +1,36 @@
1
- import { transformWorkout } from "../component/healthkit/transform/activity.js";
2
- import { transformSleep } from "../component/healthkit/transform/sleep.js";
3
- import { transformBody } from "../component/healthkit/transform/body.js";
4
- import { transformDaily, transformDailyFromSummary, } from "../component/healthkit/transform/daily.js";
5
- import { transformNutrition } from "../component/healthkit/transform/nutrition.js";
6
- import { transformMenstruation } from "../component/healthkit/transform/menstruation.js";
7
- import { transformAthlete } from "../component/healthkit/transform/athlete.js";
8
- const PROVIDER = "APPLE";
9
1
  /**
10
2
  * Client class for Apple HealthKit integration with Soma.
11
3
  *
12
4
  * Unlike {@link import("./strava.js").SomaStrava | SomaStrava} and
13
5
  * {@link import("./garmin.js").SomaGarmin | SomaGarmin}, HealthKit is an
14
6
  * on-device provider — data is queried locally on iOS, not fetched from a
15
- * cloud API. This class wraps the transform + ingest pipeline so the host
16
- * app gets the same ergonomic interface as cloud providers.
7
+ * cloud API. This class is a thin wrapper over the component's public
8
+ * mutations, which own the transform + ingest pipeline.
17
9
  *
18
- * No configuration is required HealthKit has no OAuth credentials.
10
+ * Because HealthKit permissions are managed in iOS Settings, the server
11
+ * cannot independently verify whether the user has granted or revoked
12
+ * access. The `connect()` / `disconnect()` methods therefore represent
13
+ * assertions by the host app about the state it has observed on-device:
14
+ *
15
+ * - Call {@link SomaHealthKit.connect | connect} once the React Native
16
+ * HealthKit library confirms permissions were granted.
17
+ * - Call {@link SomaHealthKit.disconnect | disconnect} when the user
18
+ * turns HealthKit off in your app's settings, or when you otherwise
19
+ * detect that syncs are no longer returning data.
20
+ *
21
+ * Sync methods refuse to run unless an active connection exists.
19
22
  *
20
23
  * @example
21
24
  * ```ts
25
+ * // Once, after the RN library confirms permissions:
26
+ * await soma.healthkit.connect(ctx, { userId: "user_123" });
27
+ *
22
28
  * // Sync workouts received from the React Native client:
23
29
  * const result = await soma.healthkit.syncActivities(ctx, {
24
30
  * userId: "user_123",
25
31
  * workouts: hkWorkouts,
26
32
  * });
27
- * // result: { data: { synced: { activities: 5 } }, errors: [] }
33
+ * // result: { data: { activities: 5 }, errors: [] }
28
34
  *
29
35
  * // Or sync everything at once:
30
36
  * await soma.healthkit.syncAll(ctx, {
@@ -33,6 +39,9 @@ const PROVIDER = "APPLE";
33
39
  * sleepSessions: [nightSamples],
34
40
  * bodySamples: hkBodySamples,
35
41
  * });
42
+ *
43
+ * // When the user disables HealthKit in your app:
44
+ * await soma.healthkit.disconnect(ctx, { userId: "user_123" });
36
45
  * ```
37
46
  */
38
47
  export class SomaHealthKit {
@@ -40,41 +49,47 @@ export class SomaHealthKit {
40
49
  constructor(component) {
41
50
  this.component = component;
42
51
  }
52
+ // ─── Connect / Disconnect ──────────────────────────────────────────────
53
+ /**
54
+ * Assert that HealthKit is connected for this user.
55
+ *
56
+ * Call this once after the React Native HealthKit library confirms
57
+ * permissions were granted. Creates the APPLE connection if missing,
58
+ * or re-activates it if previously disconnected. Idempotent.
59
+ *
60
+ * @param ctx - Mutation context from the host app
61
+ * @param args.userId - The host app's user identifier
62
+ * @returns The connection document ID
63
+ */
64
+ async connect(ctx, args) {
65
+ return await ctx.runMutation(this.component.healthkit.public.connect, args);
66
+ }
67
+ /**
68
+ * Mark the HealthKit connection inactive for this user.
69
+ *
70
+ * Call this when the user disables HealthKit in your app's settings, or
71
+ * when you detect that sync calls are no longer returning data (suggesting
72
+ * the user revoked permissions in iOS Settings). Subsequent sync methods
73
+ * will throw until {@link SomaHealthKit.connect | connect} is called again.
74
+ *
75
+ * Does not delete the connection row or any previously synced data —
76
+ * re-connecting later preserves history.
77
+ *
78
+ * @param ctx - Mutation context from the host app
79
+ * @param args.userId - The host app's user identifier
80
+ */
81
+ async disconnect(ctx, args) {
82
+ return await ctx.runMutation(this.component.healthkit.public.disconnect, args);
83
+ }
43
84
  // ─── Per-Type Sync Methods ─────────────────────────────────────────────
44
85
  /**
45
86
  * Sync workout activities from HealthKit.
46
87
  *
47
88
  * Transforms each `HKWorkout` into the Soma Activity schema and ingests it
48
89
  * with automatic deduplication by `workout.uuid`.
49
- *
50
- * @param ctx - Mutation (or action) context from the host app
51
- * @param args.userId - The host app's user identifier
52
- * @param args.workouts - Array of HKWorkout objects from HealthKit
53
90
  */
54
91
  async syncActivities(ctx, args) {
55
- const connectionId = await this.resolveConnection(ctx, args.userId);
56
- const errors = [];
57
- let count = 0;
58
- for (const workout of args.workouts) {
59
- try {
60
- const data = transformWorkout(workout);
61
- await ctx.runMutation(this.component.public.ingestActivity, {
62
- connectionId,
63
- userId: args.userId,
64
- ...data,
65
- });
66
- count++;
67
- }
68
- catch (err) {
69
- errors.push({
70
- type: "activity",
71
- id: workout.uuid,
72
- message: err instanceof Error ? err.message : String(err),
73
- });
74
- }
75
- }
76
- await this.updateLastDataUpdate(ctx, connectionId);
77
- return { data: { synced: { activities: count } }, errors };
92
+ return (await ctx.runMutation(this.component.healthkit.public.syncActivities, args));
78
93
  }
79
94
  /**
80
95
  * Sync sleep sessions from HealthKit.
@@ -82,36 +97,9 @@ export class SomaHealthKit {
82
97
  * Each inner array represents one sleep session (all `HKCategorySample`
83
98
  * stage records for a single night). Each session is aggregated into a
84
99
  * single Soma Sleep document.
85
- *
86
- * @param ctx - Mutation (or action) context from the host app
87
- * @param args.userId - The host app's user identifier
88
- * @param args.sessions - Array of sessions, each an array of sleep-stage samples
89
100
  */
90
101
  async syncSleep(ctx, args) {
91
- const connectionId = await this.resolveConnection(ctx, args.userId);
92
- const errors = [];
93
- let count = 0;
94
- for (const session of args.sessions) {
95
- const sessionId = session[0]?.uuid ?? "unknown";
96
- try {
97
- const data = transformSleep(session);
98
- await ctx.runMutation(this.component.public.ingestSleep, {
99
- connectionId,
100
- userId: args.userId,
101
- ...data,
102
- });
103
- count++;
104
- }
105
- catch (err) {
106
- errors.push({
107
- type: "sleep",
108
- id: sessionId,
109
- message: err instanceof Error ? err.message : String(err),
110
- });
111
- }
112
- }
113
- await this.updateLastDataUpdate(ctx, connectionId);
114
- return { data: { synced: { sleep: count } }, errors };
102
+ return (await ctx.runMutation(this.component.healthkit.public.syncSleep, args));
115
103
  }
116
104
  /**
117
105
  * Sync body metrics from HealthKit.
@@ -119,206 +107,42 @@ export class SomaHealthKit {
119
107
  * Accepts a mixed array of body-related quantity samples (heart rate, HRV,
120
108
  * blood pressure, SpO2, weight, etc.) for a single time window and produces
121
109
  * one Soma Body document.
122
- *
123
- * @param ctx - Mutation (or action) context from the host app
124
- * @param args.userId - The host app's user identifier
125
- * @param args.samples - Array of HKQuantitySample for the desired time range
126
- * @param args.timeRange - Optional explicit time range; auto-detected from samples if omitted
127
110
  */
128
111
  async syncBody(ctx, args) {
129
- const connectionId = await this.resolveConnection(ctx, args.userId);
130
- const errors = [];
131
- let count = 0;
132
- try {
133
- const data = transformBody(args.samples, args.timeRange);
134
- await ctx.runMutation(this.component.public.ingestBody, {
135
- connectionId,
136
- userId: args.userId,
137
- ...data,
138
- });
139
- count++;
140
- }
141
- catch (err) {
142
- errors.push({
143
- type: "body",
144
- id: "transform",
145
- message: err instanceof Error ? err.message : String(err),
146
- });
147
- }
148
- await this.updateLastDataUpdate(ctx, connectionId);
149
- return { data: { synced: { body: count } }, errors };
112
+ return (await ctx.runMutation(this.component.healthkit.public.syncBody, args));
150
113
  }
151
114
  /**
152
115
  * Sync daily activity data from HealthKit quantity samples.
153
- *
154
- * Accepts samples for a single day (steps, distance, energy, exercise time,
155
- * heart rate, etc.) and produces one Soma Daily document.
156
- *
157
- * @param ctx - Mutation (or action) context from the host app
158
- * @param args.userId - The host app's user identifier
159
- * @param args.samples - Array of HKQuantitySample for the desired day
160
- * @param args.timeRange - Optional explicit time range; auto-detected from samples if omitted
161
116
  */
162
117
  async syncDaily(ctx, args) {
163
- const connectionId = await this.resolveConnection(ctx, args.userId);
164
- const errors = [];
165
- let count = 0;
166
- try {
167
- const data = transformDaily(args.samples, args.timeRange);
168
- await ctx.runMutation(this.component.public.ingestDaily, {
169
- connectionId,
170
- userId: args.userId,
171
- ...data,
172
- });
173
- count++;
174
- }
175
- catch (err) {
176
- errors.push({
177
- type: "daily",
178
- id: "transform",
179
- message: err instanceof Error ? err.message : String(err),
180
- });
181
- }
182
- await this.updateLastDataUpdate(ctx, connectionId);
183
- return { data: { synced: { daily: count } }, errors };
118
+ return (await ctx.runMutation(this.component.healthkit.public.syncDaily, args));
184
119
  }
185
120
  /**
186
121
  * Sync daily activity data from HealthKit activity ring summaries.
187
- *
188
- * Each `HKActivitySummary` represents one day's activity rings (Move,
189
- * Exercise, Stand). Each summary produces one Soma Daily document.
190
- *
191
- * @param ctx - Mutation (or action) context from the host app
192
- * @param args.userId - The host app's user identifier
193
- * @param args.summaries - Array of HKActivitySummary from HealthKit
194
122
  */
195
123
  async syncDailyFromSummary(ctx, args) {
196
- const connectionId = await this.resolveConnection(ctx, args.userId);
197
- const errors = [];
198
- let count = 0;
199
- for (const summary of args.summaries) {
200
- const { year, month, day } = summary.dateComponents;
201
- const summaryId = `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
202
- try {
203
- const data = transformDailyFromSummary(summary);
204
- await ctx.runMutation(this.component.public.ingestDaily, {
205
- connectionId,
206
- userId: args.userId,
207
- ...data,
208
- });
209
- count++;
210
- }
211
- catch (err) {
212
- errors.push({
213
- type: "daily",
214
- id: summaryId,
215
- message: err instanceof Error ? err.message : String(err),
216
- });
217
- }
218
- }
219
- await this.updateLastDataUpdate(ctx, connectionId);
220
- return { data: { synced: { daily: count } }, errors };
124
+ return (await ctx.runMutation(this.component.healthkit.public.syncDailyFromSummary, args));
221
125
  }
222
126
  /**
223
127
  * Sync nutrition data from HealthKit.
224
- *
225
- * Accepts dietary quantity samples for a single time window and produces
226
- * one Soma Nutrition document with macros and micros.
227
- *
228
- * @param ctx - Mutation (or action) context from the host app
229
- * @param args.userId - The host app's user identifier
230
- * @param args.samples - Array of HKQuantitySample with dietary type identifiers
231
- * @param args.timeRange - Optional explicit time range; auto-detected from samples if omitted
232
128
  */
233
129
  async syncNutrition(ctx, args) {
234
- const connectionId = await this.resolveConnection(ctx, args.userId);
235
- const errors = [];
236
- let count = 0;
237
- try {
238
- const data = transformNutrition(args.samples, args.timeRange);
239
- await ctx.runMutation(this.component.public.ingestNutrition, {
240
- connectionId,
241
- userId: args.userId,
242
- ...data,
243
- });
244
- count++;
245
- }
246
- catch (err) {
247
- errors.push({
248
- type: "nutrition",
249
- id: "transform",
250
- message: err instanceof Error ? err.message : String(err),
251
- });
252
- }
253
- await this.updateLastDataUpdate(ctx, connectionId);
254
- return { data: { synced: { nutrition: count } }, errors };
130
+ return (await ctx.runMutation(this.component.healthkit.public.syncNutrition, args));
255
131
  }
256
132
  /**
257
133
  * Sync menstruation data from HealthKit.
258
- *
259
- * Accepts menstrual flow category samples for a single time window and
260
- * produces one Soma Menstruation document.
261
- *
262
- * @param ctx - Mutation (or action) context from the host app
263
- * @param args.userId - The host app's user identifier
264
- * @param args.samples - Array of HKCategorySample with menstrual flow values
265
- * @param args.timeRange - Optional explicit time range; auto-detected from samples if omitted
266
134
  */
267
135
  async syncMenstruation(ctx, args) {
268
- const connectionId = await this.resolveConnection(ctx, args.userId);
269
- const errors = [];
270
- let count = 0;
271
- try {
272
- const data = transformMenstruation(args.samples, args.timeRange);
273
- await ctx.runMutation(this.component.public.ingestMenstruation, {
274
- connectionId,
275
- userId: args.userId,
276
- ...data,
277
- });
278
- count++;
279
- }
280
- catch (err) {
281
- errors.push({
282
- type: "menstruation",
283
- id: "transform",
284
- message: err instanceof Error ? err.message : String(err),
285
- });
286
- }
287
- await this.updateLastDataUpdate(ctx, connectionId);
288
- return { data: { synced: { menstruation: count } }, errors };
136
+ return (await ctx.runMutation(this.component.healthkit.public.syncMenstruation, args));
289
137
  }
290
138
  /**
291
139
  * Sync athlete profile from HealthKit.
292
140
  *
293
141
  * HealthKit exposes limited profile data (biological sex, date of birth).
294
142
  * Produces one Soma Athlete document per connection.
295
- *
296
- * @param ctx - Mutation (or action) context from the host app
297
- * @param args.userId - The host app's user identifier
298
- * @param args.characteristics - The HKCharacteristics from HealthKit
299
143
  */
300
144
  async syncAthlete(ctx, args) {
301
- const connectionId = await this.resolveConnection(ctx, args.userId);
302
- const errors = [];
303
- let count = 0;
304
- try {
305
- const data = transformAthlete(args.characteristics);
306
- await ctx.runMutation(this.component.public.ingestAthlete, {
307
- connectionId,
308
- userId: args.userId,
309
- ...data,
310
- });
311
- count++;
312
- }
313
- catch (err) {
314
- errors.push({
315
- type: "athlete",
316
- id: "transform",
317
- message: err instanceof Error ? err.message : String(err),
318
- });
319
- }
320
- await this.updateLastDataUpdate(ctx, connectionId);
321
- return { data: { synced: { athletes: count } }, errors };
145
+ return (await ctx.runMutation(this.component.healthkit.public.syncAthlete, args));
322
146
  }
323
147
  // ─── Orchestrator ────────────────────────────────────────────────────────
324
148
  /**
@@ -327,17 +151,6 @@ export class SomaHealthKit {
327
151
  * Only data types with values provided are synced. Errors from individual
328
152
  * data types are collected — one failing type does not block others.
329
153
  *
330
- * @param ctx - Mutation (or action) context from the host app
331
- * @param args.userId - The host app's user identifier
332
- * @param args.workouts - Optional array of HKWorkout objects
333
- * @param args.sleepSessions - Optional array of sleep sessions (each an array of stage samples)
334
- * @param args.bodySamples - Optional body-related quantity samples
335
- * @param args.dailySamples - Optional daily activity quantity samples
336
- * @param args.dailySummaries - Optional daily activity ring summaries
337
- * @param args.nutritionSamples - Optional dietary quantity samples
338
- * @param args.menstruationSamples - Optional menstrual flow category samples
339
- * @param args.characteristics - Optional user characteristics
340
- *
341
154
  * @example
342
155
  * ```ts
343
156
  * const result = await soma.healthkit.syncAll(ctx, {
@@ -351,250 +164,7 @@ export class SomaHealthKit {
351
164
  * ```
352
165
  */
353
166
  async syncAll(ctx, args) {
354
- // Resolve connection once, up front
355
- const connectionId = await this.resolveConnection(ctx, args.userId);
356
- const allErrors = [];
357
- const synced = {};
358
- const run = async (fn, fallbackType) => {
359
- try {
360
- const result = await fn();
361
- Object.assign(synced, result.data.synced);
362
- allErrors.push(...result.errors);
363
- }
364
- catch (err) {
365
- allErrors.push({
366
- type: fallbackType,
367
- id: "sync",
368
- message: err instanceof Error ? err.message : String(err),
369
- });
370
- }
371
- };
372
- if (args.workouts) {
373
- await run(() => this.syncActivitiesInternal(ctx, connectionId, args.userId, args.workouts), "activity");
374
- }
375
- if (args.sleepSessions) {
376
- await run(() => this.syncSleepInternal(ctx, connectionId, args.userId, args.sleepSessions), "sleep");
377
- }
378
- if (args.bodySamples) {
379
- await run(() => this.syncBodyInternal(ctx, connectionId, args.userId, args.bodySamples, args.bodyTimeRange), "body");
380
- }
381
- if (args.dailySamples) {
382
- await run(() => this.syncDailyInternal(ctx, connectionId, args.userId, args.dailySamples, args.dailyTimeRange), "daily");
383
- }
384
- if (args.dailySummaries) {
385
- await run(() => this.syncDailyFromSummaryInternal(ctx, connectionId, args.userId, args.dailySummaries), "daily");
386
- }
387
- if (args.nutritionSamples) {
388
- await run(() => this.syncNutritionInternal(ctx, connectionId, args.userId, args.nutritionSamples, args.nutritionTimeRange), "nutrition");
389
- }
390
- if (args.menstruationSamples) {
391
- await run(() => this.syncMenstruationInternal(ctx, connectionId, args.userId, args.menstruationSamples, args.menstruationTimeRange), "menstruation");
392
- }
393
- if (args.characteristics) {
394
- await run(() => this.syncAthleteInternal(ctx, connectionId, args.userId, args.characteristics), "athlete");
395
- }
396
- // Update lastDataUpdate once at the end
397
- await this.updateLastDataUpdate(ctx, connectionId);
398
- return { data: { synced }, errors: allErrors };
399
- }
400
- // ─── Private Helpers ───────────��─────────────────────────────────────────
401
- /**
402
- * Resolve or create the APPLE connection for a user.
403
- *
404
- * HealthKit permissions are managed on-device by the React Native library,
405
- * so Soma auto-ensures the connection record exists. If the connection was
406
- * previously disconnected it is re-activated automatically.
407
- */
408
- async resolveConnection(ctx, userId) {
409
- // `connect` is idempotent: creates if missing, re-activates if inactive.
410
- return (await ctx.runMutation(this.component.public.connect, {
411
- userId,
412
- provider: PROVIDER,
413
- }));
414
- }
415
- async updateLastDataUpdate(ctx, connectionId) {
416
- await ctx.runMutation(this.component.public.updateConnection, {
417
- connectionId,
418
- lastDataUpdate: new Date().toISOString(),
419
- });
420
- }
421
- // ─── Internal sync methods (skip connection resolution + lastDataUpdate) ─
422
- async syncActivitiesInternal(ctx, connectionId, userId, workouts) {
423
- const errors = [];
424
- let count = 0;
425
- for (const workout of workouts) {
426
- try {
427
- const data = transformWorkout(workout);
428
- await ctx.runMutation(this.component.public.ingestActivity, {
429
- connectionId,
430
- userId,
431
- ...data,
432
- });
433
- count++;
434
- }
435
- catch (err) {
436
- errors.push({
437
- type: "activity",
438
- id: workout.uuid,
439
- message: err instanceof Error ? err.message : String(err),
440
- });
441
- }
442
- }
443
- return { data: { synced: { activities: count } }, errors };
444
- }
445
- async syncSleepInternal(ctx, connectionId, userId, sessions) {
446
- const errors = [];
447
- let count = 0;
448
- for (const session of sessions) {
449
- const sessionId = session[0]?.uuid ?? "unknown";
450
- try {
451
- const data = transformSleep(session);
452
- await ctx.runMutation(this.component.public.ingestSleep, {
453
- connectionId,
454
- userId,
455
- ...data,
456
- });
457
- count++;
458
- }
459
- catch (err) {
460
- errors.push({
461
- type: "sleep",
462
- id: sessionId,
463
- message: err instanceof Error ? err.message : String(err),
464
- });
465
- }
466
- }
467
- return { data: { synced: { sleep: count } }, errors };
468
- }
469
- async syncBodyInternal(ctx, connectionId, userId, samples, timeRange) {
470
- const errors = [];
471
- let count = 0;
472
- try {
473
- const data = transformBody(samples, timeRange);
474
- await ctx.runMutation(this.component.public.ingestBody, {
475
- connectionId,
476
- userId,
477
- ...data,
478
- });
479
- count++;
480
- }
481
- catch (err) {
482
- errors.push({
483
- type: "body",
484
- id: "transform",
485
- message: err instanceof Error ? err.message : String(err),
486
- });
487
- }
488
- return { data: { synced: { body: count } }, errors };
489
- }
490
- async syncDailyInternal(ctx, connectionId, userId, samples, timeRange) {
491
- const errors = [];
492
- let count = 0;
493
- try {
494
- const data = transformDaily(samples, timeRange);
495
- await ctx.runMutation(this.component.public.ingestDaily, {
496
- connectionId,
497
- userId,
498
- ...data,
499
- });
500
- count++;
501
- }
502
- catch (err) {
503
- errors.push({
504
- type: "daily",
505
- id: "transform",
506
- message: err instanceof Error ? err.message : String(err),
507
- });
508
- }
509
- return { data: { synced: { daily: count } }, errors };
510
- }
511
- async syncDailyFromSummaryInternal(ctx, connectionId, userId, summaries) {
512
- const errors = [];
513
- let count = 0;
514
- for (const summary of summaries) {
515
- const { year, month, day } = summary.dateComponents;
516
- const summaryId = `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
517
- try {
518
- const data = transformDailyFromSummary(summary);
519
- await ctx.runMutation(this.component.public.ingestDaily, {
520
- connectionId,
521
- userId,
522
- ...data,
523
- });
524
- count++;
525
- }
526
- catch (err) {
527
- errors.push({
528
- type: "daily",
529
- id: summaryId,
530
- message: err instanceof Error ? err.message : String(err),
531
- });
532
- }
533
- }
534
- return { data: { synced: { daily: count } }, errors };
535
- }
536
- async syncNutritionInternal(ctx, connectionId, userId, samples, timeRange) {
537
- const errors = [];
538
- let count = 0;
539
- try {
540
- const data = transformNutrition(samples, timeRange);
541
- await ctx.runMutation(this.component.public.ingestNutrition, {
542
- connectionId,
543
- userId,
544
- ...data,
545
- });
546
- count++;
547
- }
548
- catch (err) {
549
- errors.push({
550
- type: "nutrition",
551
- id: "transform",
552
- message: err instanceof Error ? err.message : String(err),
553
- });
554
- }
555
- return { data: { synced: { nutrition: count } }, errors };
556
- }
557
- async syncMenstruationInternal(ctx, connectionId, userId, samples, timeRange) {
558
- const errors = [];
559
- let count = 0;
560
- try {
561
- const data = transformMenstruation(samples, timeRange);
562
- await ctx.runMutation(this.component.public.ingestMenstruation, {
563
- connectionId,
564
- userId,
565
- ...data,
566
- });
567
- count++;
568
- }
569
- catch (err) {
570
- errors.push({
571
- type: "menstruation",
572
- id: "transform",
573
- message: err instanceof Error ? err.message : String(err),
574
- });
575
- }
576
- return { data: { synced: { menstruation: count } }, errors };
577
- }
578
- async syncAthleteInternal(ctx, connectionId, userId, characteristics) {
579
- const errors = [];
580
- let count = 0;
581
- try {
582
- const data = transformAthlete(characteristics);
583
- await ctx.runMutation(this.component.public.ingestAthlete, {
584
- connectionId,
585
- userId,
586
- ...data,
587
- });
588
- count++;
589
- }
590
- catch (err) {
591
- errors.push({
592
- type: "athlete",
593
- id: "transform",
594
- message: err instanceof Error ? err.message : String(err),
595
- });
596
- }
597
- return { data: { synced: { athletes: count } }, errors };
167
+ return (await ctx.runMutation(this.component.healthkit.public.syncAll, args));
598
168
  }
599
169
  }
600
170
  //# sourceMappingURL=healthkit.js.map