meshnet.ts (3014B)
1 import { ble_message_text } from "./messages.js"; 2 import type { BleMessage } from "./types.js"; 3 4 export type BleMeshnetProfile = { 5 display_name: string; 6 device_label: string; 7 }; 8 9 export type BleMeshnetPacketProfile = { 10 type: "meshnet.profile.v1"; 11 profile: BleMeshnetProfile; 12 timestamp_ms: number; 13 }; 14 15 export type BleMeshnetPacketChat = { 16 type: "meshnet.chat.v1"; 17 message_id: string; 18 profile: BleMeshnetProfile; 19 text: string; 20 timestamp_ms: number; 21 }; 22 23 export type BleMeshnetPacket = BleMeshnetPacketProfile | BleMeshnetPacketChat; 24 25 const is_record = (value: unknown): value is Record<string, unknown> => 26 typeof value === "object" && value !== null && !Array.isArray(value); 27 28 const is_string = (value: unknown): value is string => typeof value === "string"; 29 30 const is_number = (value: unknown): value is number => 31 typeof value === "number" && Number.isFinite(value); 32 33 const is_profile = (value: unknown): value is BleMeshnetProfile => { 34 if (!is_record(value)) return false; 35 return is_string(value.display_name) && is_string(value.device_label); 36 }; 37 38 const is_packet_profile = (value: unknown): value is BleMeshnetPacketProfile => { 39 if (!is_record(value)) return false; 40 if (value.type !== "meshnet.profile.v1") return false; 41 if (!is_profile(value.profile)) return false; 42 if (!is_number(value.timestamp_ms)) return false; 43 return true; 44 }; 45 46 const is_packet_chat = (value: unknown): value is BleMeshnetPacketChat => { 47 if (!is_record(value)) return false; 48 if (value.type !== "meshnet.chat.v1") return false; 49 if (!is_string(value.message_id)) return false; 50 if (!is_profile(value.profile)) return false; 51 if (!is_string(value.text)) return false; 52 if (!is_number(value.timestamp_ms)) return false; 53 return true; 54 }; 55 56 export const ble_meshnet_packet_profile = ( 57 profile: BleMeshnetProfile, 58 timestamp_ms: number, 59 ): BleMeshnetPacketProfile => { 60 return { 61 type: "meshnet.profile.v1", 62 profile, 63 timestamp_ms, 64 }; 65 }; 66 67 export const ble_meshnet_packet_chat = ( 68 profile: BleMeshnetProfile, 69 text: string, 70 message_id: string, 71 timestamp_ms: number, 72 ): BleMeshnetPacketChat => { 73 return { 74 type: "meshnet.chat.v1", 75 message_id, 76 profile, 77 text, 78 timestamp_ms, 79 }; 80 }; 81 82 export const ble_meshnet_message_from_packet = ( 83 packet: BleMeshnetPacket, 84 ): string => { 85 return JSON.stringify(packet); 86 }; 87 88 export const ble_meshnet_packet_from_text = ( 89 text: string, 90 ): BleMeshnetPacket | undefined => { 91 let parsed: unknown; 92 try { 93 parsed = JSON.parse(text); 94 } catch { 95 return; 96 } 97 if (is_packet_profile(parsed)) return parsed; 98 if (is_packet_chat(parsed)) return parsed; 99 }; 100 101 export const ble_meshnet_packet_from_message = ( 102 message: BleMessage, 103 ): BleMeshnetPacket | undefined => { 104 const text = ble_message_text(message.bytes); 105 if (!text) return; 106 return ble_meshnet_packet_from_text(text); 107 };