@bota.dev/react-native-app-sdk 2.0.0-beta.1 → 2.0.0-beta.10

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 (125) hide show
  1. package/README.md +104 -4
  2. package/android/build.gradle +1 -0
  3. package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidDevices.kt +22 -0
  4. package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidDiagnostics.kt +53 -0
  5. package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidLogs.kt +16 -0
  6. package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidRecordingUploads.kt +144 -14
  7. package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidRecordings.kt +150 -29
  8. package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKEncryptedUploadV2Materials.kt +27 -0
  9. package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKModule.kt +86 -3
  10. package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidDevicesTest.kt +35 -0
  11. package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidLogsTest.kt +33 -0
  12. package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidRecordingUploadsTest.kt +135 -6
  13. package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidRecordingsTest.kt +236 -10
  14. package/generated/codegen-contract.json +568 -8
  15. package/ios/BotaDeviceSDK.mm +114 -0
  16. package/ios/BotaDeviceSDKAppleBridge.swift +141 -4
  17. package/ios/BotaDeviceSDKAppleDevices.swift +11 -0
  18. package/ios/BotaDeviceSDKAppleDiagnostics.swift +40 -0
  19. package/ios/BotaDeviceSDKAppleLogs.swift +18 -0
  20. package/ios/BotaDeviceSDKAppleRecordingUploads.swift +138 -6
  21. package/ios/BotaDeviceSDKAppleRecordings.swift +113 -5
  22. package/ios/BotaDeviceSDKEncryptedUploadV2Materials.swift +40 -0
  23. package/lib/commonjs/BotaClient.js +16 -2
  24. package/lib/commonjs/BotaClient.js.map +1 -1
  25. package/lib/commonjs/ble/constants.js +5 -1
  26. package/lib/commonjs/ble/constants.js.map +1 -1
  27. package/lib/commonjs/ble/deviceDiagnostics.js +240 -0
  28. package/lib/commonjs/ble/deviceDiagnostics.js.map +1 -0
  29. package/lib/commonjs/client.js +183 -28
  30. package/lib/commonjs/client.js.map +1 -1
  31. package/lib/commonjs/clientPresence.js +26 -0
  32. package/lib/commonjs/clientPresence.js.map +1 -0
  33. package/lib/commonjs/index.js +29 -0
  34. package/lib/commonjs/index.js.map +1 -1
  35. package/lib/commonjs/managers/DeviceManager.js +23 -0
  36. package/lib/commonjs/managers/DeviceManager.js.map +1 -1
  37. package/lib/commonjs/managers/NativeUploadQueue.js +386 -0
  38. package/lib/commonjs/managers/NativeUploadQueue.js.map +1 -0
  39. package/lib/commonjs/managers/RecordingManager.js +83 -102
  40. package/lib/commonjs/managers/RecordingManager.js.map +1 -1
  41. package/lib/commonjs/managers/uploadRecoveryMetadata.js +83 -0
  42. package/lib/commonjs/managers/uploadRecoveryMetadata.js.map +1 -0
  43. package/lib/commonjs/sdkIdentity.js +10 -0
  44. package/lib/commonjs/sdkIdentity.js.map +1 -0
  45. package/lib/commonjs/specs/NativeBotaDeviceSDK.js.map +1 -1
  46. package/lib/commonjs/uploadRecovery.js +68 -0
  47. package/lib/commonjs/uploadRecovery.js.map +1 -0
  48. package/lib/module/BotaClient.js +16 -2
  49. package/lib/module/BotaClient.js.map +1 -1
  50. package/lib/module/ble/constants.js +4 -0
  51. package/lib/module/ble/constants.js.map +1 -1
  52. package/lib/module/ble/deviceDiagnostics.js +234 -0
  53. package/lib/module/ble/deviceDiagnostics.js.map +1 -0
  54. package/lib/module/client.js +183 -28
  55. package/lib/module/client.js.map +1 -1
  56. package/lib/module/clientPresence.js +22 -0
  57. package/lib/module/clientPresence.js.map +1 -0
  58. package/lib/module/index.js +4 -0
  59. package/lib/module/index.js.map +1 -1
  60. package/lib/module/managers/DeviceManager.js +23 -0
  61. package/lib/module/managers/DeviceManager.js.map +1 -1
  62. package/lib/module/managers/NativeUploadQueue.js +380 -0
  63. package/lib/module/managers/NativeUploadQueue.js.map +1 -0
  64. package/lib/module/managers/RecordingManager.js +83 -102
  65. package/lib/module/managers/RecordingManager.js.map +1 -1
  66. package/lib/module/managers/uploadRecoveryMetadata.js +77 -0
  67. package/lib/module/managers/uploadRecoveryMetadata.js.map +1 -0
  68. package/lib/module/sdkIdentity.js +6 -0
  69. package/lib/module/sdkIdentity.js.map +1 -0
  70. package/lib/module/specs/NativeBotaDeviceSDK.js.map +1 -1
  71. package/lib/module/uploadRecovery.js +64 -0
  72. package/lib/module/uploadRecovery.js.map +1 -0
  73. package/lib/typescript/BotaClient.d.ts +2 -0
  74. package/lib/typescript/BotaClient.d.ts.map +1 -1
  75. package/lib/typescript/ble/constants.d.ts +4 -0
  76. package/lib/typescript/ble/constants.d.ts.map +1 -1
  77. package/lib/typescript/ble/deviceDiagnostics.d.ts +10 -0
  78. package/lib/typescript/ble/deviceDiagnostics.d.ts.map +1 -0
  79. package/lib/typescript/client.d.ts +24 -3
  80. package/lib/typescript/client.d.ts.map +1 -1
  81. package/lib/typescript/clientPresence.d.ts +26 -0
  82. package/lib/typescript/clientPresence.d.ts.map +1 -0
  83. package/lib/typescript/index.d.ts +10 -4
  84. package/lib/typescript/index.d.ts.map +1 -1
  85. package/lib/typescript/managers/DeviceManager.d.ts +5 -1
  86. package/lib/typescript/managers/DeviceManager.d.ts.map +1 -1
  87. package/lib/typescript/managers/NativeUploadQueue.d.ts +41 -0
  88. package/lib/typescript/managers/NativeUploadQueue.d.ts.map +1 -0
  89. package/lib/typescript/managers/RecordingManager.d.ts +3 -8
  90. package/lib/typescript/managers/RecordingManager.d.ts.map +1 -1
  91. package/lib/typescript/managers/types.d.ts +6 -1
  92. package/lib/typescript/managers/types.d.ts.map +1 -1
  93. package/lib/typescript/managers/uploadRecoveryMetadata.d.ts +6 -0
  94. package/lib/typescript/managers/uploadRecoveryMetadata.d.ts.map +1 -0
  95. package/lib/typescript/models/Device.d.ts +42 -0
  96. package/lib/typescript/models/Device.d.ts.map +1 -1
  97. package/lib/typescript/models/Recording.d.ts +47 -0
  98. package/lib/typescript/models/Recording.d.ts.map +1 -1
  99. package/lib/typescript/models/Status.d.ts +5 -1
  100. package/lib/typescript/models/Status.d.ts.map +1 -1
  101. package/lib/typescript/sdkIdentity.d.ts +3 -0
  102. package/lib/typescript/sdkIdentity.d.ts.map +1 -0
  103. package/lib/typescript/specs/NativeBotaDeviceSDK.d.ts +70 -0
  104. package/lib/typescript/specs/NativeBotaDeviceSDK.d.ts.map +1 -1
  105. package/lib/typescript/uploadRecovery.d.ts +27 -0
  106. package/lib/typescript/uploadRecovery.d.ts.map +1 -0
  107. package/package.json +1 -1
  108. package/scripts/bota_device_sdk_spm_workaround.rb +26 -15
  109. package/src/BotaClient.ts +17 -3
  110. package/src/ble/constants.ts +5 -0
  111. package/src/ble/deviceDiagnostics.ts +238 -0
  112. package/src/client.ts +189 -18
  113. package/src/clientPresence.ts +33 -0
  114. package/src/index.ts +18 -0
  115. package/src/managers/DeviceManager.ts +23 -0
  116. package/src/managers/NativeUploadQueue.ts +330 -0
  117. package/src/managers/RecordingManager.ts +83 -116
  118. package/src/managers/types.ts +7 -1
  119. package/src/managers/uploadRecoveryMetadata.ts +59 -0
  120. package/src/models/Device.ts +43 -0
  121. package/src/models/Recording.ts +52 -0
  122. package/src/models/Status.ts +5 -1
  123. package/src/sdkIdentity.ts +3 -0
  124. package/src/specs/NativeBotaDeviceSDK.ts +66 -0
  125. package/src/uploadRecovery.ts +66 -0
