@tryterra/react-native 2.1.0 → 2.2.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.
Files changed (59) hide show
  1. package/README.md +31 -0
  2. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/Conversions.kt +103 -14
  3. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/ReadConversions.kt +250 -0
  4. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TerraBridgeOpsImpl.kt +47 -5
  5. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TerraBridgeStreamsImpl.kt +1 -3
  6. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/generated/BridgeOps.kt +89 -0
  7. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/generated/BridgeTypes.kt +672 -0
  8. package/docs/availability.md +154 -4
  9. package/frameworks/TerraSDK.xcframework/Info.plist +5 -5
  10. package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Info.plist +0 -0
  11. package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.abi.json +1385 -226
  12. package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.package.swiftinterface +37 -1
  13. package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +37 -1
  14. package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  15. package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.swiftinterface +37 -1
  16. package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/TerraSDK +0 -0
  17. package/frameworks/TerraSDK.xcframework/ios-arm64/dSYMs/TerraSDK.framework.dSYM/Contents/Info.plist +2 -2
  18. package/frameworks/TerraSDK.xcframework/ios-arm64/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/DWARF/TerraSDK +0 -0
  19. package/frameworks/TerraSDK.xcframework/ios-arm64/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/Relocations/aarch64/TerraSDK.yml +6589 -6468
  20. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Info.plist +0 -0
  21. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +1385 -226
  22. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.package.swiftinterface +37 -1
  23. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +37 -1
  24. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  25. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +37 -1
  26. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +1385 -226
  27. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.package.swiftinterface +37 -1
  28. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +37 -1
  29. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  30. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +37 -1
  31. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/TerraSDK +0 -0
  32. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/_CodeSignature/CodeResources +21 -21
  33. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Info.plist +2 -2
  34. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/DWARF/TerraSDK +0 -0
  35. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/Relocations/aarch64/TerraSDK.yml +6590 -6469
  36. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/Relocations/x86_64/TerraSDK.yml +6508 -6387
  37. package/ios/TerraBridgeCore/Conversions+Reads.swift +259 -0
  38. package/ios/TerraBridgeCore/Conversions.swift +129 -2
  39. package/ios/TerraBridgeCore/Generated/BridgeOps.swift +172 -0
  40. package/ios/TerraBridgeCore/Generated/BridgeTypes.swift +734 -1
  41. package/ios/TerraBridgeCore/TerraBridgeOpsImpl.swift +64 -0
  42. package/lib/Terra.d.ts +74 -5
  43. package/lib/Terra.js +69 -13
  44. package/lib/generated/client.d.ts +37 -1
  45. package/lib/generated/client.js +60 -0
  46. package/lib/generated/codecs.d.ts +85 -1
  47. package/lib/generated/codecs.js +710 -2
  48. package/lib/generated/ops.d.ts +1 -1
  49. package/lib/generated/ops.js +24 -0
  50. package/lib/generated/types.d.ts +314 -1
  51. package/lib/generated/types.js +133 -1
  52. package/lib/index.d.ts +1 -1
  53. package/package.json +3 -3
  54. package/src/Terra.ts +146 -16
  55. package/src/generated/client.ts +74 -2
  56. package/src/generated/codecs.ts +705 -3
  57. package/src/generated/ops.ts +36 -0
  58. package/src/generated/types.ts +556 -1
  59. package/src/index.ts +2 -0
package/src/Terra.ts CHANGED
@@ -13,19 +13,29 @@ import { GeneratedClient, type Invoker } from "./generated/client";
13
13
  import { availableOn, type OpId, type StreamId } from "./generated/ops";
14
14
  import { decodeStreamDelivery, type StreamElements } from "./generated/streams";
