web_lib

Common web application libraries
git clone https://radroots.dev/git/web_lib.git
Log | Files | Refs | LICENSE

commit 1a55ace5c450be5b2bc9e3b5c9fdeac84dd7d3c7
parent 2cc1c29d2c931c37cede4148cc34a1fa9d8eaf40
Author: triesap <triesap@radroots.dev>
Date:   Fri, 26 Dec 2025 19:50:15 +0000

ble: add meshnet packet helpers

- Export meshnet module from public index
- Define meshnet profile and packet type unions
- Add runtime validators for parsed packet payloads
- Provide packet encode/decode helpers for BleMessage text

Diffstat:
Mapps-lib/src/lib/components/view-stack.svelte | 37+++++++++++++++++--------------------
Mble/src/index.ts | 1+
Able/src/meshnet.ts | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 125 insertions(+), 20 deletions(-)

diff --git a/apps-lib/src/lib/components/view-stack.svelte b/apps-lib/src/lib/components/view-stack.svelte @@ -54,6 +54,21 @@ } = $props(); const view_context = writable<ViewContext<string>>({ + active_view: "", + mode: "stack", + fade: true, + transition_ms: DEFAULT_TRANSITION_MS, + opacity_inactive: DEFAULT_OPACITY_INACTIVE, + scale_inactive: DEFAULT_SCALE_INACTIVE, + offset_x: DEFAULT_OFFSET_X, + offset_y: DEFAULT_OFFSET_Y, + blur_inactive_px: DEFAULT_BLUR_INACTIVE_PX, + pointer_events_inactive: DEFAULT_POINTER_EVENTS_INACTIVE, + z_index_active: DEFAULT_Z_INDEX_ACTIVE, + z_index_inactive: DEFAULT_Z_INDEX_INACTIVE, + }); + + const view_context_value = $derived((): ViewContext<string> => ({ active_view: basis.active_view, mode: basis.mode ?? "stack", fade: basis.fade ?? true, @@ -67,30 +82,12 @@ basis.pointer_events_inactive ?? DEFAULT_POINTER_EVENTS_INACTIVE, z_index_active: basis.z_index_active ?? DEFAULT_Z_INDEX_ACTIVE, z_index_inactive: basis.z_index_inactive ?? DEFAULT_Z_INDEX_INACTIVE, - }); + })); set_context(VIEW_CONTEXT_KEY, view_context); $effect(() => { - view_context.set({ - active_view: basis.active_view, - mode: basis.mode ?? "stack", - fade: basis.fade ?? true, - transition_ms: basis.transition_ms ?? DEFAULT_TRANSITION_MS, - opacity_inactive: - basis.opacity_inactive ?? DEFAULT_OPACITY_INACTIVE, - scale_inactive: basis.scale_inactive ?? DEFAULT_SCALE_INACTIVE, - offset_x: basis.offset_x ?? DEFAULT_OFFSET_X, - offset_y: basis.offset_y ?? DEFAULT_OFFSET_Y, - blur_inactive_px: - basis.blur_inactive_px ?? DEFAULT_BLUR_INACTIVE_PX, - pointer_events_inactive: - basis.pointer_events_inactive ?? - DEFAULT_POINTER_EVENTS_INACTIVE, - z_index_active: basis.z_index_active ?? DEFAULT_Z_INDEX_ACTIVE, - z_index_inactive: - basis.z_index_inactive ?? DEFAULT_Z_INDEX_INACTIVE, - }); + view_context.set(view_context_value); }); const mode = $derived(basis.mode ?? "stack"); diff --git a/ble/src/index.ts b/ble/src/index.ts @@ -1,4 +1,5 @@ export * from "./error.js"; +export * from "./meshnet.js"; export * from "./messages.js"; export * from "./types.js"; export * from "./web.js"; diff --git a/ble/src/meshnet.ts b/ble/src/meshnet.ts @@ -0,0 +1,107 @@ +import { ble_message_text } from "./messages.js"; +import type { BleMessage } from "./types.js"; + +export type BleMeshnetProfile = { + display_name: string; + device_label: string; +}; + +export type BleMeshnetPacketProfile = { + type: "meshnet.profile.v1"; + profile: BleMeshnetProfile; + timestamp_ms: number; +}; + +export type BleMeshnetPacketChat = { + type: "meshnet.chat.v1"; + message_id: string; + profile: BleMeshnetProfile; + text: string; + timestamp_ms: number; +}; + +export type BleMeshnetPacket = BleMeshnetPacketProfile | BleMeshnetPacketChat; + +const is_record = (value: unknown): value is Record<string, unknown> => + typeof value === "object" && value !== null && !Array.isArray(value); + +const is_string = (value: unknown): value is string => typeof value === "string"; + +const is_number = (value: unknown): value is number => + typeof value === "number" && Number.isFinite(value); + +const is_profile = (value: unknown): value is BleMeshnetProfile => { + if (!is_record(value)) return false; + return is_string(value.display_name) && is_string(value.device_label); +}; + +const is_packet_profile = (value: unknown): value is BleMeshnetPacketProfile => { + if (!is_record(value)) return false; + if (value.type !== "meshnet.profile.v1") return false; + if (!is_profile(value.profile)) return false; + if (!is_number(value.timestamp_ms)) return false; + return true; +}; + +const is_packet_chat = (value: unknown): value is BleMeshnetPacketChat => { + if (!is_record(value)) return false; + if (value.type !== "meshnet.chat.v1") return false; + if (!is_string(value.message_id)) return false; + if (!is_profile(value.profile)) return false; + if (!is_string(value.text)) return false; + if (!is_number(value.timestamp_ms)) return false; + return true; +}; + +export const ble_meshnet_packet_profile = ( + profile: BleMeshnetProfile, + timestamp_ms: number, +): BleMeshnetPacketProfile => { + return { + type: "meshnet.profile.v1", + profile, + timestamp_ms, + }; +}; + +export const ble_meshnet_packet_chat = ( + profile: BleMeshnetProfile, + text: string, + message_id: string, + timestamp_ms: number, +): BleMeshnetPacketChat => { + return { + type: "meshnet.chat.v1", + message_id, + profile, + text, + timestamp_ms, + }; +}; + +export const ble_meshnet_message_from_packet = ( + packet: BleMeshnetPacket, +): string => { + return JSON.stringify(packet); +}; + +export const ble_meshnet_packet_from_text = ( + text: string, +): BleMeshnetPacket | undefined => { + let parsed: unknown; + try { + parsed = JSON.parse(text); + } catch { + return; + } + if (is_packet_profile(parsed)) return parsed; + if (is_packet_chat(parsed)) return parsed; +}; + +export const ble_meshnet_packet_from_message = ( + message: BleMessage, +): BleMeshnetPacket | undefined => { + const text = ble_message_text(message.bytes); + if (!text) return; + return ble_meshnet_packet_from_text(text); +};