@bota.dev/react-native-app-sdk 2.0.0-beta.1 → 2.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -3
- package/android/build.gradle +1 -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 +68 -3
- 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 +495 -8
- package/ios/BotaDeviceSDK.mm +102 -0
- package/ios/BotaDeviceSDKAppleBridge.swift +126 -4
- 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 +13 -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 +163 -28
- package/lib/commonjs/client.js.map +1 -1
- package/lib/commonjs/index.js +15 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/managers/DeviceManager.js +8 -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/specs/NativeBotaDeviceSDK.js.map +1 -1
- package/lib/module/BotaClient.js +13 -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 +163 -28
- package/lib/module/client.js.map +1 -1
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/managers/DeviceManager.js +8 -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/specs/NativeBotaDeviceSDK.js.map +1 -1
- 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 +18 -2
- package/lib/typescript/client.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +6 -4
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/managers/DeviceManager.d.ts +3 -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/specs/NativeBotaDeviceSDK.d.ts +61 -0
- package/lib/typescript/specs/NativeBotaDeviceSDK.d.ts.map +1 -1
- package/package.json +1 -1
- package/scripts/bota_device_sdk_spm_workaround.rb +26 -15
- package/src/BotaClient.ts +13 -3
- package/src/ble/constants.ts +5 -0
- package/src/ble/deviceDiagnostics.ts +238 -0
- package/src/client.ts +166 -18
- package/src/index.ts +12 -0
- package/src/managers/DeviceManager.ts +11 -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/specs/NativeBotaDeviceSDK.ts +56 -0
package/src/client.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
NativeEncryptedUploadV2Checkpoint,
|
|
11
11
|
NativeEncryptedUploadV2Progress,
|
|
12
12
|
NativeEncryptedUploadV2Recording,
|
|
13
|
+
NativePendingRecording,
|
|
13
14
|
NativeFactoryResetCompletion,
|
|
14
15
|
NativeFactoryResetGrantRequest,
|
|
15
16
|
NativeFirmwareUpdateProgress,
|
|
@@ -37,6 +38,9 @@ import type {
|
|
|
37
38
|
DeviceStatus,
|
|
38
39
|
DeviceType,
|
|
39
40
|
DeviceLogEvent,
|
|
41
|
+
DeviceDiagnosticEventType,
|
|
42
|
+
DeviceDiagnosticReasonCode,
|
|
43
|
+
DeviceDiagnosticsBatch,
|
|
40
44
|
DiscoveredDevice,
|
|
41
45
|
PairingState,
|
|
42
46
|
ReconnectOptions,
|
|
@@ -195,11 +199,24 @@ export type BotaRecordingTransferProgress = {
|
|
|
195
199
|
|
|
196
200
|
export type BotaEncryptedUploadV2Recording = NativeEncryptedUploadV2Recording;
|
|
197
201
|
|
|
202
|
+
export type BotaEncryptedUploadV2PendingRecording = Omit<BotaEncryptedUploadV2Recording, 'durationMs'> & {
|
|
203
|
+
storageFormat: 3;
|
|
204
|
+
startedAt: Date;
|
|
205
|
+
durationMs: number;
|
|
206
|
+
plaintextLength: string;
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
export type BotaEncryptedUploadV2SyncOptions = {
|
|
210
|
+
signal?: AbortSignal;
|
|
211
|
+
operationId?: string;
|
|
212
|
+
};
|
|
213
|
+
|
|
198
214
|
export type BotaEncryptedUploadV2Capability = NativeEncryptedUploadV2Capability;
|
|
199
215
|
|
|
200
216
|
export type BotaEncryptedUploadV2Checkpoint = NativeEncryptedUploadV2Checkpoint;
|
|
201
217
|
|
|
202
218
|
export type BotaEncryptedUploadV2ProviderContext = {
|
|
219
|
+
operationId: string;
|
|
203
220
|
recording: BotaEncryptedUploadV2Recording;
|
|
204
221
|
capability: BotaEncryptedUploadV2Capability;
|
|
205
222
|
checkpoint?: BotaEncryptedUploadV2Checkpoint;
|
|
@@ -287,6 +304,8 @@ export type BotaDeviceSDKOTAClient = {
|
|
|
287
304
|
};
|
|
288
305
|
|
|
289
306
|
export type BotaDeviceSDKLogClient = {
|
|
307
|
+
readDiagnosticEvents(device: ConnectedDevice): Promise<DeviceDiagnosticsBatch>;
|
|
308
|
+
acknowledgeDiagnosticEvents(device: ConnectedDevice, acceptedEventIds: string[]): Promise<void>;
|
|
290
309
|
subscribe(
|
|
291
310
|
device: ConnectedDevice,
|
|
292
311
|
onLine: (line: DeviceLogEvent) => void
|
|
@@ -310,6 +329,7 @@ export type BotaDeviceSDKWiFiClient = {
|
|
|
310
329
|
|
|
311
330
|
export type BotaDeviceSDKRecordingClient = {
|
|
312
331
|
listRecordings(device: ConnectedDevice): Promise<DeviceRecording[]>;
|
|
332
|
+
listPendingRecordings(device: ConnectedDevice): Promise<Array<DeviceRecording | BotaEncryptedUploadV2PendingRecording>>;
|
|
313
333
|
syncRecording(
|
|
314
334
|
device: ConnectedDevice,
|
|
315
335
|
recording: DeviceRecording,
|
|
@@ -319,12 +339,14 @@ export type BotaDeviceSDKRecordingClient = {
|
|
|
319
339
|
localPath: string;
|
|
320
340
|
e2eEncrypted: boolean;
|
|
321
341
|
contentSha256?: string;
|
|
342
|
+
fileSizeBytes: number;
|
|
322
343
|
}>;
|
|
323
344
|
syncEncryptedRecordingV2(
|
|
324
345
|
device: ConnectedDevice,
|
|
325
346
|
recording: BotaEncryptedUploadV2Recording,
|
|
326
347
|
provider: BotaEncryptedUploadV2ProfileProvider,
|
|
327
|
-
onProgress?: (progress: BotaEncryptedUploadV2Progress) => void
|
|
348
|
+
onProgress?: (progress: BotaEncryptedUploadV2Progress) => void,
|
|
349
|
+
options?: BotaEncryptedUploadV2SyncOptions
|
|
328
350
|
): Promise<void>;
|
|
329
351
|
confirmRecording(
|
|
330
352
|
device: ConnectedDevice,
|
|
@@ -335,6 +357,7 @@ export type BotaDeviceSDKRecordingClient = {
|
|
|
335
357
|
onProgress?: (progress: BotaRecordingTransferProgress) => void
|
|
336
358
|
): Promise<void>;
|
|
337
359
|
cancelRecordingUpload(taskId: string): Promise<void>;
|
|
360
|
+
releaseRecordingFile(taskId: string, localPath: string): Promise<void>;
|
|
338
361
|
loadUploadQueue(): Promise<UploadTask[]>;
|
|
339
362
|
saveUploadQueue(tasks: UploadTask[]): Promise<void>;
|
|
340
363
|
destroyCompatibilityOperations(): Promise<void>;
|
|
@@ -425,6 +448,13 @@ export type BotaDeviceSDKClient = {
|
|
|
425
448
|
getState(): Promise<BotaDeviceSDKState>;
|
|
426
449
|
};
|
|
427
450
|
|
|
451
|
+
const formatAdvertisedMac = (value: string | undefined): string | null => {
|
|
452
|
+
if (!value || !/^(?:[0-9a-f]{12}|(?:[0-9a-f]{2}:){5}[0-9a-f]{2})$/i.test(value)) {
|
|
453
|
+
return null;
|
|
454
|
+
}
|
|
455
|
+
return value.replace(/:/g, '').toUpperCase().match(/.{2}/g)!.join(':');
|
|
456
|
+
};
|
|
457
|
+
|
|
428
458
|
const mapDiscoveredDevice = (
|
|
429
459
|
device: NativeDiscoveredDevice
|
|
430
460
|
): DiscoveredDevice => ({
|
|
@@ -432,7 +462,7 @@ const mapDiscoveredDevice = (
|
|
|
432
462
|
name: device.name ?? '',
|
|
433
463
|
deviceType: (device.deviceType ?? 'bota_pin') as DeviceType,
|
|
434
464
|
firmwareVersion: device.firmwareVersion ?? '',
|
|
435
|
-
macAddress: device.macAddress
|
|
465
|
+
macAddress: formatAdvertisedMac(device.macAddress),
|
|
436
466
|
pairingState: (device.pairingState ?? 'unpaired') as PairingState,
|
|
437
467
|
rssi: device.rssi,
|
|
438
468
|
discoveredAt: new Date(device.discoveredAtMs),
|
|
@@ -484,6 +514,48 @@ const createOpaqueId = (): string =>
|
|
|
484
514
|
return nibble.toString(16);
|
|
485
515
|
});
|
|
486
516
|
|
|
517
|
+
const catalogInteger = (value: string | undefined, maximum = BigInt(Number.MAX_SAFE_INTEGER)): number => {
|
|
518
|
+
if (typeof value !== 'string' || !/^(0|[1-9][0-9]{0,19})$/.test(value) || BigInt(value) > maximum) {
|
|
519
|
+
throw new Error('invalid native recording catalog metadata');
|
|
520
|
+
}
|
|
521
|
+
return Number(value);
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
const mapPendingRecording = (value: NativePendingRecording): DeviceRecording | BotaEncryptedUploadV2PendingRecording => {
|
|
525
|
+
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
526
|
+
if (value.profile === 'legacy' && value.legacy && !value.encrypted) {
|
|
527
|
+
const recording = value.legacy;
|
|
528
|
+
if (typeof recording.uuid !== 'string' || !uuidPattern.test(recording.uuid) ||
|
|
529
|
+
!Number.isSafeInteger(recording.startedAtMs) || recording.startedAtMs < 0 || recording.startedAtMs > 8640000000000000 ||
|
|
530
|
+
!Number.isSafeInteger(recording.durationMs) || recording.durationMs < 0 ||
|
|
531
|
+
!Number.isSafeInteger(recording.fileSize) || recording.fileSize < 0 ||
|
|
532
|
+
typeof recording.codec !== 'string' || typeof recording.isEncrypted !== 'boolean') {
|
|
533
|
+
throw new Error('invalid native recording catalog metadata');
|
|
534
|
+
}
|
|
535
|
+
return mapRecording(recording);
|
|
536
|
+
}
|
|
537
|
+
if (value.profile !== 'encrypted_upload_v2' || !value.encrypted || value.legacy) {
|
|
538
|
+
throw new Error('invalid native recording catalog profile');
|
|
539
|
+
}
|
|
540
|
+
const recording = value.encrypted;
|
|
541
|
+
const plaintextLength = recording.plaintextLength;
|
|
542
|
+
if (typeof recording.uuid !== 'string' || !uuidPattern.test(recording.uuid) ||
|
|
543
|
+
recording.storageFormat !== 3 || typeof plaintextLength !== 'string' ||
|
|
544
|
+
!/^(0|[1-9][0-9]{0,19})$/.test(plaintextLength) || BigInt(plaintextLength) > 0xffffffffffffffffn ||
|
|
545
|
+
typeof recording.ciphertextLength !== 'string' || !/^[1-9][0-9]{0,19}$/.test(recording.ciphertextLength) || BigInt(recording.ciphertextLength) > 0xffffffffffffffffn ||
|
|
546
|
+
typeof recording.ciphertextSha256 !== 'string' || !/^[0-9a-f]{64}$/.test(recording.ciphertextSha256) ||
|
|
547
|
+
!Number.isInteger(recording.generation) || recording.generation < 0 || recording.generation > 0xffffffff) {
|
|
548
|
+
throw new Error('invalid native recording catalog identity');
|
|
549
|
+
}
|
|
550
|
+
return {
|
|
551
|
+
uuid: recording.uuid, generation: recording.generation,
|
|
552
|
+
ciphertextLength: recording.ciphertextLength, ciphertextSha256: recording.ciphertextSha256,
|
|
553
|
+
storageFormat: 3, plaintextLength, startedAtMs: recording.startedAtMs,
|
|
554
|
+
startedAt: new Date(catalogInteger(recording.startedAtMs, 8640000000000000n)),
|
|
555
|
+
durationMs: catalogInteger(recording.durationMs),
|
|
556
|
+
};
|
|
557
|
+
};
|
|
558
|
+
|
|
487
559
|
const bytesToHex = (value: Uint8Array): string =>
|
|
488
560
|
Array.from(value, (byte) => byte.toString(16).padStart(2, '0')).join('');
|
|
489
561
|
|
|
@@ -710,6 +782,7 @@ const toNativeRecordingUploadRequest = (
|
|
|
710
782
|
recordingId: task.recordingId,
|
|
711
783
|
deviceId: task.deviceId,
|
|
712
784
|
localPath: task.localPath,
|
|
785
|
+
...(task.fileSizeBytes === undefined ? {} : { fileSizeBytes: task.fileSizeBytes }),
|
|
713
786
|
uploadUrl: task.uploadUrl,
|
|
714
787
|
...(task.uploadToken ? { uploadToken: task.uploadToken } : {}),
|
|
715
788
|
...(task.completeUrl ? { completeUrl: task.completeUrl } : {}),
|
|
@@ -724,9 +797,13 @@ const toNativeRecordingUploadRequest = (
|
|
|
724
797
|
});
|
|
725
798
|
|
|
726
799
|
const parseUploadQueue = (serialized: string): UploadTask[] => {
|
|
727
|
-
const value: unknown = JSON.parse(serialized
|
|
728
|
-
if (!Array.isArray(value))
|
|
800
|
+
const value: unknown = JSON.parse(serialized);
|
|
801
|
+
if (!Array.isArray(value)) throw new Error('Invalid upload recovery journal');
|
|
729
802
|
return value.map((task) => {
|
|
803
|
+
if (!task || typeof task !== 'object' || Array.isArray(task) ||
|
|
804
|
+
typeof task.createdAt !== 'string' || typeof task.updatedAt !== 'string') {
|
|
805
|
+
throw new Error('Invalid upload recovery journal');
|
|
806
|
+
}
|
|
730
807
|
const candidate = task as UploadTask & {
|
|
731
808
|
createdAt: string | Date;
|
|
732
809
|
updatedAt: string | Date;
|
|
@@ -1056,6 +1133,11 @@ export const createBotaDeviceSDK = (nativeModule: Spec | null): BotaDeviceSDKCli
|
|
|
1056
1133
|
return values.map(mapRecording);
|
|
1057
1134
|
},
|
|
1058
1135
|
|
|
1136
|
+
async listPendingRecordings(device) {
|
|
1137
|
+
const values = await requireNativeModule().listPendingRecordings(toNativeConnectedDevice(device));
|
|
1138
|
+
return values.map(mapPendingRecording);
|
|
1139
|
+
},
|
|
1140
|
+
|
|
1059
1141
|
async syncRecording(device, recording, onProgress, sinkId) {
|
|
1060
1142
|
const module = requireNativeModule();
|
|
1061
1143
|
const subscription = module.onRecordingTransferProgress((progress) => {
|
|
@@ -1072,21 +1154,48 @@ export const createBotaDeviceSDK = (nativeModule: Spec | null): BotaDeviceSDKCli
|
|
|
1072
1154
|
}
|
|
1073
1155
|
},
|
|
1074
1156
|
|
|
1075
|
-
async syncEncryptedRecordingV2(device, recording, provider, onProgress) {
|
|
1157
|
+
async syncEncryptedRecordingV2(device, recording, provider, onProgress, options = {}) {
|
|
1158
|
+
if (options.signal?.aborted) throw new Error('encrypted upload v2 cancelled');
|
|
1076
1159
|
const module = requireNativeModule();
|
|
1077
|
-
const operationId = createOpaqueId();
|
|
1078
|
-
|
|
1160
|
+
const operationId = options.operationId ?? createOpaqueId();
|
|
1161
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(operationId)) {
|
|
1162
|
+
throw new Error('invalid encrypted upload v2 operation ID');
|
|
1163
|
+
}
|
|
1164
|
+
let active = true;
|
|
1165
|
+
let nativeStarted = false;
|
|
1166
|
+
let cancellation: Promise<void> | undefined;
|
|
1167
|
+
let cancellationFailed = false;
|
|
1168
|
+
const cancel = () => {
|
|
1169
|
+
if (nativeStarted && !cancellation) {
|
|
1170
|
+
try {
|
|
1171
|
+
cancellation = module.cancelEncryptedRecordingV2(operationId).catch(() => { cancellationFailed = true; });
|
|
1172
|
+
} catch {
|
|
1173
|
+
cancellationFailed = true;
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
};
|
|
1177
|
+
let profileSubscription: BotaEventSubscription | undefined;
|
|
1178
|
+
let progressSubscription: BotaEventSubscription | undefined;
|
|
1179
|
+
try {
|
|
1180
|
+
options.signal?.addEventListener('abort', cancel, { once: true });
|
|
1181
|
+
profileSubscription = module.onEncryptedUploadV2ProfileRequested(
|
|
1079
1182
|
(request) => {
|
|
1080
|
-
if (request.operationId !== operationId) return;
|
|
1183
|
+
if (!active || options.signal?.aborted || request.operationId !== operationId) return;
|
|
1081
1184
|
void (async () => {
|
|
1185
|
+
let decision: BotaEncryptedUploadV2ProfileDecision | undefined;
|
|
1082
1186
|
try {
|
|
1083
|
-
|
|
1187
|
+
decision = await provider({
|
|
1188
|
+
operationId,
|
|
1084
1189
|
recording: request.recording,
|
|
1085
1190
|
capability: request.capability,
|
|
1086
1191
|
...(request.checkpoint === undefined
|
|
1087
1192
|
? {}
|
|
1088
1193
|
: { checkpoint: request.checkpoint }),
|
|
1089
1194
|
});
|
|
1195
|
+
if (!active || options.signal?.aborted) {
|
|
1196
|
+
await module.releaseEncryptedUploadV2Material(decision.materialRegistrationId);
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1090
1199
|
if (decision.profile !== 'encrypted_upload_v2') {
|
|
1091
1200
|
throw new Error('encrypted upload v2 requires an explicit v2 profile');
|
|
1092
1201
|
}
|
|
@@ -1095,29 +1204,40 @@ export const createBotaDeviceSDK = (nativeModule: Spec | null): BotaDeviceSDKCli
|
|
|
1095
1204
|
decision
|
|
1096
1205
|
);
|
|
1097
1206
|
} catch (error) {
|
|
1098
|
-
await module.
|
|
1099
|
-
|
|
1100
|
-
'application_material_rejected'
|
|
1101
|
-
|
|
1207
|
+
if (decision) await module.releaseEncryptedUploadV2Material(decision.materialRegistrationId).catch(() => {});
|
|
1208
|
+
if (active && !options.signal?.aborted) {
|
|
1209
|
+
await module.rejectEncryptedUploadV2Profile(request.requestId, 'application_material_rejected');
|
|
1210
|
+
}
|
|
1102
1211
|
}
|
|
1103
1212
|
})().catch(() => {});
|
|
1104
1213
|
}
|
|
1105
1214
|
);
|
|
1106
|
-
|
|
1215
|
+
progressSubscription = module.onEncryptedUploadV2Progress(
|
|
1107
1216
|
(progress) => {
|
|
1108
|
-
if (progress.operationId !== operationId) return;
|
|
1217
|
+
if (!active || options.signal?.aborted || progress.operationId !== operationId) return;
|
|
1109
1218
|
onProgress?.(mapEncryptedUploadV2Progress(progress));
|
|
1110
1219
|
}
|
|
1111
1220
|
);
|
|
1112
|
-
|
|
1221
|
+
if (options.signal?.aborted) throw new Error('encrypted upload v2 cancelled');
|
|
1222
|
+
nativeStarted = true;
|
|
1113
1223
|
await module.syncEncryptedRecordingV2(
|
|
1114
1224
|
toNativeConnectedDevice(device),
|
|
1115
1225
|
recording,
|
|
1116
1226
|
operationId
|
|
1117
1227
|
);
|
|
1118
1228
|
} finally {
|
|
1119
|
-
|
|
1120
|
-
|
|
1229
|
+
active = false;
|
|
1230
|
+
let cleanupFailed = false;
|
|
1231
|
+
for (const remove of [
|
|
1232
|
+
() => options.signal?.removeEventListener('abort', cancel),
|
|
1233
|
+
() => profileSubscription?.remove(),
|
|
1234
|
+
() => progressSubscription?.remove(),
|
|
1235
|
+
]) {
|
|
1236
|
+
try { remove(); } catch { cleanupFailed = true; }
|
|
1237
|
+
}
|
|
1238
|
+
await cancellation;
|
|
1239
|
+
if (cancellationFailed) throw new Error('encrypted upload v2 cancellation failed');
|
|
1240
|
+
if (cleanupFailed) throw new Error('encrypted upload v2 listener cleanup failed');
|
|
1121
1241
|
}
|
|
1122
1242
|
},
|
|
1123
1243
|
|
|
@@ -1148,6 +1268,10 @@ export const createBotaDeviceSDK = (nativeModule: Spec | null): BotaDeviceSDKCli
|
|
|
1148
1268
|
await requireNativeModule().cancelRecordingUpload(taskId);
|
|
1149
1269
|
},
|
|
1150
1270
|
|
|
1271
|
+
async releaseRecordingFile(taskId, localPath) {
|
|
1272
|
+
await requireNativeModule().releaseRecordingFile(taskId, localPath);
|
|
1273
|
+
},
|
|
1274
|
+
|
|
1151
1275
|
async loadUploadQueue() {
|
|
1152
1276
|
return parseUploadQueue(
|
|
1153
1277
|
await requireNativeModule().loadCompatibilityUploadQueue()
|
|
@@ -1278,6 +1402,30 @@ export const createBotaDeviceSDK = (nativeModule: Spec | null): BotaDeviceSDKCli
|
|
|
1278
1402
|
};
|
|
1279
1403
|
|
|
1280
1404
|
const logs: BotaDeviceSDKLogClient = {
|
|
1405
|
+
async readDiagnosticEvents(device) {
|
|
1406
|
+
const batch = await requireNativeModule().readDiagnosticEvents(toNativeConnectedDevice(device));
|
|
1407
|
+
if (batch.schema_version !== 1) throw new Error('Unsupported diagnostic batch schema');
|
|
1408
|
+
return {
|
|
1409
|
+
schema_version: 1,
|
|
1410
|
+
events: batch.events.map(({ report, ...event }) => ({
|
|
1411
|
+
...event,
|
|
1412
|
+
event_type: event.event_type as DeviceDiagnosticEventType,
|
|
1413
|
+
reason_code: event.reason_code as DeviceDiagnosticReasonCode,
|
|
1414
|
+
...(report ? { report: {
|
|
1415
|
+
fault: report.fault,
|
|
1416
|
+
execution: { ...report.execution, pc_trace: [...report.execution.pc_trace] },
|
|
1417
|
+
...(report.runtime ? { runtime: report.runtime } : {}),
|
|
1418
|
+
...(report.breadcrumbs ? { breadcrumbs: [...report.breadcrumbs] } : {}),
|
|
1419
|
+
} } : {}),
|
|
1420
|
+
})),
|
|
1421
|
+
};
|
|
1422
|
+
},
|
|
1423
|
+
async acknowledgeDiagnosticEvents(device, acceptedEventIds) {
|
|
1424
|
+
if (acceptedEventIds.some((id) => !/^[0-9a-f]{16}$/.test(id))) {
|
|
1425
|
+
throw new Error('Diagnostic event_id must be 16 lowercase hex characters');
|
|
1426
|
+
}
|
|
1427
|
+
await requireNativeModule().acknowledgeDiagnosticEvents(toNativeConnectedDevice(device), [...acceptedEventIds]);
|
|
1428
|
+
},
|
|
1281
1429
|
async subscribe(device, onLine) {
|
|
1282
1430
|
const module = requireNativeModule();
|
|
1283
1431
|
const eventSubscription = module.onDeviceLog((line) => {
|
package/src/index.ts
CHANGED
|
@@ -19,6 +19,8 @@ export type {
|
|
|
19
19
|
BotaEncryptedUploadV2ProgressPhase,
|
|
20
20
|
BotaEncryptedUploadV2ProviderContext,
|
|
21
21
|
BotaEncryptedUploadV2Recording,
|
|
22
|
+
BotaEncryptedUploadV2PendingRecording,
|
|
23
|
+
BotaEncryptedUploadV2SyncOptions,
|
|
22
24
|
BotaEncryptedUploadV2SecurityPolicy,
|
|
23
25
|
BotaDeprovisionResult,
|
|
24
26
|
BotaDeviceSDKDeviceClient,
|
|
@@ -62,6 +64,10 @@ export type {
|
|
|
62
64
|
DeviceConnectionSettings,
|
|
63
65
|
DeviceFlags,
|
|
64
66
|
DeviceLogEvent,
|
|
67
|
+
DeviceDiagnosticEvent,
|
|
68
|
+
DeviceDiagnosticEventType,
|
|
69
|
+
DeviceDiagnosticReasonCode,
|
|
70
|
+
DeviceDiagnosticsBatch,
|
|
65
71
|
DeviceState,
|
|
66
72
|
DeviceStatus,
|
|
67
73
|
DeviceType,
|
|
@@ -87,6 +93,9 @@ export type {
|
|
|
87
93
|
export type {
|
|
88
94
|
AudioCodec,
|
|
89
95
|
DeviceRecording,
|
|
96
|
+
RecordingDataStore,
|
|
97
|
+
UploadRecoveryContext,
|
|
98
|
+
UploadRecoveryProvider,
|
|
90
99
|
StreamingSessionEvents,
|
|
91
100
|
StreamingState,
|
|
92
101
|
StreamingSyncOptions,
|
|
@@ -118,13 +127,16 @@ export type {
|
|
|
118
127
|
FirmwareInfo,
|
|
119
128
|
OtaProgress,
|
|
120
129
|
OtaStage,
|
|
130
|
+
RecordingManagerOptions,
|
|
121
131
|
UploadInfoProvider,
|
|
122
132
|
} from './managers/types';
|
|
123
133
|
|
|
124
134
|
export { DeviceLogDecoder } from './ble/deviceLogs';
|
|
135
|
+
export { DeviceDiagnosticsDecoder, diagnosticEventIdCommand } from './ble/deviceDiagnostics';
|
|
125
136
|
export { DeviceManager } from './managers/DeviceManager';
|
|
126
137
|
export { OTAManager } from './managers/OTAManager';
|
|
127
138
|
export { RecordingManager } from './managers/RecordingManager';
|
|
139
|
+
export const UPLOAD_RECOVERY_VERSION = 1;
|
|
128
140
|
export { StreamingSession } from './managers/StreamingSession';
|
|
129
141
|
export {
|
|
130
142
|
BotaError,
|
|
@@ -16,6 +16,7 @@ import type {
|
|
|
16
16
|
ConnectedDevice,
|
|
17
17
|
DeviceConnectionSettings,
|
|
18
18
|
DeviceLogEvent,
|
|
19
|
+
DeviceDiagnosticsBatch,
|
|
19
20
|
DeviceStatus,
|
|
20
21
|
DeviceWiFiScanResult,
|
|
21
22
|
DiscoveredDevice,
|
|
@@ -485,6 +486,16 @@ export class DeviceManager extends EventEmitter<DeviceManagerEvents> {
|
|
|
485
486
|
});
|
|
486
487
|
}
|
|
487
488
|
|
|
489
|
+
async readDiagnosticEvents(device: ConnectedDevice): Promise<DeviceDiagnosticsBatch> {
|
|
490
|
+
this.requireConnected(device.id);
|
|
491
|
+
return this.client.logs.readDiagnosticEvents(device);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
async acknowledgeDiagnosticEvents(device: ConnectedDevice, acceptedEventIds: string[]): Promise<void> {
|
|
495
|
+
this.requireConnected(device.id);
|
|
496
|
+
await this.client.logs.acknowledgeDiagnosticEvents(device, acceptedEventIds);
|
|
497
|
+
}
|
|
498
|
+
|
|
488
499
|
async readConnectionSettings(
|
|
489
500
|
device: ConnectedDevice
|
|
490
501
|
): Promise<DeviceConnectionSettings> {
|