@@ -569,6 +569,46 @@ RCT_EXPORT_MODULE(BotaDeviceSDK)
569
569
  }];
570
570
  }
571
571
 
572
+ - (void)listPendingRecordings:(JS::NativeBotaDeviceSDK::NativeConnectedDevice &)device
573
+ resolve:(RCTPromiseResolveBlock)resolve
574
+ reject:(RCTPromiseRejectBlock)reject
575
+ {
576
+ [[BotaDeviceSDKAppleBridge shared]
577
+ listPendingRecordingsWithID:device.id_()
578
+ serialNumber:device.serialNumber()
579
+ deviceType:device.deviceType()
580
+ firmwareVersion:device.firmwareVersion()
581
+ hardwareRevision:device.hardwareRevision()
582
+ isProvisioned:device.isProvisioned()
583
+ connectionState:device.connectionState()
584
+ mtu:device.mtu()
585
+ completion:^(NSArray<NSDictionary *> *_Nullable values, NSError *_Nullable error) {
586
+ if (error != nil) { BotaRejectEncryptedUploadV2Error(reject); return; }
587
+ resolve(values);
588
+ }];
589
+ }
590
+
591
+ - (void)cancelEncryptedRecordingV2:(NSString *)operationId
592
+ resolve:(RCTPromiseResolveBlock)resolve
593
+ reject:(RCTPromiseRejectBlock)reject
594
+ {
595
+ [[BotaDeviceSDKAppleBridge shared]
596
+ cancelEncryptedRecordingV2WithOperationID:operationId
597
+ completion:^(NSError *_Nullable error) {
598
+ if (error != nil) { BotaRejectEncryptedUploadV2Error(reject); return; }
599
+ resolve(nil);
600
+ }];
601
+ }
602
+
603
+ - (void)releaseEncryptedUploadV2Material:(NSString *)materialRegistrationId
604
+ resolve:(RCTPromiseResolveBlock)resolve
605
+ reject:(RCTPromiseRejectBlock)reject
606
+ {
607
+ [[BotaDeviceSDKAppleBridge shared]
608
+ releaseEncryptedUploadV2MaterialWithRegistrationID:materialRegistrationId
609
+ completion:^{ resolve(nil); }];
610
+ }
611
+
572
612
  - (void)syncRecording:(JS::NativeBotaDeviceSDK::NativeConnectedDevice &)device
