@tryterra/react-native 0.0.0-stage → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +111 -2
  3. package/TerraReactNative.podspec +48 -0
  4. package/android/build.gradle +87 -0
  5. package/android/src/bridgeCore/kotlin/README.md +1 -0
  6. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/BridgeBootstrap.kt +133 -0
  7. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/BridgeHost.kt +32 -0
  8. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/Conversions.kt +468 -0
  9. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/LauncherSlots.kt +41 -0
  10. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/Rejections.kt +150 -0
  11. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/StreamRegistry.kt +77 -0
  12. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TerraBridge.kt +86 -0
  13. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TerraBridgeOpsImpl.kt +258 -0
  14. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TerraBridgeStreamsImpl.kt +52 -0
  15. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TokenBroker.kt +198 -0
  16. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/generated/BridgeErrors.kt +217 -0
  17. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/generated/BridgeOps.kt +493 -0
  18. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/generated/BridgeTypes.kt +897 -0
  19. package/android/src/healthConnectOnly/kotlin/co/tryterra/sdk/reactnative/TerraStores.kt +11 -0
  20. package/android/src/main/AndroidManifest.xml +24 -0
  21. package/android/src/main/java/co/tryterra/sdk/reactnative/TerraBridgeModule.kt +96 -0
  22. package/android/src/main/java/co/tryterra/sdk/reactnative/TerraReactNativePackage.kt +26 -0
  23. package/android/src/main/java/co/tryterra/sdk/reactnative/TerraStartup.kt +46 -0
  24. package/android/src/samsung/kotlin/co/tryterra/sdk/reactnative/TerraStores.kt +13 -0
  25. package/docs/availability.md +246 -0
  26. package/frameworks/TerraSDK.xcframework/Info.plist +48 -0
  27. package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Info.plist +0 -0
  28. package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.abi.json +81513 -0
  29. package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.package.swiftinterface +2150 -0
  30. package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +2150 -0
  31. package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  32. package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.swiftinterface +2127 -0
  33. package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/PrivacyInfo.xcprivacy +96 -0
  34. package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/TerraSDK +0 -0
  35. package/frameworks/TerraSDK.xcframework/ios-arm64/dSYMs/TerraSDK.framework.dSYM/Contents/Info.plist +20 -0
  36. package/frameworks/TerraSDK.xcframework/ios-arm64/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/DWARF/TerraSDK +0 -0
  37. package/frameworks/TerraSDK.xcframework/ios-arm64/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/Relocations/aarch64/TerraSDK.yml +6473 -0
  38. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Info.plist +0 -0
  39. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +81513 -0
  40. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.package.swiftinterface +2150 -0
  41. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +2150 -0
  42. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  43. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +2127 -0
  44. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +81513 -0
  45. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.package.swiftinterface +2150 -0
  46. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +2150 -0
  47. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  48. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +2127 -0
  49. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/PrivacyInfo.xcprivacy +96 -0
  50. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/TerraSDK +0 -0
  51. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/_CodeSignature/CodeResources +223 -0
  52. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Info.plist +20 -0
  53. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/DWARF/TerraSDK +0 -0
  54. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/Relocations/aarch64/TerraSDK.yml +6474 -0
  55. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/Relocations/x86_64/TerraSDK.yml +6392 -0
  56. package/ios/TerraBridgeCore/BridgeBootstrap.swift +89 -0
  57. package/ios/TerraBridgeCore/BridgeHost.swift +13 -0
  58. package/ios/TerraBridgeCore/Conversions.swift +467 -0
  59. package/ios/TerraBridgeCore/Generated/BridgeErrors.swift +179 -0
  60. package/ios/TerraBridgeCore/Generated/BridgeOps.swift +795 -0
  61. package/ios/TerraBridgeCore/Generated/BridgeTypes.swift +1317 -0
  62. package/ios/TerraBridgeCore/Locked.swift +15 -0
  63. package/ios/TerraBridgeCore/README.md +1 -0
  64. package/ios/TerraBridgeCore/Rejections.swift +130 -0
  65. package/ios/TerraBridgeCore/RuntimeAttachment.swift +60 -0
  66. package/ios/TerraBridgeCore/StreamRegistry.swift +59 -0
  67. package/ios/TerraBridgeCore/TerraBridge.swift +122 -0
  68. package/ios/TerraBridgeCore/TerraBridgeOpsImpl.swift +254 -0
  69. package/ios/TerraBridgeCore/TerraBridgeStreamsImpl.swift +60 -0
  70. package/ios/TerraBridgeCore/TokenBroker.swift +147 -0
  71. package/ios/TerraBridgeModule.mm +79 -0
  72. package/ios/TerraBridgeModuleImpl.swift +49 -0
  73. package/ios/TerraReactNative.swift +19 -0
  74. package/lib/EndUser.d.ts +12 -0
  75. package/lib/EndUser.js +13 -0
  76. package/lib/NativeTerraBridge.d.ts +23 -0
  77. package/lib/NativeTerraBridge.js +13 -0
  78. package/lib/Terra.d.ts +154 -0
  79. package/lib/Terra.js +280 -0
  80. package/lib/TerraError.d.ts +42 -0
  81. package/lib/TerraError.js +95 -0
  82. package/lib/createTerra.d.ts +8 -0
  83. package/lib/createTerra.js +19 -0
  84. package/lib/generated/client.d.ts +92 -0
  85. package/lib/generated/client.js +196 -0
  86. package/lib/generated/codecs.d.ts +135 -0
  87. package/lib/generated/codecs.js +1358 -0
  88. package/lib/generated/errors.d.ts +13 -0
  89. package/lib/generated/errors.js +68 -0
  90. package/lib/generated/ops.d.ts +13 -0
  91. package/lib/generated/ops.js +137 -0
  92. package/lib/generated/streams.d.ts +23 -0
  93. package/lib/generated/streams.js +58 -0
  94. package/lib/generated/types.d.ts +481 -0
  95. package/lib/generated/types.js +92 -0
  96. package/lib/index.d.ts +29 -0
  97. package/lib/index.js +55 -0
  98. package/package.json +71 -4
  99. package/src/EndUser.ts +15 -0
  100. package/src/NativeTerraBridge.ts +27 -0
  101. package/src/Terra.ts +462 -0
  102. package/src/TerraError.ts +116 -0
  103. package/src/createTerra.ts +18 -0
  104. package/src/generated/client.ts +243 -0
  105. package/src/generated/codecs.ts +1287 -0
  106. package/src/generated/errors.ts +113 -0
  107. package/src/generated/ops.ts +197 -0
  108. package/src/generated/streams.ts +74 -0
  109. package/src/generated/types.ts +667 -0
  110. package/src/index.ts +52 -0
