@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.
- package/README.md +104 -4
- package/android/build.gradle +1 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidDevices.kt +22 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidDiagnostics.kt +53 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidLogs.kt +16 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidRecordingUploads.kt +144 -14
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidRecordings.kt +150 -29
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKEncryptedUploadV2Materials.kt +27 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKModule.kt +86 -3
- package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidDevicesTest.kt +35 -0
- package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidLogsTest.kt +33 -0
- package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidRecordingUploadsTest.kt +135 -6
- package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidRecordingsTest.kt +236 -10
- package/generated/codegen-contract.json +568 -8
- package/ios/BotaDeviceSDK.mm +114 -0
- package/ios/BotaDeviceSDKAppleBridge.swift +141 -4
- package/ios/BotaDeviceSDKAppleDevices.swift +11 -0
- package/ios/BotaDeviceSDKAppleDiagnostics.swift +40 -0
- package/ios/BotaDeviceSDKAppleLogs.swift +18 -0
- package/ios/BotaDeviceSDKAppleRecordingUploads.swift +138 -6
- package/ios/BotaDeviceSDKAppleRecordings.swift +113 -5
- package/ios/BotaDeviceSDKEncryptedUploadV2Materials.swift +40 -0
- package/lib/commonjs/BotaClient.js +16 -2
- package/lib/commonjs/BotaClient.js.map +1 -1
- package/lib/commonjs/ble/constants.js +5 -1
- package/lib/commonjs/ble/constants.js.map +1 -1
- package/lib/commonjs/ble/deviceDiagnostics.js +240 -0
- package/lib/commonjs/ble/deviceDiagnostics.js.map +1 -0
- package/lib/commonjs/client.js +183 -28
- package/lib/commonjs/client.js.map +1 -1
- package/lib/commonjs/clientPresence.js +26 -0
- package/lib/commonjs/clientPresence.js.map +1 -0
- package/lib/commonjs/index.js +29 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/managers/DeviceManager.js +23 -0
- package/lib/commonjs/managers/DeviceManager.js.map +1 -1
- package/lib/commonjs/managers/NativeUploadQueue.js +386 -0
- package/lib/commonjs/managers/NativeUploadQueue.js.map +1 -0
- package/lib/commonjs/managers/RecordingManager.js +83 -102
- package/lib/commonjs/managers/RecordingManager.js.map +1 -1
- package/lib/commonjs/managers/uploadRecoveryMetadata.js +83 -0
- package/lib/commonjs/managers/uploadRecoveryMetadata.js.map +1 -0
- package/lib/commonjs/sdkIdentity.js +10 -0
- package/lib/commonjs/sdkIdentity.js.map +1 -0
- package/lib/commonjs/specs/NativeBotaDeviceSDK.js.map +1 -1
- package/lib/commonjs/uploadRecovery.js +68 -0
- package/lib/commonjs/uploadRecovery.js.map +1 -0
- package/lib/module/BotaClient.js +16 -2
- package/lib/module/BotaClient.js.map +1 -1
- package/lib/module/ble/constants.js +4 -0
- package/lib/module/ble/constants.js.map +1 -1
- package/lib/module/ble/deviceDiagnostics.js +234 -0
- package/lib/module/ble/deviceDiagnostics.js.map +1 -0
- package/lib/module/client.js +183 -28
- package/lib/module/client.js.map +1 -1
- package/lib/module/clientPresence.js +22 -0
- package/lib/module/clientPresence.js.map +1 -0
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/managers/DeviceManager.js +23 -0
- package/lib/module/managers/DeviceManager.js.map +1 -1
- package/lib/module/managers/NativeUploadQueue.js +380 -0
- package/lib/module/managers/NativeUploadQueue.js.map +1 -0
- package/lib/module/managers/RecordingManager.js +83 -102
- package/lib/module/managers/RecordingManager.js.map +1 -1
- package/lib/module/managers/uploadRecoveryMetadata.js +77 -0
- package/lib/module/managers/uploadRecoveryMetadata.js.map +1 -0
- package/lib/module/sdkIdentity.js +6 -0
- package/lib/module/sdkIdentity.js.map +1 -0
- package/lib/module/specs/NativeBotaDeviceSDK.js.map +1 -1
- package/lib/module/uploadRecovery.js +64 -0
- package/lib/module/uploadRecovery.js.map +1 -0
- package/lib/typescript/BotaClient.d.ts +2 -0
- package/lib/typescript/BotaClient.d.ts.map +1 -1
- package/lib/typescript/ble/constants.d.ts +4 -0
- package/lib/typescript/ble/constants.d.ts.map +1 -1
- package/lib/typescript/ble/deviceDiagnostics.d.ts +10 -0
- package/lib/typescript/ble/deviceDiagnostics.d.ts.map +1 -0
- package/lib/typescript/client.d.ts +24 -3
- package/lib/typescript/client.d.ts.map +1 -1
- package/lib/typescript/clientPresence.d.ts +26 -0
- package/lib/typescript/clientPresence.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +10 -4
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/managers/DeviceManager.d.ts +5 -1
- package/lib/typescript/managers/DeviceManager.d.ts.map +1 -1
- package/lib/typescript/managers/NativeUploadQueue.d.ts +41 -0
- package/lib/typescript/managers/NativeUploadQueue.d.ts.map +1 -0
- package/lib/typescript/managers/RecordingManager.d.ts +3 -8
- package/lib/typescript/managers/RecordingManager.d.ts.map +1 -1
- package/lib/typescript/managers/types.d.ts +6 -1
- package/lib/typescript/managers/types.d.ts.map +1 -1
- package/lib/typescript/managers/uploadRecoveryMetadata.d.ts +6 -0
- package/lib/typescript/managers/uploadRecoveryMetadata.d.ts.map +1 -0
- package/lib/typescript/models/Device.d.ts +42 -0
- package/lib/typescript/models/Device.d.ts.map +1 -1
- package/lib/typescript/models/Recording.d.ts +47 -0
- package/lib/typescript/models/Recording.d.ts.map +1 -1
- package/lib/typescript/models/Status.d.ts +5 -1
- package/lib/typescript/models/Status.d.ts.map +1 -1
- package/lib/typescript/sdkIdentity.d.ts +3 -0
- package/lib/typescript/sdkIdentity.d.ts.map +1 -0
- package/lib/typescript/specs/NativeBotaDeviceSDK.d.ts +70 -0
- package/lib/typescript/specs/NativeBotaDeviceSDK.d.ts.map +1 -1
- package/lib/typescript/uploadRecovery.d.ts +27 -0
- package/lib/typescript/uploadRecovery.d.ts.map +1 -0
- package/package.json +1 -1
- package/scripts/bota_device_sdk_spm_workaround.rb +26 -15
- package/src/BotaClient.ts +17 -3
- package/src/ble/constants.ts +5 -0
- package/src/ble/deviceDiagnostics.ts +238 -0
- package/src/client.ts +189 -18
- package/src/clientPresence.ts +33 -0
- package/src/index.ts +18 -0
- package/src/managers/DeviceManager.ts +23 -0
- package/src/managers/NativeUploadQueue.ts +330 -0
- package/src/managers/RecordingManager.ts +83 -116
- package/src/managers/types.ts +7 -1
- package/src/managers/uploadRecoveryMetadata.ts +59 -0
- package/src/models/Device.ts +43 -0
- package/src/models/Recording.ts +52 -0
- package/src/models/Status.ts +5 -1
- package/src/sdkIdentity.ts +3 -0
- package/src/specs/NativeBotaDeviceSDK.ts +66 -0
- package/src/uploadRecovery.ts +66 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Foundation
|
|
2
|
+
import Darwin
|
|
2
3
|
|
|
3
4
|
struct BotaDeviceSDKAppleRecordingUploadRequest: Sendable {
|
|
4
5
|
let taskID: String
|
|
@@ -11,6 +12,7 @@ struct BotaDeviceSDKAppleRecordingUploadRequest: Sendable {
|
|
|
11
12
|
let contentSHA256: String?
|
|
12
13
|
let relayURL: String?
|
|
13
14
|
let relayBearerToken: String?
|
|
15
|
+
var fileSizeBytes: Int64? = nil
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
struct BotaDeviceSDKAppleRecordingUploadProgress: Equatable, Sendable {
|
|
@@ -25,6 +27,9 @@ actor BotaDeviceSDKAppleRecordingUploads {
|
|
|
25
27
|
case invalidResponse
|
|
26
28
|
case missingFile(String)
|
|
27
29
|
case requestFailed(Int)
|
|
30
|
+
case invalidJournal
|
|
31
|
+
case notCompleted
|
|
32
|
+
case fileSizeChanged
|
|
28
33
|
|
|
29
34
|
var errorDescription: String? {
|
|
30
35
|
switch self {
|
|
@@ -36,24 +41,42 @@ actor BotaDeviceSDKAppleRecordingUploads {
|
|
|
36
41
|
"recording file does not exist: \(path)"
|
|
37
42
|
case let .requestFailed(statusCode):
|
|
38
43
|
"recording upload failed with HTTP \(statusCode)"
|
|
44
|
+
case .invalidJournal:
|
|
45
|
+
"invalid upload recovery journal"
|
|
46
|
+
case .notCompleted:
|
|
47
|
+
"recording release requires exact durable completion"
|
|
48
|
+
case .fileSizeChanged:
|
|
49
|
+
"stored recording length changed"
|
|
39
50
|
}
|
|
40
51
|
}
|
|
41
52
|
}
|
|
42
53
|
|
|
43
54
|
private var queueFile: URL
|
|
55
|
+
private var recordingDirectory: URL
|
|
44
56
|
private let session: URLSession
|
|
57
|
+
private let syncDirectory: @Sendable (URL) throws -> Void
|
|
58
|
+
private var journalWriteUncertain = false
|
|
45
59
|
private var operations: [String: Task<Void, Error>] = [:]
|
|
60
|
+
private var cancelledAttempts = Set<String>()
|
|
46
61
|
|
|
47
62
|
init(
|
|
48
63
|
queueFile: URL = BotaDeviceSDKAppleRecordingUploads.defaultQueueFile(),
|
|
49
|
-
session: URLSession = .shared
|
|
64
|
+
session: URLSession = .shared,
|
|
65
|
+
syncDirectory: @escaping @Sendable (URL) throws -> Void = BotaDeviceSDKAppleRecordingUploads.synchronizeDirectory
|
|
50
66
|
) {
|
|
51
67
|
self.queueFile = queueFile
|
|
68
|
+
recordingDirectory = queueFile.deletingLastPathComponent().appendingPathComponent("Recordings", isDirectory: true)
|
|
52
69
|
self.session = session
|
|
70
|
+
self.syncDirectory = syncDirectory
|
|
53
71
|
}
|
|
54
72
|
|
|
55
73
|
func configure(applicationSupportDirectory: URL?) {
|
|
56
|
-
guard let applicationSupportDirectory else {
|
|
74
|
+
guard let applicationSupportDirectory else {
|
|
75
|
+
queueFile = Self.defaultQueueFile()
|
|
76
|
+
recordingDirectory = queueFile.deletingLastPathComponent().appendingPathComponent("Recordings", isDirectory: true)
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
recordingDirectory = applicationSupportDirectory.appendingPathComponent("Recordings", isDirectory: true)
|
|
57
80
|
queueFile = applicationSupportDirectory
|
|
58
81
|
.appendingPathComponent("BotaDeviceSDK", isDirectory: true)
|
|
59
82
|
.appendingPathComponent("compat-upload-queue.json")
|
|
@@ -63,6 +86,8 @@ actor BotaDeviceSDKAppleRecordingUploads {
|
|
|
63
86
|
_ upload: BotaDeviceSDKAppleRecordingUploadRequest,
|
|
64
87
|
onProgress: @escaping @Sendable (BotaDeviceSDKAppleRecordingUploadProgress) -> Void
|
|
65
88
|
) async throws {
|
|
89
|
+
guard !cancelledAttempts.contains(upload.taskID) else { throw CancellationError() }
|
|
90
|
+
try requireOwnedFile(upload.localPath)
|
|
66
91
|
guard operations[upload.taskID] == nil else {
|
|
67
92
|
throw UploadError.alreadyActive(upload.taskID)
|
|
68
93
|
}
|
|
@@ -72,16 +97,21 @@ actor BotaDeviceSDKAppleRecordingUploads {
|
|
|
72
97
|
}
|
|
73
98
|
operations[upload.taskID] = operation
|
|
74
99
|
defer { operations.removeValue(forKey: upload.taskID) }
|
|
75
|
-
try await
|
|
100
|
+
try await withTaskCancellationHandler {
|
|
101
|
+
try await operation.value
|
|
102
|
+
} onCancel: {
|
|
103
|
+
operation.cancel()
|
|
104
|
+
}
|
|
76
105
|
}
|
|
77
106
|
|
|
78
107
|
func cancel(taskID: String) {
|
|
79
|
-
|
|
108
|
+
cancelledAttempts.insert(taskID)
|
|
109
|
+
operations[taskID]?.cancel()
|
|
80
110
|
}
|
|
81
111
|
|
|
82
112
|
func cancelAll() {
|
|
113
|
+
cancelledAttempts.formUnion(operations.keys)
|
|
83
114
|
operations.values.forEach { $0.cancel() }
|
|
84
|
-
operations.removeAll()
|
|
85
115
|
}
|
|
86
116
|
|
|
87
117
|
func loadQueue() throws -> String {
|
|
@@ -90,11 +120,103 @@ actor BotaDeviceSDKAppleRecordingUploads {
|
|
|
90
120
|
}
|
|
91
121
|
|
|
92
122
|
func saveQueue(_ serializedTasks: String) throws {
|
|
123
|
+
try writeQueue(Self.serialize(Self.metadata(serializedTasks)))
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
func release(taskID: String, localPath: String) async throws {
|
|
127
|
+
guard !journalWriteUncertain else { throw UploadError.notCompleted }
|
|
128
|
+
let tasks = try Self.metadata(String(contentsOf: queueFile, encoding: .utf8))
|
|
129
|
+
guard tasks.contains(where: {
|
|
130
|
+
$0["id"] as? String == taskID && $0["localPath"] as? String == localPath &&
|
|
131
|
+
$0["status"] as? String == "completed"
|
|
132
|
+
}) else { throw UploadError.notCompleted }
|
|
133
|
+
try removeCompletedFile(localPath)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private func removeCompletedFile(_ path: String) throws {
|
|
137
|
+
try requireOwnedFile(path)
|
|
138
|
+
if FileManager.default.fileExists(atPath: path) {
|
|
139
|
+
let attributes = try FileManager.default.attributesOfItem(atPath: path)
|
|
140
|
+
guard attributes[.type] as? FileAttributeType == .typeRegular else { throw UploadError.notCompleted }
|
|
141
|
+
try FileManager.default.removeItem(atPath: path)
|
|
142
|
+
}
|
|
143
|
+
// A missing file may be an earlier unlink whose directory sync failed.
|
|
144
|
+
try syncDirectory(URL(fileURLWithPath: path).deletingLastPathComponent())
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private func requireOwnedFile(_ path: String) throws {
|
|
148
|
+
let file = URL(fileURLWithPath: path)
|
|
149
|
+
guard path.hasPrefix("/"), file.pathExtension == "recording",
|
|
150
|
+
UUID(uuidString: file.deletingPathExtension().lastPathComponent) != nil,
|
|
151
|
+
file.resolvingSymlinksInPath().deletingLastPathComponent().standardizedFileURL == recordingDirectory.resolvingSymlinksInPath().standardizedFileURL
|
|
152
|
+
else { throw UploadError.notCompleted }
|
|
153
|
+
if FileManager.default.fileExists(atPath: path) {
|
|
154
|
+
let attributes = try FileManager.default.attributesOfItem(atPath: path)
|
|
155
|
+
guard attributes[.type] as? FileAttributeType == .typeRegular else { throw UploadError.notCompleted }
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private func writeQueue(_ serializedTasks: String) throws {
|
|
160
|
+
journalWriteUncertain = true
|
|
93
161
|
try FileManager.default.createDirectory(
|
|
94
162
|
at: queueFile.deletingLastPathComponent(),
|
|
95
163
|
withIntermediateDirectories: true
|
|
96
164
|
)
|
|
165
|
+
try syncDirectory(queueFile.deletingLastPathComponent().deletingLastPathComponent())
|
|
97
166
|
try Data(serializedTasks.utf8).write(to: queueFile, options: .atomic)
|
|
167
|
+
let handle = try FileHandle(forWritingTo: queueFile)
|
|
168
|
+
defer { try? handle.close() }
|
|
169
|
+
try handle.synchronize()
|
|
170
|
+
try syncDirectory(queueFile.deletingLastPathComponent())
|
|
171
|
+
journalWriteUncertain = false
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
private static func serialize(_ tasks: [[String: Any]]) throws -> String {
|
|
175
|
+
String(decoding: try JSONSerialization.data(withJSONObject: tasks, options: [.sortedKeys]), as: UTF8.self)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private static func metadata(_ serialized: String) throws -> [[String: Any]] {
|
|
179
|
+
guard let tasks = try JSONSerialization.jsonObject(with: Data(serialized.utf8)) as? [[String: Any]] else {
|
|
180
|
+
throw UploadError.invalidJournal
|
|
181
|
+
}
|
|
182
|
+
let fields: Set<String> = ["id", "recordingId", "deviceId", "recordingUuid", "localPath", "status", "retryCount", "createdAt", "updatedAt", "recoveryScope", "fileSizeBytes", "nextAttemptAt", "contentType", "contentSha256", "relayUpload"]
|
|
183
|
+
var ids = Set<String>()
|
|
184
|
+
return try tasks.map { task in
|
|
185
|
+
guard let id = task["id"] as? String, !id.isEmpty, ids.insert(id).inserted,
|
|
186
|
+
let recordingID = task["recordingId"] as? String, !recordingID.isEmpty,
|
|
187
|
+
let deviceID = task["deviceId"] as? String, !deviceID.isEmpty,
|
|
188
|
+
task["localPath"] is String, task["createdAt"] is String, task["updatedAt"] is String,
|
|
189
|
+
let status = task["status"] as? String, ["pending", "uploading", "completed", "failed"].contains(status),
|
|
190
|
+
validInteger(task["retryCount"])
|
|
191
|
+
else { throw UploadError.invalidJournal }
|
|
192
|
+
for key in ["recordingUuid", "recoveryScope", "contentType", "contentSha256"] where task[key] != nil {
|
|
193
|
+
guard task[key] is String else { throw UploadError.invalidJournal }
|
|
194
|
+
}
|
|
195
|
+
for key in ["fileSizeBytes", "nextAttemptAt"] where task[key] != nil {
|
|
196
|
+
guard validInteger(task[key]) else { throw UploadError.invalidJournal }
|
|
197
|
+
}
|
|
198
|
+
if let relay = task["relayUpload"] {
|
|
199
|
+
guard let number = relay as? NSNumber, CFGetTypeID(number) == CFBooleanGetTypeID() else { throw UploadError.invalidJournal }
|
|
200
|
+
}
|
|
201
|
+
let dates = ISO8601DateFormatter()
|
|
202
|
+
let fractionalDates = ISO8601DateFormatter()
|
|
203
|
+
fractionalDates.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
|
|
204
|
+
for key in ["createdAt", "updatedAt"] {
|
|
205
|
+
guard let value = task[key] as? String,
|
|
206
|
+
dates.date(from: value) != nil || fractionalDates.date(from: value) != nil
|
|
207
|
+
else { throw UploadError.invalidJournal }
|
|
208
|
+
}
|
|
209
|
+
var clean = task.filter { fields.contains($0.key) }
|
|
210
|
+
guard clean.values.allSatisfy({ $0 is String || $0 is NSNumber }) else { throw UploadError.invalidJournal }
|
|
211
|
+
clean["relayUpload"] = task["relayUpload"] as? Bool ?? (task["relay"] is [String: Any])
|
|
212
|
+
return clean
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private static func validInteger(_ value: Any?) -> Bool {
|
|
217
|
+
guard let number = value as? NSNumber, CFGetTypeID(number) != CFBooleanGetTypeID() else { return false }
|
|
218
|
+
let value = number.doubleValue
|
|
219
|
+
return value.isFinite && value >= 0 && value <= 9_007_199_254_740_991 && value.rounded() == value
|
|
98
220
|
}
|
|
99
221
|
|
|
100
222
|
private static func performUpload(
|
|
@@ -108,6 +230,8 @@ actor BotaDeviceSDKAppleRecordingUploads {
|
|
|
108
230
|
}
|
|
109
231
|
let attributes = try FileManager.default.attributesOfItem(atPath: fileURL.path)
|
|
110
232
|
let totalBytes = (attributes[.size] as? NSNumber)?.int64Value ?? 0
|
|
233
|
+
if let expected = upload.fileSizeBytes, expected != totalBytes { throw UploadError.fileSizeChanged }
|
|
234
|
+
try Task.checkCancellation()
|
|
111
235
|
onProgress(.init(taskID: upload.taskID, completedBytes: 0, totalBytes: totalBytes))
|
|
112
236
|
|
|
113
237
|
let relay = upload.relayURL != nil
|
|
@@ -123,6 +247,7 @@ actor BotaDeviceSDKAppleRecordingUploads {
|
|
|
123
247
|
}
|
|
124
248
|
let (_, response) = try await session.upload(for: request, fromFile: fileURL)
|
|
125
249
|
try Self.requireSuccess(response)
|
|
250
|
+
try Task.checkCancellation()
|
|
126
251
|
|
|
127
252
|
if !relay,
|
|
128
253
|
let completeURL = upload.completeURL,
|
|
@@ -144,12 +269,12 @@ actor BotaDeviceSDKAppleRecordingUploads {
|
|
|
144
269
|
try requireSuccess(completeResponse)
|
|
145
270
|
}
|
|
146
271
|
|
|
272
|
+
try Task.checkCancellation()
|
|
147
273
|
onProgress(.init(
|
|
148
274
|
taskID: upload.taskID,
|
|
149
275
|
completedBytes: totalBytes,
|
|
150
276
|
totalBytes: totalBytes
|
|
151
277
|
))
|
|
152
|
-
try FileManager.default.removeItem(at: fileURL)
|
|
153
278
|
}
|
|
154
279
|
|
|
155
280
|
private static func requiredURL(_ value: String) throws -> URL {
|
|
@@ -175,4 +300,11 @@ actor BotaDeviceSDKAppleRecordingUploads {
|
|
|
175
300
|
.appendingPathComponent("BotaDeviceSDK", isDirectory: true)
|
|
176
301
|
.appendingPathComponent("compat-upload-queue.json")
|
|
177
302
|
}
|
|
303
|
+
|
|
304
|
+
private static func synchronizeDirectory(_ directory: URL) throws {
|
|
305
|
+
let descriptor = Darwin.open(directory.path, O_RDONLY)
|
|
306
|
+
guard descriptor >= 0 else { throw POSIXError(POSIXErrorCode(rawValue: errno) ?? .EIO) }
|
|
307
|
+
defer { Darwin.close(descriptor) }
|
|
308
|
+
guard Darwin.fsync(descriptor) == 0 else { throw POSIXError(POSIXErrorCode(rawValue: errno) ?? .EIO) }
|
|
309
|
+
}
|
|
178
310
|
}
|
|
@@ -5,10 +5,12 @@ struct BotaDeviceSDKAppleRecordingFile: Equatable, Sendable {
|
|
|
5
5
|
let localPath: String
|
|
6
6
|
let isE2EEncrypted: Bool
|
|
7
7
|
let contentSHA256Hex: String?
|
|
8
|
+
let fileSizeBytes: UInt64
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
protocol BotaDeviceSDKAppleRecordingClient: Sendable {
|
|
11
12
|
func listRecordings(_ device: ConnectedDevice) async throws -> [DeviceRecording]
|
|
13
|
+
func listPendingRecordings(_ device: ConnectedDevice) async throws -> [PendingRecording]
|
|
12
14
|
func syncRecording(
|
|
13
15
|
_ device: ConnectedDevice,
|
|
14
16
|
recording: DeviceRecording,
|
|
@@ -18,8 +20,10 @@ protocol BotaDeviceSDKAppleRecordingClient: Sendable {
|
|
|
18
20
|
func syncEncryptedRecordingV2(
|
|
19
21
|
_ device: ConnectedDevice,
|
|
20
22
|
recording: EncryptedUploadV2Recording,
|
|
23
|
+
operationID: UUID,
|
|
21
24
|
provider: @escaping EncryptedUploadV2ProfileProvider
|
|
22
25
|
) async throws
|
|
26
|
+
func cancelEncryptedUploadV2Operation(_ operationID: UUID) async throws
|
|
23
27
|
func confirmRecording(_ device: ConnectedDevice, recordingUUID: String) async throws
|
|
24
28
|
func observeUploadOwnership(
|
|
25
29
|
_ device: ConnectedDevice,
|
|
@@ -50,6 +54,10 @@ struct BotaDeviceSDKSharedAppleRecordingClient: BotaDeviceSDKAppleRecordingClien
|
|
|
50
54
|
try await recordings.listRecordings(device)
|
|
51
55
|
}
|
|
52
56
|
|
|
57
|
+
func listPendingRecordings(_ device: ConnectedDevice) async throws -> [PendingRecording] {
|
|
58
|
+
try await recordings.listPendingRecordings(device)
|
|
59
|
+
}
|
|
60
|
+
|
|
53
61
|
func syncRecording(
|
|
54
62
|
_ device: ConnectedDevice,
|
|
55
63
|
recording: DeviceRecording,
|
|
@@ -70,15 +78,21 @@ struct BotaDeviceSDKSharedAppleRecordingClient: BotaDeviceSDKAppleRecordingClien
|
|
|
70
78
|
func syncEncryptedRecordingV2(
|
|
71
79
|
_ device: ConnectedDevice,
|
|
72
80
|
recording: EncryptedUploadV2Recording,
|
|
81
|
+
operationID: UUID,
|
|
73
82
|
provider: @escaping EncryptedUploadV2ProfileProvider
|
|
74
83
|
) async throws {
|
|
75
84
|
try await recordings.syncEncryptedRecordingV2(
|
|
76
85
|
device,
|
|
77
86
|
recording: recording,
|
|
87
|
+
operationID: operationID,
|
|
78
88
|
provider: provider
|
|
79
89
|
)
|
|
80
90
|
}
|
|
81
91
|
|
|
92
|
+
func cancelEncryptedUploadV2Operation(_ operationID: UUID) async throws {
|
|
93
|
+
try await recordings.cancelEncryptedUploadV2Operation(operationID)
|
|
94
|
+
}
|
|
95
|
+
|
|
82
96
|
func confirmRecording(_ device: ConnectedDevice, recordingUUID: String) async throws {
|
|
83
97
|
try await recordings.confirmRecording(device, recordingUUID: recordingUUID)
|
|
84
98
|
}
|
|
@@ -153,11 +167,16 @@ actor BotaDeviceSDKAppleRecordings {
|
|
|
153
167
|
}
|
|
154
168
|
|
|
155
169
|
private let client: any BotaDeviceSDKAppleRecordingClient
|
|
170
|
+
private let fileSize: @Sendable (String) throws -> UInt64
|
|
156
171
|
private struct EncryptedUploadV2Request {
|
|
172
|
+
let operationID: String
|
|
157
173
|
let recording: EncryptedUploadV2Recording
|
|
158
174
|
let continuation: CheckedContinuation<EncryptedUploadV2Material, Error>
|
|
159
175
|
}
|
|
160
176
|
private var encryptedUploadV2Requests: [String: EncryptedUploadV2Request] = [:]
|
|
177
|
+
private var encryptedUploadV2Operations: [String: UUID] = [:]
|
|
178
|
+
private var encryptedUploadV2Tasks: [String: Task<Void, Error>] = [:]
|
|
179
|
+
private var cancelledEncryptedUploadV2Operations: Set<String> = []
|
|
161
180
|
private var destinationRequests: [
|
|
162
181
|
String: CheckedContinuation<StreamingUploadDestination, Error>
|
|
163
182
|
] = [:]
|
|
@@ -166,15 +185,28 @@ actor BotaDeviceSDKAppleRecordings {
|
|
|
166
185
|
|
|
167
186
|
init(
|
|
168
187
|
client: any BotaDeviceSDKAppleRecordingClient =
|
|
169
|
-
BotaDeviceSDKSharedAppleRecordingClient()
|
|
188
|
+
BotaDeviceSDKSharedAppleRecordingClient(),
|
|
189
|
+
fileSize: @escaping @Sendable (String) throws -> UInt64 = { path in
|
|
190
|
+
let attributes = try FileManager.default.attributesOfItem(atPath: path)
|
|
191
|
+
guard attributes[.type] as? FileAttributeType == .typeRegular,
|
|
192
|
+
let size = attributes[.size] as? NSNumber else {
|
|
193
|
+
throw CocoaError(.fileReadCorruptFile)
|
|
194
|
+
}
|
|
195
|
+
return size.uint64Value
|
|
196
|
+
}
|
|
170
197
|
) {
|
|
171
198
|
self.client = client
|
|
199
|
+
self.fileSize = fileSize
|
|
172
200
|
}
|
|
173
201
|
|
|
174
202
|
func listRecordings(_ device: ConnectedDevice) async throws -> [DeviceRecording] {
|
|
175
203
|
try await client.listRecordings(device)
|
|
176
204
|
}
|
|
177
205
|
|
|
206
|
+
func listPendingRecordings(_ device: ConnectedDevice) async throws -> [PendingRecording] {
|
|
207
|
+
try await client.listPendingRecordings(device)
|
|
208
|
+
}
|
|
209
|
+
|
|
178
210
|
func syncRecording(
|
|
179
211
|
_ device: ConnectedDevice,
|
|
180
212
|
recording: DeviceRecording,
|
|
@@ -200,7 +232,8 @@ actor BotaDeviceSDKAppleRecordings {
|
|
|
200
232
|
return BotaDeviceSDKAppleRecordingFile(
|
|
201
233
|
localPath: path,
|
|
202
234
|
isE2EEncrypted: metadata?.isE2EEncrypted ?? false,
|
|
203
|
-
contentSHA256Hex: metadata?.contentSHA256Hex
|
|
235
|
+
contentSHA256Hex: metadata?.contentSHA256Hex,
|
|
236
|
+
fileSizeBytes: try fileSize(path)
|
|
204
237
|
)
|
|
205
238
|
}
|
|
206
239
|
|
|
@@ -210,11 +243,51 @@ actor BotaDeviceSDKAppleRecordings {
|
|
|
210
243
|
operationID: String,
|
|
211
244
|
onProfileRequest: @escaping @Sendable ([String: Any]) -> Void,
|
|
212
245
|
onProgress: @escaping @Sendable ([String: Any]) -> Void
|
|
246
|
+
) async throws {
|
|
247
|
+
guard encryptedUploadV2Operations[operationID] == nil else {
|
|
248
|
+
throw RecordingError.invalidEncryptedUploadV2Selection
|
|
249
|
+
}
|
|
250
|
+
guard !cancelledEncryptedUploadV2Operations.contains(operationID) else { throw CancellationError() }
|
|
251
|
+
let nativeID = UUID()
|
|
252
|
+
let task = Task {
|
|
253
|
+
try Task.checkCancellation()
|
|
254
|
+
try await self.runEncryptedRecordingV2(
|
|
255
|
+
device,
|
|
256
|
+
recording: recording,
|
|
257
|
+
operationID: operationID,
|
|
258
|
+
nativeID: nativeID,
|
|
259
|
+
onProfileRequest: onProfileRequest,
|
|
260
|
+
onProgress: onProgress
|
|
261
|
+
)
|
|
262
|
+
}
|
|
263
|
+
encryptedUploadV2Operations[operationID] = nativeID
|
|
264
|
+
encryptedUploadV2Tasks[operationID] = task
|
|
265
|
+
defer {
|
|
266
|
+
encryptedUploadV2Operations.removeValue(forKey: operationID)
|
|
267
|
+
encryptedUploadV2Tasks.removeValue(forKey: operationID)
|
|
268
|
+
BotaDeviceSDKEncryptedUploadV2Materials.removeContext(operationID)
|
|
269
|
+
}
|
|
270
|
+
try await withTaskCancellationHandler {
|
|
271
|
+
try await task.value
|
|
272
|
+
} onCancel: {
|
|
273
|
+
task.cancel()
|
|
274
|
+
Task { try? await self.cancelEncryptedRecordingV2(operationID) }
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
private func runEncryptedRecordingV2(
|
|
279
|
+
_ device: ConnectedDevice,
|
|
280
|
+
recording: EncryptedUploadV2Recording,
|
|
281
|
+
operationID: String,
|
|
282
|
+
nativeID: UUID,
|
|
283
|
+
onProfileRequest: @escaping @Sendable ([String: Any]) -> Void,
|
|
284
|
+
onProgress: @escaping @Sendable ([String: Any]) -> Void
|
|
213
285
|
) async throws {
|
|
214
286
|
do {
|
|
215
287
|
try await client.syncEncryptedRecordingV2(
|
|
216
288
|
device,
|
|
217
|
-
recording: recording
|
|
289
|
+
recording: recording,
|
|
290
|
+
operationID: nativeID
|
|
218
291
|
) { context in
|
|
219
292
|
let completedBytes = String(context.checkpoint?.nextCiphertextOffset ?? 0)
|
|
220
293
|
let checkpointRevision = context.checkpoint?.revision
|
|
@@ -264,6 +337,25 @@ actor BotaDeviceSDKAppleRecordings {
|
|
|
264
337
|
}
|
|
265
338
|
}
|
|
266
339
|
|
|
340
|
+
func cancelEncryptedRecordingV2(_ operationID: String) async throws {
|
|
341
|
+
cancelledEncryptedUploadV2Operations.insert(operationID)
|
|
342
|
+
let task = encryptedUploadV2Tasks[operationID]
|
|
343
|
+
task?.cancel()
|
|
344
|
+
let pending = encryptedUploadV2Requests.filter { $0.value.operationID == operationID }
|
|
345
|
+
for (id, request) in pending {
|
|
346
|
+
encryptedUploadV2Requests.removeValue(forKey: id)
|
|
347
|
+
request.continuation.resume(throwing: CancellationError())
|
|
348
|
+
}
|
|
349
|
+
BotaDeviceSDKEncryptedUploadV2Materials.removeContext(operationID)
|
|
350
|
+
var cancellationError: Error?
|
|
351
|
+
if let nativeID = encryptedUploadV2Operations[operationID] {
|
|
352
|
+
do { try await client.cancelEncryptedUploadV2Operation(nativeID) }
|
|
353
|
+
catch { cancellationError = error }
|
|
354
|
+
}
|
|
355
|
+
_ = await task?.result
|
|
356
|
+
if let cancellationError { throw cancellationError }
|
|
357
|
+
}
|
|
358
|
+
|
|
267
359
|
func resolveEncryptedUploadV2Profile(
|
|
268
360
|
requestID: String,
|
|
269
361
|
profile: String,
|
|
@@ -275,6 +367,7 @@ actor BotaDeviceSDKAppleRecordings {
|
|
|
275
367
|
guard let request = encryptedUploadV2Requests.removeValue(forKey: requestID) else {
|
|
276
368
|
throw RecordingError.missingEncryptedUploadV2Request
|
|
277
369
|
}
|
|
370
|
+
BotaDeviceSDKEncryptedUploadV2Materials.removeContext(request.operationID)
|
|
278
371
|
do {
|
|
279
372
|
guard profile == "encrypted_upload_v2",
|
|
280
373
|
let sessionID = UUID(uuidString: uploadSessionID)
|
|
@@ -299,6 +392,7 @@ actor BotaDeviceSDKAppleRecordings {
|
|
|
299
392
|
guard let request = encryptedUploadV2Requests.removeValue(forKey: requestID) else {
|
|
300
393
|
throw RecordingError.missingEncryptedUploadV2Request
|
|
301
394
|
}
|
|
395
|
+
BotaDeviceSDKEncryptedUploadV2Materials.removeContext(request.operationID)
|
|
302
396
|
guard errorCode == "application_material_rejected" else {
|
|
303
397
|
request.continuation.resume(throwing: RecordingError.invalidEncryptedUploadV2Selection)
|
|
304
398
|
throw RecordingError.invalidEncryptedUploadV2Selection
|
|
@@ -453,6 +547,9 @@ actor BotaDeviceSDKAppleRecordings {
|
|
|
453
547
|
}
|
|
454
548
|
|
|
455
549
|
func cancelAll() async {
|
|
550
|
+
let operationIDs = Array(encryptedUploadV2Tasks.keys)
|
|
551
|
+
for id in operationIDs { encryptedUploadV2Tasks[id]?.cancel() }
|
|
552
|
+
for id in operationIDs { try? await cancelEncryptedRecordingV2(id) }
|
|
456
553
|
rejectPendingRequests(message: "recording operations were cancelled")
|
|
457
554
|
try? await client.cancelCurrentOperation()
|
|
458
555
|
}
|
|
@@ -462,9 +559,13 @@ actor BotaDeviceSDKAppleRecordings {
|
|
|
462
559
|
context: EncryptedUploadV2ProviderContext,
|
|
463
560
|
onRequest: @escaping @Sendable ([String: Any]) -> Void
|
|
464
561
|
) async throws -> EncryptedUploadV2Material {
|
|
562
|
+
guard encryptedUploadV2Operations[operationID] != nil,
|
|
563
|
+
!cancelledEncryptedUploadV2Operations.contains(operationID) else { throw CancellationError() }
|
|
465
564
|
let requestID = UUID().uuidString.lowercased()
|
|
565
|
+
try BotaDeviceSDKEncryptedUploadV2Materials.registerContext(operationID, context: context)
|
|
466
566
|
return try await withCheckedThrowingContinuation { continuation in
|
|
467
567
|
encryptedUploadV2Requests[requestID] = .init(
|
|
568
|
+
operationID: operationID,
|
|
468
569
|
recording: context.recording,
|
|
469
570
|
continuation: continuation
|
|
470
571
|
)
|
|
@@ -525,12 +626,15 @@ actor BotaDeviceSDKAppleRecordings {
|
|
|
525
626
|
destinationRequests.removeAll()
|
|
526
627
|
finalizeRequests.removeAll()
|
|
527
628
|
encryptedUploadV2Requests.removeAll()
|
|
528
|
-
encryptedUploadV2.forEach {
|
|
629
|
+
encryptedUploadV2.forEach {
|
|
630
|
+
BotaDeviceSDKEncryptedUploadV2Materials.removeContext($0.operationID)
|
|
631
|
+
$0.continuation.resume(throwing: error)
|
|
632
|
+
}
|
|
529
633
|
destinations.forEach { $0.resume(throwing: error) }
|
|
530
634
|
finalizations.forEach { $0.resume(throwing: error) }
|
|
531
635
|
}
|
|
532
636
|
|
|
533
|
-
|
|
637
|
+
static func encryptedUploadV2Recording(
|
|
534
638
|
_ recording: EncryptedUploadV2Recording
|
|
535
639
|
) -> [String: Any] {
|
|
536
640
|
[
|
|
@@ -538,6 +642,10 @@ actor BotaDeviceSDKAppleRecordings {
|
|
|
538
642
|
"generation": recording.generation,
|
|
539
643
|
"ciphertextLength": String(recording.ciphertextLength),
|
|
540
644
|
"ciphertextSha256": recording.ciphertextSHA256.hexString,
|
|
645
|
+
"startedAtMs": String(recording.startedAtMs),
|
|
646
|
+
"durationMs": String(recording.durationMs),
|
|
647
|
+
"plaintextLength": String(recording.plaintextLength),
|
|
648
|
+
"storageFormat": Int(recording.storageFormat),
|
|
541
649
|
]
|
|
542
650
|
}
|
|
543
651
|
|
|
@@ -15,6 +15,25 @@ public enum BotaDeviceSDKEncryptedUploadV2Materials {
|
|
|
15
15
|
storage.remove(registrationID)
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
public static func readAuthNonce(_ operationID: String) async throws -> Data {
|
|
19
|
+
let entry = try storage.context(operationID)
|
|
20
|
+
let nonce = try await entry.value.readAuthNonce()
|
|
21
|
+
guard try storage.context(operationID).lease == entry.lease else {
|
|
22
|
+
throw CancellationError()
|
|
23
|
+
}
|
|
24
|
+
return nonce
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public static func release(_ registrationID: String) async {
|
|
28
|
+
if let material = storage.remove(registrationID) { await material.cancelPreparation() }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static func registerContext(_ operationID: String, context: EncryptedUploadV2ProviderContext) throws {
|
|
32
|
+
try storage.registerContext(operationID, value: context)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static func removeContext(_ operationID: String) { storage.removeContext(operationID) }
|
|
36
|
+
|
|
18
37
|
static func consume(
|
|
19
38
|
_ registrationID: String,
|
|
20
39
|
recording: EncryptedUploadV2Recording,
|
|
@@ -33,6 +52,10 @@ public enum BotaDeviceSDKEncryptedUploadV2Materials {
|
|
|
33
52
|
}
|
|
34
53
|
|
|
35
54
|
private final class BotaDeviceSDKEncryptedUploadV2MaterialStorage: @unchecked Sendable {
|
|
55
|
+
struct Context: Sendable {
|
|
56
|
+
let lease: UUID
|
|
57
|
+
let value: EncryptedUploadV2ProviderContext
|
|
58
|
+
}
|
|
36
59
|
private enum RegistrationError: LocalizedError {
|
|
37
60
|
case invalidSelection
|
|
38
61
|
case unknownRegistration
|
|
@@ -49,6 +72,23 @@ private final class BotaDeviceSDKEncryptedUploadV2MaterialStorage: @unchecked Se
|
|
|
49
72
|
|
|
50
73
|
private let lock = NSLock()
|
|
51
74
|
private var materials: [String: EncryptedUploadV2Material] = [:]
|
|
75
|
+
private var contexts: [String: Context] = [:]
|
|
76
|
+
|
|
77
|
+
func registerContext(_ operationID: String, value: EncryptedUploadV2ProviderContext) throws {
|
|
78
|
+
try lock.withLock {
|
|
79
|
+
guard contexts[operationID] == nil else { throw RegistrationError.invalidSelection }
|
|
80
|
+
contexts[operationID] = Context(lease: UUID(), value: value)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
func context(_ operationID: String) throws -> Context {
|
|
85
|
+
try lock.withLock {
|
|
86
|
+
guard let context = contexts[operationID] else { throw RegistrationError.unknownRegistration }
|
|
87
|
+
return context
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
func removeContext(_ operationID: String) { _ = lock.withLock { contexts.removeValue(forKey: operationID) } }
|
|
52
92
|
|
|
53
93
|
func register(_ material: EncryptedUploadV2Material) -> String {
|
|
54
94
|
let registrationID = UUID().uuidString.lowercased()
|
|
@@ -9,6 +9,7 @@ var _runtime = require("./compatibility/runtime");
|
|
|
9
9
|
var _DeviceManager = require("./managers/DeviceManager");
|
|
10
10
|
var _OTAManager = require("./managers/OTAManager");
|
|
11
11
|
var _RecordingManager = require("./managers/RecordingManager");
|
|
12
|
+
var _uploadRecoveryMetadata = require("./managers/uploadRecoveryMetadata");
|
|
12
13
|
var _errors = require("./utils/errors");
|
|
13
14
|
var _logger = require("./utils/logger");
|
|
14
15
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -18,9 +19,15 @@ const normalizeConfig = config => ({
|
|
|
18
19
|
backgroundSyncEnabled: config.backgroundSyncEnabled ?? true,
|
|
19
20
|
wifiOnlyUpload: config.wifiOnlyUpload ?? false,
|
|
20
21
|
logLevel: config.logLevel ?? 'warn',
|
|
21
|
-
debug: config.debug ?? false
|
|
22
|
+
debug: config.debug ?? false,
|
|
23
|
+
...(config.uploadRecoveryProvider ? {
|
|
24
|
+
uploadRecoveryProvider: config.uploadRecoveryProvider
|
|
25
|
+
} : {})
|
|
22
26
|
});
|
|
23
27
|
class BotaClientImpl extends _eventemitter.default {
|
|
28
|
+
clientPresence = {
|
|
29
|
+
nextReport: async deviceId => this._deviceManager?.clientPresence.nextReport(deviceId) ?? null
|
|
30
|
+
};
|
|
24
31
|
_config = null;
|
|
25
32
|
_state = 'uninitialized';
|
|
26
33
|
_bluetoothState = 'unknown';
|
|
@@ -60,6 +67,11 @@ class BotaClientImpl extends _eventemitter.default {
|
|
|
60
67
|
return this._otaManager;
|
|
61
68
|
}
|
|
62
69
|
configure(config = {}) {
|
|
70
|
+
try {
|
|
71
|
+
(0, _uploadRecoveryMetadata.rejectRecordingDataStore)(config.recordingDataStore);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
return Promise.reject(error);
|
|
74
|
+
}
|
|
63
75
|
if (this._configurePromise && !this._destroyPromise) {
|
|
64
76
|
return this._configurePromise;
|
|
65
77
|
}
|
|
@@ -140,7 +152,9 @@ class BotaClientImpl extends _eventemitter.default {
|
|
|
140
152
|
this.setBluetoothState(capabilities.bluetooth ? 'poweredOn' : 'unsupported');
|
|
141
153
|
deviceManager = new _DeviceManager.DeviceManager();
|
|
142
154
|
await deviceManager.initialize();
|
|
143
|
-
recordingManager = new _RecordingManager.RecordingManager(
|
|
155
|
+
recordingManager = new _RecordingManager.RecordingManager({
|
|
156
|
+
uploadRecoveryProvider: config.uploadRecoveryProvider
|
|
157
|
+
});
|
|
144
158
|
await recordingManager.initialize();
|
|
145
159
|
otaManager = new _OTAManager.OTAManager(deviceManager);
|
|
146
160
|
this._deviceManager = deviceManager;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_eventemitter","_interopRequireDefault","require","_runtime","_DeviceManager","_OTAManager","_RecordingManager","_errors","_logger","e","__esModule","default","log","logger","tag","normalizeConfig","config","environment","backgroundSyncEnabled","wifiOnlyUpload","logLevel","debug","BotaClientImpl","EventEmitter","_config","_state","_bluetoothState","_client","
|
|
1
|
+
{"version":3,"names":["_eventemitter","_interopRequireDefault","require","_runtime","_DeviceManager","_OTAManager","_RecordingManager","_uploadRecoveryMetadata","_errors","_logger","e","__esModule","default","log","logger","tag","normalizeConfig","config","environment","backgroundSyncEnabled","wifiOnlyUpload","logLevel","debug","uploadRecoveryProvider","BotaClientImpl","EventEmitter","clientPresence","nextReport","deviceId","_deviceManager","_config","_state","_bluetoothState","_client","_recordingManager","_otaManager","_nativeOwned","_operation","Promise","resolve","_configurePromise","_destroyPromise","state","bluetoothState","isBluetoothReady","isInitialized","devices","SdkError","notInitialized","recordings","ota","configure","rejectRecordingDataStore","recordingDataStore","error","reject","normalized","operation","enqueue","performConfigure","finally","catch","undefined","waitForBluetooth","timeoutMs","BluetoothError","unavailable","deadline","Date","now","setTimeout","capabilities","getCapabilities","bluetooth","setBluetoothState","timeout","setLogLevel","level","setLevel","setLogHandler","handler","setHandler","destroy","releaseOwnerGraph","removeAllListeners","queued","then","warn","setState","client","getCompatibilityClient","deviceManager","recordingManager","otaManager","nativeState","getState","invalidState","DeviceManager","initialize","RecordingManager","OTAManager","info","failure","Error","String","emit","BotaClient","exports"],"sourceRoot":"../../src","sources":["BotaClient.ts"],"mappings":"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","ignoreList":[]}
|