react-native-ble-mesh 1.0.1
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/CHANGELOG.md +151 -0
- package/LICENSE +21 -0
- package/README.md +706 -0
- package/docs/API.md +462 -0
- package/docs/ARCHITECTURE.md +384 -0
- package/docs/CONTRIBUTING.md +244 -0
- package/docs/NODE_QUICKSTART.md +259 -0
- package/docs/PROTOCOL.md +195 -0
- package/docs/REACT_NATIVE.md +315 -0
- package/docs/SECURITY.md +152 -0
- package/docs/api/constants_audio.js.html +184 -0
- package/docs/api/constants_ble.js.html +165 -0
- package/docs/api/constants_crypto.js.html +107 -0
- package/docs/api/constants_errors.js.html +256 -0
- package/docs/api/constants_events.js.html +148 -0
- package/docs/api/constants_index.js.html +76 -0
- package/docs/api/constants_protocol.js.html +205 -0
- package/docs/api/crypto_aead.js.html +243 -0
- package/docs/api/crypto_chacha20.js.html +235 -0
- package/docs/api/crypto_hkdf.js.html +241 -0
- package/docs/api/crypto_hmac.js.html +197 -0
- package/docs/api/crypto_index.js.html +126 -0
- package/docs/api/crypto_keys_KeyManager.js.html +325 -0
- package/docs/api/crypto_keys_KeyPair.js.html +270 -0
- package/docs/api/crypto_keys_SecureStorage.js.html +273 -0
- package/docs/api/crypto_keys_index.js.html +86 -0
- package/docs/api/crypto_noise_handshake.js.html +464 -0
- package/docs/api/crypto_noise_index.js.html +81 -0
- package/docs/api/crypto_noise_session.js.html +307 -0
- package/docs/api/crypto_noise_state.js.html +322 -0
- package/docs/api/crypto_poly1305.js.html +167 -0
- package/docs/api/crypto_sha256.js.html +294 -0
- package/docs/api/crypto_x25519.js.html +208 -0
- package/docs/api/errors_AudioError.js.html +218 -0
- package/docs/api/errors_ConnectionError.js.html +163 -0
- package/docs/api/errors_CryptoError.js.html +157 -0
- package/docs/api/errors_HandshakeError.js.html +176 -0
- package/docs/api/errors_MeshError.js.html +154 -0
- package/docs/api/errors_MessageError.js.html +183 -0
- package/docs/api/errors_ValidationError.js.html +204 -0
- package/docs/api/errors_index.js.html +78 -0
- package/docs/api/fonts/OpenSans-Bold-webfont.eot +0 -0
- package/docs/api/fonts/OpenSans-Bold-webfont.svg +1830 -0
- package/docs/api/fonts/OpenSans-Bold-webfont.woff +0 -0
- package/docs/api/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
- package/docs/api/fonts/OpenSans-BoldItalic-webfont.svg +1830 -0
- package/docs/api/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
- package/docs/api/fonts/OpenSans-Italic-webfont.eot +0 -0
- package/docs/api/fonts/OpenSans-Italic-webfont.svg +1830 -0
- package/docs/api/fonts/OpenSans-Italic-webfont.woff +0 -0
- package/docs/api/fonts/OpenSans-Light-webfont.eot +0 -0
- package/docs/api/fonts/OpenSans-Light-webfont.svg +1831 -0
- package/docs/api/fonts/OpenSans-Light-webfont.woff +0 -0
- package/docs/api/fonts/OpenSans-LightItalic-webfont.eot +0 -0
- package/docs/api/fonts/OpenSans-LightItalic-webfont.svg +1835 -0
- package/docs/api/fonts/OpenSans-LightItalic-webfont.woff +0 -0
- package/docs/api/fonts/OpenSans-Regular-webfont.eot +0 -0
- package/docs/api/fonts/OpenSans-Regular-webfont.svg +1831 -0
- package/docs/api/fonts/OpenSans-Regular-webfont.woff +0 -0
- package/docs/api/hooks_AppStateManager.js.html +233 -0
- package/docs/api/hooks_index.js.html +87 -0
- package/docs/api/hooks_useMesh.js.html +213 -0
- package/docs/api/hooks_useMessages.js.html +263 -0
- package/docs/api/hooks_usePeers.js.html +165 -0
- package/docs/api/index.html +868 -0
- package/docs/api/index.js.html +236 -0
- package/docs/api/mesh_dedup_BloomFilter.js.html +261 -0
- package/docs/api/mesh_dedup_DedupManager.js.html +266 -0
- package/docs/api/mesh_dedup_MessageCache.js.html +273 -0
- package/docs/api/mesh_dedup_index.js.html +70 -0
- package/docs/api/mesh_fragment_Assembler.js.html +335 -0
- package/docs/api/mesh_fragment_Fragmenter.js.html +230 -0
- package/docs/api/mesh_fragment_index.js.html +75 -0
- package/docs/api/mesh_index.js.html +72 -0
- package/docs/api/mesh_peer_Peer.js.html +296 -0
- package/docs/api/mesh_peer_PeerDiscovery.js.html +334 -0
- package/docs/api/mesh_peer_PeerManager.js.html +320 -0
- package/docs/api/mesh_peer_index.js.html +70 -0
- package/docs/api/mesh_router_MessageRouter.js.html +411 -0
- package/docs/api/mesh_router_PathFinder.js.html +386 -0
- package/docs/api/mesh_router_RouteTable.js.html +346 -0
- package/docs/api/mesh_router_index.js.html +70 -0
- package/docs/api/module-audio_buffer.html +168 -0
- package/docs/api/module-audio_buffer_FrameBuffer-FrameBuffer.html +2971 -0
- package/docs/api/module-audio_buffer_FrameBuffer.html +178 -0
- package/docs/api/module-audio_buffer_JitterBuffer-JitterBuffer.html +2761 -0
- package/docs/api/module-audio_buffer_JitterBuffer.html +178 -0
- package/docs/api/module-audio_codec.html +168 -0
- package/docs/api/module-audio_codec_LC3Codec-LC3Codec.html +2876 -0
- package/docs/api/module-audio_codec_LC3Codec.html +178 -0
- package/docs/api/module-audio_codec_LC3Decoder-LC3Decoder.html +1788 -0
- package/docs/api/module-audio_codec_LC3Decoder.html +178 -0
- package/docs/api/module-audio_codec_LC3Encoder-LC3Encoder.html +1512 -0
- package/docs/api/module-audio_codec_LC3Encoder.html +178 -0
- package/docs/api/module-audio_session.html +168 -0
- package/docs/api/module-audio_session_AudioSession-AudioSession.html +3922 -0
- package/docs/api/module-audio_session_AudioSession.html +178 -0
- package/docs/api/module-audio_session_VoiceMessage-VoiceMessage.html +3690 -0
- package/docs/api/module-audio_session_VoiceMessage-VoiceMessageRecorder.html +1780 -0
- package/docs/api/module-audio_session_VoiceMessage.html +332 -0
- package/docs/api/module-audio_transport.html +168 -0
- package/docs/api/module-audio_transport_AudioFragmenter-AudioAssembler.html +1545 -0
- package/docs/api/module-audio_transport_AudioFragmenter-AudioFragmenter.html +658 -0
- package/docs/api/module-audio_transport_AudioFragmenter.html +181 -0
- package/docs/api/module-audio_transport_AudioFramer.html +1414 -0
- package/docs/api/module-constants.html +168 -0
- package/docs/api/module-constants_audio.html +1782 -0
- package/docs/api/module-constants_ble.html +940 -0
- package/docs/api/module-constants_crypto.html +823 -0
- package/docs/api/module-constants_errors.html +316 -0
- package/docs/api/module-constants_events.html +244 -0
- package/docs/api/module-constants_protocol.html +1534 -0
- package/docs/api/module-crypto.html +169 -0
- package/docs/api/module-crypto_aead.html +1625 -0
- package/docs/api/module-crypto_chacha20.html +1440 -0
- package/docs/api/module-crypto_hkdf.html +1421 -0
- package/docs/api/module-crypto_hmac.html +828 -0
- package/docs/api/module-crypto_keys.html +169 -0
- package/docs/api/module-crypto_keys_KeyManager-KeyManager.html +2364 -0
- package/docs/api/module-crypto_keys_KeyManager.html +252 -0
- package/docs/api/module-crypto_keys_KeyPair.html +245 -0
- package/docs/api/module-crypto_keys_SecureStorage-MemorySecureStorage.html +923 -0
- package/docs/api/module-crypto_keys_SecureStorage-SecureStorage.html +942 -0
- package/docs/api/module-crypto_keys_SecureStorage.html +516 -0
- package/docs/api/module-crypto_noise.html +169 -0
- package/docs/api/module-crypto_noise_handshake-NoiseHandshake.html +2240 -0
- package/docs/api/module-crypto_noise_handshake.html +782 -0
- package/docs/api/module-crypto_noise_session-NoiseSession.html +1804 -0
- package/docs/api/module-crypto_noise_session.html +325 -0
- package/docs/api/module-crypto_noise_state-SymmetricState.html +1387 -0
- package/docs/api/module-crypto_noise_state.html +324 -0
- package/docs/api/module-crypto_poly1305.html +884 -0
- package/docs/api/module-crypto_sha256-HashContext.html +447 -0
- package/docs/api/module-crypto_sha256.html +942 -0
- package/docs/api/module-crypto_x25519.html +1503 -0
- package/docs/api/module-errors.html +168 -0
- package/docs/api/module-errors_AudioError-AudioError.html +4711 -0
- package/docs/api/module-errors_AudioError.html +178 -0
- package/docs/api/module-errors_ConnectionError-ConnectionError.html +3649 -0
- package/docs/api/module-errors_ConnectionError.html +178 -0
- package/docs/api/module-errors_CryptoError-CryptoError.html +3453 -0
- package/docs/api/module-errors_CryptoError.html +178 -0
- package/docs/api/module-errors_HandshakeError-HandshakeError.html +4261 -0
- package/docs/api/module-errors_HandshakeError.html +178 -0
- package/docs/api/module-errors_MeshError-MeshError.html +2155 -0
- package/docs/api/module-errors_MeshError.html +178 -0
- package/docs/api/module-errors_MessageError-MessageError.html +4545 -0
- package/docs/api/module-errors_MessageError.html +178 -0
- package/docs/api/module-errors_ValidationError-ValidationError.html +3432 -0
- package/docs/api/module-errors_ValidationError.html +178 -0
- package/docs/api/module-hooks.html +182 -0
- package/docs/api/module-hooks_AppStateManager-AppStateManager.html +1620 -0
- package/docs/api/module-hooks_AppStateManager.html +178 -0
- package/docs/api/module-hooks_useMesh.html +457 -0
- package/docs/api/module-hooks_useMessages.html +466 -0
- package/docs/api/module-hooks_usePeers.html +348 -0
- package/docs/api/module-mesh.html +168 -0
- package/docs/api/module-mesh_dedup.html +168 -0
- package/docs/api/module-mesh_dedup_BloomFilter-BloomFilter.html +2158 -0
- package/docs/api/module-mesh_dedup_BloomFilter.html +178 -0
- package/docs/api/module-mesh_dedup_DedupManager-DedupManager.html +2880 -0
- package/docs/api/module-mesh_dedup_DedupManager.html +178 -0
- package/docs/api/module-mesh_dedup_MessageCache-CacheNode.html +246 -0
- package/docs/api/module-mesh_dedup_MessageCache-MessageCache.html +2314 -0
- package/docs/api/module-mesh_dedup_MessageCache.html +181 -0
- package/docs/api/module-mesh_fragment.html +168 -0
- package/docs/api/module-mesh_fragment_Assembler-Assembler.html +2869 -0
- package/docs/api/module-mesh_fragment_Assembler-PendingFragmentSet.html +895 -0
- package/docs/api/module-mesh_fragment_Assembler.html +181 -0
- package/docs/api/module-mesh_fragment_Fragmenter.html +1084 -0
- package/docs/api/module-mesh_peer.html +168 -0
- package/docs/api/module-mesh_peer_Peer-Peer.html +4986 -0
- package/docs/api/module-mesh_peer_Peer.html +178 -0
- package/docs/api/module-mesh_peer_PeerDiscovery-PeerDiscovery.html +3423 -0
- package/docs/api/module-mesh_peer_PeerDiscovery.html +438 -0
- package/docs/api/module-mesh_peer_PeerManager-PeerManager.html +5258 -0
- package/docs/api/module-mesh_peer_PeerManager.html +178 -0
- package/docs/api/module-mesh_router.html +168 -0
- package/docs/api/module-mesh_router_MessageRouter-MessageRouter.html +3285 -0
- package/docs/api/module-mesh_router_MessageRouter.html +178 -0
- package/docs/api/module-mesh_router_PathFinder-PathFinder.html +3323 -0
- package/docs/api/module-mesh_router_PathFinder.html +421 -0
- package/docs/api/module-mesh_router_RouteTable-RouteTable.html +4115 -0
- package/docs/api/module-mesh_router_RouteTable.html +421 -0
- package/docs/api/module-protocol.html +169 -0
- package/docs/api/module-protocol_crc32.html +815 -0
- package/docs/api/module-protocol_deserializer.html +1393 -0
- package/docs/api/module-protocol_header-MessageHeader.html +2879 -0
- package/docs/api/module-protocol_header.html +892 -0
- package/docs/api/module-protocol_message-Message.html +4682 -0
- package/docs/api/module-protocol_message.html +178 -0
- package/docs/api/module-protocol_serializer.html +911 -0
- package/docs/api/module-protocol_validator.html +1396 -0
- package/docs/api/module-rn-ble-mesh.html +866 -0
- package/docs/api/module-service.html +168 -0
- package/docs/api/module-service_HandshakeManager-HandshakeManager.html +185 -0
- package/docs/api/module-service_HandshakeManager.html +175 -0
- package/docs/api/module-service_MeshService-MeshService.html +185 -0
- package/docs/api/module-service_MeshService.html +175 -0
- package/docs/api/module-service_SessionManager-SessionManager.html +174 -0
- package/docs/api/module-service_SessionManager.html +175 -0
- package/docs/api/module-service_audio.html +168 -0
- package/docs/api/module-service_audio_AudioManager-AudioManager.html +4653 -0
- package/docs/api/module-service_audio_AudioManager.html +254 -0
- package/docs/api/module-service_text.html +168 -0
- package/docs/api/module-service_text_TextManager-TextManager.html +6104 -0
- package/docs/api/module-service_text_TextManager.html +254 -0
- package/docs/api/module-service_text_broadcast.html +168 -0
- package/docs/api/module-service_text_broadcast_BroadcastManager-BroadcastManager.html +2434 -0
- package/docs/api/module-service_text_broadcast_BroadcastManager.html +254 -0
- package/docs/api/module-service_text_channel.html +168 -0
- package/docs/api/module-service_text_channel_Channel-Channel.html +4337 -0
- package/docs/api/module-service_text_channel_Channel.html +178 -0
- package/docs/api/module-service_text_channel_ChannelManager-ChannelManager.html +1927 -0
- package/docs/api/module-service_text_channel_ChannelManager.html +175 -0
- package/docs/api/module-service_text_message.html +168 -0
- package/docs/api/module-service_text_message_TextMessage-TextMessage.html +4162 -0
- package/docs/api/module-service_text_message_TextMessage.html +178 -0
- package/docs/api/module-service_text_message_TextSerializer.html +1725 -0
- package/docs/api/module-storage.html +168 -0
- package/docs/api/module-storage_AsyncStorageAdapter-AsyncStorageAdapter.html +4159 -0
- package/docs/api/module-storage_AsyncStorageAdapter.html +178 -0
- package/docs/api/module-storage_MemoryStorage-MemoryStorage.html +3154 -0
- package/docs/api/module-storage_MemoryStorage.html +178 -0
- package/docs/api/module-storage_MessageStore-MessageStore.html +5299 -0
- package/docs/api/module-storage_MessageStore.html +178 -0
- package/docs/api/module-storage_Storage-Storage.html +4169 -0
- package/docs/api/module-storage_Storage.html +178 -0
- package/docs/api/module-transport.html +168 -0
- package/docs/api/module-transport_BLEAdapter-BLEAdapter.html +4724 -0
- package/docs/api/module-transport_BLEAdapter.html +178 -0
- package/docs/api/module-transport_BLETransport-BLETransport.html +3263 -0
- package/docs/api/module-transport_BLETransport.html +178 -0
- package/docs/api/module-transport_MockTransport-MockTransport.html +3947 -0
- package/docs/api/module-transport_MockTransport.html +178 -0
- package/docs/api/module-transport_NodeBLEAdapter-NodeBLEAdapter.html +3216 -0
- package/docs/api/module-transport_NodeBLEAdapter.html +178 -0
- package/docs/api/module-transport_RNBLEAdapter-RNBLEAdapter.html +3216 -0
- package/docs/api/module-transport_RNBLEAdapter.html +178 -0
- package/docs/api/module-transport_Transport-Transport.html +4071 -0
- package/docs/api/module-transport_Transport.html +254 -0
- package/docs/api/module-transport_adapters.html +168 -0
- package/docs/api/module-transport_adapters_BLEAdapter-BLEAdapter.html +4724 -0
- package/docs/api/module-transport_adapters_BLEAdapter.html +178 -0
- package/docs/api/module-transport_adapters_NodeBLEAdapter-NodeBLEAdapter.html +3216 -0
- package/docs/api/module-transport_adapters_NodeBLEAdapter.html +178 -0
- package/docs/api/module-transport_adapters_RNBLEAdapter-RNBLEAdapter.html +3216 -0
- package/docs/api/module-transport_adapters_RNBLEAdapter.html +178 -0
- package/docs/api/module-utils.html +168 -0
- package/docs/api/module-utils_BoundedMap-BoundedMap.html +3301 -0
- package/docs/api/module-utils_BoundedMap.html +178 -0
- package/docs/api/module-utils_EventEmitter-EventEmitter.html +3358 -0
- package/docs/api/module-utils_EventEmitter.html +178 -0
- package/docs/api/module-utils_LRUCache-LRUCache.html +4134 -0
- package/docs/api/module-utils_LRUCache.html +178 -0
- package/docs/api/module-utils_RateLimiter-RateLimiter.html +2176 -0
- package/docs/api/module-utils_RateLimiter.html +500 -0
- package/docs/api/module-utils_TimeoutManager-TimeoutManager.html +1781 -0
- package/docs/api/module-utils_TimeoutManager.html +175 -0
- package/docs/api/module-utils_bytes.html +1789 -0
- package/docs/api/module-utils_debug.html +837 -0
- package/docs/api/module-utils_encoding.html +1184 -0
- package/docs/api/module-utils_retry.html +1457 -0
- package/docs/api/module-utils_time.html +1665 -0
- package/docs/api/module-utils_uuid.html +1269 -0
- package/docs/api/module-utils_validation.html +2176 -0
- package/docs/api/protocol_crc32.js.html +147 -0
- package/docs/api/protocol_deserializer.js.html +295 -0
- package/docs/api/protocol_header.js.html +276 -0
- package/docs/api/protocol_index.js.html +120 -0
- package/docs/api/protocol_message.js.html +287 -0
- package/docs/api/protocol_serializer.js.html +240 -0
- package/docs/api/protocol_validator.js.html +330 -0
- package/docs/api/scripts/linenumber.js +25 -0
- package/docs/api/scripts/prettify/Apache-License-2.0.txt +202 -0
- package/docs/api/scripts/prettify/lang-css.js +2 -0
- package/docs/api/scripts/prettify/prettify.js +28 -0
- package/docs/api/service_HandshakeManager.js.html +232 -0
- package/docs/api/service_MeshService.js.html +371 -0
- package/docs/api/service_SessionManager.js.html +153 -0
- package/docs/api/service_audio_AudioManager.js.html +541 -0
- package/docs/api/service_audio_buffer_FrameBuffer.js.html +223 -0
- package/docs/api/service_audio_buffer_JitterBuffer.js.html +244 -0
- package/docs/api/service_audio_buffer_index.js.html +68 -0
- package/docs/api/service_audio_codec_LC3Codec.js.html +345 -0
- package/docs/api/service_audio_codec_LC3Decoder.js.html +185 -0
- package/docs/api/service_audio_codec_LC3Encoder.js.html +194 -0
- package/docs/api/service_audio_codec_index.js.html +70 -0
- package/docs/api/service_audio_index.js.html +96 -0
- package/docs/api/service_audio_session_AudioSession.js.html +348 -0
- package/docs/api/service_audio_session_VoiceMessage.js.html +432 -0
- package/docs/api/service_audio_session_index.js.html +70 -0
- package/docs/api/service_audio_transport_AudioFragmenter.js.html +314 -0
- package/docs/api/service_audio_transport_AudioFramer.js.html +236 -0
- package/docs/api/service_audio_transport_index.js.html +69 -0
- package/docs/api/service_index.js.html +93 -0
- package/docs/api/service_text_TextManager.js.html +578 -0
- package/docs/api/service_text_broadcast_BroadcastManager.js.html +276 -0
- package/docs/api/service_text_broadcast_index.js.html +66 -0
- package/docs/api/service_text_channel_Channel.js.html +280 -0
- package/docs/api/service_text_channel_ChannelManager.js.html +225 -0
- package/docs/api/service_text_channel_index.js.html +68 -0
- package/docs/api/service_text_index.js.html +85 -0
- package/docs/api/service_text_message_TextMessage.js.html +350 -0
- package/docs/api/service_text_message_TextSerializer.js.html +218 -0
- package/docs/api/service_text_message_index.js.html +68 -0
- package/docs/api/storage_AsyncStorageAdapter.js.html +357 -0
- package/docs/api/storage_MemoryStorage.js.html +279 -0
- package/docs/api/storage_MessageStore.js.html +369 -0
- package/docs/api/storage_Storage.js.html +214 -0
- package/docs/api/storage_index.js.html +72 -0
- package/docs/api/styles/jsdoc-default.css +358 -0
- package/docs/api/styles/prettify-jsdoc.css +111 -0
- package/docs/api/styles/prettify-tomorrow.css +132 -0
- package/docs/api/transport_BLEAdapter.js.html +231 -0
- package/docs/api/transport_BLETransport.js.html +411 -0
- package/docs/api/transport_MockTransport.js.html +339 -0
- package/docs/api/transport_NodeBLEAdapter.js.html +479 -0
- package/docs/api/transport_RNBLEAdapter.js.html +382 -0
- package/docs/api/transport_Transport.js.html +242 -0
- package/docs/api/transport_adapters_BLEAdapter.js.html +231 -0
- package/docs/api/transport_adapters_NodeBLEAdapter.js.html +479 -0
- package/docs/api/transport_adapters_RNBLEAdapter.js.html +382 -0
- package/docs/api/transport_adapters_index.js.html +70 -0
- package/docs/api/transport_index.js.html +87 -0
- package/docs/api/utils_BoundedMap.js.html +205 -0
- package/docs/api/utils_EventEmitter.js.html +259 -0
- package/docs/api/utils_LRUCache.js.html +256 -0
- package/docs/api/utils_RateLimiter.js.html +256 -0
- package/docs/api/utils_TimeoutManager.js.html +218 -0
- package/docs/api/utils_base64%0A%0AThis%20implementation%20avoids%20string%20concatenation%20in%20loops%20which%20is%20O(n%C2%B2).%0AUses%20array%20building%20which%20is%20O(n)%20-%20critical%20for%20React%20Native%20performance.module_.html +717 -0
- package/docs/api/utils_base64.js.html +205 -0
- package/docs/api/utils_bytes.js.html +241 -0
- package/docs/api/utils_debug.js.html +205 -0
- package/docs/api/utils_encoding.js.html +302 -0
- package/docs/api/utils_index.js.html +160 -0
- package/docs/api/utils_retry.js.html +200 -0
- package/docs/api/utils_time.js.html +220 -0
- package/docs/api/utils_uuid.js.html +199 -0
- package/docs/api/utils_validation.js.html +259 -0
- package/examples/node-chat/chat.js +220 -0
- package/examples/node-quickstart/index.js +94 -0
- package/examples/testing/test-helper.js +182 -0
- package/package.json +111 -0
- package/src/constants/audio.js +130 -0
- package/src/constants/ble.js +111 -0
- package/src/constants/crypto.js +53 -0
- package/src/constants/errors.js +202 -0
- package/src/constants/events.js +94 -0
- package/src/constants/index.js +22 -0
- package/src/constants/protocol.js +151 -0
- package/src/crypto/aead.js +189 -0
- package/src/crypto/chacha20.js +181 -0
- package/src/crypto/hkdf.js +187 -0
- package/src/crypto/hmac.js +143 -0
- package/src/crypto/index.js +72 -0
- package/src/crypto/keys/KeyManager.js +271 -0
- package/src/crypto/keys/KeyPair.js +216 -0
- package/src/crypto/keys/SecureStorage.js +219 -0
- package/src/crypto/keys/index.js +32 -0
- package/src/crypto/noise/handshake.js +410 -0
- package/src/crypto/noise/index.js +27 -0
- package/src/crypto/noise/session.js +253 -0
- package/src/crypto/noise/state.js +268 -0
- package/src/crypto/poly1305.js +113 -0
- package/src/crypto/sha256.js +240 -0
- package/src/crypto/x25519.js +154 -0
- package/src/errors/AudioError.js +164 -0
- package/src/errors/ConnectionError.js +109 -0
- package/src/errors/CryptoError.js +103 -0
- package/src/errors/HandshakeError.js +122 -0
- package/src/errors/MeshError.js +100 -0
- package/src/errors/MessageError.js +129 -0
- package/src/errors/ValidationError.js +150 -0
- package/src/errors/index.js +24 -0
- package/src/hooks/AppStateManager.js +179 -0
- package/src/hooks/index.js +33 -0
- package/src/hooks/useMesh.js +159 -0
- package/src/hooks/useMessages.js +209 -0
- package/src/hooks/usePeers.js +111 -0
- package/src/index.d.ts +494 -0
- package/src/index.js +182 -0
- package/src/index.mjs +62 -0
- package/src/mesh/dedup/BloomFilter.js +207 -0
- package/src/mesh/dedup/DedupManager.js +212 -0
- package/src/mesh/dedup/MessageCache.js +219 -0
- package/src/mesh/dedup/index.js +16 -0
- package/src/mesh/fragment/Assembler.js +281 -0
- package/src/mesh/fragment/Fragmenter.js +176 -0
- package/src/mesh/fragment/index.js +21 -0
- package/src/mesh/index.js +18 -0
- package/src/mesh/peer/Peer.js +242 -0
- package/src/mesh/peer/PeerDiscovery.js +280 -0
- package/src/mesh/peer/PeerManager.js +266 -0
- package/src/mesh/peer/index.js +16 -0
- package/src/mesh/router/MessageRouter.js +357 -0
- package/src/mesh/router/PathFinder.js +332 -0
- package/src/mesh/router/RouteTable.js +292 -0
- package/src/mesh/router/index.js +16 -0
- package/src/protocol/crc32.js +93 -0
- package/src/protocol/deserializer.js +241 -0
- package/src/protocol/header.js +222 -0
- package/src/protocol/index.js +66 -0
- package/src/protocol/message.js +233 -0
- package/src/protocol/serializer.js +186 -0
- package/src/protocol/validator.js +276 -0
- package/src/service/HandshakeManager.js +178 -0
- package/src/service/MeshService.js +317 -0
- package/src/service/SessionManager.js +99 -0
- package/src/service/audio/AudioManager.js +487 -0
- package/src/service/audio/buffer/FrameBuffer.js +169 -0
- package/src/service/audio/buffer/JitterBuffer.js +190 -0
- package/src/service/audio/buffer/index.js +14 -0
- package/src/service/audio/codec/LC3Codec.js +291 -0
- package/src/service/audio/codec/LC3Decoder.js +131 -0
- package/src/service/audio/codec/LC3Encoder.js +140 -0
- package/src/service/audio/codec/index.js +16 -0
- package/src/service/audio/index.js +42 -0
- package/src/service/audio/session/AudioSession.js +294 -0
- package/src/service/audio/session/VoiceMessage.js +378 -0
- package/src/service/audio/session/index.js +16 -0
- package/src/service/audio/transport/AudioFragmenter.js +260 -0
- package/src/service/audio/transport/AudioFramer.js +182 -0
- package/src/service/audio/transport/index.js +15 -0
- package/src/service/index.js +39 -0
- package/src/service/text/TextManager.js +524 -0
- package/src/service/text/broadcast/BroadcastManager.js +222 -0
- package/src/service/text/broadcast/index.js +12 -0
- package/src/service/text/channel/Channel.js +226 -0
- package/src/service/text/channel/ChannelManager.js +171 -0
- package/src/service/text/channel/index.js +14 -0
- package/src/service/text/index.js +31 -0
- package/src/service/text/message/TextMessage.js +296 -0
- package/src/service/text/message/TextSerializer.js +164 -0
- package/src/service/text/message/index.js +14 -0
- package/src/storage/AsyncStorageAdapter.js +303 -0
- package/src/storage/MemoryStorage.js +225 -0
- package/src/storage/MessageStore.js +315 -0
- package/src/storage/Storage.js +160 -0
- package/src/storage/index.js +18 -0
- package/src/transport/BLEAdapter.js +177 -0
- package/src/transport/BLETransport.js +357 -0
- package/src/transport/MockTransport.js +285 -0
- package/src/transport/NodeBLEAdapter.js +425 -0
- package/src/transport/RNBLEAdapter.js +328 -0
- package/src/transport/Transport.js +188 -0
- package/src/transport/adapters/BLEAdapter.js +177 -0
- package/src/transport/adapters/NodeBLEAdapter.js +425 -0
- package/src/transport/adapters/RNBLEAdapter.js +328 -0
- package/src/transport/adapters/index.js +16 -0
- package/src/transport/index.js +33 -0
- package/src/utils/BoundedMap.js +151 -0
- package/src/utils/EventEmitter.js +205 -0
- package/src/utils/LRUCache.js +202 -0
- package/src/utils/RateLimiter.js +202 -0
- package/src/utils/TimeoutManager.js +164 -0
- package/src/utils/base64.js +151 -0
- package/src/utils/bytes.js +187 -0
- package/src/utils/debug.js +151 -0
- package/src/utils/encoding.js +248 -0
- package/src/utils/index.js +106 -0
- package/src/utils/retry.js +146 -0
- package/src/utils/time.js +166 -0
- package/src/utils/uuid.js +145 -0
- package/src/utils/validation.js +205 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Message container class combining header and payload.
|
|
5
|
+
* @module protocol/message
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { MessageHeader, HEADER_SIZE, generateUuid } = require('./header');
|
|
9
|
+
const { MESSAGE_FLAGS, MESH_CONFIG } = require('../constants');
|
|
10
|
+
const { MessageError } = require('../errors');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Message class representing a complete mesh network message.
|
|
14
|
+
* @class Message
|
|
15
|
+
*/
|
|
16
|
+
class Message {
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new Message instance.
|
|
19
|
+
* @param {MessageHeader} header - Message header
|
|
20
|
+
* @param {Uint8Array} payload - Message payload
|
|
21
|
+
*/
|
|
22
|
+
constructor(header, payload) {
|
|
23
|
+
/** @type {MessageHeader} */
|
|
24
|
+
this.header = header;
|
|
25
|
+
/** @type {Uint8Array} */
|
|
26
|
+
this.payload = payload;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Creates a new message with the given options.
|
|
31
|
+
* @param {Object} options - Message options
|
|
32
|
+
* @param {number} options.type - Message type from MESSAGE_TYPE
|
|
33
|
+
* @param {Uint8Array|string} options.payload - Message payload
|
|
34
|
+
* @param {number} [options.flags=0] - Message flags
|
|
35
|
+
* @param {number} [options.maxHops=7] - Maximum hops
|
|
36
|
+
* @param {number} [options.ttlMs] - Time-to-live in ms
|
|
37
|
+
* @param {number} [options.fragmentIndex=0] - Fragment index
|
|
38
|
+
* @param {number} [options.fragmentTotal=1] - Total fragments
|
|
39
|
+
* @param {Uint8Array} [options.messageId] - Optional message ID
|
|
40
|
+
* @returns {Message} New message instance
|
|
41
|
+
*/
|
|
42
|
+
static create(options) {
|
|
43
|
+
let payload = options.payload;
|
|
44
|
+
|
|
45
|
+
// Convert string payload to bytes
|
|
46
|
+
if (typeof payload === 'string') {
|
|
47
|
+
payload = new TextEncoder().encode(payload);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!(payload instanceof Uint8Array)) {
|
|
51
|
+
payload = new Uint8Array(0);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
const ttl = options.ttlMs ?? MESH_CONFIG.MESSAGE_TTL_MS;
|
|
56
|
+
|
|
57
|
+
const header = new MessageHeader({
|
|
58
|
+
type: options.type,
|
|
59
|
+
flags: options.flags ?? MESSAGE_FLAGS.NONE,
|
|
60
|
+
maxHops: options.maxHops ?? MESH_CONFIG.MAX_HOPS,
|
|
61
|
+
messageId: options.messageId ?? generateUuid(),
|
|
62
|
+
timestamp: now,
|
|
63
|
+
expiresAt: now + ttl,
|
|
64
|
+
payloadLength: payload.length,
|
|
65
|
+
fragmentIndex: options.fragmentIndex ?? 0,
|
|
66
|
+
fragmentTotal: options.fragmentTotal ?? 1
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
return new Message(header, payload);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Deserializes a message from bytes.
|
|
74
|
+
* @param {Uint8Array} data - Raw message bytes
|
|
75
|
+
* @returns {Message} Parsed message
|
|
76
|
+
* @throws {MessageError} If data is invalid
|
|
77
|
+
*/
|
|
78
|
+
static fromBytes(data) {
|
|
79
|
+
if (!(data instanceof Uint8Array)) {
|
|
80
|
+
throw MessageError.invalidFormat(null, { reason: 'Data must be Uint8Array' });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (data.length < HEADER_SIZE) {
|
|
84
|
+
throw MessageError.invalidFormat(null, {
|
|
85
|
+
reason: 'Message too small',
|
|
86
|
+
size: data.length,
|
|
87
|
+
minSize: HEADER_SIZE
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const header = MessageHeader.fromBytes(data);
|
|
92
|
+
const expectedLength = HEADER_SIZE + header.payloadLength;
|
|
93
|
+
|
|
94
|
+
if (data.length < expectedLength) {
|
|
95
|
+
throw MessageError.invalidFormat(header.getMessageIdHex(), {
|
|
96
|
+
reason: 'Incomplete payload',
|
|
97
|
+
expected: expectedLength,
|
|
98
|
+
actual: data.length
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const payload = data.slice(HEADER_SIZE, HEADER_SIZE + header.payloadLength);
|
|
103
|
+
|
|
104
|
+
return new Message(header, payload);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Serializes the message to bytes.
|
|
109
|
+
* @returns {Uint8Array} Complete message bytes
|
|
110
|
+
*/
|
|
111
|
+
toBytes() {
|
|
112
|
+
const headerBytes = this.header.toBytes();
|
|
113
|
+
const result = new Uint8Array(headerBytes.length + this.payload.length);
|
|
114
|
+
result.set(headerBytes, 0);
|
|
115
|
+
result.set(this.payload, headerBytes.length);
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Checks if the message has expired.
|
|
121
|
+
* @returns {boolean} True if expired
|
|
122
|
+
*/
|
|
123
|
+
isExpired() {
|
|
124
|
+
return Date.now() > this.header.expiresAt;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Checks if the message is a fragment.
|
|
129
|
+
* @returns {boolean} True if this is a fragment
|
|
130
|
+
*/
|
|
131
|
+
isFragment() {
|
|
132
|
+
return this.header.fragmentTotal > 1 ||
|
|
133
|
+
(this.header.flags & MESSAGE_FLAGS.IS_FRAGMENT) !== 0;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Checks if the message payload is encrypted.
|
|
138
|
+
* @returns {boolean} True if encrypted
|
|
139
|
+
*/
|
|
140
|
+
isEncrypted() {
|
|
141
|
+
return (this.header.flags & MESSAGE_FLAGS.ENCRYPTED) !== 0;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Checks if the message requires acknowledgment.
|
|
146
|
+
* @returns {boolean} True if ACK required
|
|
147
|
+
*/
|
|
148
|
+
requiresAck() {
|
|
149
|
+
return (this.header.flags & MESSAGE_FLAGS.REQUIRES_ACK) !== 0;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Checks if the message is a broadcast.
|
|
154
|
+
* @returns {boolean} True if broadcast
|
|
155
|
+
*/
|
|
156
|
+
isBroadcast() {
|
|
157
|
+
return (this.header.flags & MESSAGE_FLAGS.IS_BROADCAST) !== 0;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Checks if the message is high priority.
|
|
162
|
+
* @returns {boolean} True if high priority
|
|
163
|
+
*/
|
|
164
|
+
isHighPriority() {
|
|
165
|
+
return (this.header.flags & MESSAGE_FLAGS.HIGH_PRIORITY) !== 0;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Gets the payload content as a UTF-8 string.
|
|
170
|
+
* Only meaningful for text message types.
|
|
171
|
+
* @returns {string} Decoded payload content
|
|
172
|
+
*/
|
|
173
|
+
getContent() {
|
|
174
|
+
return new TextDecoder().decode(this.payload);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Gets the message ID as a hex string.
|
|
179
|
+
* @returns {string} Message ID hex string
|
|
180
|
+
*/
|
|
181
|
+
getMessageId() {
|
|
182
|
+
return this.header.getMessageIdHex();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Gets the total size of the message in bytes.
|
|
187
|
+
* @returns {number} Total message size
|
|
188
|
+
*/
|
|
189
|
+
getSize() {
|
|
190
|
+
return HEADER_SIZE + this.payload.length;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Increments the hop count. Used when relaying messages.
|
|
195
|
+
* @throws {MessageError} If max hops exceeded
|
|
196
|
+
*/
|
|
197
|
+
incrementHopCount() {
|
|
198
|
+
if (this.header.hopCount >= this.header.maxHops) {
|
|
199
|
+
throw MessageError.maxHopsExceeded(this.getMessageId(), {
|
|
200
|
+
hopCount: this.header.hopCount,
|
|
201
|
+
maxHops: this.header.maxHops
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
this.header.hopCount++;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Creates a clone of this message.
|
|
209
|
+
* @returns {Message} Cloned message
|
|
210
|
+
*/
|
|
211
|
+
clone() {
|
|
212
|
+
const headerClone = new MessageHeader({
|
|
213
|
+
version: this.header.version,
|
|
214
|
+
type: this.header.type,
|
|
215
|
+
flags: this.header.flags,
|
|
216
|
+
hopCount: this.header.hopCount,
|
|
217
|
+
maxHops: this.header.maxHops,
|
|
218
|
+
messageId: new Uint8Array(this.header.messageId),
|
|
219
|
+
timestamp: this.header.timestamp,
|
|
220
|
+
expiresAt: this.header.expiresAt,
|
|
221
|
+
payloadLength: this.header.payloadLength,
|
|
222
|
+
fragmentIndex: this.header.fragmentIndex,
|
|
223
|
+
fragmentTotal: this.header.fragmentTotal,
|
|
224
|
+
checksum: this.header.checksum
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
return new Message(headerClone, new Uint8Array(this.payload));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
module.exports = {
|
|
232
|
+
Message
|
|
233
|
+
};
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Binary serialization for mesh protocol messages.
|
|
5
|
+
* @module protocol/serializer
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { HEADER_SIZE } = require('./header');
|
|
9
|
+
const { Message } = require('./message');
|
|
10
|
+
const { crc32 } = require('./crc32');
|
|
11
|
+
const { PROTOCOL_VERSION, MESSAGE_FLAGS, MESH_CONFIG } = require('../constants');
|
|
12
|
+
const { MessageError } = require('../errors');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Serializes a message header to bytes.
|
|
16
|
+
* Calculates CRC32 checksum and includes it in the output.
|
|
17
|
+
*
|
|
18
|
+
* @param {MessageHeader|Object} header - Header to serialize
|
|
19
|
+
* @returns {Uint8Array} 48-byte serialized header
|
|
20
|
+
* @throws {MessageError} If header is invalid
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* const header = MessageHeader.create({ type: MESSAGE_TYPE.TEXT, payloadLength: 5 });
|
|
24
|
+
* const bytes = serializeHeader(header);
|
|
25
|
+
*/
|
|
26
|
+
function serializeHeader(header) {
|
|
27
|
+
if (!header || typeof header !== 'object') {
|
|
28
|
+
throw MessageError.invalidFormat(null, { reason: 'Header must be an object' });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const buffer = new Uint8Array(HEADER_SIZE);
|
|
32
|
+
const view = new DataView(buffer.buffer);
|
|
33
|
+
|
|
34
|
+
// Validate required fields
|
|
35
|
+
if (header.messageId === null || header.messageId === undefined || header.messageId.length !== 16) {
|
|
36
|
+
throw new Error('Invalid messageId');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Byte 0: version
|
|
40
|
+
buffer[0] = header.version ?? PROTOCOL_VERSION;
|
|
41
|
+
|
|
42
|
+
// Byte 1: type
|
|
43
|
+
buffer[1] = header.type;
|
|
44
|
+
|
|
45
|
+
// Byte 2: flags
|
|
46
|
+
buffer[2] = header.flags ?? MESSAGE_FLAGS.NONE;
|
|
47
|
+
|
|
48
|
+
// Byte 3: hopCount
|
|
49
|
+
buffer[3] = header.hopCount ?? 0;
|
|
50
|
+
|
|
51
|
+
// Byte 4: maxHops
|
|
52
|
+
buffer[4] = header.maxHops ?? MESH_CONFIG.MAX_HOPS;
|
|
53
|
+
|
|
54
|
+
// Bytes 5-7: reserved (already zeros)
|
|
55
|
+
|
|
56
|
+
// Bytes 8-23: messageId (16 bytes)
|
|
57
|
+
buffer.set(header.messageId, 8);
|
|
58
|
+
|
|
59
|
+
// Bytes 24-31: timestamp (big-endian uint64)
|
|
60
|
+
writeUint64BE(view, 24, header.timestamp);
|
|
61
|
+
|
|
62
|
+
// Bytes 32-39: expiresAt (big-endian uint64)
|
|
63
|
+
writeUint64BE(view, 32, header.expiresAt);
|
|
64
|
+
|
|
65
|
+
// Bytes 40-41: payloadLength (big-endian uint16)
|
|
66
|
+
view.setUint16(40, header.payloadLength, false);
|
|
67
|
+
|
|
68
|
+
// Byte 42: fragmentIndex
|
|
69
|
+
buffer[42] = header.fragmentIndex ?? 0;
|
|
70
|
+
|
|
71
|
+
// Byte 43: fragmentTotal
|
|
72
|
+
buffer[43] = header.fragmentTotal ?? 1;
|
|
73
|
+
|
|
74
|
+
// Bytes 44-47: checksum (calculated over bytes 0-43)
|
|
75
|
+
const checksumData = buffer.slice(0, 44);
|
|
76
|
+
const checksum = crc32(checksumData);
|
|
77
|
+
view.setUint32(44, checksum, false);
|
|
78
|
+
|
|
79
|
+
return buffer;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Serializes a complete message (header + payload) to bytes.
|
|
84
|
+
*
|
|
85
|
+
* @param {Message|Object} message - Message to serialize
|
|
86
|
+
* @returns {Uint8Array} Serialized message bytes
|
|
87
|
+
* @throws {MessageError} If message is invalid
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* const message = Message.create({ type: MESSAGE_TYPE.TEXT, payload: 'Hello' });
|
|
91
|
+
* const bytes = serialize(message);
|
|
92
|
+
*/
|
|
93
|
+
function serialize(message) {
|
|
94
|
+
if (!message || typeof message !== 'object') {
|
|
95
|
+
throw MessageError.invalidFormat(null, { reason: 'Message must be an object' });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Handle Message instances
|
|
99
|
+
if (message instanceof Message) {
|
|
100
|
+
return message.toBytes();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Handle plain objects with header and payload
|
|
104
|
+
const header = message.header;
|
|
105
|
+
let payload = message.payload;
|
|
106
|
+
|
|
107
|
+
if (!header) {
|
|
108
|
+
throw MessageError.invalidFormat(null, { reason: 'Message must have a header' });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Convert string payload to bytes
|
|
112
|
+
if (typeof payload === 'string') {
|
|
113
|
+
payload = new TextEncoder().encode(payload);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Default to empty payload
|
|
117
|
+
if (!payload) {
|
|
118
|
+
payload = new Uint8Array(0);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!(payload instanceof Uint8Array)) {
|
|
122
|
+
throw MessageError.invalidFormat(null, { reason: 'Payload must be Uint8Array or string' });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Update payloadLength in header
|
|
126
|
+
const headerWithLength = {
|
|
127
|
+
...header,
|
|
128
|
+
payloadLength: payload.length
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const headerBytes = serializeHeader(headerWithLength);
|
|
132
|
+
const result = new Uint8Array(headerBytes.length + payload.length);
|
|
133
|
+
|
|
134
|
+
result.set(headerBytes, 0);
|
|
135
|
+
result.set(payload, headerBytes.length);
|
|
136
|
+
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Writes a 64-bit unsigned integer in big-endian format.
|
|
142
|
+
* JavaScript numbers can safely represent integers up to 2^53.
|
|
143
|
+
*
|
|
144
|
+
* @param {DataView} view - DataView to write to
|
|
145
|
+
* @param {number} offset - Byte offset
|
|
146
|
+
* @param {number} value - Value to write
|
|
147
|
+
*/
|
|
148
|
+
function writeUint64BE(view, offset, value) {
|
|
149
|
+
// Split into high and low 32-bit parts
|
|
150
|
+
const high = Math.floor(value / 0x100000000);
|
|
151
|
+
const low = value >>> 0;
|
|
152
|
+
|
|
153
|
+
view.setUint32(offset, high, false);
|
|
154
|
+
view.setUint32(offset + 4, low, false);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Serializes multiple messages into a single buffer.
|
|
159
|
+
* Useful for batch transmission.
|
|
160
|
+
*
|
|
161
|
+
* @param {Array<Message>} messages - Array of messages
|
|
162
|
+
* @returns {Uint8Array} Concatenated message bytes
|
|
163
|
+
*/
|
|
164
|
+
function serializeBatch(messages) {
|
|
165
|
+
if (!Array.isArray(messages)) {
|
|
166
|
+
throw MessageError.invalidFormat(null, { reason: 'Messages must be an array' });
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const serialized = messages.map(msg => serialize(msg));
|
|
170
|
+
const totalLength = serialized.reduce((sum, arr) => sum + arr.length, 0);
|
|
171
|
+
const result = new Uint8Array(totalLength);
|
|
172
|
+
|
|
173
|
+
let offset = 0;
|
|
174
|
+
for (const bytes of serialized) {
|
|
175
|
+
result.set(bytes, offset);
|
|
176
|
+
offset += bytes.length;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return result;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
module.exports = {
|
|
183
|
+
serialize,
|
|
184
|
+
serializeHeader,
|
|
185
|
+
serializeBatch
|
|
186
|
+
};
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Message and header validation for mesh protocol.
|
|
5
|
+
* @module protocol/validator
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { HEADER_SIZE } = require('./header');
|
|
9
|
+
const { crc32 } = require('./crc32');
|
|
10
|
+
const { PROTOCOL_VERSION, MESSAGE_TYPE, MESH_CONFIG } = require('../constants');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Set of valid message type values for fast lookup.
|
|
14
|
+
* @type {Set<number>}
|
|
15
|
+
*/
|
|
16
|
+
const VALID_MESSAGE_TYPES = new Set(Object.values(MESSAGE_TYPE));
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Validates a message header.
|
|
20
|
+
*
|
|
21
|
+
* @param {MessageHeader|Object} header - Header to validate
|
|
22
|
+
* @returns {{ valid: boolean, errors: string[] }} Validation result
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* const result = validateHeader(header);
|
|
26
|
+
* if (!result.valid) {
|
|
27
|
+
* console.error('Header errors:', result.errors);
|
|
28
|
+
* }
|
|
29
|
+
*/
|
|
30
|
+
function validateHeader(header) {
|
|
31
|
+
const errors = [];
|
|
32
|
+
|
|
33
|
+
if (!header || typeof header !== 'object') {
|
|
34
|
+
return { valid: false, errors: ['Header must be an object'] };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Validate protocol version
|
|
38
|
+
if (header.version !== PROTOCOL_VERSION) {
|
|
39
|
+
errors.push(`Unsupported protocol version: ${header.version}, expected: ${PROTOCOL_VERSION}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Validate message type
|
|
43
|
+
if (!VALID_MESSAGE_TYPES.has(header.type)) {
|
|
44
|
+
errors.push(`Invalid message type: 0x${header.type?.toString(16) ?? 'undefined'}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Validate hop count
|
|
48
|
+
if (typeof header.hopCount !== 'number' || header.hopCount < 0) {
|
|
49
|
+
errors.push(`Invalid hop count: ${header.hopCount}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Validate max hops
|
|
53
|
+
if (typeof header.maxHops !== 'number' || header.maxHops < 1) {
|
|
54
|
+
errors.push(`Invalid max hops: ${header.maxHops}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Validate hop count vs max hops
|
|
58
|
+
if (header.hopCount > header.maxHops) {
|
|
59
|
+
errors.push(`Hop count (${header.hopCount}) exceeds max hops (${header.maxHops})`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Validate message ID
|
|
63
|
+
if (!header.messageId || !(header.messageId instanceof Uint8Array)) {
|
|
64
|
+
errors.push('Message ID must be a Uint8Array');
|
|
65
|
+
} else if (header.messageId.length !== 16) {
|
|
66
|
+
errors.push(`Message ID must be 16 bytes, got ${header.messageId.length}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Validate timestamps
|
|
70
|
+
if (typeof header.timestamp !== 'number' || header.timestamp <= 0) {
|
|
71
|
+
errors.push(`Invalid timestamp: ${header.timestamp}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (typeof header.expiresAt !== 'number' || header.expiresAt <= 0) {
|
|
75
|
+
errors.push(`Invalid expiresAt: ${header.expiresAt}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Check if expired
|
|
79
|
+
if (header.expiresAt <= Date.now()) {
|
|
80
|
+
errors.push('Message has expired');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Validate payload length
|
|
84
|
+
if (typeof header.payloadLength !== 'number' || header.payloadLength < 0) {
|
|
85
|
+
errors.push(`Invalid payload length: ${header.payloadLength}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (header.payloadLength > MESH_CONFIG.MAX_MESSAGE_SIZE) {
|
|
89
|
+
errors.push(`Payload too large: ${header.payloadLength} > ${MESH_CONFIG.MAX_MESSAGE_SIZE}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Validate fragment fields
|
|
93
|
+
if (typeof header.fragmentIndex !== 'number' || header.fragmentIndex < 0) {
|
|
94
|
+
errors.push(`Invalid fragment index: ${header.fragmentIndex}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (typeof header.fragmentTotal !== 'number' || header.fragmentTotal < 1) {
|
|
98
|
+
errors.push(`Invalid fragment total: ${header.fragmentTotal}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (header.fragmentIndex >= header.fragmentTotal) {
|
|
102
|
+
errors.push(`Fragment index (${header.fragmentIndex}) >= total (${header.fragmentTotal})`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
valid: errors.length === 0,
|
|
107
|
+
errors
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Validates a complete message (header + payload).
|
|
113
|
+
*
|
|
114
|
+
* @param {Message|Object} message - Message to validate
|
|
115
|
+
* @returns {{ valid: boolean, errors: string[] }} Validation result
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* const result = validateMessage(message);
|
|
119
|
+
* if (!result.valid) {
|
|
120
|
+
* console.error('Message errors:', result.errors);
|
|
121
|
+
* }
|
|
122
|
+
*/
|
|
123
|
+
function validateMessage(message) {
|
|
124
|
+
const errors = [];
|
|
125
|
+
|
|
126
|
+
if (!message || typeof message !== 'object') {
|
|
127
|
+
return { valid: false, errors: ['Message must be an object'] };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Validate header exists
|
|
131
|
+
if (!message.header) {
|
|
132
|
+
errors.push('Message must have a header');
|
|
133
|
+
return { valid: false, errors };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Validate header
|
|
137
|
+
const headerResult = validateHeader(message.header);
|
|
138
|
+
errors.push(...headerResult.errors);
|
|
139
|
+
|
|
140
|
+
// Validate payload exists
|
|
141
|
+
if (!message.payload) {
|
|
142
|
+
errors.push('Message must have a payload');
|
|
143
|
+
} else if (!(message.payload instanceof Uint8Array)) {
|
|
144
|
+
errors.push('Payload must be a Uint8Array');
|
|
145
|
+
} else {
|
|
146
|
+
// Validate payload length matches header
|
|
147
|
+
if (message.payload.length !== message.header.payloadLength) {
|
|
148
|
+
errors.push(
|
|
149
|
+
`Payload length mismatch: header says ${message.header.payloadLength}, ` +
|
|
150
|
+
`actual is ${message.payload.length}`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
valid: errors.length === 0,
|
|
157
|
+
errors
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Validates message checksum against the header data.
|
|
163
|
+
*
|
|
164
|
+
* @param {Uint8Array} headerBytes - Raw 48-byte header
|
|
165
|
+
* @returns {{ valid: boolean, expected: number, actual: number }} Checksum result
|
|
166
|
+
*/
|
|
167
|
+
function validateChecksum(headerBytes) {
|
|
168
|
+
if (!(headerBytes instanceof Uint8Array) || headerBytes.length < HEADER_SIZE) {
|
|
169
|
+
return { valid: false, expected: 0, actual: 0 };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const view = new DataView(headerBytes.buffer, headerBytes.byteOffset, HEADER_SIZE);
|
|
173
|
+
const storedChecksum = view.getUint32(44, false);
|
|
174
|
+
const checksumData = headerBytes.slice(0, 44);
|
|
175
|
+
const calculatedChecksum = crc32(checksumData);
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
valid: storedChecksum === calculatedChecksum,
|
|
179
|
+
expected: storedChecksum,
|
|
180
|
+
actual: calculatedChecksum
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Checks if a message type is valid.
|
|
186
|
+
*
|
|
187
|
+
* @param {number} type - Message type to check
|
|
188
|
+
* @returns {boolean} True if valid
|
|
189
|
+
*/
|
|
190
|
+
function isValidMessageType(type) {
|
|
191
|
+
return VALID_MESSAGE_TYPES.has(type);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Checks if a message is expired.
|
|
196
|
+
*
|
|
197
|
+
* @param {Message|MessageHeader|Object} messageOrHeader - Message or header
|
|
198
|
+
* @returns {boolean} True if expired
|
|
199
|
+
*/
|
|
200
|
+
function isExpired(messageOrHeader) {
|
|
201
|
+
const header = messageOrHeader.header || messageOrHeader;
|
|
202
|
+
return header.expiresAt <= Date.now();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Checks if hop count has exceeded maximum.
|
|
207
|
+
*
|
|
208
|
+
* @param {Message|MessageHeader|Object} messageOrHeader - Message or header
|
|
209
|
+
* @returns {boolean} True if exceeded
|
|
210
|
+
*/
|
|
211
|
+
function hasExceededMaxHops(messageOrHeader) {
|
|
212
|
+
const header = messageOrHeader.header || messageOrHeader;
|
|
213
|
+
return header.hopCount > header.maxHops;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Validates raw bytes can be parsed as a valid message.
|
|
218
|
+
* Does not fully parse, just validates structure.
|
|
219
|
+
*
|
|
220
|
+
* @param {Uint8Array} data - Raw message bytes
|
|
221
|
+
* @returns {{ valid: boolean, errors: string[] }} Validation result
|
|
222
|
+
*/
|
|
223
|
+
function validateRawMessage(data) {
|
|
224
|
+
const errors = [];
|
|
225
|
+
|
|
226
|
+
if (!(data instanceof Uint8Array)) {
|
|
227
|
+
return { valid: false, errors: ['Data must be a Uint8Array'] };
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (data.length < HEADER_SIZE) {
|
|
231
|
+
return { valid: false, errors: [`Data too short: ${data.length} < ${HEADER_SIZE}`] };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Check version
|
|
235
|
+
if (data[0] !== PROTOCOL_VERSION) {
|
|
236
|
+
errors.push(`Unsupported version: ${data[0]}`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Check message type
|
|
240
|
+
if (!VALID_MESSAGE_TYPES.has(data[1])) {
|
|
241
|
+
errors.push(`Invalid message type: 0x${data[1].toString(16)}`);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Validate checksum
|
|
245
|
+
const checksumResult = validateChecksum(data.slice(0, HEADER_SIZE));
|
|
246
|
+
if (!checksumResult.valid) {
|
|
247
|
+
errors.push(
|
|
248
|
+
`Checksum mismatch: expected 0x${checksumResult.expected.toString(16)}, ` +
|
|
249
|
+
`got 0x${checksumResult.actual.toString(16)}`
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Check payload length
|
|
254
|
+
const view = new DataView(data.buffer, data.byteOffset, HEADER_SIZE);
|
|
255
|
+
const payloadLength = view.getUint16(40, false);
|
|
256
|
+
const expectedTotal = HEADER_SIZE + payloadLength;
|
|
257
|
+
|
|
258
|
+
if (data.length < expectedTotal) {
|
|
259
|
+
errors.push(`Incomplete message: expected ${expectedTotal} bytes, got ${data.length}`);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
valid: errors.length === 0,
|
|
264
|
+
errors
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
module.exports = {
|
|
269
|
+
validateMessage,
|
|
270
|
+
validateHeader,
|
|
271
|
+
validateChecksum,
|
|
272
|
+
validateRawMessage,
|
|
273
|
+
isValidMessageType,
|
|
274
|
+
isExpired,
|
|
275
|
+
hasExceededMaxHops
|
|
276
|
+
};
|