react-native-ble-mesh 2.0.0 → 2.1.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 (93) hide show
  1. package/README.md +2 -2
  2. package/docs/OPTIMIZATION.md +165 -52
  3. package/package.json +1 -1
  4. package/src/MeshNetwork.js +63 -53
  5. package/src/constants/audio.js +4 -4
  6. package/src/constants/ble.js +1 -1
  7. package/src/constants/crypto.js +1 -1
  8. package/src/constants/errors.js +2 -2
  9. package/src/constants/events.js +1 -1
  10. package/src/constants/protocol.js +2 -2
  11. package/src/crypto/AutoCrypto.js +16 -3
  12. package/src/crypto/CryptoProvider.js +17 -17
  13. package/src/crypto/providers/ExpoCryptoProvider.js +15 -9
  14. package/src/crypto/providers/QuickCryptoProvider.js +41 -12
  15. package/src/crypto/providers/TweetNaClProvider.js +10 -8
  16. package/src/errors/AudioError.js +2 -1
  17. package/src/errors/ConnectionError.js +2 -2
  18. package/src/errors/CryptoError.js +1 -1
  19. package/src/errors/HandshakeError.js +2 -2
  20. package/src/errors/MeshError.js +4 -4
  21. package/src/errors/MessageError.js +2 -2
  22. package/src/errors/ValidationError.js +3 -3
  23. package/src/expo/withBLEMesh.js +10 -10
  24. package/src/hooks/AppStateManager.js +11 -2
  25. package/src/hooks/useMesh.js +23 -10
  26. package/src/hooks/useMessages.js +17 -16
  27. package/src/hooks/usePeers.js +19 -14
  28. package/src/index.js +2 -2
  29. package/src/mesh/dedup/BloomFilter.js +45 -57
  30. package/src/mesh/dedup/DedupManager.js +36 -8
  31. package/src/mesh/dedup/MessageCache.js +3 -0
  32. package/src/mesh/fragment/Assembler.js +5 -4
  33. package/src/mesh/fragment/Fragmenter.js +3 -3
  34. package/src/mesh/monitor/ConnectionQuality.js +59 -25
  35. package/src/mesh/monitor/NetworkMonitor.js +80 -28
  36. package/src/mesh/peer/Peer.js +9 -11
  37. package/src/mesh/peer/PeerDiscovery.js +18 -19
  38. package/src/mesh/peer/PeerManager.js +29 -17
  39. package/src/mesh/router/MessageRouter.js +28 -20
  40. package/src/mesh/router/PathFinder.js +10 -13
  41. package/src/mesh/router/RouteTable.js +25 -14
  42. package/src/mesh/store/StoreAndForwardManager.js +32 -24
  43. package/src/protocol/deserializer.js +9 -10
  44. package/src/protocol/header.js +13 -7
  45. package/src/protocol/message.js +18 -14
  46. package/src/protocol/serializer.js +9 -12
  47. package/src/protocol/validator.js +29 -10
  48. package/src/service/BatteryOptimizer.js +22 -18
  49. package/src/service/EmergencyManager.js +18 -25
  50. package/src/service/HandshakeManager.js +112 -18
  51. package/src/service/MeshService.js +106 -22
  52. package/src/service/SessionManager.js +50 -13
  53. package/src/service/audio/AudioManager.js +80 -38
  54. package/src/service/audio/buffer/FrameBuffer.js +7 -8
  55. package/src/service/audio/buffer/JitterBuffer.js +1 -1
  56. package/src/service/audio/codec/LC3Codec.js +18 -19
  57. package/src/service/audio/codec/LC3Decoder.js +10 -10
  58. package/src/service/audio/codec/LC3Encoder.js +11 -9
  59. package/src/service/audio/session/AudioSession.js +14 -17
  60. package/src/service/audio/session/VoiceMessage.js +15 -22
  61. package/src/service/audio/transport/AudioFragmenter.js +17 -9
  62. package/src/service/audio/transport/AudioFramer.js +8 -12
  63. package/src/service/file/FileAssembler.js +4 -2
  64. package/src/service/file/FileChunker.js +1 -1
  65. package/src/service/file/FileManager.js +26 -20
  66. package/src/service/file/FileMessage.js +7 -12
  67. package/src/service/text/TextManager.js +75 -42
  68. package/src/service/text/broadcast/BroadcastManager.js +14 -17
  69. package/src/service/text/channel/Channel.js +10 -14
  70. package/src/service/text/channel/ChannelManager.js +10 -10
  71. package/src/service/text/message/TextMessage.js +12 -19
  72. package/src/service/text/message/TextSerializer.js +2 -2
  73. package/src/storage/AsyncStorageAdapter.js +17 -14
  74. package/src/storage/MemoryStorage.js +11 -8
  75. package/src/storage/MessageStore.js +77 -32
  76. package/src/storage/Storage.js +9 -9
  77. package/src/transport/BLETransport.js +27 -16
  78. package/src/transport/MockTransport.js +7 -2
  79. package/src/transport/MultiTransport.js +43 -11
  80. package/src/transport/Transport.js +9 -9
  81. package/src/transport/WiFiDirectTransport.js +26 -20
  82. package/src/transport/adapters/BLEAdapter.js +19 -19
  83. package/src/transport/adapters/NodeBLEAdapter.js +24 -23
  84. package/src/transport/adapters/RNBLEAdapter.js +14 -11
  85. package/src/utils/EventEmitter.js +15 -16
  86. package/src/utils/LRUCache.js +10 -4
  87. package/src/utils/RateLimiter.js +1 -1
  88. package/src/utils/bytes.js +12 -10
  89. package/src/utils/compression.js +10 -8
  90. package/src/utils/encoding.js +39 -8
  91. package/src/utils/retry.js +11 -13
  92. package/src/utils/time.js +9 -4
  93. package/src/utils/validation.js +1 -1