15
15
  import type {
16
+ AthleteProfile,
16
17
  BodyEntry,
18
+ BodyReading,
17
19
  BootstrapState,
18
20
  Connection,
19
21
  DataCategory,
22
+ DayTotal,
20
23
  DiagnosticsState,
24
+ DietaryQuantity,
21
25
  EndUser,
22
26
  HealthAccess,
23
27
  HealthChange,
24
28
  HeartRateEntry,
25
29
  HeartRateSample,
30
+ HydrationSample,
31
+ IntradaySample,
32
+ MenstrualEvent,
33
+ MenstruationFlowDay,
26
34
  NutritionEntry,
27
35
  PermissionStatus,
28
36
  PlannedWorkout,
37
+ RestingHeartRateSample,
38
+ StoreAvailability,
29
39
  WorkoutSchedulingStatus,
30
40
  SleepEntry,
31
41
  SleepSegment,
@@ -37,6 +47,8 @@ import type {
37
47
  TimeRange,
38
48
  Workout,
39
49
  WorkoutEntry,
50
+ WorkoutIntensitySample,
51
+ WorkoutRoute,
40
52
  WorkoutScope,
41
53
  } from "./generated/types";
42
54
 
@@ -113,6 +125,17 @@ interface Changes {
113
125
 
114
126
  export interface BodyMetrics extends Changes {
115
127
  heartRate(range: TimeRange): Promise<readonly HeartRateSample[]>;
128
+ /**
129
+ * The store's own resting heart rate, one per day it computed, never derived from `heartRate`.
130
+ * Health Connect grants it under `daily`, so on Android ask for `daily` as well as `body`.
131
+ */
132
+ restingHeartRate(range: TimeRange): Promise<readonly RestingHeartRateSample[]>;
133
+ /**
134
+ * Weight, height, BMI (iOS), body fat, lean mass, heart rate variability, VO2 max, blood pressure,
135
+ * blood glucose, oxygen saturation, temperature and respiratory rate. Each reading's `kind` says
136
+ * what it measured and fixes the unit; blood glucose keeps the unit its writer chose.
137
+ */
138
+ readings(range: TimeRange): Promise<readonly BodyReading[]>;
116
139
  logHeartRate(entries: readonly HeartRateEntry[]): Promise<readonly string[]>;
117
140
  log(entries: readonly BodyEntry[]): Promise<readonly string[]>;
118
141
  delete(id: string): Promise<void>;
@@ -120,6 +143,14 @@ export interface BodyMetrics extends Changes {
120
143
 
121
144
  export interface DailyMetrics extends Changes {
122
145
  steps(range: TimeRange): Promise<readonly StepCount[]>;
146
+ /**
147
+ * The store's own total for each whole local day the range touches: distance, active and basal
148
+ * energy, flights climbed and, on iOS, exercise minutes. A day with nothing recorded has no total
149
+ * rather than a zero. Steps are `steps`, which keeps writers apart.
150
+ */
151
+ totals(range: TimeRange): Promise<readonly DayTotal[]>;
152
+ /** The active energy, basal energy (iOS), distance and floors records the totals are made of. */
153
+ intraday(range: TimeRange): Promise<readonly IntradaySample[]>;
123
154
  log(entries: readonly StepEntry[]): Promise<readonly string[]>;
124
155
  delete(id: string): Promise<void>;
125
156
  }
@@ -132,16 +163,51 @@ export interface SleepMetrics extends Changes {
132
163
 
133
164
  export interface ActivityMetrics extends Changes {
134
165
  workouts(range: TimeRange): Promise<readonly Workout[]>;
166
+ /** The GPS tracks of the workouts in the range, each naming its workout by `workoutId`. */
167
+ routes(range: TimeRange): Promise<readonly WorkoutRoute[]>;
168
+ /**
169
+ * Speed, power and cycling cadence readings in the range. iOS names each one's workout; Health
170
+ * Connect stores them free-standing, so on Android `workoutId` is absent and `start` equals `end`.
171
+ */
172
+ intensity(range: TimeRange): Promise<readonly WorkoutIntensitySample[]>;
135
173
  log(entries: readonly WorkoutEntry[]): Promise<readonly string[]>;
136
174
  delete(id: string): Promise<void>;
137
175
  }
138
176
 
139
- /** Write-only on both platforms. */
140
177
  export interface NutritionMetrics {
178
+ /**
179
+ * One quantity per nutrient a meal recorded in the range, not grouped into meals. Quantities of
180
+ * one meal share `foodCorrelationId`. Water is not among them. Each `value` is in its nutrient's
181
+ * fixed unit: kcal for `energyConsumed`, grams for the macronutrients, milligrams for the rest.
182
+ */
183
+ quantities(range: TimeRange): Promise<readonly DietaryQuantity[]>;
184
+ /** Water drunk in the range, one sample per drink, in millilitres. */
185
+ hydration(range: TimeRange): Promise<readonly HydrationSample[]>;
141
186
  log(entries: readonly NutritionEntry[]): Promise<readonly string[]>;
142
187
  delete(id: string): Promise<void>;
143
188
  }
144
189
 
190
+ /** Read-only on both platforms: neither SDK has a write route for menstruation. */
191
+ export interface MenstruationMetrics {
192
+ /**
193
+ * Logged flow days overlapping the range, one per day, each bounded by that day's local
194
+ * midnights. A day logged with no flow comes back as `none` rather than being left out.
195
+ */
196
+ flow(range: TimeRange): Promise<readonly MenstruationFlowDay[]>;
197
+ /** Ovulation tests, cervical mucus and intermenstrual bleeding logged in the range. */
198
+ events(range: TimeRange): Promise<readonly MenstrualEvent[]>;
199
+ }
200
+
201
+ /** iOS only, and read-only. */
202
+ export interface AthleteMetrics {
203
+ /**
204
+ * HealthKit's characteristics as one snapshot. No range, because characteristics have no
205
+ * history. `undefined` where the store has no notion of them at all; a store that has them and
206
+ * holds no values answers a profile whose `biologicalSex` is `notSet`.
207
+ */
208
+ profile(): Promise<AthleteProfile | undefined>;
209
+ }
210
+
145
211
  /** iOS only. */
146
212
  export interface PlannedWorkouts {
147
213
  planned(): Promise<readonly PlannedWorkout[]>;
@@ -166,13 +232,21 @@ export class Terra {
166
232
  readonly sleep: SleepMetrics;
167
233
  readonly activity: ActivityMetrics;
168
234
  readonly nutrition: NutritionMetrics;
235
+ readonly menstruation: MenstruationMetrics;
236
+ /** `undefined` on Android, which has no characteristics to read. */
237
+ readonly athlete: AthleteMetrics | undefined;
169
238
  /** `undefined` on Android, which has no planned workouts. */
170
239
  readonly plannedWorkouts: PlannedWorkouts | undefined;
171
240
 
172
241
  private readonly ops: GeneratedClient;
173
242
  private readonly streams: StreamHub;
174
- /** Settles once the token provider, when there is one, can be asked. */
175
- private readonly tokensReady: Promise<void>;
243
+ private readonly tokenProvider: TokenProvider | undefined;
244
+ private readonly tokenTimeoutMs: number;
245
+ /**
246
+ * The provider's registration, in flight or done. `undefined` before it starts and after it failed
247
+ * or its stream ended, so the next connect that needs it registers again.
248
+ */
249
+ private tokenSetup: Promise<void> | undefined;
176
250
 
177
251
  constructor(native: NativeBridge, platform: Platform, options: TerraOptions = {}) {
178
252
  this.platform = platform;
@@ -180,11 +254,11 @@ export class Terra {
180
254
  const ops = new GeneratedClient(invoker);
181
255
  this.ops = ops;
182
256
  this.streams = new StreamHub(native);
183
- this.tokensReady = options.tokenProvider === undefined
184
- ? Promise.resolve()
185
- : this.answerTokens(options.tokenProvider, options.tokenTimeoutMs ?? 30_000);
186
- // The runtime can report an unhandled rejection before a later call awaits this promise.
187
- this.tokensReady.catch(() => undefined);
257
+ this.tokenProvider = options.tokenProvider;
258
+ this.tokenTimeoutMs = options.tokenTimeoutMs ?? 30_000;
259
+ // Registered now, so the provider is in place before anything asks. A failure here is reported
260
+ // to the connect that needs the provider, which also tries again.
261
+ void this.tokensReady();
188
262
 
189
263
  const on = (op: OpId) => availableOn[op].includes(platform);
190
264
  const stream = <S extends StreamId>(id: S) =>
@@ -232,6 +306,8 @@ export class Terra {
232
306
 
233
307
  this.body = {
234
308
  heartRate: async (range) => (await ops.bodyHeartRate({ range })).samples,
309
+ restingHeartRate: async (range) => (await ops.bodyRestingHeartRate({ range })).samples,
310
+ readings: async (range) => (await ops.bodyReadings({ range })).samples,
235
311
  logHeartRate: async (entries) => (await ops.bodyLogHeartRate({ entries })).ids,
236
312
  log: async (entries) => (await ops.bodyLog({ entries })).ids,
237
313
  delete: async (id) => {
@@ -242,6 +318,8 @@ export class Terra {
242
318
 
243
319
  this.daily = {
244
320
  steps: async (range) => (await ops.dailySteps({ range })).samples,
321
+ totals: async (range) => (await ops.dailyTotals({ range })).totals,
322
+ intraday: async (range) => (await ops.dailyIntraday({ range })).samples,
245
323
  log: async (entries) => (await ops.dailyLog({ entries })).ids,
246
324
  delete: async (id) => {
247
325
  await ops.dailyDelete({ id });
@@ -260,6 +338,8 @@ export class Terra {
260
338
 
261
339
  this.activity = {
262
340
  workouts: async (range) => (await ops.activityWorkouts({ range })).samples,
341
+ routes: async (range) => (await ops.activityRoutes({ range })).routes,
342
+ intensity: async (range) => (await ops.activityIntensity({ range })).samples,
263
343
  log: async (entries) => (await ops.activityLog({ entries })).ids,
264
344
  delete: async (id) => {
265
345
  await ops.activityDelete({ id });
@@ -268,12 +348,23 @@ export class Terra {
268
348
  };
269
349
 
270
350
  this.nutrition = {
351
+ quantities: async (range) => (await ops.nutritionQuantities({ range })).samples,
352
+ hydration: async (range) => (await ops.nutritionHydration({ range })).samples,
271
353
  log: async (entries) => (await ops.nutritionLog({ entries })).ids,
272
354
  delete: async (id) => {
273
355
  await ops.nutritionDelete({ id });
274
356
  },
275
357
  };
276
358
 
359
+ this.menstruation = {
360
+ flow: async (range) => (await ops.menstruationFlow({ range })).samples,
361
+ events: async (range) => (await ops.menstruationEvents({ range })).samples,
362
+ };
363
+
364
+ this.athlete = on("athlete.profile")
365
+ ? { profile: async () => (await ops.athleteProfile({})).profile }
366
+ : undefined;
367
+
277
368
  this.plannedWorkouts = on("plannedWorkouts.planned")
278
369
  ? {
279
370
  planned: async () => (await ops.plannedWorkoutsPlanned({})).workouts,
@@ -305,7 +396,8 @@ export class Terra {
305
396
  * the `tokenProvider` is asked, and only when a new link is actually needed.
306
397
  */
307
398
  async connect(source: Source, endUser: EndUser, options: { readonly token?: string } = {}): Promise<Connection> {
308
- await this.tokensReady;
399
+ // A connect that carries its token never asks the provider, so it does not wait on it either.
400
+ if (options.token === undefined) await this.tokensReady();
309
401
  return this.ops.clientConnect({ source, endUser, token: options.token });
310
402
  }
311
403
 
@@ -317,9 +409,35 @@ export class Terra {
317
409
  await this.ops.clientDisconnect({ source });
318
410
  }
319
411
 
320
- /** Answers the native broker's token requests with `provider`, for the lifetime of this runtime. */
321
- private async answerTokens(provider: TokenProvider, timeoutMs: number): Promise<void> {
322
- await this.streams.open("bridge.tokenRequest", ({ requestId }) => {
412
+ /**
413
+ * Whether the device can serve `source` at all, and if not, why. Needs no connection and prompts
414
+ * for nothing, so an app can decide whether to offer the link before `connect`. iOS answers
415
+ * `available` or `unsupportedDevice`; Android names the reason, such as `providerAppMissing` when
416
+ * Health Connect needs installing.
417
+ */
418
+ async availability(source?: Source): Promise<StoreAvailability> {
419
+ return (await this.ops.clientAvailability({ source })).availability;
420
+ }
421
+
422
+ /** Settles once the token provider, when there is one, can be asked. */
423
+ private tokensReady(): Promise<void> {
424
+ const provider = this.tokenProvider;
425
+ if (provider === undefined) return Promise.resolve();
426
+ if (this.tokenSetup !== undefined) return this.tokenSetup;
427
+ const forget = () => {
428
+ if (this.tokenSetup === setup) this.tokenSetup = undefined;
429
+ };
430
+ const setup = this.answerTokens(provider, this.tokenTimeoutMs, forget);
431
+ this.tokenSetup = setup;
432
+ // Handles the failure, so it is never an unhandled rejection, and forgets the setup so the next
433
+ // connect tries again rather than rethrowing a failure that may have passed.
434
+ setup.catch(forget);
435
+ return setup;
436
+ }
437
+
438
+ /** Answers the native broker's token requests with `provider` until the stream ends. */
439
+ private async answerTokens(provider: TokenProvider, timeoutMs: number, onEnd: () => void): Promise<void> {
440
+ const id = await this.streams.open("bridge.tokenRequest", ({ requestId }) => {
323
441
  void (async () => {
324
442
  let token: string;
325
443
  try {
@@ -331,8 +449,14 @@ export class Terra {
331
449
  }
332
450
  await this.ops.tokenResolve({ requestId, token }).catch(() => undefined);
333
451
  })();
334
- });
335
- await this.ops.tokenSetProviderPresent({ present: true, timeoutMs });
452
+ }, undefined, onEnd);
453
+ try {
454
+ await this.ops.tokenSetProviderPresent({ present: true, timeoutMs });
455
+ } catch (failed) {
456
+ // A provider the broker was never told about answers nothing, so its stream is let go of.
457
+ this.streams.close(id);
458
+ throw failed;
459
+ }
336
460
  }
337
461
  }
338
462
 
@@ -344,11 +468,15 @@ class StreamHub {
344
468
 
345
469
  constructor(private readonly native: NativeBridge) {}
346
470
 
347
- /** Subscribes and answers once the native stream is open. */
471
+ /**
472
+ * Subscribes and answers once the native stream is open. `onEnd` runs once the stream ends from the
473
+ * native side, by error or by completing; not when it is closed from here.
474
+ */
348
475
  async open<S extends StreamId>(
349
476
  stream: S,
350
477
  listener: StreamListener<StreamElements[S]>,
351
478
  onError?: (error: TerraError) => void,
479
+ onEnd?: () => void,
352
480
  ): Promise<string> {
353
481
  this.listen();
354
482
  this.pending += 1;
@@ -368,9 +496,11 @@ class StreamHub {
368
496
  case "error":
369
497
  this.ended(id);
370
498
  onError?.(new TerraError(delivery.error));
499
+ onEnd?.();
371
500
  return;
372
501
  case "complete":
373
502
  this.ended(id);
503
+ onEnd?.();
374
504
  return;
375
505
  }
376
506
  };
@@ -427,7 +557,7 @@ class StreamHub {
427
557
  };
428
558
  }
429
559
 
430
- private close(id: string): void {
560
+ close(id: string): void {
431
561
  this.handlers.delete(id);
432
562
  this.early.delete(id);
433
563
  this.native.unsubscribe(id);
@@ -1,8 +1,8 @@
1
1
  // Generated from surface/terra-surface.json by surface/gen. Do not edit by hand.
2
2
  // Regenerate with: node surface/gen/src/cli.ts generate
3
3
 
4
- import type { BodyEntryList, BoolResult, BootstrapState, CategoryRequest, ConnectRequest, Connection, ConnectionList, DiagnosticsState, Empty, HealthAccess, HeartRateEntryList, HeartRateList, LastDeliveryResult, MarkCompleteRequest, NutritionEntryList, PermissionGrant, PermissionRequest, PlannedWorkoutList, RangeRequest, RecordIdList, RecordIdRequest, SleepEntryList, SleepSegmentList, SourceRequest, StatusResult, StatusesResult, StepCountList, StepEntryList, SupportReport, TokenProviderRequest, TokenRejectRequest, TokenResolveRequest, WorkoutEntryList, WorkoutList, WorkoutScheduling } from "./types";
5
- import { encodeBodyEntryList, encodeCategoryRequest, encodeConnectRequest, encodeEmpty, encodeHeartRateEntryList, encodeMarkCompleteRequest, encodeNutritionEntryList, encodePermissionRequest, encodeRangeRequest, encodeRecordIdRequest, encodeSleepEntryList, encodeSourceRequest, encodeStepEntryList, encodeTokenProviderRequest, encodeTokenRejectRequest, encodeTokenResolveRequest, encodeWorkoutEntryList, decodeBoolResult, decodeBootstrapState, decodeConnection, decodeConnectionList, decodeDiagnosticsState, decodeEmpty, decodeHealthAccess, decodeHeartRateList, decodeLastDeliveryResult, decodePermissionGrant, decodePlannedWorkoutList, decodeRecordIdList, decodeSleepSegmentList, decodeStatusResult, decodeStatusesResult, decodeStepCountList, decodeSupportReport, decodeWorkoutList, decodeWorkoutScheduling } from "./codecs";
4
+ import type { AthleteProfileResult, AvailabilityResult, BodyEntryList, BodyReadingList, BoolResult, BootstrapState, CategoryRequest, ConnectRequest, Connection, ConnectionList, DayTotalList, DiagnosticsState, DietaryQuantityList, Empty, HealthAccess, HeartRateEntryList, HeartRateList, HydrationList, IntradaySampleList, LastDeliveryResult, MarkCompleteRequest, MenstrualEventList, MenstruationFlowDayList, NutritionEntryList, PermissionGrant, PermissionRequest, PlannedWorkoutList, RangeRequest, RecordIdList, RecordIdRequest, RestingHeartRateList, SleepEntryList, SleepSegmentList, SourceRequest, StatusResult, StatusesResult, StepCountList, StepEntryList, SupportReport, TokenProviderRequest, TokenRejectRequest, TokenResolveRequest, WorkoutEntryList, WorkoutIntensityList, WorkoutList, WorkoutRouteList, WorkoutScheduling } from "./types";
5
+ import { encodeBodyEntryList, encodeCategoryRequest, encodeConnectRequest, encodeEmpty, encodeHeartRateEntryList, encodeMarkCompleteRequest, encodeNutritionEntryList, encodePermissionRequest, encodeRangeRequest, encodeRecordIdRequest, encodeSleepEntryList, encodeSourceRequest, encodeStepEntryList, encodeTokenProviderRequest, encodeTokenRejectRequest, encodeTokenResolveRequest, encodeWorkoutEntryList, decodeAthleteProfileResult, decodeAvailabilityResult, decodeBodyReadingList, decodeBoolResult, decodeBootstrapState, decodeConnection, decodeConnectionList, decodeDayTotalList, decodeDiagnosticsState, decodeDietaryQuantityList, decodeEmpty, decodeHealthAccess, decodeHeartRateList, decodeHydrationList, decodeIntradaySampleList, decodeLastDeliveryResult, decodeMenstrualEventList, decodeMenstruationFlowDayList, decodePermissionGrant, decodePlannedWorkoutList, decodeRecordIdList, decodeRestingHeartRateList, decodeSleepSegmentList, decodeStatusResult, decodeStatusesResult, decodeStepCountList, decodeSupportReport, decodeWorkoutIntensityList, decodeWorkoutList, decodeWorkoutRouteList, decodeWorkoutScheduling } from "./codecs";
6
6
  import type { OpId } from "./ops";
7
7
  import { TerraError } from "../TerraError";
8
8
 
@@ -60,6 +60,11 @@ export class GeneratedClient {
60
60
  return this.call("client.connections", () => encodeEmpty(request), (raw) => decodeConnectionList(raw, "ConnectionList"));
61
61
  }
62
62
 
63
+ /** Whether the device can serve the source at all. Needs no connection and prompts for nothing. */
64
+ async clientAvailability(request: SourceRequest): Promise<AvailabilityResult> {
65
+ return this.call("client.availability", () => encodeSourceRequest(request), (raw) => decodeAvailabilityResult(raw, "AvailabilityResult"));
66
+ }
67
+
63
68
  async clientDisconnect(request: SourceRequest): Promise<Empty> {
64
69
  return this.call("client.disconnect", () => encodeSourceRequest(request), (raw) => decodeEmpty(raw, "Empty"));
65
70
  }
@@ -148,6 +153,18 @@ export class GeneratedClient {
148
153
  return this.call("body.heartRate", () => encodeRangeRequest(request), (raw) => decodeHeartRateList(raw, "HeartRateList"));
149
154
  }
150
155
 
156
+ /**
157
+ * The store's own daily figures. Health Connect grants them under daily, so Android answers
158
+ * authorization_not_determined where only body was asked for.
159
+ */
160
+ async bodyRestingHeartRate(request: RangeRequest): Promise<RestingHeartRateList> {
161
+ return this.call("body.restingHeartRate", () => encodeRangeRequest(request), (raw) => decodeRestingHeartRateList(raw, "RestingHeartRateList"));
162
+ }
163
+
164
+ async bodyReadings(request: RangeRequest): Promise<BodyReadingList> {
165
+ return this.call("body.readings", () => encodeRangeRequest(request), (raw) => decodeBodyReadingList(raw, "BodyReadingList"));
166
+ }
167
+
151
168
  /** Its own op rather than an overload: Swift overloads log by element type and JS cannot. */
152
169
  async bodyLogHeartRate(request: HeartRateEntryList): Promise<RecordIdList> {
153
170
  return this.call("body.logHeartRate", () => encodeHeartRateEntryList(request), (raw) => decodeRecordIdList(raw, "RecordIdList"));
@@ -165,6 +182,19 @@ export class GeneratedClient {
165
182
  return this.call("daily.steps", () => encodeRangeRequest(request), (raw) => decodeStepCountList(raw, "StepCountList"));
166
183
  }
167
184
 
185
+ /**
186
+ * One total per metric per whole local day the range touches. A day with nothing recorded has
187
+ * no total, not a zero.
188
+ */
189
+ async dailyTotals(request: RangeRequest): Promise<DayTotalList> {
190
+ return this.call("daily.totals", () => encodeRangeRequest(request), (raw) => decodeDayTotalList(raw, "DayTotalList"));
191
+ }
192
+
193
+ /** The active energy, basal energy, distance and floors records the totals are made of. */
194
+ async dailyIntraday(request: RangeRequest): Promise<IntradaySampleList> {
195
+ return this.call("daily.intraday", () => encodeRangeRequest(request), (raw) => decodeIntradaySampleList(raw, "IntradaySampleList"));
196
+ }
197
+
168
198
  async dailyLog(request: StepEntryList): Promise<RecordIdList> {
169
199
  return this.call("daily.log", () => encodeStepEntryList(request), (raw) => decodeRecordIdList(raw, "RecordIdList"));
170
200
  }
@@ -189,6 +219,16 @@ export class GeneratedClient {
189
219
  return this.call("activity.workouts", () => encodeRangeRequest(request), (raw) => decodeWorkoutList(raw, "WorkoutList"));
190
220
  }
191
221
 
222
+ /** The GPS tracks of the workouts in the range, each naming its workout. */
223
+ async activityRoutes(request: RangeRequest): Promise<WorkoutRouteList> {
224
+ return this.call("activity.routes", () => encodeRangeRequest(request), (raw) => decodeWorkoutRouteList(raw, "WorkoutRouteList"));
225
+ }
226
+
227
+ /** Speed, power and cycling cadence readings in the range. */
228
+ async activityIntensity(request: RangeRequest): Promise<WorkoutIntensityList> {
229
+ return this.call("activity.intensity", () => encodeRangeRequest(request), (raw) => decodeWorkoutIntensityList(raw, "WorkoutIntensityList"));
230
+ }
231
+
192
232
  async activityLog(request: WorkoutEntryList): Promise<RecordIdList> {
193
233
  return this.call("activity.log", () => encodeWorkoutEntryList(request), (raw) => decodeRecordIdList(raw, "RecordIdList"));
194
234
  }
@@ -197,6 +237,11 @@ export class GeneratedClient {
197
237
  return this.call("activity.delete", () => encodeRecordIdRequest(request), (raw) => decodeEmpty(raw, "Empty"));
198
238
  }
199
239
 
240
+ /** One quantity per nutrient a meal recorded, not grouped into meals. Water is not among them. */
241
+ async nutritionQuantities(request: RangeRequest): Promise<DietaryQuantityList> {
242
+ return this.call("nutrition.quantities", () => encodeRangeRequest(request), (raw) => decodeDietaryQuantityList(raw, "DietaryQuantityList"));
243
+ }
244
+
200
245
  async nutritionLog(request: NutritionEntryList): Promise<RecordIdList> {
201
246
  return this.call("nutrition.log", () => encodeNutritionEntryList(request), (raw) => decodeRecordIdList(raw, "RecordIdList"));
202
247
  }
@@ -205,6 +250,33 @@ export class GeneratedClient {
205
250
  return this.call("nutrition.delete", () => encodeRecordIdRequest(request), (raw) => decodeEmpty(raw, "Empty"));
206
251
  }
207
252
 
253
+ /** Water drunk in the range, one sample per drink. */
254
+ async nutritionHydration(request: RangeRequest): Promise<HydrationList> {
255
+ return this.call("nutrition.hydration", () => encodeRangeRequest(request), (raw) => decodeHydrationList(raw, "HydrationList"));
256
+ }
257
+
258
+ /**
259
+ * Logged flow days overlapping the range, one per day. A day logged with no flow is noFlow,
260
+ * not left out.
261
+ */
262
+ async menstruationFlow(request: RangeRequest): Promise<MenstruationFlowDayList> {
263
+ return this.call("menstruation.flow", () => encodeRangeRequest(request), (raw) => decodeMenstruationFlowDayList(raw, "MenstruationFlowDayList"));
264
+ }
265
+
266
+ /** Ovulation tests, cervical mucus and intermenstrual bleeding logged in the range. */
267
+ async menstruationEvents(request: RangeRequest): Promise<MenstrualEventList> {
268
+ return this.call("menstruation.events", () => encodeRangeRequest(request), (raw) => decodeMenstrualEventList(raw, "MenstrualEventList"));
269
+ }
270
+
271
+ /**
272
+ * Characteristics have no history.
273
+ *
274
+ * Available on ios only.
275
+ */
276
+ async athleteProfile(request: Empty): Promise<AthleteProfileResult> {
277
+ return this.call("athlete.profile", () => encodeEmpty(request), (raw) => decodeAthleteProfileResult(raw, "AthleteProfileResult"));
278
+ }
279
+
208
280
  /** Available on ios only. */
209
281
  async plannedWorkoutsPlanned(request: Empty): Promise<PlannedWorkoutList> {
210
282
  return this.call("plannedWorkouts.planned", () => encodeEmpty(request), (raw) => decodePlannedWorkoutList(raw, "PlannedWorkoutList"));