573
613
  recording:(JS::NativeBotaDeviceSDK::NativeDeviceRecording &)recording
574
614
  sinkId:(NSString *)sinkId
@@ -626,6 +666,10 @@ RCT_EXPORT_MODULE(BotaDeviceSDK)
626
666
  generation:recording.generation()
627
667
  ciphertextLength:recording.ciphertextLength()
628
668
  ciphertextSHA256:recording.ciphertextSha256()
669
+ startedAtMs:recording.startedAtMs()
670
+ durationMs:recording.durationMs()
671
+ plaintextLength:recording.plaintextLength()
672
+ storageFormat:recording.storageFormat().has_value() ? @(recording.storageFormat().value()) : nil
629
673
  operationID:operationId
630
674
  onProfileRequest:^(NSDictionary *request) {
631
675
  [weakSelf emitOnEncryptedUploadV2ProfileRequested:request];
@@ -807,6 +851,7 @@ RCT_EXPORT_MODULE(BotaDeviceSDK)
807
851
  uploadRecordingFileWithTaskID:request.taskId()
808
852
  recordingID:request.recordingId()
809
853
  localPath:request.localPath()
854
+ fileSizeBytes:request.fileSizeBytes().has_value() ? @(request.fileSizeBytes().value()) : nil
810
855
  uploadURL:request.uploadUrl()
811
856
  uploadToken:request.uploadToken()
812
857
  completeURL:request.completeUrl()
@@ -826,6 +871,23 @@ RCT_EXPORT_MODULE(BotaDeviceSDK)
826
871
  }];
827
872
  }
828
873
 
874
+ - (void)releaseRecordingFile:(NSString *)taskId
875
+ localPath:(NSString *)localPath
876
+ resolve:(RCTPromiseResolveBlock)resolve
877
+ reject:(RCTPromiseRejectBlock)reject
878
+ {
879
+ [[BotaDeviceSDKAppleBridge shared]
880
+ releaseRecordingFileWithTaskID:taskId
881
+ localPath:localPath
882
+ completion:^(NSError *error) {
883
+ if (error != nil) {
884
+ BotaRejectAppleError(error, reject);
885
+ return;
886
+ }
887
+ resolve(nil);
888
+ }];
889
+ }
890
+
829
891
  - (void)cancelRecordingUpload:(NSString *)taskId
830
892
  resolve:(RCTPromiseResolveBlock)resolve
831
893
  reject:(__unused RCTPromiseRejectBlock)reject
@@ -966,6 +1028,46 @@ RCT_EXPORT_MODULE(BotaDeviceSDK)
966
1028
  }];
967
1029
  }
968
1030
 
