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
@@ -21,14 +21,14 @@ class RNBLEAdapter extends BLEAdapter {
21
21
  /**
22
22
  * Creates a new RNBLEAdapter instance
23
23
  * @param {Object} [options={}] - Adapter options
24
- * @param {Object} [options.BleManager] - BleManager class from react-native-ble-plx
24
+ * @param {any} [options.BleManager] - BleManager class from react-native-ble-plx
25
25
  */
26
26
  constructor(options = {}) {
27
27
  super(options);
28
28
 
29
29
  /**
30
30
  * BleManager instance
31
- * @type {Object|null}
31
+ * @type {any}
32
32
  * @private
33
33
  */
34
34
  this._manager = null;
@@ -45,32 +45,33 @@ class RNBLEAdapter extends BLEAdapter {
45
45
  * @type {string|null}
46
46
  * @private
47
47
  */
48
+ // @ts-ignore
48
49
  this._restoreIdentifier = options.restoreIdentifier || null;
49
50
 
50
51
  /**
51
52
  * Connected devices map
52
- * @type {Map<string, Object>}
53
+ * @type {Map<string, any>}
53
54
  * @private
54
55
  */
55
56
  this._devices = new Map();
56
57
 
57
58
  /**
58
59
  * Subscription handlers map
59
- * @type {Map<string, Object>}
60
+ * @type {Map<string, any>}
60
61
  * @private
61
62
  */
62
63
  this._subscriptions = new Map();
63
64
 
64
65
  /**
65
66
  * Scan subscription reference
66
- * @type {Object|null}
67
+ * @type {any}
67
68
  * @private
68
69
  */
69
70
  this._scanSubscription = null;
70
71
 
71
72
  /**
72
73
  * State subscription reference
73
- * @type {Object|null}
74
+ * @type {any}
74
75
  * @private
75
76
  */
76
77
  this._stateSubscription = null;
@@ -96,6 +97,7 @@ class RNBLEAdapter extends BLEAdapter {
96
97
  // Try to load BleManager if not provided
97
98
  if (!this._BleManager) {
98
99
  try {
100
+ // @ts-ignore
99
101
  const blePlx = require('react-native-ble-plx');
100
102
  this._BleManager = blePlx.BleManager;
101
103
  } catch (error) {
@@ -108,7 +110,7 @@ class RNBLEAdapter extends BLEAdapter {
108
110
  const managerOptions = {};
109
111
  if (this._restoreIdentifier) {
110
112
  managerOptions.restoreStateIdentifier = this._restoreIdentifier;
111
- managerOptions.restoreStateFunction = (restoredState) => {
113
+ managerOptions.restoreStateFunction = (/** @type {any} */ restoredState) => {
112
114
  // Re-populate devices from restored state
113
115
  if (restoredState && restoredState.connectedPeripherals) {
114
116
  for (const peripheral of restoredState.connectedPeripherals) {
@@ -117,10 +119,11 @@ class RNBLEAdapter extends BLEAdapter {
117
119
  }
118
120
  };
119
121
  }
122
+ // @ts-ignore
120
123
  this._manager = new this._BleManager(managerOptions);
121
124
 
122
125
  // Subscribe to state changes
123
- this._stateSubscription = this._manager.onStateChange((state) => {
126
+ this._stateSubscription = this._manager.onStateChange((/** @type {any} */ state) => {
124
127
  this._notifyStateChange(this._mapState(state));
125
128
  }, true);
126
129
 
@@ -174,7 +177,7 @@ class RNBLEAdapter extends BLEAdapter {
174
177
  async startScan(serviceUUIDs, callback) {
175
178
  this._ensureInitialized();
176
179
 
177
- this._manager.startDeviceScan(serviceUUIDs, null, (error, device) => {
180
+ this._manager.startDeviceScan(serviceUUIDs, null, (/** @type {any} */ error, /** @type {any} */ device) => {
178
181
  if (error) {
179
182
  return;
180
183
  }
@@ -292,7 +295,7 @@ class RNBLEAdapter extends BLEAdapter {
292
295
  deviceId,
293
296
  serviceUUID,
294
297
  charUUID,
295
- (error, characteristic) => {
298
+ (/** @type {any} */ error, /** @type {any} */ characteristic) => {
296
299
  if (!error && characteristic) {
297
300
  const data = this._base64ToUint8Array(characteristic.value);
298
301
  callback(data);
@@ -328,7 +331,7 @@ class RNBLEAdapter extends BLEAdapter {
328
331
  PoweredOff: BLEAdapter.STATE.POWERED_OFF,
329
332
  PoweredOn: BLEAdapter.STATE.POWERED_ON
330
333
  };
331
- return stateMap[state] || BLEAdapter.STATE.UNKNOWN;
334
+ return /** @type {any} */ (stateMap)[state] || BLEAdapter.STATE.UNKNOWN;
332
335
  }
333
336
 
334
337
  /**
@@ -12,8 +12,8 @@
12
12
  class EventEmitter {
13
13
  /**
14
14
  * Creates a new EventEmitter
15
- * @param {Object} [options] - Configuration options
16
- * @param {number} [options.maxListeners=10] - Maximum listeners per event
15
+ * @param {any} [options] - Configuration options
16
+ *
17
17
  */
18
18
  constructor(options = {}) {
19
19
  /**
@@ -47,6 +47,7 @@ class EventEmitter {
47
47
  }
48
48
 
49
49
  const listeners = this._events.get(event);
50
+ if (!listeners) { return this; }
50
51
 
51
52
  // Warn if max listeners exceeded
52
53
  if (listeners.length >= this._maxListeners) {
@@ -76,7 +77,7 @@ class EventEmitter {
76
77
  this._events.set(event, []);
77
78
  }
78
79
 
79
- this._events.get(event).push({ listener, once: true });
80
+ this._events.get(event)?.push({ listener, once: true });
80
81
  return this;
81
82
  }
82
83
 
@@ -92,13 +93,13 @@ class EventEmitter {
92
93
  }
93
94
 
94
95
  const listeners = this._events.get(event);
95
- const index = listeners.findIndex(entry => entry.listener === listener);
96
+ const index = listeners?.findIndex(entry => entry.listener === listener) ?? -1;
96
97
 
97
98
  if (index !== -1) {
98
- listeners.splice(index, 1);
99
+ listeners?.splice(index, 1);
99
100
  }
100
101
 
101
- if (listeners.length === 0) {
102
+ if (listeners?.length === 0) {
102
103
  this._events.delete(event);
103
104
  }
104
105
 
@@ -116,10 +117,12 @@ class EventEmitter {
116
117
  return false;
117
118
  }
118
119
 
119
- const listeners = this._events.get(event).slice();
120
- const toRemove = [];
120
+ const listeners = this._events.get(event);
121
+ if (!listeners) { return false; }
122
+ const hasOnce = listeners.some(e => e.once);
123
+ const iterList = hasOnce ? listeners.slice() : listeners;
121
124
 
122
- for (const entry of listeners) {
125
+ for (const entry of iterList) {
123
126
  try {
124
127
  entry.listener.apply(this, args);
125
128
  } catch (error) {
@@ -130,15 +133,11 @@ class EventEmitter {
130
133
  console.error('Error in error handler:', error);
131
134
  }
132
135
  }
133
-
134
- if (entry.once) {
135
- toRemove.push(entry);
136
- }
137
136
  }
138
137
 
139
138
  // Remove one-time listeners
140
- if (toRemove.length > 0) {
141
- const remaining = this._events.get(event).filter(e => !toRemove.includes(e));
139
+ if (hasOnce) {
140
+ const remaining = listeners.filter(e => !e.once);
142
141
  if (remaining.length === 0) {
143
142
  this._events.delete(event);
144
143
  } else {
@@ -172,7 +171,7 @@ class EventEmitter {
172
171
  if (!this._events.has(event)) {
173
172
  return 0;
174
173
  }
175
- return this._events.get(event).length;
174
+ return this._events.get(event)?.length ?? 0;
176
175
  }
177
176
 
178
177
  /**
@@ -47,7 +47,7 @@ class LRUCache {
47
47
  }
48
48
 
49
49
  // Move to end (most recently used)
50
- const value = this._cache.get(key);
50
+ const value = /** @type {V} */ (this._cache.get(key));
51
51
  this._cache.delete(key);
52
52
  this._cache.set(key, value);
53
53
 
@@ -67,7 +67,9 @@ class LRUCache {
67
67
  } else if (this._cache.size >= this._maxSize) {
68
68
  // Remove least recently used (first item)
69
69
  const firstKey = this._cache.keys().next().value;
70
- this._cache.delete(firstKey);
70
+ if (firstKey !== undefined) {
71
+ this._cache.delete(firstKey);
72
+ }
71
73
  }
72
74
 
73
75
  this._cache.set(key, value);
@@ -134,7 +136,7 @@ class LRUCache {
134
136
 
135
137
  /**
136
138
  * Returns all entries in the cache (most recent last)
137
- * @returns {Array} Array of [key, value] pairs
139
+ * @returns {any[]} Array of [key, value] pairs
138
140
  */
139
141
  entries() {
140
142
  return Array.from(this._cache.entries());
@@ -194,7 +196,11 @@ class LRUCache {
194
196
  // Evict oldest items if over new limit
195
197
  while (this._cache.size > this._maxSize) {
196
198
  const firstKey = this._cache.keys().next().value;
197
- this._cache.delete(firstKey);
199
+ if (firstKey !== undefined) {
200
+ this._cache.delete(firstKey);
201
+ } else {
202
+ break;
203
+ }
198
204
  }
199
205
  }
200
206
  }
@@ -162,7 +162,7 @@ class RateLimiter {
162
162
 
163
163
  /**
164
164
  * Gets rate limiter configuration
165
- * @returns {Object} Configuration object
165
+ * @returns {any} Configuration object
166
166
  */
167
167
  getConfig() {
168
168
  return {
@@ -11,9 +11,16 @@
11
11
  * @returns {Uint8Array} Concatenated array
12
12
  */
13
13
  function concat(...arrays) {
14
- // Filter out undefined/null and calculate total length
15
- const validArrays = arrays.filter(arr => arr !== null && arr !== undefined);
16
- const totalLength = validArrays.reduce((sum, arr) => sum + arr.length, 0);
14
+ // Single-pass: filter out undefined/null and calculate total length
15
+ const validArrays = [];
16
+ let totalLength = 0;
17
+ for (let i = 0; i < arrays.length; i++) {
18
+ const arr = arrays[i];
19
+ if (arr !== null && arr !== undefined) {
20
+ validArrays.push(arr);
21
+ totalLength += arr.length;
22
+ }
23
+ }
17
24
 
18
25
  const result = new Uint8Array(totalLength);
19
26
  let offset = 0;
@@ -119,10 +126,7 @@ function xor(a, b) {
119
126
  * @returns {Uint8Array} The filled array (same reference)
120
127
  */
121
128
  function fill(array, value) {
122
- const byte = value & 0xff;
123
- for (let i = 0; i < array.length; i++) {
124
- array[i] = byte;
125
- }
129
+ array.fill(value & 0xff);
126
130
  return array;
127
131
  }
128
132
 
@@ -132,9 +136,7 @@ function fill(array, value) {
132
136
  * @returns {Uint8Array} Copy of the array
133
137
  */
134
138
  function copy(array) {
135
- const result = new Uint8Array(array.length);
136
- result.set(array);
137
- return result;
139
+ return array.slice();
138
140
  }
139
141
 
140
142
  /**
@@ -15,7 +15,7 @@ const { ValidationError, MeshError } = require('../errors');
15
15
 
16
16
  /**
17
17
  * Default compression configuration
18
- * @constant {Object}
18
+ * @constant {any}
19
19
  */
20
20
  const DEFAULT_CONFIG = Object.freeze({
21
21
  /** Minimum payload size to compress (bytes) */
@@ -45,8 +45,8 @@ const DEFAULT_CONFIG = Object.freeze({
45
45
  class MessageCompressor {
46
46
  /**
47
47
  * Creates a new MessageCompressor instance.
48
- * @param {Object} [options={}] - Compression options
49
- * @param {number} [options.threshold=100] - Min size to compress
48
+ * @param {any} [options={}] - Compression options
49
+ *
50
50
  */
51
51
  constructor(options = {}) {
52
52
  this._config = { ...DEFAULT_CONFIG, ...options };
@@ -57,6 +57,9 @@ class MessageCompressor {
57
57
  bytesIn: 0,
58
58
  bytesOut: 0
59
59
  };
60
+
61
+ // Pre-allocate hash table to avoid per-call allocation
62
+ this._hashTable = new Int32Array(this._config.hashTableSize);
60
63
  }
61
64
 
62
65
  /**
@@ -86,7 +89,7 @@ class MessageCompressor {
86
89
  this._stats.bytesOut += compressed.length;
87
90
  return { data: compressed, compressed: true };
88
91
  }
89
- } catch (error) {
92
+ } catch (/** @type {any} */ error) {
90
93
  // Log compression error at debug level for troubleshooting
91
94
  if (typeof console !== 'undefined' && console.debug) {
92
95
  console.debug('Compression failed, using uncompressed:', error.message);
@@ -129,7 +132,7 @@ class MessageCompressor {
129
132
 
130
133
  /**
131
134
  * Gets compression statistics.
132
- * @returns {Object} Statistics
135
+ * @returns {any} Statistics
133
136
  */
134
137
  getStats() {
135
138
  const ratio = this._stats.bytesIn > 0
@@ -177,9 +180,8 @@ class MessageCompressor {
177
180
  output[outputPos++] = (inputLen >> 16) & 0xff;
178
181
  output[outputPos++] = (inputLen >> 24) & 0xff;
179
182
 
180
- // Hash table for finding matches
181
- // Using Knuth's multiplicative hash constant (2654435761) for good distribution
182
- const hashTable = new Int32Array(this._config.hashTableSize);
183
+ // Reuse pre-allocated hash table, reset for this call
184
+ const hashTable = this._hashTable;
183
185
  hashTable.fill(-1);
184
186
 
185
187
  let anchor = 0;
@@ -14,18 +14,47 @@ for (let i = 0; i < 16; i++) {
14
14
  HEX_LOOKUP[HEX_CHARS.toUpperCase().charCodeAt(i)] = i;
15
15
  }
16
16
 
17
+ // Pre-computed byte-to-hex lookup table (avoids per-byte toString + padStart)
18
+ const HEX_TABLE = new Array(256);
19
+ for (let i = 0; i < 256; i++) {
20
+ HEX_TABLE[i] = HEX_CHARS[i >> 4] + HEX_CHARS[i & 0x0f];
21
+ }
22
+
23
+ // Cached TextEncoder/TextDecoder singletons (avoid per-call allocation)
24
+ /** @type {any} */ let _cachedEncoder = null;
25
+ /** @type {any} */ let _cachedDecoder = null;
26
+
27
+ /**
28
+ * @returns {any}
29
+ */
30
+ function _getEncoder() {
31
+ if (!_cachedEncoder && typeof TextEncoder !== 'undefined') {
32
+ _cachedEncoder = new TextEncoder();
33
+ }
34
+ return _cachedEncoder;
35
+ }
36
+
37
+ /**
38
+ * @returns {any}
39
+ */
40
+ function _getDecoder() {
41
+ if (!_cachedDecoder && typeof TextDecoder !== 'undefined') {
42
+ _cachedDecoder = new TextDecoder();
43
+ }
44
+ return _cachedDecoder;
45
+ }
46
+
17
47
  /**
18
48
  * Converts a byte array to a hexadecimal string
19
49
  * @param {Uint8Array} bytes - Bytes to convert
20
50
  * @returns {string} Hexadecimal string
21
51
  */
22
52
  function bytesToHex(bytes) {
23
- let result = '';
53
+ const parts = new Array(bytes.length);
24
54
  for (let i = 0; i < bytes.length; i++) {
25
- result += HEX_CHARS[bytes[i] >> 4];
26
- result += HEX_CHARS[bytes[i] & 0x0f];
55
+ parts[i] = HEX_TABLE[bytes[i]];
27
56
  }
28
- return result;
57
+ return parts.join('');
29
58
  }
30
59
 
31
60
  /**
@@ -62,8 +91,9 @@ function hexToBytes(hex) {
62
91
  * @returns {Uint8Array} UTF-8 encoded bytes
63
92
  */
64
93
  function stringToBytes(str) {
65
- if (typeof TextEncoder !== 'undefined') {
66
- return new TextEncoder().encode(str);
94
+ const encoder = _getEncoder();
95
+ if (encoder) {
96
+ return encoder.encode(str);
67
97
  }
68
98
 
69
99
  // Fallback for environments without TextEncoder
@@ -99,8 +129,9 @@ function stringToBytes(str) {
99
129
  * @returns {string} Decoded string
100
130
  */
101
131
  function bytesToString(bytes) {
102
- if (typeof TextDecoder !== 'undefined') {
103
- return new TextDecoder().decode(bytes);
132
+ const decoder = _getDecoder();
133
+ if (decoder) {
134
+ return decoder.decode(bytes);
104
135
  }
105
136
 
106
137
  // Fallback for environments without TextDecoder
@@ -7,7 +7,7 @@
7
7
 
8
8
  /**
9
9
  * Default retry options
10
- * @constant {Object}
10
+ * @constant {any}
11
11
  */
12
12
  const DEFAULT_OPTIONS = {
13
13
  maxRetries: 3,
@@ -21,7 +21,7 @@ const DEFAULT_OPTIONS = {
21
21
  /**
22
22
  * Calculates delay with optional jitter
23
23
  * @param {number} attempt - Current attempt number (0-based)
24
- * @param {Object} options - Retry options
24
+ * @param {any} options - Retry options
25
25
  * @returns {number} Delay in milliseconds
26
26
  * @private
27
27
  */
@@ -46,13 +46,10 @@ function calculateDelay(attempt, options) {
46
46
  * Retries an async function with exponential backoff
47
47
  * @template T
48
48
  * @param {function(): Promise<T>} fn - Async function to retry
49
- * @param {Object} [options] - Retry options
50
- * @param {number} [options.maxRetries=3] - Maximum number of retries
51
- * @param {number} [options.initialDelay=100] - Initial delay in milliseconds
52
- * @param {number} [options.maxDelay=10000] - Maximum delay in milliseconds
53
- * @param {number} [options.factor=2] - Exponential factor
54
- * @param {boolean} [options.jitter=true] - Add randomness to delays
49
+ * @param {any} [options] - Retry options *
50
+ * @param {object} options
55
51
  * @param {function(Error, number): boolean} [options.shouldRetry] - Predicate to determine if should retry
52
+ * @param {object} options
56
53
  * @param {function(Error, number): void} [options.onRetry] - Callback on each retry
57
54
  * @returns {Promise<T>} Result of the function
58
55
  * @throws {Error} Last error if all retries fail
@@ -66,7 +63,7 @@ async function retry(fn, options = {}) {
66
63
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
67
64
  try {
68
65
  return await fn();
69
- } catch (error) {
66
+ } catch (/** @type {any} */ error) {
70
67
  lastError = error;
71
68
 
72
69
  // Check if we should retry
@@ -94,11 +91,12 @@ async function retry(fn, options = {}) {
94
91
  * Creates a retryable version of an async function
95
92
  * @template T
96
93
  * @param {function(...*): Promise<T>} fn - Async function to wrap
97
- * @param {Object} [options] - Retry options
94
+ * @param {any} [options] - Retry options
98
95
  * @returns {function(...*): Promise<T>} Wrapped function with retry logic
99
96
  */
100
97
  function retryable(fn, options = {}) {
101
98
  return function retryableFn(...args) {
99
+ // @ts-ignore
102
100
  return retry(() => fn.apply(this, args), options);
103
101
  };
104
102
  }
@@ -109,7 +107,7 @@ function retryable(fn, options = {}) {
109
107
  * @returns {function(Error): boolean} Predicate function
110
108
  */
111
109
  function retryOn(errorTypes) {
112
- return (error) => {
110
+ return (/** @type {any} */ error) => {
113
111
  return errorTypes.some(ErrorType => error instanceof ErrorType);
114
112
  };
115
113
  }
@@ -120,7 +118,7 @@ function retryOn(errorTypes) {
120
118
  * @returns {function(Error): boolean} Predicate function
121
119
  */
122
120
  function retryExcept(errorTypes) {
123
- return (error) => {
121
+ return (/** @type {any} */ error) => {
124
122
  return !errorTypes.some(ErrorType => error instanceof ErrorType);
125
123
  };
126
124
  }
@@ -131,7 +129,7 @@ function retryExcept(errorTypes) {
131
129
  * @returns {function(Error): boolean} Predicate function
132
130
  */
133
131
  function retryOnCodes(codes) {
134
- return (error) => {
132
+ return (/** @type {any} */ error) => {
135
133
  return error.code && codes.includes(error.code);
136
134
  };
137
135
  }
package/src/utils/time.js CHANGED
@@ -23,8 +23,9 @@ function delay(ms) {
23
23
  * @returns {Promise<T>} Promise that rejects on timeout
24
24
  */
25
25
  function withTimeout(promise, ms, message = 'Operation timed out') {
26
- let timeoutId;
26
+ /** @type {any} */ let timeoutId;
27
27
 
28
+ /** @type {Promise<T>} */
28
29
  const timeoutPromise = new Promise((_, reject) => {
29
30
  timeoutId = setTimeout(() => {
30
31
  reject(new Error(message));
@@ -32,10 +33,10 @@ function withTimeout(promise, ms, message = 'Operation timed out') {
32
33
  });
33
34
 
34
35
  return Promise.race([
35
- promise.then(result => {
36
+ promise.then((/** @type {T} */ result) => {
36
37
  clearTimeout(timeoutId);
37
38
  return result;
38
- }).catch(error => {
39
+ }).catch((/** @type {any} */ error) => {
39
40
  clearTimeout(timeoutId);
40
41
  throw error;
41
42
  }),
@@ -57,7 +58,9 @@ function now() {
57
58
  * @returns {number} High-resolution timestamp
58
59
  */
59
60
  function hrTime() {
61
+ // @ts-ignore - performance may be available in some environments
60
62
  if (typeof performance !== 'undefined' && performance.now) {
63
+ // @ts-ignore
61
64
  return performance.now();
62
65
  }
63
66
  return Date.now();
@@ -119,7 +122,7 @@ function formatDuration(ms) {
119
122
  * @returns {function(...*): void} Debounced function
120
123
  */
121
124
  function debounce(fn, waitMs) {
122
- let timeoutId = null;
125
+ /** @type {any} */ let timeoutId = null;
123
126
 
124
127
  return function debounced(...args) {
125
128
  if (timeoutId !== null) {
@@ -128,6 +131,7 @@ function debounce(fn, waitMs) {
128
131
 
129
132
  timeoutId = setTimeout(() => {
130
133
  timeoutId = null;
134
+ // @ts-ignore
131
135
  fn.apply(this, args);
132
136
  }, waitMs);
133
137
  };
@@ -148,6 +152,7 @@ function throttle(fn, intervalMs) {
148
152
 
149
153
  if (currentTime - lastCall >= intervalMs) {
150
154
  lastCall = currentTime;
155
+ // @ts-ignore
151
156
  return fn.apply(this, args);
152
157
  }
153
158
  };
@@ -74,7 +74,7 @@ function validatePositiveInt(value, name) {
74
74
  * Validates that a value is one of the allowed values
75
75
  * @param {*} value - Value to validate
76
76
  * @param {string} name - Name of the parameter for error messages
77
- * @param {Array} allowed - Array of allowed values
77
+ * @param {any[]} allowed - Array of allowed values
78
78
  * @throws {ValidationError} If value is not in allowed list
79
79
  */
80
80
  function validateEnum(value, name, allowed) {