@@ -16,7 +16,7 @@ const AudioError = require('../../../errors/AudioError');
16
16
  class LC3Encoder extends EventEmitter {
17
17
  /**
18
18
  * Creates a new LC3Encoder
19
- * @param {LC3Codec} codec - Initialized LC3 codec instance
19
+ * @param {any} codec - Initialized LC3 codec instance
20
20
  */
21
21
  constructor(codec) {
22
22
  super();
@@ -25,17 +25,17 @@ class LC3Encoder extends EventEmitter {
25
25
  throw AudioError.codecInitFailed({ reason: 'Codec must be initialized' });
26
26
  }
27
27
 
28
- /** @private */
28
+ /** @type {any} @private */
29
29
  this._codec = codec;
30
- /** @private */
30
+ /** @type {number} @private */
31
31
  this._frameSamples = codec.getFrameSamples();
32
32
  /** @private */
33
33
  this._sampleBuffer = new Int16Array(this._frameSamples * 2);
34
- /** @private */
34
+ /** @type {number} @private */
35
35
  this._bufferOffset = 0;
36
- /** @private */
36
+ /** @type {number} @private */
37
37
  this._framesEncoded = 0;
38
- /** @private */
38
+ /** @type {number} @private */
39
39
  this._errors = 0;
40
40
  }
41
41
 
@@ -45,6 +45,7 @@ class LC3Encoder extends EventEmitter {
45
45
  * @returns {Promise<Uint8Array[]>} Array of encoded LC3 frames
46
46
  */
47
47
  async pushSamples(samples) {
48
+ /** @type {Uint8Array[]} */
48
49
  const frames = [];
49
50
  let offset = 0;
50
51
 
@@ -68,7 +69,7 @@ class LC3Encoder extends EventEmitter {
68
69
  frames.push(encoded);
69
70
  this._framesEncoded++;
70
71
  this.emit('frame', { frame: encoded, index: this._framesEncoded });
71
- } catch (error) {
72
+ } catch (/** @type {any} */ error) {
72
73
  this._errors++;
73
74
  this.emit('error', error);
74
75
  }
@@ -84,6 +85,7 @@ class LC3Encoder extends EventEmitter {
84
85
  * @returns {Promise<Uint8Array[]>} Array of encoded LC3 frames
85
86
  */
86
87
  async flush() {
88
+ /** @type {Uint8Array[]} */
87
89
  const frames = [];
88
90
 
89
91
  if (this._bufferOffset > 0) {
@@ -96,7 +98,7 @@ class LC3Encoder extends EventEmitter {
96
98
  frames.push(encoded);
97
99
  this._framesEncoded++;
98
100
  this.emit('frame', { frame: encoded, index: this._framesEncoded, padded: true });
99
- } catch (error) {
101
+ } catch (/** @type {any} */ error) {
100
102
  this._errors++;
101
103
  this.emit('error', error);
102
104
  }
@@ -117,7 +119,7 @@ class LC3Encoder extends EventEmitter {
117
119
 
118
120
  /**
119
121
  * Returns encoder statistics
120
- * @returns {Object}
122
+ * @returns {any}
121
123
  */
122
124
  getStats() {
123
125
  return {
@@ -20,11 +20,7 @@ const LC3Decoder = require('../codec/LC3Decoder');
20
20
  class AudioSession extends EventEmitter {
21
21
  /**
22
22
  * Creates a new AudioSession
23
- * @param {Object} options - Session options
24
- * @param {string} options.peerId - Remote peer ID
25
- * @param {LC3Codec} options.codec - Initialized codec
26
- * @param {boolean} options.isInitiator - Whether local peer initiated
27
- * @param {Function} [options.sendCallback] - Callback to send data
23
+ * @param {any} options - Session options
28
24
  */
29
25
  constructor(options) {
30
26
  super();
@@ -35,15 +31,15 @@ class AudioSession extends EventEmitter {
35
31
  throw AudioError.codecInitFailed({ reason: 'Codec must be initialized' });
36
32
  }
37
33
 
38
- /** @private */
34
+ /** @type {string} @private */
39
35
  this._peerId = peerId;
40
- /** @private */
36
+ /** @type {any} @private */
41
37
  this._codec = codec;
42
- /** @private */
38
+ /** @type {boolean} @private */
43
39
  this._isInitiator = isInitiator;
44
- /** @private */
40
+ /** @type {Function} @private */
45
41
  this._sendCallback = sendCallback || (() => {});
46
- /** @private */
42
+ /** @type {string} @private */
47
43
  this._state = AUDIO_SESSION_STATE.IDLE;
48
44
  /** @private */
49
45
  this._encoder = new LC3Encoder(codec);
@@ -54,11 +50,11 @@ class AudioSession extends EventEmitter {
54
50
  depth: AUDIO_STREAM_CONFIG.JITTER_BUFFER_FRAMES,
55
51
  frameMs: codec.getConfig().frameMs
56
52
  });
57
- /** @private */
53
+ /** @type {number} @private */
58
54
  this._sendSequence = 0;
59
- /** @private */
55
+ /** @type {number | null} @private */
60
56
  this._startTime = null;
61
- /** @private */
57
+ /** @type {any} @private */
62
58
  this._stats = {
63
59
  framesSent: 0,
64
60
  framesReceived: 0,
@@ -74,7 +70,7 @@ class AudioSession extends EventEmitter {
74
70
  * @private
75
71
  */
76
72
  _setupBufferEvents() {
77
- this._jitterBuffer.on('underrun', (data) => {
73
+ this._jitterBuffer.on('underrun', (/** @type {any} */ data) => {
78
74
  this.emit('underrun', data);
79
75
  });
80
76
 
@@ -110,7 +106,7 @@ class AudioSession extends EventEmitter {
110
106
  const frames = await this._encoder.pushSamples(samples);
111
107
 
112
108
  for (const frame of frames) {
113
- const timestampDelta = Date.now() - this._startTime;
109
+ const timestampDelta = Date.now() - (this._startTime || 0);
114
110
 
115
111
  await this._sendCallback({
116
112
  peerId: this._peerId,
@@ -142,7 +138,7 @@ class AudioSession extends EventEmitter {
142
138
 
143
139
  /**
144
140
  * Gets decoded audio for playback
145
- * @returns {Promise<{samples: Int16Array, isPLC: boolean}|null>}
141
+ * @returns {Promise<any>}
146
142
  */
147
143
  async getAudio() {
148
144
  if (this._state !== AUDIO_SESSION_STATE.ACTIVE) {
@@ -215,6 +211,7 @@ class AudioSession extends EventEmitter {
215
211
 
216
212
  /**
217
213
  * Sets session state
214
+ * @param {string} newState
218
215
  * @private
219
216
  */
220
217
  _setState(newState) {
@@ -249,7 +246,7 @@ class AudioSession extends EventEmitter {
249
246
 
250
247
  /**
251
248
  * Returns session statistics
252
- * @returns {Object}
249
+ * @returns {any}
253
250
  */
254
251
  getStats() {
255
252
  const bufferStats = this._jitterBuffer.getStats();
@@ -29,17 +29,14 @@ const VOICE_MESSAGE_MAGIC = new Uint8Array([0x56, 0x4D, 0x53, 0x47]); // 'VMSG'
29
29
  class VoiceMessage {
30
30
  /**
31
31
  * Creates a VoiceMessage from recorded frames
32
- * @param {Object} options - Message options
33
- * @param {FrameBuffer} options.frames - Recorded frames
34
- * @param {Object} options.metadata - Recording metadata
35
- * @param {Uint8Array} options.senderId - Sender ID (32 bytes)
32
+ * @param {any} options - Message options
36
33
  */
37
34
  constructor(options) {
38
35
  const { frames, metadata, senderId } = options;
39
36
 
40
- /** @private */
37
+ /** @type {any} @private */
41
38
  this._frames = frames;
42
- /** @private */
39
+ /** @type {any} @private */
43
40
  this._metadata = {
44
41
  version: 1,
45
42
  codec: metadata.codec || AUDIO_CODEC_TYPE.LC3,
@@ -49,17 +46,15 @@ class VoiceMessage {
49
46
  channels: metadata.channels || 1,
50
47
  createdAt: metadata.createdAt || Date.now()
51
48
  };
52
- /** @private */
49
+ /** @type {Uint8Array} @private */
53
50
  this._senderId = senderId || new Uint8Array(32);
54
- /** @private */
51
+ /** @type {number} @private */
55
52
  this._playbackPosition = 0;
56
53
  }
57
54
 
58
55
  /**
59
56
  * Starts recording a new voice message
60
- * @param {Object} options - Recording options
61
- * @param {LC3Codec} options.codec - Initialized codec
62
- * @param {Uint8Array} options.senderId - Sender ID
57
+ * @param {any} options - Recording options
63
58
  * @returns {VoiceMessageRecorder}
64
59
  */
65
60
  static startRecording(options) {
@@ -160,7 +155,7 @@ class VoiceMessage {
160
155
 
161
156
  /**
162
157
  * Returns metadata
163
- * @returns {Object}
158
+ * @returns {any}
164
159
  */
165
160
  getMetadata() {
166
161
  return {
@@ -190,7 +185,7 @@ class VoiceMessage {
190
185
 
191
186
  /**
192
187
  * Gets next frame for playback
193
- * @returns {{frame: Uint8Array, index: number, done: boolean}|null}
188
+ * @returns {any}
194
189
  */
195
190
  getNextFrame() {
196
191
  if (this._playbackPosition >= this._frames.getFrameCount()) {
@@ -237,9 +232,7 @@ class VoiceMessage {
237
232
  class VoiceMessageRecorder extends EventEmitter {
238
233
  /**
239
234
  * Creates a new recorder
240
- * @param {Object} options - Recorder options
241
- * @param {LC3Codec} options.codec - Initialized codec
242
- * @param {Uint8Array} options.senderId - Sender ID
235
+ * @param {any} options - Recorder options
243
236
  */
244
237
  constructor(options) {
245
238
  super();
@@ -250,19 +243,19 @@ class VoiceMessageRecorder extends EventEmitter {
250
243
  throw AudioError.codecInitFailed({ reason: 'Codec must be initialized' });
251
244
  }
252
245
 
253
- /** @private */
246
+ /** @type {any} @private */
254
247
  this._codec = codec;
255
- /** @private */
248
+ /** @type {Uint8Array} @private */
256
249
  this._senderId = senderId || new Uint8Array(32);
257
250
  /** @private */
258
251
  this._frames = new FrameBuffer();
259
- /** @private */
252
+ /** @type {any} @private */
260
253
  this._encoder = null;
261
- /** @private */
254
+ /** @type {number} @private */
262
255
  this._startTime = Date.now();
263
- /** @private */
256
+ /** @type {boolean} @private */
264
257
  this._cancelled = false;
265
- /** @private */
258
+ /** @type {any} @private */
266
259
  this._config = codec.getConfig();
267
260
 
268
261
  // Lazy load encoder
@@ -17,15 +17,15 @@ class AudioFragmenter {
17
17
  /**
18
18
  * Fragments voice message data
19
19
  * @param {Uint8Array} voiceData - Serialized voice message
20
- * @param {Object} metadata - Voice message metadata
21
- * @param {string} metadata.messageId - Unique message ID
20
+ * @param {any} metadata - Voice message metadata
22
21
  * @param {number} [chunkSize] - Chunk size (default from config)
23
22
  * @returns {Uint8Array[]} Array of fragments
24
23
  */
25
24
  static fragment(voiceData, metadata, chunkSize = VOICE_MESSAGE_CONFIG.CHUNK_SIZE) {
26
25
  const { messageId } = metadata;
27
- const messageIdBytes = this._stringToBytes(messageId, 16);
26
+ const messageIdBytes = AudioFragmenter._stringToBytes(messageId, 16);
28
27
  const totalChunks = Math.ceil(voiceData.length / chunkSize);
28
+ /** @type {Uint8Array[]} */
29
29
  const fragments = [];
30
30
 
31
31
  for (let i = 0; i < totalChunks; i++) {
@@ -77,12 +77,15 @@ class AudioFragmenter {
77
77
 
78
78
  /**
79
79
  * Converts string to fixed-length bytes
80
+ * @param {string} str - String to convert
81
+ * @param {number} length - Target byte length
82
+ * @returns {Uint8Array}
80
83
  * @private
81
84
  */
82
85
  static _stringToBytes(str, length) {
83
86
  const bytes = new Uint8Array(length);
84
87
  const encoder = new TextEncoder();
85
- const encoded = encoder.encode(str.slice(0, length));
88
+ const encoded = encoder.encode(String(str).slice(0, length));
86
89
  bytes.set(encoded.slice(0, length));
87
90
  return bytes;
88
91
  }
@@ -96,15 +99,14 @@ class AudioFragmenter {
96
99
  class AudioAssembler extends EventEmitter {
97
100
  /**
98
101
  * Creates a new AudioAssembler
99
- * @param {Object} [options] - Assembler options
100
- * @param {number} [options.timeout=120000] - Assembly timeout in ms
102
+ * @param {any} [options] - Assembler options
101
103
  */
102
104
  constructor(options = {}) {
103
105
  super();
104
106
 
105
- /** @private */
107
+ /** @type {number} @private */
106
108
  this._timeout = options.timeout || VOICE_MESSAGE_CONFIG.TIMEOUT_MS;
107
- /** @private */
109
+ /** @type {Map<string, any>} @private */
108
110
  this._pending = new Map(); // messageId -> { fragments, totalSize, receivedSize, timer }
109
111
  }
110
112
 
@@ -120,6 +122,7 @@ class AudioAssembler extends EventEmitter {
120
122
  const index = view.getUint16(17, false);
121
123
  const total = view.getUint16(19, false);
122
124
 
125
+ /** @type {Uint8Array} */
123
126
  let chunkData;
124
127
  let totalSize = 0;
125
128
 
@@ -171,6 +174,8 @@ class AudioAssembler extends EventEmitter {
171
174
 
172
175
  /**
173
176
  * Assembles complete voice message
177
+ * @param {string} messageId - Message ID
178
+ * @returns {Uint8Array|null}
174
179
  * @private
175
180
  */
176
181
  _assemble(messageId) {
@@ -199,6 +204,7 @@ class AudioAssembler extends EventEmitter {
199
204
 
200
205
  /**
201
206
  * Handles assembly timeout
207
+ * @param {string} messageId - Message ID
202
208
  * @private
203
209
  */
204
210
  _handleTimeout(messageId) {
@@ -209,7 +215,7 @@ class AudioAssembler extends EventEmitter {
209
215
  /**
210
216
  * Gets assembly progress
211
217
  * @param {string} messageId - Message ID
212
- * @returns {Object|null}
218
+ * @returns {any}
213
219
  */
214
220
  getProgress(messageId) {
215
221
  const entry = this._pending.get(messageId);
@@ -245,6 +251,8 @@ class AudioAssembler extends EventEmitter {
245
251
 
246
252
  /**
247
253
  * Converts bytes to string
254
+ * @param {Uint8Array} bytes - Bytes to convert
255
+ * @returns {string}
248
256
  * @private
249
257
  */
250
258
  _bytesToString(bytes) {
@@ -16,7 +16,7 @@ const AUDIO_FRAME_HEADER_SIZE = AUDIO_STREAM_CONFIG.FRAME_HEADER_SIZE;
16
16
 
17
17
  /**
18
18
  * Audio frame flags
19
- * @constant {Object}
19
+ * @constant {any}
20
20
  */
21
21
  const FRAME_FLAGS = Object.freeze({
22
22
  NONE: 0x00,
@@ -27,12 +27,7 @@ const FRAME_FLAGS = Object.freeze({
27
27
 
28
28
  /**
29
29
  * Packs an audio frame with header for transmission
30
- * @param {Object} options - Frame options
31
- * @param {number} options.type - Message type
32
- * @param {Uint8Array} options.frame - Audio frame data
33
- * @param {number} options.sequenceNumber - Sequence number
34
- * @param {number} [options.timestampDelta=0] - Timestamp delta
35
- * @param {number} [options.flags=0] - Frame flags
30
+ * @param {any} options - Frame options
36
31
  * @returns {Uint8Array} Packed frame with header
37
32
  */
38
33
  function packFrame(options) {
@@ -64,7 +59,7 @@ function packFrame(options) {
64
59
  /**
65
60
  * Unpacks an audio frame from received data
66
61
  * @param {Uint8Array} data - Received data
67
- * @returns {Object} Unpacked frame info
62
+ * @returns {any} Unpacked frame info
68
63
  */
69
64
  function unpackFrame(data) {
70
65
  if (data.length < AUDIO_FRAME_HEADER_SIZE) {
@@ -99,13 +94,13 @@ function unpackFrame(data) {
99
94
 
100
95
  /**
101
96
  * Packs multiple frames into a single packet
102
- * @param {Array<Object>} frames - Array of frame objects
97
+ * @param {any[]} frames - Array of frame objects
103
98
  * @returns {Uint8Array} Packed multi-frame data
104
99
  */
105
100
  function packMultiFrame(frames) {
106
101
  // Format: [count(1)][frame1][frame2]...
107
- const packedFrames = frames.map(f => packFrame(f));
108
- const totalLen = 1 + packedFrames.reduce((sum, p) => sum + p.length, 0);
102
+ const packedFrames = frames.map((/** @type {any} */ f) => packFrame(f));
103
+ const totalLen = 1 + packedFrames.reduce((/** @type {number} */ sum, /** @type {any} */ p) => sum + p.length, 0);
109
104
 
110
105
  const result = new Uint8Array(totalLen);
111
106
  result[0] = frames.length;
@@ -122,7 +117,7 @@ function packMultiFrame(frames) {
122
117
  /**
123
118
  * Unpacks multiple frames from a packet
124
119
  * @param {Uint8Array} data - Packed multi-frame data
125
- * @returns {Array<Object>} Array of unpacked frames
120
+ * @returns {any[]} Array of unpacked frames
126
121
  */
127
122
  function unpackMultiFrame(data) {
128
123
  if (data.length < 1) {
@@ -130,6 +125,7 @@ function unpackMultiFrame(data) {
130
125
  }
131
126
 
132
127
  const count = data[0];
128
+ /** @type {any[]} */
133
129
  const frames = [];
134
130
  let offset = 1;
135
131
 
@@ -84,8 +84,10 @@ class FileAssembler {
84
84
 
85
85
  for (let i = 0; i < this._totalChunks; i++) {
86
86
  const chunk = this._chunks.get(i);
87
- result.set(chunk, offset);
88
- offset += chunk.length;
87
+ if (chunk) {
88
+ result.set(chunk, offset);
89
+ offset += chunk.length;
90
+ }
89
91
  }
90
92
 
91
93
  // Free chunk memory immediately after assembly
@@ -27,7 +27,7 @@ class FileChunker {
27
27
  * Splits data into chunks
28
28
  * @param {Uint8Array} data - File data
29
29
  * @param {string} transferId - Transfer ID
30
- * @returns {Object[]} Array of chunk objects
30
+ * @returns {any[]} Array of chunk objects
31
31
  * @throws {Error} If data exceeds max file size
32
32
  */
33
33
  chunk(data, transferId) {
@@ -12,7 +12,7 @@ const { FileMessage, FILE_TRANSFER_STATE } = require('./FileMessage');
12
12
 
13
13
  /**
14
14
  * Default file transfer configuration
15
- * @constant {Object}
15
+ * @constant {any}
16
16
  */
17
17
  const DEFAULT_CONFIG = Object.freeze({
18
18
  chunkSize: 4096,
@@ -36,21 +36,22 @@ const DEFAULT_CONFIG = Object.freeze({
36
36
  */
37
37
  class FileManager extends EventEmitter {
38
38
  /**
39
- * @param {Object} [config={}]
39
+ * @param {any} [config]
40
40
  */
41
41
  constructor(config = {}) {
42
42
  super();
43
+ /** @type {any} */
43
44
  this._config = { ...DEFAULT_CONFIG, ...config };
44
45
  this._chunker = new FileChunker({
45
46
  chunkSize: this._config.chunkSize,
46
47
  maxFileSize: this._config.maxFileSize
47
48
  });
48
49
 
49
- /** @type {Map<string, Object>} Active outgoing transfers */
50
+ /** @type {Map<string, any>} Active outgoing transfers */
50
51
  this._outgoing = new Map();
51
- /** @type {Map<string, Object>} Active incoming transfers */
52
+ /** @type {Map<string, any>} Active incoming transfers */
52
53
  this._incoming = new Map();
53
- /** @type {Map<string, NodeJS.Timeout>} Transfer timeouts */
54
+ /** @type {Map<string, any>} Transfer timeouts */
54
55
  this._timeouts = new Map();
55
56
  }
56
57
 
@@ -59,11 +60,8 @@ class FileManager extends EventEmitter {
59
60
  * The caller (MeshNetwork) is responsible for actually sending chunks via transport.
60
61
  *
61
62
  * @param {string} peerId - Target peer ID
62
- * @param {Object} fileInfo - File information
63
- * @param {Uint8Array} fileInfo.data - File data
64
- * @param {string} fileInfo.name - File name
65
- * @param {string} [fileInfo.mimeType='application/octet-stream'] - MIME type
66
- * @returns {Object} Transfer object with id, offer, and chunks
63
+ * @param {any} fileInfo - File information
64
+ * @returns {any} Transfer object with id, offer, and chunks
67
65
  */
68
66
  prepareSend(peerId, fileInfo) {
69
67
  if (this._outgoing.size >= this._config.maxConcurrentTransfers) {
@@ -105,7 +103,7 @@ class FileManager extends EventEmitter {
105
103
  /**
106
104
  * Marks a chunk as sent and emits progress
107
105
  * @param {string} transferId - Transfer ID
108
- * @param {number} chunkIndex - Chunk index that was sent
106
+ * @param {number} _chunkIndex - Chunk index that was sent
109
107
  */
110
108
  markChunkSent(transferId, _chunkIndex) {
111
109
  const transfer = this._outgoing.get(transferId);
@@ -141,7 +139,7 @@ class FileManager extends EventEmitter {
141
139
 
142
140
  /**
143
141
  * Handles an incoming file offer
144
- * @param {Object} offer - File offer metadata
142
+ * @param {any} offer - File offer metadata
145
143
  * @param {string} senderId - Sender peer ID
146
144
  * @returns {string} Transfer ID
147
145
  */
@@ -234,15 +232,17 @@ class FileManager extends EventEmitter {
234
232
 
235
233
  if (this._outgoing.has(transferId)) {
236
234
  const transfer = this._outgoing.get(transferId);
237
- transfer.state = FILE_TRANSFER_STATE.CANCELLED;
235
+ if (transfer) { transfer.state = FILE_TRANSFER_STATE.CANCELLED; }
238
236
  this._outgoing.delete(transferId);
239
237
  this.emit('transferCancelled', { transferId, direction: 'outgoing' });
240
238
  }
241
239
 
242
240
  if (this._incoming.has(transferId)) {
243
241
  const transfer = this._incoming.get(transferId);
244
- transfer.meta.state = FILE_TRANSFER_STATE.CANCELLED;
245
- transfer.assembler.clear();
242
+ if (transfer) {
243
+ transfer.meta.state = FILE_TRANSFER_STATE.CANCELLED;
244
+ transfer.assembler.clear();
245
+ }
246
246
  this._incoming.delete(transferId);
247
247
  this.emit('transferCancelled', { transferId, direction: 'incoming' });
248
248
  }
@@ -250,16 +250,16 @@ class FileManager extends EventEmitter {
250
250
 
251
251
  /**
252
252
  * Gets active transfers
253
- * @returns {Object} { outgoing: [], incoming: [] }
253
+ * @returns {any}
254
254
  */
255
255
  getActiveTransfers() {
256
256
  return {
257
- outgoing: Array.from(this._outgoing.values()).map(t => ({
257
+ outgoing: Array.from(this._outgoing.values()).map((/** @type {any} */ t) => ({
258
258
  id: t.id, peerId: t.peerId, name: t.meta.name,
259
259
  progress: Math.round((t.sentChunks / t.chunks.length) * 100),
260
260
  state: t.state
261
261
  })),
262
- incoming: Array.from(this._incoming.values()).map(t => ({
262
+ incoming: Array.from(this._incoming.values()).map((/** @type {any} */ t) => ({
263
263
  id: t.meta.id, from: t.senderId, name: t.meta.name,
264
264
  progress: t.assembler.progress,
265
265
  state: t.meta.state
@@ -282,7 +282,10 @@ class FileManager extends EventEmitter {
282
282
  this.removeAllListeners();
283
283
  }
284
284
 
285
- /** @private */
285
+ /**
286
+ * @param {string} transferId
287
+ * @private
288
+ */
286
289
  _setTransferTimeout(transferId) {
287
290
  const timer = setTimeout(() => {
288
291
  this.cancelTransfer(transferId);
@@ -294,7 +297,10 @@ class FileManager extends EventEmitter {
294
297
  this._timeouts.set(transferId, timer);
295
298
  }
296
299
 
297
- /** @private */
300
+ /**
301
+ * @param {string} transferId
302
+ * @private
303
+ */
298
304
  _clearTransferTimeout(transferId) {
299
305
  const timer = this._timeouts.get(transferId);
300
306
  if (timer) {
@@ -7,7 +7,7 @@
7
7
 
8
8
  /**
9
9
  * File transfer message types
10
- * @constant {Object}
10
+ * @constant {any}
11
11
  */
12
12
  const FILE_MESSAGE_TYPE = Object.freeze({
13
13
  /** Initial file offer with metadata */
@@ -22,7 +22,7 @@ const FILE_MESSAGE_TYPE = Object.freeze({
22
22
 
23
23
  /**
24
24
  * File transfer states
25
- * @constant {Object}
25
+ * @constant {any}
26
26
  */
27
27
  const FILE_TRANSFER_STATE = Object.freeze({
28
28
  PENDING: 'pending',
@@ -38,14 +38,7 @@ const FILE_TRANSFER_STATE = Object.freeze({
38
38
  */
39
39
  class FileMessage {
40
40
  /**
41
- * @param {Object} options
42
- * @param {string} options.id - Transfer ID
43
- * @param {string} options.name - File name
44
- * @param {string} options.mimeType - MIME type
45
- * @param {number} options.size - Total size in bytes
46
- * @param {number} options.totalChunks - Total number of chunks
47
- * @param {number} [options.chunkSize=4096] - Chunk size in bytes
48
- * @param {string} [options.senderId] - Sender peer ID
41
+ * @param {any} options
49
42
  */
50
43
  constructor(options) {
51
44
  this.id = options.id;
@@ -57,7 +50,9 @@ class FileMessage {
57
50
  this.senderId = options.senderId || null;
58
51
  this.receivedChunks = 0;
59
52
  this.state = FILE_TRANSFER_STATE.PENDING;
53
+ /** @type {number | null} */
60
54
  this.startedAt = null;
55
+ /** @type {number | null} */
61
56
  this.completedAt = null;
62
57
  }
63
58
 
@@ -82,7 +77,7 @@ class FileMessage {
82
77
 
83
78
  /**
84
79
  * Serializes the file offer metadata
85
- * @returns {Object}
80
+ * @returns {any}
86
81
  */
87
82
  toOffer() {
88
83
  return {
@@ -98,7 +93,7 @@ class FileMessage {
98
93
 
99
94
  /**
100
95
  * Creates a FileMessage from an offer
101
- * @param {Object} offer
96
+ * @param {any} offer
102
97
  * @param {string} senderId
103
98
  * @returns {FileMessage}
104
99
  */