1031
+ - (void)readDiagnosticEvents:(JS::NativeBotaDeviceSDK::NativeConnectedDevice &)device
1032
+ resolve:(RCTPromiseResolveBlock)resolve
1033
+ reject:(RCTPromiseRejectBlock)reject
1034
+ {
1035
+ [[BotaDeviceSDKAppleBridge shared]
1036
+ readDiagnosticEventsWithID:device.id_()
1037
+ serialNumber:device.serialNumber()
1038
+ deviceType:device.deviceType()
1039
+ firmwareVersion:device.firmwareVersion()
1040
+ hardwareRevision:device.hardwareRevision()
1041
+ isProvisioned:device.isProvisioned()
1042
+ connectionState:device.connectionState()
1043
+ mtu:device.mtu()
1044
+ completion:^(NSDictionary *_Nullable batch, NSError *_Nullable error) {
1045
+ if (error != nil) { BotaRejectAppleError(error, reject); return; }
1046
+ resolve(batch);
1047
+ }];
1048
+ }
1049
+
1050
+ - (void)acknowledgeDiagnosticEvents:(JS::NativeBotaDeviceSDK::NativeConnectedDevice &)device
1051
+ acceptedEventIds:(NSArray *)acceptedEventIds
1052
+ resolve:(RCTPromiseResolveBlock)resolve
1053
+ reject:(RCTPromiseRejectBlock)reject
1054
+ {
1055
+ [[BotaDeviceSDKAppleBridge shared]
1056
+ acknowledgeDiagnosticEventsWithID:device.id_()
1057
+ serialNumber:device.serialNumber()
1058
+ deviceType:device.deviceType()
1059
+ firmwareVersion:device.firmwareVersion()
1060
+ hardwareRevision:device.hardwareRevision()
1061
+ isProvisioned:device.isProvisioned()
1062
+ connectionState:device.connectionState()
1063
+ mtu:device.mtu()
1064
+ acceptedEventIds:acceptedEventIds
1065
+ completion:^(NSError *_Nullable error) {
1066
+ if (error != nil) { BotaRejectAppleError(error, reject); return; }
1067
+ resolve(nil);
1068
+ }];
1069
+ }
1070
+
969
1071
  - (void)stopDeviceLogs:(RCTPromiseResolveBlock)resolve
970
1072
  reject:(__unused RCTPromiseRejectBlock)reject
971
1073
  {
@@ -974,6 +1076,18 @@ RCT_EXPORT_MODULE(BotaDeviceSDK)
974
1076
  }];
975
1077
  }
976
1078
 
1079
+ - (void)nextClientPresence:(NSString *)deviceId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject
1080
+ {
1081
+ [[BotaDeviceSDKAppleBridge shared] nextClientPresenceWithDeviceID:deviceId
1082
+ completion:^(NSDictionary *_Nullable report, NSError *_Nullable error) {
1083
+ if (error != nil) {
1084
+ reject(@"client_presence_failed", error.localizedDescription, error);
1085
+ } else {
1086
+ resolve(report);
1087
+ }
1088
+ }];
1089
+ }
1090
+
977
1091
  - (void)readStatus:(RCTPromiseResolveBlock)resolve
978
1092
  reject:(RCTPromiseRejectBlock)reject
979
1093
  {
@@ -801,6 +801,48 @@ public final class BotaDeviceSDKAppleBridge: NSObject, @unchecked Sendable {
801
801
  }
802
802
  }
803
803
 
804
+ @objc(listPendingRecordingsWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:completion:)
805
+ public func listPendingRecordings(
806
+ id: String, serialNumber: String, deviceType: String, firmwareVersion: String,
807
+ hardwareRevision: String?, isProvisioned: Bool, connectionState: String, mtu: Double,
808
+ completion: @escaping @Sendable ([[String: Any]]?, NSError?) -> Void
809
+ ) {
810
+ Task {
811
+ do {
812
+ let values = try await recordings.listPendingRecordings(Self.connectedDevice(
813
+ id: id, serialNumber: serialNumber, deviceType: deviceType,
814
+ firmwareVersion: firmwareVersion, hardwareRevision: hardwareRevision,
815
+ isProvisioned: isProvisioned, connectionState: connectionState, mtu: mtu
816
+ ))
817
+ completion(values.map { value in
818
+ switch value {
819
+ case let .legacy(recording):
820
+ ["profile": "legacy", "legacy": Self.recording(recording)]
821
+ case let .encryptedV2(recording):
822
+ ["profile": "encrypted_upload_v2", "encrypted": BotaDeviceSDKAppleRecordings.encryptedUploadV2Recording(recording)]
823
+ }
824
+ }, nil)
825
+ } catch { completion(nil, error as NSError) }
826
+ }
827
+ }
828
+
829
+ @objc(cancelEncryptedRecordingV2WithOperationID:completion:)
830
+ public func cancelEncryptedRecordingV2(
831
+ operationID: String, completion: @escaping @Sendable (NSError?) -> Void
832
+ ) {
833
+ Task {
834
+ do { try await recordings.cancelEncryptedRecordingV2(operationID); completion(nil) }
835
+ catch { completion(error as NSError) }
836
+ }
837
+ }
838
+
839
+ @objc(releaseEncryptedUploadV2MaterialWithRegistrationID:completion:)
840
+ public func releaseEncryptedUploadV2Material(
841
+ registrationID: String, completion: @escaping @Sendable () -> Void
842
+ ) {
843
+ Task { await BotaDeviceSDKEncryptedUploadV2Materials.release(registrationID); completion() }
844
+ }
845
+
804
846
  @objc(syncRecordingWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:recordingUUID:startedAtMilliseconds:durationMilliseconds:fileSize:codec:isEncrypted:sinkID:onProgress:completion:)