@@ -0,0 +1,897 @@
1
+ // Generated from surface/terra-surface.json by surface/gen. Do not edit by hand.
2
+ // Regenerate with: node surface/gen/src/cli.ts generate
3
+
4
+ package co.tryterra.sdk.bridge.generated
5
+
6
+ import kotlinx.serialization.ExperimentalSerializationApi
7
+ import kotlinx.serialization.SerializationException
8
+ import kotlinx.serialization.KSerializer
9
+ import kotlinx.serialization.SerialName
10
+ import kotlinx.serialization.Serializable
11
+ import kotlinx.serialization.builtins.ListSerializer
12
+ import kotlinx.serialization.descriptors.SerialDescriptor
13
+ import kotlinx.serialization.encoding.Decoder
14
+ import kotlinx.serialization.encoding.Encoder
15
+ import kotlinx.serialization.json.JsonClassDiscriminator
16
+ import kotlinx.serialization.json.JsonObject
17
+ import kotlinx.serialization.json.JsonPrimitive
18
+ import kotlinx.serialization.json.booleanOrNull
19
+ import kotlinx.serialization.json.doubleOrNull
20
+ import kotlinx.serialization.json.longOrNull
21
+ import java.time.Instant
22
+ import java.time.ZoneOffset
23
+ import java.time.format.DateTimeFormatter
24
+
25
+ /**
26
+ * The one spelling of an instant on the wire: UTC, and always exactly three fractional digits.
27
+ *
28
+ * `Instant.toString()` is not it: it omits the fractional part when it is zero and prints nine
29
+ * digits when it is not, so the same moment would produce different bytes here and in Swift.
30
+ * The DTOs carry instants as String, so this is the only place the format is decided, and the
31
+ * hand-written ops implementation must go through it rather than formatting its own.
32
+ */
33
+ public object BridgeInstant {
34
+ private val formatter: DateTimeFormatter =
35
+ DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").withZone(ZoneOffset.UTC)
36
+
37
+ public fun format(instant: Instant): String = formatter.format(instant)
38
+
39
+ /** Tolerant on the way in: a peer that omitted the fractional part is still understood. */
40
+ public fun parse(text: String): Instant = Instant.parse(text)
41
+ }
42
+
43
+ /** A health store the SDK can link. One connection per source per device. */
44
+ @Serializable
45
+ public enum class Source {
46
+ @SerialName("health")
47
+ HEALTH,
48
+ /**
49
+ * Samsung Health, through the terra-android-samsung add-on. Connect, disconnect, permissions
50
+ * and diagnostics take it; reads, writes and change streams are Health Connect's.
51
+ *
52
+ * Available on android only.
53
+ */
54
+ @SerialName("samsungHealth")
55
+ SAMSUNG_HEALTH,
56
+ }
57
+
58
+ /** The unit permissions are asked for and changes are reported against. */
59
+ @Serializable
60
+ public enum class DataCategory {
61
+ @SerialName("activity")
62
+ ACTIVITY,
63
+ @SerialName("body")
64
+ BODY,
65
+ @SerialName("daily")
66
+ DAILY,
67
+ @SerialName("sleep")
68
+ SLEEP,
69
+ @SerialName("nutrition")
70
+ NUTRITION,
71
+ }
72
+
73
+ @Serializable
74
+ public enum class PermissionStatus {
75
+ @SerialName("granted")
76
+ GRANTED,
77
+ @SerialName("denied")
78
+ DENIED,
79
+ @SerialName("notDetermined")
80
+ NOT_DETERMINED,
81
+ /**
82
+ * A read grant HealthKit will not report. Not an error: it is the documented answer for reads
83
+ * on iOS.
84
+ */
85
+ @SerialName("notDisclosed")
86
+ NOT_DISCLOSED,
87
+ @SerialName("unsupported")
88
+ UNSUPPORTED,
89
+ /**
90
+ * Android answers unsupported where Apple would answer unavailable.
91
+ *
92
+ * Available on ios only.
93
+ */
94
+ @SerialName("unavailable")
95
+ UNAVAILABLE,
96
+ }
97
+
98
+ @Serializable
99
+ public enum class MotionContext {
100
+ @SerialName("unknown")
101
+ UNKNOWN,
102
+ @SerialName("sedentary")
103
+ SEDENTARY,
104
+ @SerialName("active")
105
+ ACTIVE,
106
+ }
107
+
108
+ /**
109
+ * Why background delivery is restricted. Android discloses which; Apple reports the state
110
+ * without a reason.
111
+ *
112
+ * Available on android only.
113
+ */
114
+ @Serializable
115
+ public enum class BackgroundRestrictionReason {
116
+ @SerialName("oemRestricted")
117
+ OEM_RESTRICTED,
118
+ @SerialName("backgroundReadDenied")
119
+ BACKGROUND_READ_DENIED,
120
+ @SerialName("healthConnectFeatureUnavailable")
121
+ HEALTH_CONNECT_FEATURE_UNAVAILABLE,
122
+ @SerialName("storeFeatureUnavailable")
123
+ STORE_FEATURE_UNAVAILABLE,
124
+ @SerialName("ticksNotRunning")
125
+ TICKS_NOT_RUNNING,
126
+ }
127
+
128
+ @Serializable
129
+ public enum class DiagnosticsReason {
130
+ @SerialName("uploadFailed")
131
+ UPLOAD_FAILED,
132
+ @SerialName("backfillAbandoned")
133
+ BACKFILL_ABANDONED,
134
+ @SerialName("backgroundObservationUnavailable")
135
+ BACKGROUND_OBSERVATION_UNAVAILABLE,
136
+ @SerialName("backgroundRestricted")
137
+ BACKGROUND_RESTRICTED,
138
+ @SerialName("backgroundRefreshUnscheduled")
139
+ BACKGROUND_REFRESH_UNSCHEDULED,
140
+ @SerialName("quiet")
141
+ QUIET,
142
+ @SerialName("requested")
143
+ REQUESTED,
144
+ }
145
+
146
+ @Serializable
147
+ public enum class DeviceKeyRevokeOutcome {
148
+ @SerialName("revoked")
149
+ REVOKED,
150
+ @SerialName("alreadyRevoked")
151
+ ALREADY_REVOKED,
152
+ @SerialName("routeAbsent")
153
+ ROUTE_ABSENT,
154
+ }
155
+
156
+ /**
157
+ * The redacted error name an event carries. Deliberately not TerraErrorCode: an event names
158
+ * the failure without the detail bag, because a support report leaves the device.
159
+ */
160
+ @Serializable
161
+ public enum class FailureCode {
162
+ @SerialName("healthDataUnavailable")
163
+ HEALTH_DATA_UNAVAILABLE,
164
+ @SerialName("notConnected")
165
+ NOT_CONNECTED,
166
+ @SerialName("tokenUnavailable")
167
+ TOKEN_UNAVAILABLE,
168
+ @SerialName("tokenRejected")
169
+ TOKEN_REJECTED,
170
+ @SerialName("tokenProviderFailed")
171
+ TOKEN_PROVIDER_FAILED,
172
+ @SerialName("offline")
173
+ OFFLINE,
174
+ @SerialName("timedOut")
175
+ TIMED_OUT,
176
+ @SerialName("serverError")
177
+ SERVER_ERROR,
178
+ @SerialName("transport")
179
+ TRANSPORT,
180
+ @SerialName("malformedResponse")
181
+ MALFORMED_RESPONSE,
182
+ @SerialName("userLimitReached")
183
+ USER_LIMIT_REACHED,
184
+ @SerialName("upgradeRequired")
185
+ UPGRADE_REQUIRED,
186
+ @SerialName("platform")
187
+ PLATFORM,
188
+ @SerialName("deviceKeyUnavailable")
189
+ DEVICE_KEY_UNAVAILABLE,
190
+ @SerialName("signatureRejected")
191
+ SIGNATURE_REJECTED,
192
+ @SerialName("linkRevoked")
193
+ LINK_REVOKED,
194
+ @SerialName("linkConflict")
195
+ LINK_CONFLICT,
196
+ @SerialName("clockSkewed")
197
+ CLOCK_SKEWED,
198
+ @SerialName("devIDMismatch")
199
+ DEV_IDMISMATCH,
200
+ @SerialName("syncAlreadyRunning")
201
+ SYNC_ALREADY_RUNNING,
202
+ @SerialName("linkAlreadyRunning")
203
+ LINK_ALREADY_RUNNING,
204
+ @SerialName("storeLocked")
205
+ STORE_LOCKED,
206
+ @SerialName("storeUnreadable")
207
+ STORE_UNREADABLE,
208
+ @SerialName("deadlineExceeded")
209
+ DEADLINE_EXCEEDED,
210
+ @SerialName("permissionDenied")
211
+ PERMISSION_DENIED,
212
+ @SerialName("authorizationNotDetermined")
213
+ AUTHORIZATION_NOT_DETERMINED,
214
+ @SerialName("sampleNotFound")
215
+ SAMPLE_NOT_FOUND,
216
+ @SerialName("rollbackIncomplete")
217
+ ROLLBACK_INCOMPLETE,
218
+ @SerialName("distanceNotAttributable")
219
+ DISTANCE_NOT_ATTRIBUTABLE,
220
+ @SerialName("bloodPressureIncomplete")
221
+ BLOOD_PRESSURE_INCOMPLETE,
222
+ @SerialName("authorizationUnavailableInExtension")
223
+ AUTHORIZATION_UNAVAILABLE_IN_EXTENSION,
224
+ @SerialName("routeAwaitingConverter")
225
+ ROUTE_AWAITING_CONVERTER,
226
+ /** Available on android only. */
227
+ @SerialName("readQuotaExceeded")
228
+ READ_QUOTA_EXCEEDED,
229
+ /**
230
+ * Apple's FailureCode carries it; Android's does not, because there is no Android
231
+ * planned-workout model.
232
+ *
233
+ * Available on ios only.
234
+ */
235
+ @SerialName("plannedWorkoutUnnamed")
236
+ PLANNED_WORKOUT_UNNAMED,
237
+ }
238
+
239
+ /**
240
+ * Whether Terra's plans can be put on the person's Apple Watch. unsupported before iOS 17 or
241
+ * where WorkoutKit cannot schedule.
242
+ *
243
+ * Available on ios only.
244
+ */
245
+ @Serializable
246
+ public enum class WorkoutSchedulingStatus {
247
+ @SerialName("unsupported")
248
+ UNSUPPORTED,
249
+ @SerialName("notDetermined")
250
+ NOT_DETERMINED,
251
+ @SerialName("denied")
252
+ DENIED,
253
+ @SerialName("authorized")
254
+ AUTHORIZED,
255
+ }
256
+
257
+ /**
258
+ * The person a connect is for. A stored link is restored only for the person it was made for;
259
+ * anybody else gets a fresh link. Never sent to Terra as identity.
260
+ */
261
+ @Serializable
262
+ @OptIn(ExperimentalSerializationApi::class)
263
+ @JsonClassDiscriminator("kind")
264
+ public sealed interface EndUser {
265
+ /** The app's id for this person: the same value its backend puts in the token. */
266
+ @Serializable
267
+ @SerialName("reference")
268
+ public data class Reference(public val id: String) : EndUser
269
+ /** No reference: one link per install. */
270
+ @Serializable
271
+ @SerialName("anonymous")
272
+ public data object Anonymous : EndUser
273
+ }
274
+
275
+ /** A half-open window [start, end). Resolved against the device's calendar natively. */
276
+ @Serializable
277
+ @OptIn(ExperimentalSerializationApi::class)
278
+ @JsonClassDiscriminator("kind")
279
+ public sealed interface TimeRange {
280
+ @Serializable
281
+ @SerialName("lastHours")
282
+ public data class LastHours(public val hours: Int) : TimeRange
283
+ @Serializable
284
+ @SerialName("lastDays")
285
+ public data class LastDays(public val days: Int) : TimeRange
286
+ @Serializable
287
+ @SerialName("today")
288
+ public data object Today : TimeRange
289
+ @Serializable
290
+ @SerialName("between")
291
+ public data class Between(public val start: String, public val end: String) : TimeRange
292
+ }
293
+
294
+ @Serializable
295
+ @OptIn(ExperimentalSerializationApi::class)
296
+ @JsonClassDiscriminator("kind")
297
+ public sealed interface SleepStage {
298
+ @Serializable
299
+ @SerialName("inBed")
300
+ public data object InBed : SleepStage
301
+ @Serializable
302
+ @SerialName("awake")
303
+ public data object Awake : SleepStage
304
+ @Serializable
305
+ @SerialName("asleep")
306
+ public data object Asleep : SleepStage
307
+ @Serializable
308
+ @SerialName("light")
309
+ public data object Light : SleepStage
310
+ @Serializable
311
+ @SerialName("deep")
312
+ public data object Deep : SleepStage
313
+ @Serializable
314
+ @SerialName("rem")
315
+ public data object Rem : SleepStage
316
+ /**
317
+ * A stage the store reported that neither SDK maps. The raw value is carried so an app can log
318
+ * it.
319
+ */
320
+ @Serializable
321
+ @SerialName("unknown")
322
+ public data class Unknown(public val raw: String) : SleepStage
323
+ }
324
+
325
+ @Serializable
326
+ @OptIn(ExperimentalSerializationApi::class)
327
+ @JsonClassDiscriminator("kind")
328
+ public sealed interface WorkoutActivity {
329
+ @Serializable
330
+ @SerialName("running")
331
+ public data object Running : WorkoutActivity
332
+ @Serializable
333
+ @SerialName("walking")
334
+ public data object Walking : WorkoutActivity
335
+ @Serializable
336
+ @SerialName("cycling")
337
+ public data object Cycling : WorkoutActivity
338
+ @Serializable
339
+ @SerialName("swimming")
340
+ public data object Swimming : WorkoutActivity
341
+ @Serializable
342
+ @SerialName("strengthTraining")
343
+ public data object StrengthTraining : WorkoutActivity
344
+ @Serializable
345
+ @SerialName("other")
346
+ public data class Other(public val label: String) : WorkoutActivity
347
+ }
348
+
349
+ /**
350
+ * What the SDK believes about delivery right now. Wire spelling matches DiagnosticsWire.state
351
+ * and Apple's DiagnosticsState Encodable.
352
+ */
353
+ @Serializable
354
+ @OptIn(ExperimentalSerializationApi::class)
355
+ @JsonClassDiscriminator("state")
356
+ public sealed interface DiagnosticsState {
357
+ @Serializable
358
+ @SerialName("healthy")
359
+ public data object Healthy : DiagnosticsState
360
+ @Serializable
361
+ @SerialName("quiet")
362
+ public data class Quiet(public val since: String) : DiagnosticsState
363
+ @Serializable
364
+ @SerialName("backgroundRestricted")
365
+ public data class BackgroundRestricted(public val reason: BackgroundRestrictionReason? = null) : DiagnosticsState
366
+ @Serializable
367
+ @SerialName("backgroundRefreshUnscheduled")
368
+ public data object BackgroundRefreshUnscheduled : DiagnosticsState
369
+ @Serializable
370
+ @SerialName("pendingRetry")
371
+ public data object PendingRetry : DiagnosticsState
372
+ @Serializable
373
+ @SerialName("linkRevoked")
374
+ public data object LinkRevoked : DiagnosticsState
375
+ /**
376
+ * Apple reports the mismatch as a state. Android raises dev_id_mismatch and has no state for
377
+ * it.
378
+ *
379
+ * Available on ios only.
380
+ */
381
+ @Serializable
382
+ @SerialName("devIDMismatch")
383
+ public data object DevIDMismatch : DiagnosticsState
384
+ }
385
+
386
+ /**
387
+ * What a subscription delivers. A stream can fail or finish, and without saying so the
388
+ * framework layer has to invent a convention for both - two of those, one per wrapper, is a
389
+ * contract nobody wrote down.
390
+ */
391
+ @Serializable
392
+ @OptIn(ExperimentalSerializationApi::class)
393
+ @JsonClassDiscriminator("kind")
394
+ public sealed interface StreamMessage {
395
+ @Serializable
396
+ @SerialName("data")
397
+ public data class Data(public val element: JsonObject) : StreamMessage
398
+ /** The stream failed. The subscription is over; the id will deliver nothing more. */
399
+ @Serializable
400
+ @SerialName("error")
401
+ public data class Error(public val error: TerraErrorPayload) : StreamMessage
402
+ /** The stream ended on its own. The host should drop the subscription rather than unsubscribe. */
403
+ @Serializable
404
+ @SerialName("complete")
405
+ public data object Complete : StreamMessage
406
+ }
407
+
408
+ public object DataCategorySetSerializer : KSerializer<List<DataCategory>> {
409
+ private val list = ListSerializer(DataCategory.serializer())
410
+ override val descriptor: SerialDescriptor = list.descriptor
411
+
412
+ override fun serialize(encoder: Encoder, value: List<DataCategory>): Unit = list.serialize(encoder, canonical(value))
413
+
414
+ override fun deserialize(decoder: Decoder): List<DataCategory> = canonical(list.deserialize(decoder))
415
+
416
+ private fun canonical(value: List<DataCategory>): List<DataCategory> =
417
+ value.distinct().sortedBy { DataCategory.serializer().descriptor.getElementName(it.ordinal) }
418
+ }
419
+
420
+ /** Who recorded a sample. The only stable handle an app has for filtering by app or device. */
421
+ @Serializable
422
+ public data class SampleSource(
423
+ public val name: String,
424
+ public val bundleId: String,
425
+ public val productType: String? = null,
426
+ public val deviceType: String? = null,
427
+ )
428
+
429
+ /**
430
+ * A link record. A value, not a handle: Apple's Connection carries sync and disconnect
431
+ * methods, and an object with behaviour cannot cross the bridge, so those are ops on the
432
+ * client.
433
+ */
434
+ @Serializable
435
+ public data class Connection(
436
+ public val source: Source,
437
+ public val userId: String,
438
+ /**
439
+ * Android's StoredConnection carries the store-side id. Apple's Connection does not.
440
+ *
441
+ * Available on android only.
442
+ */
443
+ public val providerUserId: String? = null,
444
+ )
445
+
446
+ @Serializable
447
+ public data class HeartRateSample(
448
+ public val id: String,
449
+ public val time: String,
450
+ public val beatsPerMinute: Double,
451
+ public val source: SampleSource,
452
+ public val motionContext: MotionContext? = null,
453
+ public val userEntered: Boolean,
454
+ public val timeZone: String? = null,
455
+ )
456
+
457
+ /**
458
+ * A step total over an interval. Apple derives the id from the source and start; Android
459
+ * carries a RecordId when it has one, so the id is optional on the wire.
460
+ */
461
+ @Serializable
462
+ public data class StepCount(
463
+ public val id: String? = null,
464
+ public val start: String,
465
+ public val end: String,
466
+ public val steps: Int,
467
+ public val source: SampleSource,
468
+ public val userEntered: Boolean,
469
+ public val timeZone: String? = null,
470
+ )
471
+
472
+ @Serializable
473
+ public data class SleepSegment(
474
+ public val id: String,
475
+ public val start: String,
476
+ public val end: String,
477
+ public val stage: SleepStage,
478
+ public val source: SampleSource,
479
+ public val userEntered: Boolean,
480
+ public val timeZone: String? = null,
481
+ )
482
+
483
+ /**
484
+ * indoor is optional because Health Connect does not always record it; Apple always has an
485
+ * answer.
486
+ */
487
+ @Serializable
488
+ public data class Workout(
489
+ public val id: String,
490
+ public val start: String,
491
+ public val end: String,
492
+ public val activity: WorkoutActivity,
493
+ public val activityTypeCode: Int,
494
+ public val indoor: Boolean? = null,
495
+ public val activeEnergyKilocalories: Double? = null,
496
+ public val distanceMeters: Double? = null,
497
+ public val source: SampleSource,
498
+ public val userEntered: Boolean,
499
+ public val timeZone: String? = null,
500
+ )
501
+
502
+ /** A day whose data changed. Carries no health value by construction. */
503
+ @Serializable
504
+ public data class HealthChange(
505
+ public val category: DataCategory,
506
+ public val day: String,
507
+ )
508
+
509
+ @Serializable
510
+ public data class WorkoutScope(
511
+ public val activity: WorkoutActivity,
512
+ public val energy: Boolean,
513
+ public val distance: Boolean,
514
+ )
515
+
516
+ /**
517
+ * Lists rather than sets: JSON has no set, so the category lists are marked `set` and every
518
+ * generated codec de-duplicates and sorts them, so two orderings compare equal.
519
+ */
520
+ @Serializable
521
+ public data class HealthAccess(
522
+ @Serializable(with = DataCategorySetSerializer::class) public val read: List<DataCategory>,
523
+ @Serializable(with = DataCategorySetSerializer::class) public val write: List<DataCategory>,
524
+ public val workouts: List<WorkoutScope>,
525
+ )
526
+
527
+ @Serializable
528
+ public data class HeartRateEntry(
529
+ public val time: String,
530
+ public val beatsPerMinute: Double,
531
+ public val recordId: String? = null,
532
+ )
533
+
534
+ @Serializable
535
+ public data class StepEntry(
536
+ public val start: String,
537
+ public val end: String,
538
+ public val steps: Int,
539
+ public val recordId: String? = null,
540
+ )
541
+
542
+ @Serializable
543
+ public data class SleepEntry(
544
+ public val start: String,
545
+ public val end: String,
546
+ public val stage: SleepStage,
547
+ public val recordId: String? = null,
548
+ )
549
+
550
+ @Serializable
551
+ public data class WorkoutEntry(
552
+ public val start: String,
553
+ public val end: String,
554
+ public val activity: WorkoutActivity,
555
+ public val totalEnergyKilocalories: Double? = null,
556
+ public val totalDistanceMeters: Double? = null,
557
+ public val recordId: String? = null,
558
+ )
559
+
560
+ @Serializable
561
+ public data class BodyEntry(
562
+ public val time: String,
563
+ public val bodyMassKilograms: Double? = null,
564
+ public val bodyFatPercent: Double? = null,
565
+ public val bodyMassIndex: Double? = null,
566
+ public val heightMeters: Double? = null,
567
+ public val leanBodyMassKilograms: Double? = null,
568
+ public val bodyTemperatureCelsius: Double? = null,
569
+ public val oxygenSaturationPercent: Double? = null,
570
+ public val bloodGlucoseMilligramsPerDeciliter: Double? = null,
571
+ public val bloodPressureSystolicMillimetersOfMercury: Double? = null,
572
+ public val bloodPressureDiastolicMillimetersOfMercury: Double? = null,
573
+ public val recordId: String? = null,
574
+ )
575
+
576
+ /**
577
+ * Every field optional and every unit in the name, matching NutritionEntry.Nutrients on both
578
+ * platforms field for field.
579
+ */
580
+ @Serializable
581
+ public data class Nutrients(
582
+ public val energyKilocalories: Double? = null,
583
+ public val proteinGrams: Double? = null,
584
+ public val carbohydratesGrams: Double? = null,
585
+ public val fiberGrams: Double? = null,
586
+ public val sugarGrams: Double? = null,
587
+ public val fatTotalGrams: Double? = null,
588
+ public val fatSaturatedGrams: Double? = null,
589
+ public val fatMonounsaturatedGrams: Double? = null,
590
+ public val fatPolyunsaturatedGrams: Double? = null,
591
+ public val cholesterolMilligrams: Double? = null,
592
+ public val sodiumMilligrams: Double? = null,
593
+ public val potassiumMilligrams: Double? = null,
594
+ public val calciumMilligrams: Double? = null,
595
+ public val ironMilligrams: Double? = null,
596
+ public val magnesiumMilligrams: Double? = null,
597
+ public val zincMilligrams: Double? = null,
598
+ public val waterLiters: Double? = null,
599
+ public val caffeineMilligrams: Double? = null,
600
+ public val vitaminAMicrograms: Double? = null,
601
+ public val vitaminCMilligrams: Double? = null,
602
+ public val vitaminDMicrograms: Double? = null,
603
+ public val vitaminEMilligrams: Double? = null,
604
+ public val vitaminKMicrograms: Double? = null,
605
+ public val vitaminB6Milligrams: Double? = null,
606
+ public val vitaminB12Micrograms: Double? = null,
607
+ public val folateMicrograms: Double? = null,
608
+ public val thiaminMilligrams: Double? = null,
609
+ public val riboflavinMilligrams: Double? = null,
610
+ public val niacinMilligrams: Double? = null,
611
+ public val biotinMicrograms: Double? = null,
612
+ public val pantothenicAcidMilligrams: Double? = null,
613
+ public val phosphorusMilligrams: Double? = null,
614
+ public val iodineMicrograms: Double? = null,
615
+ public val chromiumMicrograms: Double? = null,
616
+ public val copperMilligrams: Double? = null,
617
+ public val manganeseMilligrams: Double? = null,
618
+ public val molybdenumMicrograms: Double? = null,
619
+ public val seleniumMicrograms: Double? = null,
620
+ public val chlorideMilligrams: Double? = null,
621
+ )
622
+
623
+ @Serializable
624
+ public data class NutritionEntry(
625
+ public val start: String,
626
+ public val end: String,
627
+ public val name: String? = null,
628
+ public val nutrients: Nutrients,
629
+ public val recordId: String? = null,
630
+ )
631
+
632
+ /**
633
+ * An error carried as a value rather than thrown, inside a sync result. The same shape the
634
+ * bridge rejects a promise with.
635
+ */
636
+ @Serializable
637
+ public data class TerraErrorPayload(
638
+ public val code: String,
639
+ public val retryable: Boolean,
640
+ public val message: String,
641
+ public val source: Source? = null,
642
+ public val details: JsonObject? = null,
643
+ )
644
+
645
+ /** Available on ios only. */
646
+ @Serializable
647
+ public data class PlannedWorkout(
648
+ public val id: String? = null,
649
+ public val plannedOn: String? = null,
650
+ public val activity: WorkoutActivity? = null,
651
+ public val title: String? = null,
652
+ public val completedAt: String? = null,
653
+ )
654
+
655
+ /**
656
+ * The flat object both SDKs already emit: `at`, `event`, and whatever keys that kind carries.
657
+ * Carried through unchanged rather than reshaped, because reshaping it would be a hand-written
658
+ * second contract on each platform that no vector could pin - and the encoding it would be
659
+ * translating is one DiagnosticsWire and Apple's Encodable already agree on, and that
660
+ * EventPrivacyTest already guards against carrying a health value.
661
+ */
662
+ public typealias TerraEvent = JsonObject
663
+
664
+ /**
665
+ * The redacted report, carried as the wire object DiagnosticsWire.report and Apple's
666
+ * SupportReport.json already produce.
667
+ */
668
+ @Serializable
669
+ public data class SupportReport(
670
+ public val sdkVersion: String,
671
+ public val generatedAt: String,
672
+ public val state: DiagnosticsState,
673
+ public val lastDelivery: String? = null,
674
+ public val isConnected: Boolean,
675
+ public val requestedCategories: List<String>,
676
+ public val requestedWrites: List<String>,
677
+ public val requestedWorkouts: List<String>,
678
+ public val recentEvents: List<TerraEvent>,
679
+ )
680
+
681
+ /**
682
+ * What the JS side needs to know about the native client it is attaching to. bootstrapped
683
+ * false is a host setup bug, and the wrapper says so with the exact line to add.
684
+ */
685
+ @Serializable
686
+ public data class BootstrapState(
687
+ public val bootstrapped: Boolean,
688
+ public val devId: String? = null,
689
+ public val platform: String,
690
+ public val sdkVersion: String,
691
+ public val backgroundRegistered: Boolean,
692
+ )
693
+
694
+ /**
695
+ * The broker asking JS for a link token. Answered by token.resolve or token.reject with the
696
+ * same requestId.
697
+ */
698
+ @Serializable
699
+ public data class TokenRequest(
700
+ public val requestId: String,
701
+ )
702
+
703
+ /** No request fields, or no response value. */
704
+ @Serializable
705
+ public class Empty
706
+
707
+ @Serializable
708
+ public data class SourceRequest(
709
+ public val source: Source? = null,
710
+ )
711
+
712
+ @Serializable
713
+ public data class RangeRequest(
714
+ public val range: TimeRange,
715
+ )
716
+
717
+ @Serializable
718
+ public data class CategoryRequest(
719
+ public val category: DataCategory,
720
+ public val source: Source? = null,
721
+ )
722
+
723
+ @Serializable
724
+ public data class RecordIdRequest(
725
+ public val id: String,
726
+ )
727
+
728
+ @Serializable
729
+ public data class ConnectRequest(
730
+ public val source: Source? = null,
731
+ public val endUser: EndUser,
732
+ /**
733
+ * A grant already in hand, from a QR code or a deep link. Absent means the token provider is
734
+ * asked.
735
+ */
736
+ public val token: String? = null,
737
+ )
738
+
739
+ @Serializable
740
+ public data class PermissionRequest(
741
+ @Serializable(with = DataCategorySetSerializer::class) public val read: List<DataCategory>,
742
+ @Serializable(with = DataCategorySetSerializer::class) public val write: List<DataCategory>,
743
+ public val workouts: List<WorkoutScope>,
744
+ public val source: Source? = null,
745
+ )
746
+
747
+ /**
748
+ * What the sheet granted. Android answers with the access this call obtained; HealthKit will
749
+ * not report read grants, so Apple answers nothing at all.
750
+ */
751
+ @Serializable
752
+ public data class PermissionGrant(
753
+ /** Available on android only. */
754
+ public val granted: HealthAccess? = null,
755
+ )
756
+
757
+ @Serializable
758
+ public data class TokenResolveRequest(
759
+ public val requestId: String,
760
+ public val token: String,
761
+ )
762
+
763
+ @Serializable
764
+ public data class TokenRejectRequest(
765
+ public val requestId: String,
766
+ public val message: String,
767
+ )
768
+
769
+ @Serializable
770
+ public data class TokenProviderRequest(
771
+ public val present: Boolean,
772
+ public val timeoutMs: Int,
773
+ )
774
+
775
+ /** Available on ios only. */
776
+ @Serializable
777
+ public data class MarkCompleteRequest(
778
+ public val id: String? = null,
779
+ public val at: String? = null,
780
+ )
781
+
782
+ @Serializable
783
+ public data class BoolResult(
784
+ public val value: Boolean,
785
+ )
786
+
787
+ @Serializable
788
+ public data class StatusResult(
789
+ public val status: PermissionStatus,
790
+ )
791
+
792
+ /** Available on android only. */
793
+ @Serializable
794
+ public data class StatusesResult(
795
+ public val statuses: Map<DataCategory, PermissionStatus>,
796
+ )
797
+
798
+ @Serializable
799
+ public data class RecordIdList(
800
+ public val ids: List<String>,
801
+ )
802
+
803
+ @Serializable
804
+ public data class ConnectionList(
805
+ public val connections: List<Connection>,
806
+ )
807
+
808
+ @Serializable
809
+ public data class HeartRateList(
810
+ public val samples: List<HeartRateSample>,
811
+ )
812
+
813
+ @Serializable
814
+ public data class StepCountList(
815
+ public val samples: List<StepCount>,
816
+ )
817
+
818
+ @Serializable
819
+ public data class SleepSegmentList(
820
+ public val samples: List<SleepSegment>,
821
+ )
822
+
823
+ @Serializable
824
+ public data class WorkoutList(
825
+ public val samples: List<Workout>,
826
+ )
827
+
828
+ /** Available on ios only. */
829
+ @Serializable
830
+ public data class PlannedWorkoutList(
831
+ public val workouts: List<PlannedWorkout>? = null,
832
+ )
833
+
834
+ /**
835
+ * Once authorized, every sync run keeps the Watch schedule in step with Terra.
836
+ *
837
+ * Available on ios only.
838
+ */
839
+ @Serializable
840
+ public data class WorkoutScheduling(
841
+ public val status: WorkoutSchedulingStatus? = null,
842
+ )
843
+
844
+ @Serializable
845
+ public data class LastDeliveryResult(
846
+ public val at: String? = null,
847
+ )
848
+
849
+ @Serializable
850
+ public data class HeartRateEntryList(
851
+ public val entries: List<HeartRateEntry>,
852
+ )
853
+
854
+ @Serializable
855
+ public data class BodyEntryList(
856
+ public val entries: List<BodyEntry>,
857
+ )
858
+
859
+ @Serializable
860
+ public data class StepEntryList(
861
+ public val entries: List<StepEntry>,
862
+ )
863
+
864
+ @Serializable
865
+ public data class SleepEntryList(
866
+ public val entries: List<SleepEntry>,
867
+ )
868
+
869
+ @Serializable
870
+ public data class WorkoutEntryList(
871
+ public val entries: List<WorkoutEntry>,
872
+ )
873
+
874
+ @Serializable
875
+ public data class NutritionEntryList(
876
+ public val entries: List<NutritionEntry>,
877
+ )
878
+
879
+ public object TerraEventEnvelope {
880
+ public fun validate(value: TerraEvent) {
881
+ if ((value["at"] as? JsonPrimitive)?.isString != true) {
882
+ throw SerializationException("TerraEvent.at is not a string")
883
+ }
884
+ if ((value["event"] as? JsonPrimitive)?.isString != true) {
885
+ throw SerializationException("TerraEvent.event is not a string")
886
+ }
887
+ }
888
+ }
889
+
890
+ public object SupportReportEnvelope {
891
+ public fun validate(value: SupportReport) {
892
+ for (level0 in value.recentEvents) {
893
+ TerraEventEnvelope.validate(level0)
894
+ }
895
+ }
896
+ }
897
+