@bota-dev/react-native-sdk 0.0.2

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 (177) hide show
  1. package/README.md +279 -0
  2. package/lib/commonjs/BotaClient.js +223 -0
  3. package/lib/commonjs/BotaClient.js.map +1 -0
  4. package/lib/commonjs/ble/BleManager.js +494 -0
  5. package/lib/commonjs/ble/BleManager.js.map +1 -0
  6. package/lib/commonjs/ble/constants.js +166 -0
  7. package/lib/commonjs/ble/constants.js.map +1 -0
  8. package/lib/commonjs/ble/index.js +54 -0
  9. package/lib/commonjs/ble/index.js.map +1 -0
  10. package/lib/commonjs/ble/parsers.js +345 -0
  11. package/lib/commonjs/ble/parsers.js.map +1 -0
  12. package/lib/commonjs/index.js +81 -0
  13. package/lib/commonjs/index.js.map +1 -0
  14. package/lib/commonjs/managers/DeviceManager.js +437 -0
  15. package/lib/commonjs/managers/DeviceManager.js.map +1 -0
  16. package/lib/commonjs/managers/OTAManager.js +227 -0
  17. package/lib/commonjs/managers/OTAManager.js.map +1 -0
  18. package/lib/commonjs/managers/RecordingManager.js +384 -0
  19. package/lib/commonjs/managers/RecordingManager.js.map +1 -0
  20. package/lib/commonjs/managers/index.js +27 -0
  21. package/lib/commonjs/managers/index.js.map +1 -0
  22. package/lib/commonjs/models/Device.js +2 -0
  23. package/lib/commonjs/models/Device.js.map +1 -0
  24. package/lib/commonjs/models/Recording.js +2 -0
  25. package/lib/commonjs/models/Recording.js.map +1 -0
  26. package/lib/commonjs/models/Status.js +6 -0
  27. package/lib/commonjs/models/Status.js.map +1 -0
  28. package/lib/commonjs/models/index.js +39 -0
  29. package/lib/commonjs/models/index.js.map +1 -0
  30. package/lib/commonjs/protocol/ProtocolHandler.js +343 -0
  31. package/lib/commonjs/protocol/ProtocolHandler.js.map +1 -0
  32. package/lib/commonjs/protocol/index.js +13 -0
  33. package/lib/commonjs/protocol/index.js.map +1 -0
  34. package/lib/commonjs/storage/StorageManager.js +333 -0
  35. package/lib/commonjs/storage/StorageManager.js.map +1 -0
  36. package/lib/commonjs/storage/index.js +19 -0
  37. package/lib/commonjs/storage/index.js.map +1 -0
  38. package/lib/commonjs/upload/S3Uploader.js +133 -0
  39. package/lib/commonjs/upload/S3Uploader.js.map +1 -0
  40. package/lib/commonjs/upload/UploadQueue.js +280 -0
  41. package/lib/commonjs/upload/UploadQueue.js.map +1 -0
  42. package/lib/commonjs/upload/index.js +20 -0
  43. package/lib/commonjs/upload/index.js.map +1 -0
  44. package/lib/commonjs/utils/errors.js +187 -0
  45. package/lib/commonjs/utils/errors.js.map +1 -0
  46. package/lib/commonjs/utils/index.js +40 -0
  47. package/lib/commonjs/utils/index.js.map +1 -0
  48. package/lib/commonjs/utils/logger.js +135 -0
  49. package/lib/commonjs/utils/logger.js.map +1 -0
  50. package/lib/commonjs/utils/retry.js +160 -0
  51. package/lib/commonjs/utils/retry.js.map +1 -0
  52. package/lib/module/BotaClient.js +216 -0
  53. package/lib/module/BotaClient.js.map +1 -0
  54. package/lib/module/ble/BleManager.js +484 -0
  55. package/lib/module/ble/BleManager.js.map +1 -0
  56. package/lib/module/ble/constants.js +159 -0
  57. package/lib/module/ble/constants.js.map +1 -0
  58. package/lib/module/ble/index.js +8 -0
  59. package/lib/module/ble/index.js.map +1 -0
  60. package/lib/module/ble/parsers.js +328 -0
  61. package/lib/module/ble/parsers.js.map +1 -0
  62. package/lib/module/index.js +22 -0
  63. package/lib/module/index.js.map +1 -0
  64. package/lib/module/managers/DeviceManager.js +429 -0
  65. package/lib/module/managers/DeviceManager.js.map +1 -0
  66. package/lib/module/managers/OTAManager.js +219 -0
  67. package/lib/module/managers/OTAManager.js.map +1 -0
  68. package/lib/module/managers/RecordingManager.js +376 -0
  69. package/lib/module/managers/RecordingManager.js.map +1 -0
  70. package/lib/module/managers/index.js +8 -0
  71. package/lib/module/managers/index.js.map +1 -0
  72. package/lib/module/models/Device.js +2 -0
  73. package/lib/module/models/Device.js.map +1 -0
  74. package/lib/module/models/Recording.js +2 -0
  75. package/lib/module/models/Recording.js.map +1 -0
  76. package/lib/module/models/Status.js +2 -0
  77. package/lib/module/models/Status.js.map +1 -0
  78. package/lib/module/models/index.js +8 -0
  79. package/lib/module/models/index.js.map +1 -0
  80. package/lib/module/protocol/ProtocolHandler.js +336 -0
  81. package/lib/module/protocol/ProtocolHandler.js.map +1 -0
  82. package/lib/module/protocol/index.js +6 -0
  83. package/lib/module/protocol/index.js.map +1 -0
  84. package/lib/module/storage/StorageManager.js +324 -0
  85. package/lib/module/storage/StorageManager.js.map +1 -0
  86. package/lib/module/storage/index.js +6 -0
  87. package/lib/module/storage/index.js.map +1 -0
  88. package/lib/module/upload/S3Uploader.js +126 -0
  89. package/lib/module/upload/S3Uploader.js.map +1 -0
  90. package/lib/module/upload/UploadQueue.js +272 -0
  91. package/lib/module/upload/UploadQueue.js.map +1 -0
  92. package/lib/module/upload/index.js +7 -0
  93. package/lib/module/upload/index.js.map +1 -0
  94. package/lib/module/utils/errors.js +173 -0
  95. package/lib/module/utils/errors.js.map +1 -0
  96. package/lib/module/utils/index.js +8 -0
  97. package/lib/module/utils/index.js.map +1 -0
  98. package/lib/module/utils/logger.js +129 -0
  99. package/lib/module/utils/logger.js.map +1 -0
  100. package/lib/module/utils/retry.js +149 -0
  101. package/lib/module/utils/retry.js.map +1 -0
  102. package/lib/typescript/src/BotaClient.d.ts +77 -0
  103. package/lib/typescript/src/BotaClient.d.ts.map +1 -0
  104. package/lib/typescript/src/ble/BleManager.d.ts +111 -0
  105. package/lib/typescript/src/ble/BleManager.d.ts.map +1 -0
  106. package/lib/typescript/src/ble/constants.d.ts +111 -0
  107. package/lib/typescript/src/ble/constants.d.ts.map +1 -0
  108. package/lib/typescript/src/ble/index.d.ts +7 -0
  109. package/lib/typescript/src/ble/index.d.ts.map +1 -0
  110. package/lib/typescript/src/ble/parsers.d.ts +100 -0
  111. package/lib/typescript/src/ble/parsers.d.ts.map +1 -0
  112. package/lib/typescript/src/index.d.ts +16 -0
  113. package/lib/typescript/src/index.d.ts.map +1 -0
  114. package/lib/typescript/src/managers/DeviceManager.d.ts +84 -0
  115. package/lib/typescript/src/managers/DeviceManager.d.ts.map +1 -0
  116. package/lib/typescript/src/managers/OTAManager.d.ts +78 -0
  117. package/lib/typescript/src/managers/OTAManager.d.ts.map +1 -0
  118. package/lib/typescript/src/managers/RecordingManager.d.ts +90 -0
  119. package/lib/typescript/src/managers/RecordingManager.d.ts.map +1 -0
  120. package/lib/typescript/src/managers/index.d.ts +7 -0
  121. package/lib/typescript/src/managers/index.d.ts.map +1 -0
  122. package/lib/typescript/src/models/Device.d.ts +139 -0
  123. package/lib/typescript/src/models/Device.d.ts.map +1 -0
  124. package/lib/typescript/src/models/Recording.d.ts +110 -0
  125. package/lib/typescript/src/models/Recording.d.ts.map +1 -0
  126. package/lib/typescript/src/models/Status.d.ts +104 -0
  127. package/lib/typescript/src/models/Status.d.ts.map +1 -0
  128. package/lib/typescript/src/models/index.d.ts +7 -0
  129. package/lib/typescript/src/models/index.d.ts.map +1 -0
  130. package/lib/typescript/src/protocol/ProtocolHandler.d.ts +69 -0
  131. package/lib/typescript/src/protocol/ProtocolHandler.d.ts.map +1 -0
  132. package/lib/typescript/src/protocol/index.d.ts +5 -0
  133. package/lib/typescript/src/protocol/index.d.ts.map +1 -0
  134. package/lib/typescript/src/storage/StorageManager.d.ts +116 -0
  135. package/lib/typescript/src/storage/StorageManager.d.ts.map +1 -0
  136. package/lib/typescript/src/storage/index.d.ts +5 -0
  137. package/lib/typescript/src/storage/index.d.ts.map +1 -0
  138. package/lib/typescript/src/upload/S3Uploader.d.ts +38 -0
  139. package/lib/typescript/src/upload/S3Uploader.d.ts.map +1 -0
  140. package/lib/typescript/src/upload/UploadQueue.d.ts +95 -0
  141. package/lib/typescript/src/upload/UploadQueue.d.ts.map +1 -0
  142. package/lib/typescript/src/upload/index.d.ts +6 -0
  143. package/lib/typescript/src/upload/index.d.ts.map +1 -0
  144. package/lib/typescript/src/utils/errors.d.ts +82 -0
  145. package/lib/typescript/src/utils/errors.d.ts.map +1 -0
  146. package/lib/typescript/src/utils/index.d.ts +7 -0
  147. package/lib/typescript/src/utils/index.d.ts.map +1 -0
  148. package/lib/typescript/src/utils/logger.d.ts +68 -0
  149. package/lib/typescript/src/utils/logger.d.ts.map +1 -0
  150. package/lib/typescript/src/utils/retry.d.ts +53 -0
  151. package/lib/typescript/src/utils/retry.d.ts.map +1 -0
  152. package/package.json +95 -0
  153. package/src/BotaClient.ts +238 -0
  154. package/src/ble/BleManager.ts +573 -0
  155. package/src/ble/constants.ts +158 -0
  156. package/src/ble/index.ts +7 -0
  157. package/src/ble/parsers.ts +395 -0
  158. package/src/index.ts +64 -0
  159. package/src/managers/DeviceManager.ts +545 -0
  160. package/src/managers/OTAManager.ts +263 -0
  161. package/src/managers/RecordingManager.ts +434 -0
  162. package/src/managers/index.ts +12 -0
  163. package/src/models/Device.ts +164 -0
  164. package/src/models/Recording.ts +123 -0
  165. package/src/models/Status.ts +126 -0
  166. package/src/models/index.ts +7 -0
  167. package/src/protocol/ProtocolHandler.ts +459 -0
  168. package/src/protocol/index.ts +5 -0
  169. package/src/storage/StorageManager.ts +343 -0
  170. package/src/storage/index.ts +5 -0
  171. package/src/upload/S3Uploader.ts +164 -0
  172. package/src/upload/UploadQueue.ts +310 -0
  173. package/src/upload/index.ts +6 -0
  174. package/src/utils/errors.ts +310 -0
  175. package/src/utils/index.ts +7 -0
  176. package/src/utils/logger.ts +137 -0
  177. package/src/utils/retry.ts +177 -0