805
847
  public func syncRecording(
806
848
  id: String,
@@ -855,6 +897,7 @@ public final class BotaDeviceSDKAppleBridge: NSObject, @unchecked Sendable {
855
897
  var value: [String: Any] = [
856
898
  "localPath": result.localPath,
857
899
  "e2eEncrypted": result.isE2EEncrypted,
900
+ "fileSizeBytes": result.fileSizeBytes,
858
901
  ]
859
902
  if let contentSHA256Hex = result.contentSHA256Hex {
860
903
  value["contentSha256"] = contentSHA256Hex
@@ -866,7 +909,7 @@ public final class BotaDeviceSDKAppleBridge: NSObject, @unchecked Sendable {
866
909
  }
867
910
  }
868
911
 
869
- @objc(syncEncryptedRecordingV2WithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:recordingUUID:generation:ciphertextLength:ciphertextSHA256:operationID:onProfileRequest:onProgress:completion:)
912
+ @objc(syncEncryptedRecordingV2WithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:recordingUUID:generation:ciphertextLength:ciphertextSHA256:startedAtMs:durationMs:plaintextLength:storageFormat:operationID:onProfileRequest:onProgress:completion:)
870
913
  public func syncEncryptedRecordingV2(
871
914
  id: String,
872
915
  serialNumber: String,
@@ -880,6 +923,10 @@ public final class BotaDeviceSDKAppleBridge: NSObject, @unchecked Sendable {
880
923
  generation: Double,
881
924
  ciphertextLength: String,
882
925
  ciphertextSHA256: String,
926
+ startedAtMs: String?,
927
+ durationMs: String?,
928
+ plaintextLength: String?,
929
+ storageFormat: NSNumber?,
883
930
  operationID: String,
884
931
  onProfileRequest: @escaping @Sendable ([String: Any]) -> Void,
885
932
  onProgress: @escaping @Sendable ([String: Any]) -> Void,
@@ -895,6 +942,9 @@ public final class BotaDeviceSDKAppleBridge: NSObject, @unchecked Sendable {
895
942
  throw BotaDeviceSDKAppleBridgeInputError.invalidUnsignedInteger
896
943
  }
897
944
  let digest = try Self.sha256Data(ciphertextSHA256)
945
+ guard let format = UInt8(exactly: try Self.unsignedInteger(storageFormat?.doubleValue ?? 3)) else {
946
+ throw BotaDeviceSDKAppleBridgeInputError.invalidUnsignedInteger
947
+ }
898
948
  try await recordings.syncEncryptedRecordingV2(
899
949
  Self.connectedDevice(
900
950
  id: id,
@@ -910,7 +960,11 @@ public final class BotaDeviceSDKAppleBridge: NSObject, @unchecked Sendable {
910
960
  uuid: recordingUUID,
911
961
  generation: generationValue,
912
962
  ciphertextLength: length,
913
- ciphertextSHA256: digest
963
+ ciphertextSHA256: digest,
964
+ startedAtMs: try Self.catalogDecimal(startedAtMs),
965
+ durationMs: try Self.catalogDecimal(durationMs),
966
+ plaintextLength: try Self.catalogDecimal(plaintextLength),
967
+ storageFormat: format
914
968
  ),
915
969
  operationID: operationID,
916
970
  onProfileRequest: onProfileRequest,
@@ -923,6 +977,14 @@ public final class BotaDeviceSDKAppleBridge: NSObject, @unchecked Sendable {
923
977
  }
924
978
  }
925
979
 
980
+ private static func catalogDecimal(_ text: String?) throws -> UInt64 {
981
+ guard let text else { return 0 }
982
+ guard let value = UInt64(text), String(value) == text else {
983
+ throw BotaDeviceSDKAppleBridgeInputError.invalidUnsignedInteger
984
+ }
985
+ return value
986
+ }
987
+
926
988
  @objc(resolveEncryptedUploadV2ProfileWithRequestID:profile:uploadSessionID:ownerRevision:securityPolicy:materialRegistrationID:completion:)
927
989
  public func resolveEncryptedUploadV2Profile(
928
990
  requestID: String,
@@ -1126,11 +1188,12 @@ public final class BotaDeviceSDKAppleBridge: NSObject, @unchecked Sendable {
1126
1188
  }
1127
1189
  }
1128
1190
 
1129
- @objc(uploadRecordingFileWithTaskID:recordingID:localPath:uploadURL:uploadToken:completeURL:contentType:contentSHA256:relayURL:relayBearerToken:onProgress:completion:)
1191
+ @objc(uploadRecordingFileWithTaskID:recordingID:localPath:fileSizeBytes:uploadURL:uploadToken:completeURL:contentType:contentSHA256:relayURL:relayBearerToken:onProgress:completion:)
1130
1192
  public func uploadRecordingFile(
1131
1193
  taskID: String,
1132
1194
  recordingID: String,
1133
1195
  localPath: String,
1196
+ fileSizeBytes: NSNumber?,
1134
1197
  uploadURL: String,
1135
1198
  uploadToken: String?,
1136
1199
  completeURL: String?,
@@ -1143,6 +1206,11 @@ public final class BotaDeviceSDKAppleBridge: NSObject, @unchecked Sendable {
1143
1206
  ) {
1144
1207
  Task {
1145
1208
  do {
1209
+ let expectedSize = try fileSizeBytes.map { value -> Int64 in
1210
+ let integer = try Self.unsignedInteger(value.doubleValue)
1211
+ guard integer <= UInt64(Int64.max) else { throw CocoaError(.fileReadCorruptFile) }
1212
+ return Int64(integer)
1213
+ }
1146
1214
  try await recordingUploads.upload(.init(
1147
1215
  taskID: taskID,
1148
1216
  recordingID: recordingID,
@@ -1153,7 +1221,8 @@ public final class BotaDeviceSDKAppleBridge: NSObject, @unchecked Sendable {
1153
1221
  contentType: contentType,
1154
1222
  contentSHA256: contentSHA256,
1155
1223
  relayURL: relayURL,
1156
- relayBearerToken: relayBearerToken
1224
+ relayBearerToken: relayBearerToken,
1225
+ fileSizeBytes: expectedSize
1157
1226
  )) { progress in
1158
1227
  onProgress([
1159
1228
  "taskId": progress.taskID,
@@ -1168,6 +1237,22 @@ public final class BotaDeviceSDKAppleBridge: NSObject, @unchecked Sendable {
1168
1237
  }
1169
1238
  }
1170
1239
 
1240
+ @objc(releaseRecordingFileWithTaskID:localPath:completion:)
1241
+ public func releaseRecordingFile(
1242
+ taskID: String,
1243
+ localPath: String,
1244
+ completion: @escaping @Sendable (NSError?) -> Void
1245
+ ) {
1246
+ Task {
1247
+ do {
1248
+ try await recordingUploads.release(taskID: taskID, localPath: localPath)
1249
+ completion(nil)
1250
+ } catch {
1251
+ completion(error as NSError)
1252
+ }
1253
+ }
1254
+ }
1255
+
1171
1256
  @objc(cancelRecordingUploadWithTaskID:completion:)
1172
1257
  public func cancelRecordingUpload(
1173
1258
  taskID: String,
@@ -1359,6 +1444,43 @@ public final class BotaDeviceSDKAppleBridge: NSObject, @unchecked Sendable {
1359
1444
  }
1360
1445
  }
1361
1446
 
1447
+ @objc(readDiagnosticEventsWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:completion:)
1448
+ public func readDiagnosticEvents(
1449
+ id: String, serialNumber: String, deviceType: String, firmwareVersion: String,
1450
+ hardwareRevision: String?, isProvisioned: Bool, connectionState: String, mtu: Double,
1451
+ completion: @escaping @Sendable ([String: Any]?, NSError?) -> Void
1452
+ ) {
1453
+ Task {
1454
+ do {
1455
+ let device = try Self.connectedDevice(
1456
+ id: id, serialNumber: serialNumber, deviceType: deviceType,
1457
+ firmwareVersion: firmwareVersion, hardwareRevision: hardwareRevision,
1458
+ isProvisioned: isProvisioned, connectionState: connectionState, mtu: mtu
1459
+ )
1460
+ completion(diagnosticBatchPayload(try await logs.readDiagnosticEvents(device)), nil)
1461
+ } catch { completion(nil, error as NSError) }
1462
+ }
1463
+ }
1464
+
1465
+ @objc(acknowledgeDiagnosticEventsWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:acceptedEventIds:completion:)
1466
+ public func acknowledgeDiagnosticEvents(
1467
+ id: String, serialNumber: String, deviceType: String, firmwareVersion: String,
1468
+ hardwareRevision: String?, isProvisioned: Bool, connectionState: String, mtu: Double,
1469
+ acceptedEventIds: [String], completion: @escaping @Sendable (NSError?) -> Void
1470
+ ) {
1471
+ Task {
1472
+ do {
1473
+ let device = try Self.connectedDevice(
1474
+ id: id, serialNumber: serialNumber, deviceType: deviceType,
1475
+ firmwareVersion: firmwareVersion, hardwareRevision: hardwareRevision,
1476
+ isProvisioned: isProvisioned, connectionState: connectionState, mtu: mtu
1477
+ )
1478
+ try await logs.acknowledgeDiagnosticEvents(device, acceptedEventIds: acceptedEventIds)
1479
+ completion(nil)
1480
+ } catch { completion(error as NSError) }
1481
+ }
1482
+ }
1483
+
1362
1484
  @objc(stopDeviceLogsWithCompletion:)
1363
1485
  public func stopDeviceLogs(completion: @escaping @Sendable () -> Void) {
1364
1486
  Task {
@@ -1367,6 +1489,21 @@ public final class BotaDeviceSDKAppleBridge: NSObject, @unchecked Sendable {
1367
1489
  }
1368
1490
  }
1369
1491
 
1492
+ @objc(nextClientPresenceWithDeviceID:completion:)
1493
+ public func nextClientPresence(deviceID: String, completion: @escaping @Sendable (NSDictionary?, NSError?) -> Void) {
1494
+ Task {
1495
+ do {
1496
+ guard let report = try await devices.nextClientPresence(deviceID: deviceID) else {
1497
+ completion(nil, nil)
1498
+ return
1499
+ }
1500
+ completion(["schemaVersion": report.schemaVersion, "sessionId": report.sessionID,
1501
+ "sequence": report.sequence, "platform": report.platform,
1502
+ "sdkPackage": report.sdkPackage, "sdkVersion": report.sdkVersion], nil)
1503
+ } catch { completion(nil, error as NSError) }
1504
+ }
1505
+ }
1506
+
1370
1507
  @objc(readStatusWithCompletion:)
1371
1508
  public func readStatus(
1372
1509
  completion: @escaping @Sendable ([String: Any]?, NSError?) -> Void
@@ -14,14 +14,17 @@ protocol BotaDeviceSDKAppleDeviceClient: Sendable {
14
14
  ) async throws -> ConnectedDevice
15
15
  func disconnect() async throws
16
16
  func readStatus() async throws -> DeviceStatus
17
+ func nextClientPresence(deviceID: String) async throws -> SDKClientContext?
17
18
  func statusUpdates() async throws -> AsyncThrowingStream<DeviceStatus, Error>
18
19
  }
19
20
 
20
21
  struct BotaDeviceSDKSharedAppleDeviceClient: BotaDeviceSDKAppleDeviceClient {
21
22
  private let devices: DeviceManager
23
+ private let presence: ClientPresence
22
24
 
23
25
  init(client: BotaDeviceClient = .shared) {
24
26
  devices = client.devices
27
+ presence = client.clientPresence
25
28
  }
26
29
 
27
30
  func startScan(
@@ -57,6 +60,10 @@ struct BotaDeviceSDKSharedAppleDeviceClient: BotaDeviceSDKAppleDeviceClient {
57
60
  try await devices.readStatus()
58
61
  }
59
62
 
63
+ func nextClientPresence(deviceID: String) async throws -> SDKClientContext? {
64
+ try await presence.nextReport(deviceID: deviceID)
65
+ }
66
+
60
67
  func statusUpdates() async throws -> AsyncThrowingStream<DeviceStatus, Error> {
61
68
  try await devices.statusUpdates()
62
69
  }
@@ -142,6 +149,10 @@ actor BotaDeviceSDKAppleDevices {
142
149
  try await client.readStatus()
143
150
  }
144
151
 
152
+ func nextClientPresence(deviceID: String) async throws -> SDKClientContext? {
153
+ try await client.nextClientPresence(deviceID: deviceID)
154
+ }
155
+
145
156
  func startStatusUpdates(
146
157
  onStatus: @escaping @Sendable (DeviceStatus) -> Void,
147
158
  onError: @escaping @Sendable (Error) -> Void = { _ in }
@@ -0,0 +1,40 @@
1
+ import BotaAppSDK
2
+ import Foundation
3
+
4
+ func diagnosticBatchPayload(_ batch: DeviceDiagnosticsBatch) -> [String: Any] {
5
+ ["schema_version": batch.schemaVersion, "events": batch.events.map(diagnosticEventPayload)]
6
+ }
7
+
8
+ private func diagnosticEventPayload(_ event: DeviceDiagnosticEvent) -> [String: Any] {
9
+ var payload: [String: Any] = [
10
+ "event_id": event.eventId, "event_type": event.eventType,
11
+ "reason_code": event.reasonCode, "uptime_ms": event.uptimeMs,
12
+ "signature": event.signature, "firmware_build_id": event.firmwareBuildId,
13
+ "subsystem": event.subsystem, "state_before_event": event.stateBeforeEvent,
14
+ ]
15
+ if let report = event.report {
16
+ var execution: [String: Any] = ["pc_trace": report.execution.pcTrace]
17
+ execution["task"] = report.execution.task
18
+ execution["reti"] = report.execution.reti
19
+ execution["rets"] = report.execution.rets
20
+ var value: [String: Any] = [
21
+ "fault": [
22
+ "cpu_id": report.fault.cpuId, "cpu_emu": report.fault.cpuEmu,
23
+ "core_emu": report.fault.coreEmu, "hsb_emu": report.fault.hsbEmu,
24
+ "audio_emu": report.fault.audioEmu, "wireless_emu": report.fault.wirelessEmu,
25
+ ],
26
+ "execution": execution,
27
+ ]
28
+ if let runtime = report.runtime {
29
+ var fields: [String: Any] = [:]
30
+ fields["heap_free_bytes"] = runtime.heapFreeBytes
31
+ fields["task_stack_remaining_bytes"] = runtime.taskStackRemainingBytes
32
+ value["runtime"] = fields
33
+ }
34
+ if let breadcrumbs = report.breadcrumbs {
35
+ value["breadcrumbs"] = breadcrumbs.map { ["delta_ms": $0.deltaMs, "code": $0.code, "arg0": $0.arg0] as [String: Any] }
36
+ }
37
+ payload["report"] = value
38
+ }
39
+ return payload
40
+ }
@@ -2,6 +2,8 @@ import BotaAppSDK
2
2
  import Foundation
3
3
 
4
4
  protocol BotaDeviceSDKAppleLogClient: Sendable {
5
+ func readDiagnosticEvents(_ device: ConnectedDevice) async throws -> DeviceDiagnosticsBatch
6
+ func acknowledgeDiagnosticEvents(_ device: ConnectedDevice, acceptedEventIds: [String]) async throws
5
7
  func streamLogs(
6
8
  _ device: ConnectedDevice
7
9
  ) async throws -> AsyncThrowingStream<DeviceLogLine, Error>
@@ -24,6 +26,14 @@ struct BotaDeviceSDKSharedAppleLogClient: BotaDeviceSDKAppleLogClient {
24
26
  func stop() async throws {
25
27
  try await logs.stop()
26
28
  }
29
+
30
+ func readDiagnosticEvents(_ device: ConnectedDevice) async throws -> DeviceDiagnosticsBatch {
31
+ try await logs.readDiagnosticEvents(device)
32
+ }
33
+
34
+ func acknowledgeDiagnosticEvents(_ device: ConnectedDevice, acceptedEventIds: [String]) async throws {
35
+ try await logs.acknowledgeDiagnosticEvents(device, acceptedEventIds: acceptedEventIds)
36
+ }
27
37
  }
28
38
 
29
39
  actor BotaDeviceSDKAppleLogs {
@@ -39,6 +49,14 @@ actor BotaDeviceSDKAppleLogs {
39
49
  self.client = client
40
50
  }
41
51
 
52
+ func readDiagnosticEvents(_ device: ConnectedDevice) async throws -> DeviceDiagnosticsBatch {
53
+ try await client.readDiagnosticEvents(device)
54
+ }
55
+
56
+ func acknowledgeDiagnosticEvents(_ device: ConnectedDevice, acceptedEventIds: [String]) async throws {
57
+ try await client.acknowledgeDiagnosticEvents(device, acceptedEventIds: acceptedEventIds)
58
+ }
59
+
42
60
  func start(
43
61
  _ device: ConnectedDevice,
44
62
  onLine: @escaping @Sendable (DeviceLogLine) -> Void,