@@ -0,0 +1,310 @@
1
+ /**
2
+ * Upload Queue - Manages persistent upload queue with retry logic
3
+ */
4
+
5
+ import EventEmitter from 'eventemitter3';
6
+
7
+ import type { UploadTask } from '../models/Recording';
8
+ import { StorageManager, generateTaskId } from '../storage/StorageManager';
9
+ import { S3Uploader } from './S3Uploader';
10
+ import { shouldContinueRetrying } from '../utils/retry';
11
+ import { logger } from '../utils/logger';
12
+
13
+ const log = logger.tag('UploadQueue');
14
+
15
+ /**
16
+ * Events emitted by UploadQueue
17
+ */
18
+ interface UploadQueueEvents {
19
+ taskAdded: (task: UploadTask) => void;
20
+ taskUpdated: (task: UploadTask) => void;
21
+ taskCompleted: (taskId: string, recordingId: string) => void;
22
+ taskFailed: (taskId: string, error: Error) => void;
23
+ uploadProgress: (taskId: string, progress: number) => void;
24
+ queueEmpty: () => void;
25
+ }
26
+
27
+ /**
28
+ * Upload Queue configuration
29
+ */
30
+ export interface UploadQueueConfig {
31
+ /** Maximum concurrent uploads (default: 2) */
32
+ maxConcurrent?: number;
33
+ /** Maximum retry attempts (default: 6) */
34
+ maxRetries?: number;
35
+ /** Auto-start processing (default: true) */
36
+ autoStart?: boolean;
37
+ }
38
+
39
+ /**
40
+ * Upload Queue class
41
+ */
42
+ export class UploadQueue extends EventEmitter<UploadQueueEvents> {
43
+ private storage: StorageManager;
44
+ private uploader: S3Uploader;
45
+ private maxConcurrent: number;
46
+ private maxRetries: number;
47
+ private activeUploads: Set<string> = new Set();
48
+ private isPaused = false;
49
+ private isProcessing = false;
50
+ private abortControllers: Map<string, AbortController> = new Map();
51
+
52
+ constructor(storage: StorageManager, config: UploadQueueConfig = {}) {
53
+ super();
54
+ this.storage = storage;
55
+ this.uploader = new S3Uploader();
56
+ this.maxConcurrent = config.maxConcurrent ?? 2;
57
+ this.maxRetries = config.maxRetries ?? 6;
58
+
59
+ if (config.autoStart !== false) {
60
+ this.processQueue();
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Add a new upload task to the queue
66
+ */
67
+ async enqueue(params: {
68
+ recordingId: string;
69
+ deviceId: string;
70
+ localPath: string;
71
+ uploadUrl: string;
72
+ uploadToken: string;
73
+ completeUrl: string;
74
+ }): Promise<UploadTask> {
75
+ const task: UploadTask = {
76
+ id: generateTaskId(),
77
+ recordingId: params.recordingId,
78
+ deviceId: params.deviceId,
79
+ localPath: params.localPath,
80
+ uploadUrl: params.uploadUrl,
81
+ uploadToken: params.uploadToken,
82
+ completeUrl: params.completeUrl,
83
+ status: 'pending',
84
+ retryCount: 0,
85
+ createdAt: new Date(),
86
+ updatedAt: new Date(),
87
+ };
88
+
89
+ await this.storage.addUploadTask(task);
90
+ this.emit('taskAdded', task);
91
+
92
+ log.info('Task enqueued', { taskId: task.id, recordingId: task.recordingId });
93
+
94
+ // Trigger queue processing
95
+ this.processQueue();
96
+
97
+ return task;
98
+ }
99
+
100
+ /**
101
+ * Cancel a specific upload task
102
+ */
103
+ async cancel(taskId: string): Promise<void> {
104
+ log.info('Cancelling task', { taskId });
105
+
106
+ // Abort if currently uploading
107
+ const controller = this.abortControllers.get(taskId);
108
+ if (controller) {
109
+ controller.abort();
110
+ this.abortControllers.delete(taskId);
111
+ }
112
+
113
+ // Remove from active uploads
114
+ this.activeUploads.delete(taskId);
115
+
116
+ // Remove from queue
117
+ await this.storage.removeUploadTask(taskId);
118
+ }
119
+
120
+ /**
121
+ * Cancel all uploads
122
+ */
123
+ async cancelAll(): Promise<void> {
124
+ log.info('Cancelling all uploads');
125
+
126
+ // Abort all active uploads
127
+ for (const controller of this.abortControllers.values()) {
128
+ controller.abort();
129
+ }
130
+ this.abortControllers.clear();
131
+ this.activeUploads.clear();
132
+
133
+ // Clear queue
134
+ await this.storage.clearAllTasks();
135
+ this.emit('queueEmpty');
136
+ }
137
+
138
+ /**
139
+ * Pause queue processing
140
+ */
141
+ pause(): void {
142
+ log.info('Pausing upload queue');
143
+ this.isPaused = true;
144
+ }
145
+
146
+ /**
147
+ * Resume queue processing
148
+ */
149
+ resume(): void {
150
+ log.info('Resuming upload queue');
151
+ this.isPaused = false;
152
+ this.processQueue();
153
+ }
154
+
155
+ /**
156
+ * Retry all failed uploads
157
+ */
158
+ async retryFailed(): Promise<void> {
159
+ log.info('Retrying failed uploads');
160
+
161
+ const failedTasks = this.storage.getFailedUploads();
162
+ for (const task of failedTasks) {
163
+ if (task.retryCount < this.maxRetries) {
164
+ await this.storage.updateTaskStatus(task.id, 'pending');
165
+ }
166
+ }
167
+
168
+ this.processQueue();
169
+ }
170
+
171
+ /**
172
+ * Get all pending tasks
173
+ */
174
+ getPendingTasks(): UploadTask[] {
175
+ return this.storage.getPendingUploads();
176
+ }
177
+
178
+ /**
179
+ * Get all tasks
180
+ */
181
+ getAllTasks(): UploadTask[] {
182
+ return this.storage.getUploadQueue();
183
+ }
184
+
185
+ /**
186
+ * Process the upload queue
187
+ */
188
+ private async processQueue(): Promise<void> {
189
+ if (this.isPaused || this.isProcessing) {
190
+ return;
191
+ }
192
+
193
+ this.isProcessing = true;
194
+
195
+ try {
196
+ while (!this.isPaused) {
197
+ // Check if we can start more uploads
198
+ if (this.activeUploads.size >= this.maxConcurrent) {
199
+ break;
200
+ }
201
+
202
+ // Get next pending task
203
+ const pendingTasks = this.storage.getPendingUploads();
204
+ const nextTask = pendingTasks.find(
205
+ (t) => !this.activeUploads.has(t.id) && t.status === 'pending'
206
+ );
207
+
208
+ if (!nextTask) {
209
+ // No more tasks to process
210
+ if (this.activeUploads.size === 0) {
211
+ this.emit('queueEmpty');
212
+ }
213
+ break;
214
+ }
215
+
216
+ // Start upload in background
217
+ this.processTask(nextTask);
218
+ }
219
+ } finally {
220
+ this.isProcessing = false;
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Process a single upload task
226
+ */
227
+ private async processTask(task: UploadTask): Promise<void> {
228
+ this.activeUploads.add(task.id);
229
+
230
+ const abortController = new AbortController();
231
+ this.abortControllers.set(task.id, abortController);
232
+
233
+ try {
234
+ // Update status to uploading
235
+ await this.storage.updateTaskStatus(task.id, 'uploading');
236
+ this.emit('taskUpdated', { ...task, status: 'uploading' });
237
+
238
+ log.info('Starting upload', { taskId: task.id, recordingId: task.recordingId });
239
+
240
+ // Load the audio data
241
+ const audioData = await this.storage.loadRecordingData(task.localPath);
242
+
243
+ // Upload to S3
244
+ await this.uploader.upload(audioData, task.uploadUrl, {
245
+ onProgress: (progress) => {
246
+ this.emit('uploadProgress', task.id, progress);
247
+ },
248
+ abortSignal: abortController.signal,
249
+ });
250
+
251
+ // Notify completion
252
+ await this.uploader.notifyCompletion(
253
+ task.completeUrl,
254
+ task.recordingId,
255
+ task.uploadToken
256
+ );
257
+
258
+ // Mark as completed
259
+ await this.storage.updateTaskStatus(task.id, 'completed');
260
+
261
+ // Clean up local file
262
+ await this.storage.deleteRecordingData(task.localPath);
263
+
264
+ log.info('Upload completed', { taskId: task.id, recordingId: task.recordingId });
265
+
266
+ this.emit('taskCompleted', task.id, task.recordingId);
267
+ } catch (error) {
268
+ const err = error as Error;
269
+ log.error('Upload failed', err, { taskId: task.id });
270
+
271
+ // Check if we should retry
272
+ if (
273
+ task.retryCount < this.maxRetries &&
274
+ shouldContinueRetrying(task.createdAt)
275
+ ) {
276
+ await this.storage.incrementRetryCount(task.id);
277
+ await this.storage.updateTaskStatus(task.id, 'pending', err.message);
278
+ log.info('Task will be retried', {
279
+ taskId: task.id,
280
+ retryCount: task.retryCount + 1,
281
+ });
282
+ } else {
283
+ await this.storage.updateTaskStatus(task.id, 'failed', err.message);
284
+ this.emit('taskFailed', task.id, err);
285
+ }
286
+ } finally {
287
+ this.activeUploads.delete(task.id);
288
+ this.abortControllers.delete(task.id);
289
+
290
+ // Process next task
291
+ this.processQueue();
292
+ }
293
+ }
294
+
295
+ /**
296
+ * Clean up resources
297
+ */
298
+ destroy(): void {
299
+ this.isPaused = true;
300
+
301
+ // Abort all active uploads
302
+ for (const controller of this.abortControllers.values()) {
303
+ controller.abort();
304
+ }
305
+ this.abortControllers.clear();
306
+ this.activeUploads.clear();
307
+
308
+ this.removeAllListeners();
309
+ }
310
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Upload module exports
3
+ */
4
+
5
+ export { S3Uploader, type UploadProgressCallback, type UploadOptions } from './S3Uploader';
6
+ export { UploadQueue, type UploadQueueConfig } from './UploadQueue';
@@ -0,0 +1,310 @@
1
+ /**
2
+ * Custom error types for the Bota SDK
3
+ */
4
+
5
+ /**
6
+ * Base error class for all Bota SDK errors
7
+ */
8
+ export class BotaError extends Error {
9
+ constructor(
10
+ message: string,
11
+ public readonly code: string,
12
+ public readonly cause?: Error
13
+ ) {
14
+ super(message);
15
+ this.name = 'BotaError';
16
+ Object.setPrototypeOf(this, BotaError.prototype);
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Bluetooth-related errors
22
+ */
23
+ export class BluetoothError extends BotaError {
24
+ constructor(message: string, code: string, cause?: Error) {
25
+ super(message, code, cause);
26
+ this.name = 'BluetoothError';
27
+ Object.setPrototypeOf(this, BluetoothError.prototype);
28
+ }
29
+
30
+ static unavailable(): BluetoothError {
31
+ return new BluetoothError(
32
+ 'Bluetooth is not available on this device',
33
+ 'BLUETOOTH_UNAVAILABLE'
34
+ );
35
+ }
36
+
37
+ static unauthorized(): BluetoothError {
38
+ return new BluetoothError(
39
+ 'Bluetooth permission not granted',
40
+ 'BLUETOOTH_UNAUTHORIZED'
41
+ );
42
+ }
43
+
44
+ static poweredOff(): BluetoothError {
45
+ return new BluetoothError('Bluetooth is powered off', 'BLUETOOTH_OFF');
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Device-related errors
51
+ */
52
+ export class DeviceError extends BotaError {
53
+ constructor(
54
+ message: string,
55
+ code: string,
56
+ public readonly deviceId?: string,
57
+ cause?: Error
58
+ ) {
59
+ super(message, code, cause);
60
+ this.name = 'DeviceError';
61
+ Object.setPrototypeOf(this, DeviceError.prototype);
62
+ }
63
+
64
+ static notFound(deviceId: string): DeviceError {
65
+ return new DeviceError(
66
+ `Device ${deviceId} not found`,
67
+ 'DEVICE_NOT_FOUND',
68
+ deviceId
69
+ );
70
+ }
71
+
72
+ static connectionFailed(deviceId: string, cause?: Error): DeviceError {
73
+ return new DeviceError(
74
+ `Failed to connect to device ${deviceId}`,
75
+ 'CONNECTION_FAILED',
76
+ deviceId,
77
+ cause
78
+ );
79
+ }
80
+
81
+ static connectionLost(deviceId: string, during?: string): DeviceError {
82
+ const message = during
83
+ ? `Connection to device ${deviceId} lost during ${during}`
84
+ : `Connection to device ${deviceId} lost`;
85
+ return new DeviceError(message, 'CONNECTION_LOST', deviceId);
86
+ }
87
+
88
+ static bondingFailed(deviceId: string, cause?: Error): DeviceError {
89
+ return new DeviceError(
90
+ `Bonding failed for device ${deviceId}`,
91
+ 'BONDING_FAILED',
92
+ deviceId,
93
+ cause
94
+ );
95
+ }
96
+
97
+ static notConnected(deviceId: string): DeviceError {
98
+ return new DeviceError(
99
+ `Device ${deviceId} is not connected`,
100
+ 'NOT_CONNECTED',
101
+ deviceId
102
+ );
103
+ }
104
+
105
+ static alreadyConnected(deviceId: string): DeviceError {
106
+ return new DeviceError(
107
+ `Device ${deviceId} is already connected`,
108
+ 'ALREADY_CONNECTED',
109
+ deviceId
110
+ );
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Provisioning-related errors
116
+ */
117
+ export class ProvisioningError extends BotaError {
118
+ constructor(
119
+ message: string,
120
+ code: string,
121
+ public readonly deviceId?: string,
122
+ cause?: Error
123
+ ) {
124
+ super(message, code, cause);
125
+ this.name = 'ProvisioningError';
126
+ Object.setPrototypeOf(this, ProvisioningError.prototype);
127
+ }
128
+
129
+ static invalidToken(deviceId: string): ProvisioningError {
130
+ return new ProvisioningError(
131
+ 'Device rejected the token as invalid',
132
+ 'INVALID_TOKEN',
133
+ deviceId
134
+ );
135
+ }
136
+
137
+ static storageError(deviceId: string): ProvisioningError {
138
+ return new ProvisioningError(
139
+ 'Device failed to store the token',
140
+ 'STORAGE_ERROR',
141
+ deviceId
142
+ );
143
+ }
144
+
145
+ static chunkError(deviceId: string): ProvisioningError {
146
+ return new ProvisioningError(
147
+ 'Token chunk transfer failed',
148
+ 'CHUNK_ERROR',
149
+ deviceId
150
+ );
151
+ }
152
+
153
+ static alreadyProvisioned(deviceId: string): ProvisioningError {
154
+ return new ProvisioningError(
155
+ 'Device is already provisioned',
156
+ 'ALREADY_PROVISIONED',
157
+ deviceId
158
+ );
159
+ }
160
+
161
+ static timeout(deviceId: string): ProvisioningError {
162
+ return new ProvisioningError(
163
+ 'Provisioning timed out waiting for device response',
164
+ 'PROVISIONING_TIMEOUT',
165
+ deviceId
166
+ );
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Transfer-related errors
172
+ */
173
+ export class TransferError extends BotaError {
174
+ constructor(
175
+ message: string,
176
+ code: string,
177
+ public readonly recordingUuid?: string,
178
+ cause?: Error
179
+ ) {
180
+ super(message, code, cause);
181
+ this.name = 'TransferError';
182
+ Object.setPrototypeOf(this, TransferError.prototype);
183
+ }
184
+
185
+ static recordingNotFound(uuid: string): TransferError {
186
+ return new TransferError(
187
+ `Recording ${uuid} not found on device`,
188
+ 'RECORDING_NOT_FOUND',
189
+ uuid
190
+ );
191
+ }
192
+
193
+ static checksumMismatch(uuid: string): TransferError {
194
+ return new TransferError(
195
+ `Checksum mismatch for recording ${uuid}`,
196
+ 'CHECKSUM_MISMATCH',
197
+ uuid
198
+ );
199
+ }
200
+
201
+ static interrupted(uuid: string, cause?: Error): TransferError {
202
+ return new TransferError(
203
+ `Transfer interrupted for recording ${uuid}`,
204
+ 'TRANSFER_INTERRUPTED',
205
+ uuid,
206
+ cause
207
+ );
208
+ }
209
+
210
+ static timeout(uuid: string): TransferError {
211
+ return new TransferError(
212
+ `Transfer timed out for recording ${uuid}`,
213
+ 'TRANSFER_TIMEOUT',
214
+ uuid
215
+ );
216
+ }
217
+
218
+ static deviceError(uuid: string, errorCode: number): TransferError {
219
+ return new TransferError(
220
+ `Device error (code: ${errorCode}) during transfer of ${uuid}`,
221
+ 'DEVICE_ERROR',
222
+ uuid
223
+ );
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Upload-related errors
229
+ */
230
+ export class UploadError extends BotaError {
231
+ constructor(
232
+ message: string,
233
+ code: string,
234
+ public readonly taskId?: string,
235
+ cause?: Error
236
+ ) {
237
+ super(message, code, cause);
238
+ this.name = 'UploadError';
239
+ Object.setPrototypeOf(this, UploadError.prototype);
240
+ }
241
+
242
+ static urlExpired(taskId: string): UploadError {
243
+ return new UploadError(
244
+ 'Upload URL has expired',
245
+ 'URL_EXPIRED',
246
+ taskId
247
+ );
248
+ }
249
+
250
+ static networkUnavailable(): UploadError {
251
+ return new UploadError(
252
+ 'Network is not available',
253
+ 'NETWORK_UNAVAILABLE'
254
+ );
255
+ }
256
+
257
+ static uploadFailed(taskId: string, cause?: Error): UploadError {
258
+ return new UploadError(
259
+ `Upload failed for task ${taskId}`,
260
+ 'UPLOAD_FAILED',
261
+ taskId,
262
+ cause
263
+ );
264
+ }
265
+
266
+ static completionFailed(taskId: string, cause?: Error): UploadError {
267
+ return new UploadError(
268
+ `Failed to notify completion for task ${taskId}`,
269
+ 'COMPLETION_FAILED',
270
+ taskId,
271
+ cause
272
+ );
273
+ }
274
+ }
275
+
276
+ /**
277
+ * SDK state errors
278
+ */
279
+ export class SdkError extends BotaError {
280
+ constructor(message: string, code: string, cause?: Error) {
281
+ super(message, code, cause);
282
+ this.name = 'SdkError';
283
+ Object.setPrototypeOf(this, SdkError.prototype);
284
+ }
285
+
286
+ static notInitialized(): SdkError {
287
+ return new SdkError(
288
+ 'SDK has not been initialized. Call BotaClient.configure() first.',
289
+ 'NOT_INITIALIZED'
290
+ );
291
+ }
292
+
293
+ static invalidState(expected: string, actual: string): SdkError {
294
+ return new SdkError(
295
+ `Invalid state: expected ${expected}, got ${actual}`,
296
+ 'INVALID_STATE'
297
+ );
298
+ }
299
+
300
+ static timeout(operation: string): SdkError {
301
+ return new SdkError(`Operation timed out: ${operation}`, 'TIMEOUT');
302
+ }
303
+ }
304
+
305
+ /**
306
+ * Type guard to check if an error is a BotaError
307
+ */
308
+ export function isBotaError(error: unknown): error is BotaError {
309
+ return error instanceof BotaError;
310
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Utils module exports
3
+ */
4
+
5
+ export * from './errors';
6
+ export { logger } from './logger';
7
+ export * from './retry';