@soapbox.pub/nostr-lora 0.1.0 → 0.1.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.
|
@@ -2,7 +2,8 @@ var D = Object.defineProperty;
|
|
|
2
2
|
var U = (o, s, t) => s in o ? D(o, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : o[s] = t;
|
|
3
3
|
var a = (o, s, t) => U(o, typeof s != "symbol" ? s + "" : s, t);
|
|
4
4
|
import { verifyEvent as K } from "nostr-tools";
|
|
5
|
-
import
|
|
5
|
+
import P from "@liamcottle/meshcore.js/src/constants.js";
|
|
6
|
+
import L from "@liamcottle/meshcore.js/src/connection/web_serial_connection.js";
|
|
6
7
|
const g = {
|
|
7
8
|
DATA: 1,
|
|
8
9
|
REQUEST: 2,
|
|
@@ -506,7 +507,7 @@ class x extends A {
|
|
|
506
507
|
return e;
|
|
507
508
|
}
|
|
508
509
|
}
|
|
509
|
-
class
|
|
510
|
+
class z extends A {
|
|
510
511
|
constructor(t, e = {}) {
|
|
511
512
|
super();
|
|
512
513
|
a(this, "connection");
|
|
@@ -826,7 +827,7 @@ class j extends A {
|
|
|
826
827
|
t - n > this.dedupTimeout && this.recentEventIds.delete(e);
|
|
827
828
|
}
|
|
828
829
|
}
|
|
829
|
-
class
|
|
830
|
+
class W extends A {
|
|
830
831
|
constructor(t) {
|
|
831
832
|
super();
|
|
832
833
|
a(this, "connection", null);
|
|
@@ -948,8 +949,8 @@ export {
|
|
|
948
949
|
V as A,
|
|
949
950
|
d as D,
|
|
950
951
|
x as G,
|
|
951
|
-
|
|
952
|
+
z as L,
|
|
952
953
|
O as S,
|
|
953
|
-
|
|
954
|
+
W as a
|
|
954
955
|
};
|
|
955
956
|
//# sourceMappingURL=serial-connection.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serial-connection.js","sources":["../lib/protocol.ts","../lib/event-codec.ts","../lib/utils.ts","../lib/assembler.ts","../lib/constants.ts","../lib/send-queue.ts","../lib/gm-protocol.ts","../lib/lora-transport.ts","../lib/serial-connection.ts"],"sourcesContent":["export const PacketType = {\n DATA: 0x01,\n REQUEST: 0x02,\n GM: 0x03,\n} as const;\n\nexport type PacketTypeValue = typeof PacketType[keyof typeof PacketType];\n\nexport const Limits = {\n // MeshCore companion firmware constrains serial frames to MAX_FRAME_SIZE=172 bytes.\n // TX: cmd_frame = CMD(1) + path_len(1) + packet → packet ≤ 170\n // RX: onRawDataRecv builds push_frame = push_code(1) + snr(1) + rssi(1) +\n // reserved(1) + packet into out_frame[MAX_FRAME_SIZE+1]. The buffer-\n // overflow guard allows payload ≤ 169, BUT writeFrame() rejects any\n // frame longer than MAX_FRAME_SIZE (172). push_frame = 4 + packet,\n // so writeFrame accepts only packet ≤ 168.\n // Effective max packet size = min(170, 168) = 168.\n MAX_PACKET_SIZE: 168,\n // First chunk header: flags(1) + chunk_index(2) + event_id(32) +\n // total_chunks(2) + event_kind(2) + checksum(4) + ttl(1) = 44 bytes\n FIRST_CHUNK_HEADER: 44,\n FIRST_CHUNK_PAYLOAD: 124, // 168 - 44\n\n // Subsequent chunk header: flags(1) + event_id_prefix(14) + chunk_index(2) = 17 bytes\n SUBSEQUENT_CHUNK_HEADER: 17,\n SUBSEQUENT_CHUNK_PAYLOAD: 151, // 168 - 17\n\n EVENT_ID_PREFIX_LEN: 14, // first 14 bytes of event_id (2^112 collision resistance)\n MAX_CHUNKS: 8,\n MAX_COMPRESSED_SIZE: 1152, // 1024 content + ~128 for 64-byte sig (hex)\n} as const;\n\n// ── CRC32 ────────────────────────────────────────────────────────────────────\n\nexport class CRC32 {\n private static table: Uint32Array | null = null;\n\n private static makeTable(): void {\n if (this.table) return;\n this.table = new Uint32Array(256);\n for (let i = 0; i < 256; i++) {\n let c = i;\n for (let k = 0; k < 8; k++) {\n c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);\n }\n this.table[i] = c;\n }\n }\n\n static calculate(data: Uint8Array): number {\n this.makeTable();\n let crc = 0xFFFFFFFF;\n for (let i = 0; i < data.length; i++) {\n crc = (crc >>> 8) ^ this.table![(crc ^ data[i]!) & 0xFF]!;\n }\n return (crc ^ 0xFFFFFFFF) >>> 0;\n }\n}\n\n// ── Compression ───────────────────────────────────────────────────────────────\n\nexport class Compression {\n static async compress(data: Uint8Array): Promise<Uint8Array> {\n const stream = new Blob([data as Uint8Array<ArrayBuffer>]).stream();\n const compressedStream = stream.pipeThrough(\n new CompressionStream('deflate-raw')\n );\n const blob = await new Response(compressedStream).blob();\n return new Uint8Array(await blob.arrayBuffer());\n }\n\n static async decompress(data: Uint8Array): Promise<Uint8Array> {\n const stream = new Blob([data as Uint8Array<ArrayBuffer>]).stream();\n const decompressedStream = stream.pipeThrough(\n new DecompressionStream('deflate-raw')\n );\n const blob = await new Response(decompressedStream).blob();\n return new Uint8Array(await blob.arrayBuffer());\n }\n}\n\n// ── DataPacket ────────────────────────────────────────────────────────────────\n\nexport interface FirstChunk {\n type: 'first';\n eventId: Uint8Array;\n eventKind: number;\n totalChunks: number;\n checksum: number;\n ttl: number;\n payload: Uint8Array;\n isLast: boolean;\n}\n\nexport interface SubsequentChunk {\n type: 'subsequent';\n eventIdPrefix: Uint8Array;\n chunkIndex: number;\n payload: Uint8Array;\n isLast: boolean;\n}\n\nexport type DecodedDataChunk = FirstChunk | SubsequentChunk;\n\nexport class DataPacket {\n static createFromCompressed(\n eventId: Uint8Array,\n eventKind: number,\n compressed: Uint8Array,\n ttl: number,\n ): Uint8Array[] {\n const checksum = CRC32.calculate(compressed);\n\n let remaining = compressed.length - Limits.FIRST_CHUNK_PAYLOAD;\n let numChunks = 1;\n while (remaining > 0) {\n numChunks++;\n remaining -= Limits.SUBSEQUENT_CHUNK_PAYLOAD;\n }\n\n if (numChunks > Limits.MAX_CHUNKS) {\n throw new Error(`Event requires ${numChunks} chunks, max is ${Limits.MAX_CHUNKS}`);\n }\n\n const packets: Uint8Array[] = [];\n let offset = 0;\n\n for (let i = 0; i < numChunks; i++) {\n const isLast = i === numChunks - 1;\n const maxPayload = i === 0 ? Limits.FIRST_CHUNK_PAYLOAD : Limits.SUBSEQUENT_CHUNK_PAYLOAD;\n const payloadSize = Math.min(maxPayload, compressed.length - offset);\n const payload = compressed.slice(offset, offset + payloadSize);\n\n if (i === 0) {\n packets.push(this.encodeFirst(\n eventId, eventKind, numChunks, checksum, ttl, payload, isLast\n ));\n } else {\n packets.push(this.encodeSubsequent(\n eventId.slice(0, Limits.EVENT_ID_PREFIX_LEN),\n i,\n payload,\n isLast,\n ));\n }\n\n offset += payloadSize;\n }\n\n return packets;\n }\n\n static encodeFirst(\n eventId: Uint8Array,\n eventKind: number,\n totalChunks: number,\n checksum: number,\n ttl: number,\n payload: Uint8Array,\n isLast: boolean,\n ): Uint8Array {\n const packet = new Uint8Array(44 + payload.length);\n let o = 0;\n\n packet[o++] = (PacketType.DATA << 4) | (isLast ? 0x01 : 0x00);\n\n packet[o++] = 0x00;\n packet[o++] = 0x00;\n\n packet.set(eventId.slice(0, 32), o); o += 32;\n\n packet[o++] = (totalChunks >> 8) & 0xFF;\n packet[o++] = totalChunks & 0xFF;\n\n packet[o++] = (eventKind >> 8) & 0xFF;\n packet[o++] = eventKind & 0xFF;\n\n packet[o++] = (checksum >> 24) & 0xFF;\n packet[o++] = (checksum >> 16) & 0xFF;\n packet[o++] = (checksum >> 8) & 0xFF;\n packet[o++] = checksum & 0xFF;\n\n packet[o++] = ttl;\n\n packet.set(payload, o);\n\n return packet;\n }\n\n static encodeSubsequent(\n eventIdPrefix: Uint8Array,\n chunkIndex: number,\n payload: Uint8Array,\n isLast: boolean,\n ): Uint8Array {\n const packet = new Uint8Array(17 + payload.length);\n let o = 0;\n\n packet[o++] = (PacketType.DATA << 4) | (isLast ? 0x01 : 0x00);\n\n packet.set(eventIdPrefix.slice(0, 14), o); o += 14;\n\n packet[o++] = (chunkIndex >> 8) & 0xFF;\n packet[o++] = chunkIndex & 0xFF;\n\n packet.set(payload, o);\n\n return packet;\n }\n\n /**\n * Decode DATA packet.\n * Distinguishes first vs subsequent by peeking at bytes 1-2:\n * - First chunk: bytes 1-2 are chunk_index == 0x0000, event_id at offset 3\n * - Subsequent chunk: bytes 1-2 are part of event_id_prefix, chunk_index at offset 15\n *\n * For subsequent chunks, the first 2 bytes of event_id_prefix are statistically\n * never 0x0000 for real event IDs, so 0x0000 reliably identifies first chunks.\n */\n static decode(packet: Uint8Array): DecodedDataChunk {\n const flags = packet[0]!;\n const isLast = (flags & 0x01) !== 0;\n\n const chunkIndex = (packet[1]! << 8) | packet[2]!;\n\n if (chunkIndex === 0) {\n const eventId = packet.slice(3, 35);\n const totalChunks = (packet[35]! << 8) | packet[36]!;\n const eventKind = (packet[37]! << 8) | packet[38]!;\n const checksum = ((packet[39]! << 24) | (packet[40]! << 16) |\n (packet[41]! << 8) | packet[42]!) >>> 0;\n const ttl = packet[43]!;\n const payload = packet.slice(44);\n\n return { type: 'first', eventId, eventKind, totalChunks, checksum, ttl, payload, isLast };\n } else {\n const eventIdPrefix = packet.slice(1, 15);\n const realChunkIndex = (packet[15]! << 8) | packet[16]!;\n const payload = packet.slice(17);\n\n return { type: 'subsequent', eventIdPrefix, chunkIndex: realChunkIndex, payload, isLast };\n }\n }\n}\n\n// ── RequestPacket ─────────────────────────────────────────────────────────────\n\nexport interface DecodedRequestPacket {\n eventIdPrefix: Uint8Array;\n missingChunks: number[];\n}\n\nexport class RequestPacket {\n static create(eventIdPrefix: Uint8Array, missingChunks: number[]): Uint8Array {\n const packet = new Uint8Array(16);\n let o = 0;\n\n packet[o++] = (PacketType.REQUEST << 4);\n\n packet.set(eventIdPrefix.slice(0, Limits.EVENT_ID_PREFIX_LEN), o);\n o += Limits.EVENT_ID_PREFIX_LEN;\n\n let bitmap = 0;\n for (const idx of missingChunks) {\n if (idx < 8) bitmap |= (1 << idx);\n }\n packet[o++] = bitmap & 0xFF;\n\n return packet;\n }\n\n static decode(packet: Uint8Array): DecodedRequestPacket {\n const eventIdPrefix = packet.slice(1, 1 + Limits.EVENT_ID_PREFIX_LEN);\n const bitmap = packet[1 + Limits.EVENT_ID_PREFIX_LEN]!;\n\n const missingChunks: number[] = [];\n for (let i = 0; i < 8; i++) {\n if (bitmap & (1 << i)) missingChunks.push(i);\n }\n\n return { eventIdPrefix, missingChunks };\n }\n}\n\n// ── GmPacket ──────────────────────────────────────────────────────────────────\n\nexport interface DecodedGmPacket {\n nodeId: Uint8Array;\n timestamp: number;\n eventCount: number;\n recentSince: number;\n}\n\nexport class GmPacket {\n static create(\n nodeId: Uint8Array,\n eventCount: number,\n recentSince: number,\n timestamp: number = Math.floor(Date.now() / 1000),\n ): Uint8Array {\n const packet = new Uint8Array(21);\n let o = 0;\n\n packet[o++] = (PacketType.GM << 4);\n\n packet.set(nodeId.slice(0, 8), o); o += 8;\n\n packet[o++] = (timestamp >> 24) & 0xFF;\n packet[o++] = (timestamp >> 16) & 0xFF;\n packet[o++] = (timestamp >> 8) & 0xFF;\n packet[o++] = timestamp & 0xFF;\n\n packet[o++] = (eventCount >> 24) & 0xFF;\n packet[o++] = (eventCount >> 16) & 0xFF;\n packet[o++] = (eventCount >> 8) & 0xFF;\n packet[o++] = eventCount & 0xFF;\n\n packet[o++] = (recentSince >> 24) & 0xFF;\n packet[o++] = (recentSince >> 16) & 0xFF;\n packet[o++] = (recentSince >> 8) & 0xFF;\n packet[o++] = recentSince & 0xFF;\n\n return packet;\n }\n\n static decode(packet: Uint8Array): DecodedGmPacket {\n const nodeId = packet.slice(1, 9);\n const timestamp = ((packet[9]! << 24) | (packet[10]! << 16) | (packet[11]! << 8) | packet[12]!) >>> 0;\n const eventCount = ((packet[13]! << 24) | (packet[14]! << 16) | (packet[15]! << 8) | packet[16]!) >>> 0;\n const recentSince = ((packet[17]! << 24) | (packet[18]! << 16) | (packet[19]! << 8) | packet[20]!) >>> 0;\n return { nodeId, timestamp, eventCount, recentSince };\n }\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\n/** Read the packet type from the flags byte without fully decoding the packet. */\nexport function parsePacketType(packet: Uint8Array): number {\n if (!packet || packet.length === 0) return -1;\n return (packet[0]! >> 4) & 0x0F;\n}\n","import { verifyEvent } from 'nostr-tools';\nimport { Compression, CRC32, Limits } from './protocol.js';\n\nconst _encoder = new TextEncoder();\nconst _decoder = new TextDecoder();\n\nexport interface NostrEvent {\n id: string;\n pubkey: string;\n created_at: number;\n kind: number;\n tags: string[][];\n content: string;\n sig: string;\n}\n\nexport interface SerializedEvent {\n compressed: Uint8Array;\n checksum: number;\n}\n\nexport async function serializeEvent(event: NostrEvent): Promise<SerializedEvent> {\n if (!event.id || typeof event.id !== 'string')\n throw new Error('Event must have string id');\n if (!event.pubkey || typeof event.pubkey !== 'string')\n throw new Error('Event must have string pubkey');\n if (!event.created_at || typeof event.created_at !== 'number')\n throw new Error('Event must have numeric created_at');\n if (event.kind === undefined || typeof event.kind !== 'number')\n throw new Error('Event must have numeric kind');\n if (!Array.isArray(event.tags))\n throw new Error('Event tags must be array');\n if (typeof event.content !== 'string')\n throw new Error('Event content must be string');\n if (!event.sig || typeof event.sig !== 'string')\n throw new Error('Event must have string sig');\n\n // NIP-01 canonical wire array (index 6 carries the signature)\n const canonical = JSON.stringify([\n 0,\n event.pubkey,\n event.created_at,\n event.kind,\n event.tags,\n event.content,\n event.sig,\n ]);\n\n const compressed = await Compression.compress(_encoder.encode(canonical));\n\n if (compressed.length > Limits.MAX_COMPRESSED_SIZE) {\n throw new Error(\n `Compressed event too large: ${compressed.length} bytes (max ${Limits.MAX_COMPRESSED_SIZE})`\n );\n }\n\n const checksum = CRC32.calculate(compressed);\n return { compressed, checksum };\n}\n\nexport async function deserializeEvent(\n chunkPayloads: Uint8Array[],\n expectedChecksum: number,\n eventIdHex: string,\n): Promise<NostrEvent> {\n const totalLength = chunkPayloads.reduce((sum, c) => sum + c.length, 0);\n const compressed = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of chunkPayloads) {\n compressed.set(chunk, offset);\n offset += chunk.length;\n }\n\n const computedChecksum = CRC32.calculate(compressed);\n if (computedChecksum !== expectedChecksum) {\n throw new Error(\n `Checksum mismatch: expected 0x${expectedChecksum.toString(16)}, ` +\n `got 0x${computedChecksum.toString(16)}`\n );\n }\n\n const decompressed = await Compression.decompress(compressed);\n const arr = JSON.parse(_decoder.decode(decompressed)) as unknown[];\n\n const event: NostrEvent = {\n id: eventIdHex,\n pubkey: arr[1] as string,\n created_at: arr[2] as number,\n kind: arr[3] as number,\n tags: arr[4] as string[][],\n content: arr[5] as string,\n sig: arr[6] as string,\n };\n\n // Mandatory cryptographic signature verification — drop forged events\n if (!verifyEvent(event)) {\n throw new Error(`Invalid signature for event ${eventIdHex.substring(0, 16)}…`);\n }\n\n return event;\n}\n","export function hexToBytes(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(hex.substr(i * 2, 2), 16);\n }\n return bytes;\n}\n\nexport function bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');\n}\n\n/** True if the first `prefix.length` bytes of `id` match `prefix`. */\nexport function matchesPrefix(id: Uint8Array, prefix: Uint8Array): boolean {\n for (let i = 0; i < prefix.length; i++) {\n if (id[i] !== prefix[i]) return false;\n }\n return true;\n}\n\nexport function sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * A subset of the `console` API. Pass any object with `log`, `warn`,\n * `error`, and `debug` methods — for example `console` itself — to enable\n * internal diagnostic logging. Omit (or pass `null`) to silence all output.\n */\nexport interface Logger {\n log(message: string, ...args: unknown[]): void;\n warn(message: string, ...args: unknown[]): void;\n error(message: string, ...args: unknown[]): void;\n debug(message: string, ...args: unknown[]): void;\n}\ntype Listener<T extends unknown[]> = (...args: T) => void;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type EventMap = Record<string, any[]>;\n\nexport class EventEmitter<Events extends EventMap = EventMap> {\n private listeners: Map<string, Set<Listener<unknown[]>>> = new Map();\n\n on<K extends keyof Events & string>(event: K, fn: Listener<Events[K]>): () => void {\n if (!this.listeners.has(event)) this.listeners.set(event, new Set());\n this.listeners.get(event)!.add(fn as Listener<unknown[]>);\n return () => this.listeners.get(event)?.delete(fn as Listener<unknown[]>);\n }\n\n emit<K extends keyof Events & string>(event: K, ...args: Events[K]): void {\n for (const fn of (this.listeners.get(event) ?? [])) {\n try { fn(...args); } catch (err) { console.error(`[EventEmitter] '${event}' listener threw:`, err); }\n }\n }\n}\n","import { Limits } from './protocol.js';\nimport { hexToBytes, bytesToHex, matchesPrefix } from './utils.js';\nimport type { FirstChunk, SubsequentChunk } from './protocol.js';\n\n// ─── AssemblySession ─────────────────────────────────────────────────────────\n\n/** Holds the per-event state while chunks are being collected. Not exported. */\nclass AssemblySession {\n readonly eventId: Uint8Array;\n readonly eventKind: number;\n readonly totalChunks: number;\n readonly checksum: number;\n readonly chunks: Array<Uint8Array | undefined>;\n receivedChunks: number;\n\n constructor(first: FirstChunk) {\n this.eventId = first.eventId;\n this.eventKind = first.eventKind;\n this.totalChunks = first.totalChunks;\n this.checksum = first.checksum;\n\n this.chunks = new Array<Uint8Array | undefined>(this.totalChunks);\n this.chunks[0] = first.payload;\n this.receivedChunks = 1;\n }\n\n get isComplete(): boolean {\n return this.receivedChunks === this.totalChunks;\n }\n\n /** Returns true if this was a new chunk, false if duplicate. */\n addChunk(index: number, payload: Uint8Array): boolean {\n if (this.chunks[index] !== undefined) return false;\n this.chunks[index] = payload;\n this.receivedChunks++;\n return true;\n }\n\n missingIndices(): number[] {\n const missing: number[] = [];\n for (let i = 0; i < this.totalChunks; i++) {\n if (this.chunks[i] === undefined) missing.push(i);\n }\n return missing;\n }\n}\n\n// ─── Action types ─────────────────────────────────────────────────────────────\n\nexport interface CompleteAction { action: 'complete'; session: AssemblySession }\nexport interface StartedAction { action: 'started'; session: AssemblySession }\nexport interface ProgressAction { action: 'progress'; session: AssemblySession; eventIdHex: string }\nexport interface PendingAction { action: 'pending'; prefix: Uint8Array }\nexport interface NoneAction { action: 'none' }\n\nexport type FirstChunkResult = CompleteAction | StartedAction | NoneAction;\nexport type SubsequentChunkResult = CompleteAction | ProgressAction | PendingAction | NoneAction;\n\n// ─── Pending buffer entry ─────────────────────────────────────────────────────\n\ninterface PendingEntry {\n eventIdPrefix: Uint8Array;\n chunkIndex: number;\n payload: Uint8Array;\n isLast: boolean;\n arrivedAt: number;\n}\n\n// ─── Assembler ───────────────────────────────────────────────────────────────\n\nexport class Assembler {\n /** In-progress sessions keyed by the full event ID as a hex string. */\n sessions: Map<string, AssemblySession> = new Map();\n\n /** Subsequent chunks that arrived before their first chunk. */\n pendingSubsequent: PendingEntry[] = [];\n\n // ─── First-chunk path ───────────────────────────────────────────────────\n\n handleFirst(decoded: FirstChunk, seenIds: Map<string, number>): FirstChunkResult {\n const eventIdHex = bytesToHex(decoded.eventId);\n\n if (decoded.ttl === 0) return { action: 'none' };\n if (decoded.totalChunks > Limits.MAX_CHUNKS) return { action: 'none' };\n if (seenIds.has(eventIdHex)) return { action: 'none' };\n if (this.sessions.has(eventIdHex)) return { action: 'none' };\n\n const session = new AssemblySession(decoded);\n this.sessions.set(eventIdHex, session);\n\n this._replayPending(eventIdHex, session);\n\n if (session.isComplete) {\n this.sessions.delete(eventIdHex);\n return { action: 'complete', session };\n }\n\n return { action: 'started', session };\n }\n\n // ─── Subsequent-chunk path ──────────────────────────────────────────────\n\n handleSubsequent(decoded: SubsequentChunk, eventTimeout: number): SubsequentChunkResult {\n const eventIdHex = this._findSessionForPrefix(decoded.eventIdPrefix);\n\n if (!eventIdHex) {\n this.pendingSubsequent.push({\n eventIdPrefix: decoded.eventIdPrefix,\n chunkIndex: decoded.chunkIndex,\n payload: decoded.payload,\n isLast: decoded.isLast,\n arrivedAt: Date.now(),\n });\n this._prunePending(eventTimeout);\n return { action: 'pending', prefix: decoded.eventIdPrefix };\n }\n\n const session = this.sessions.get(eventIdHex)!;\n const added = session.addChunk(decoded.chunkIndex, decoded.payload);\n if (!added) return { action: 'none' };\n\n if (session.isComplete) {\n this.sessions.delete(eventIdHex);\n return { action: 'complete', session };\n }\n\n return { action: 'progress', session, eventIdHex };\n }\n\n // ─── Timer-driven queries ────────────────────────────────────────────────\n\n /** Returns missing chunk indices for a session, or null if it no longer exists. */\n missingChunks(eventIdHex: string): number[] | null {\n const session = this.sessions.get(eventIdHex);\n return session ? session.missingIndices() : null;\n }\n\n /** Abandon an in-progress session after max retries exceeded. */\n abandonSession(eventIdHex: string): void {\n this.sessions.delete(eventIdHex);\n }\n\n /**\n * Returns missing chunk indices for a pending-subsequent group\n * (chunk 0 still missing), or null if no buffered chunks exist for this prefix.\n */\n missingChunksForPending(prefixHex: string): number[] | null {\n const buffered = this.pendingSubsequent.filter(\n p => bytesToHex(p.eventIdPrefix) === prefixHex\n );\n if (buffered.length === 0) return null;\n\n const haveIndices = new Set(buffered.map(p => p.chunkIndex));\n\n const lastChunk = buffered.find(p => p.isLast);\n const maxIndex = Math.max(...haveIndices);\n const totalChunks = lastChunk ? lastChunk.chunkIndex + 1 : maxIndex + 1;\n\n const missing: number[] = [];\n for (let i = 0; i < totalChunks; i++) {\n if (!haveIndices.has(i)) missing.push(i);\n }\n return missing.length > 0 ? missing : null;\n }\n\n /** Abandon all pending entries for a given prefix after max retries exceeded. */\n abandonPending(prefixHex: string): void {\n this.pendingSubsequent = this.pendingSubsequent.filter(\n p => bytesToHex(p.eventIdPrefix) !== prefixHex\n );\n }\n\n /** True if a session is currently in progress for this event ID. */\n hasSession(eventIdHex: string): boolean {\n return this.sessions.has(eventIdHex);\n }\n\n /**\n * Returns the raw eventId bytes for an in-progress session, or null.\n * Used by the transport to build REQUEST prefix bytes.\n */\n getSessionEventId(eventIdHex: string): Uint8Array | null {\n return this.sessions.get(eventIdHex)?.eventId ?? null;\n }\n\n // ─── Internals ───────────────────────────────────────────────────────────\n\n private _findSessionForPrefix(prefix: Uint8Array): string | null {\n for (const [id] of this.sessions.entries()) {\n if (matchesPrefix(hexToBytes(id), prefix)) return id;\n }\n return null;\n }\n\n private _replayPending(eventIdHex: string, session: AssemblySession): void {\n const stillPending: PendingEntry[] = [];\n for (const pending of this.pendingSubsequent) {\n if (matchesPrefix(hexToBytes(eventIdHex), pending.eventIdPrefix)) {\n session.addChunk(pending.chunkIndex, pending.payload);\n } else {\n stillPending.push(pending);\n }\n }\n this.pendingSubsequent = stillPending;\n }\n\n private _prunePending(eventTimeout: number): void {\n const cutoff = Date.now() - eventTimeout;\n this.pendingSubsequent = this.pendingSubsequent.filter(\n p => p.arrivedAt > cutoff\n );\n }\n}\n","const Defaults = {\n // ── SendQueue ─────────────────────────────────────────────────────────────\n\n /** Base delay between packets within a single queue item (ms). */\n INTER_PACKET_DELAY: 2000,\n\n /** Maximum additional random jitter added on top of INTER_PACKET_DELAY (ms). */\n INTER_PACKET_JITTER: 500,\n\n // ── GmProtocol ────────────────────────────────────────────────────────────\n\n /** Per-peer minimum response interval before the first backoff multiplier kicks in (ms). */\n GM_BACKOFF_BASE: 5 * 60 * 1000,\n\n /** Maximum number of our own events to re-share in response to one GM. */\n GM_MAX_SHARE_EVENTS: 3,\n\n /** Lower bound of the random pre-share / pre-reply delay (ms). */\n GM_JITTER_MIN: 500,\n\n /** Upper bound of the random pre-share / pre-reply delay (ms). */\n GM_JITTER_MAX: 1500,\n\n // ── LoRaTransport ─────────────────────────────────────────────────────────\n\n /** How long to wait for the next chunk before declaring a gap and sending REQUEST (ms). */\n REQUEST_INACTIVITY: 30_000,\n\n /** Number of REQUEST retries before abandoning a partial event. */\n REQUEST_MAX_RETRIES: 3,\n\n /** How long to keep sent chunk packets around for potential retransmission (ms). */\n SENT_CHUNKS_TTL: 5 * 60 * 1000,\n\n /** How long before we stop deduplicating a previously-seen event ID (ms). */\n DEDUP_TIMEOUT: 15 * 60 * 1000,\n\n /** How long to wait for subsequent chunks of a partial event (ms). */\n EVENT_TIMEOUT: 30_000,\n\n /** TTL assigned to locally-created events (decremented on each re-broadcast hop). */\n INITIAL_TTL: 6,\n} as const;\n\nexport default Defaults;\n\n/** Widened (plain `number`) version of `Defaults` — use for constructor option types. */\nexport type DefaultOptions = { [K in keyof typeof Defaults]: number };\n","import { EventEmitter, sleep } from \"./utils.js\";\nimport Defaults, { type DefaultOptions } from \"./constants.js\";\nimport type { Logger } from \"./utils.js\";\n\nexport interface QueueItemMeta {\n _onSent?: () => Promise<void>;\n}\n\nexport interface QueueItem {\n id: number;\n label: string;\n type: string;\n packets: Uint8Array[];\n meta: QueueItemMeta;\n status: \"pending\" | \"sending\";\n}\n\nexport interface QueueSnapshot {\n id: number;\n label: string;\n type: string;\n packetCount: number;\n status: \"pending\" | \"sending\";\n}\n\nexport type SendQueueEvents = {\n \"packet:send\": [packet: Uint8Array, item: QueueItem];\n \"queue:update\": [snapshot: QueueSnapshot[]];\n \"error\": [err: Error];\n};\n\nexport class SendQueue extends EventEmitter<SendQueueEvents> {\n private sendFn: (packet: Uint8Array) => Promise<void>;\n private items: QueueItem[] = [];\n private running: boolean = false;\n private seq: number = 0;\n private log: Logger | null;\n\n INTER_PACKET_DELAY: number;\n INTER_PACKET_JITTER: number;\n\n constructor(\n sendFn: (packet: Uint8Array) => Promise<void>,\n options: Partial<\n Pick<DefaultOptions, \"INTER_PACKET_DELAY\" | \"INTER_PACKET_JITTER\">\n > = {},\n logger?: Logger | null,\n ) {\n super();\n if (typeof sendFn !== \"function\") {\n throw new Error(\"SendQueue requires a sendFn(packet) argument\");\n }\n this.sendFn = sendFn;\n this.log = logger ?? null;\n\n this.INTER_PACKET_DELAY = options.INTER_PACKET_DELAY ??\n Defaults.INTER_PACKET_DELAY;\n this.INTER_PACKET_JITTER = options.INTER_PACKET_JITTER ??\n Defaults.INTER_PACKET_JITTER;\n }\n\n // ─── Public API ──────────────────────────────────────────────────────────\n\n enqueue(\n packets: Uint8Array[],\n label: string,\n type: string,\n meta: QueueItemMeta = {},\n ): number {\n const id = ++this.seq;\n this.items.push({ id, label, type, packets, meta, status: \"pending\" });\n this.notifyUpdate();\n this.drain();\n return id;\n }\n\n get snapshot(): QueueSnapshot[] {\n return this.items.map((item) => ({\n id: item.id,\n label: item.label,\n type: item.type,\n packetCount: item.packets.length,\n status: item.status,\n }));\n }\n\n // ─── Internal ────────────────────────────────────────────────────────────\n\n private notifyUpdate(): void {\n this.emit(\"queue:update\", this.snapshot);\n }\n\n private drain(): void {\n if (this.running) return;\n this.running = true;\n this.run().catch((err) => {\n this.log?.error(\"[nostr-lora] send queue drain error:\", err);\n this.running = false;\n });\n }\n\n private async run(): Promise<void> {\n while (this.items.length > 0) {\n const item = this.items[0]!;\n item.status = \"sending\";\n this.notifyUpdate();\n\n try {\n for (let i = 0; i < item.packets.length; i++) {\n const pkt = item.packets[i]!;\n await this.sendFn(pkt);\n this.emit(\"packet:send\", pkt, item);\n\n if (i < item.packets.length - 1) {\n await sleep(\n this.INTER_PACKET_DELAY +\n Math.random() * this.INTER_PACKET_JITTER,\n );\n }\n }\n\n if (item.meta._onSent) {\n await item.meta._onSent();\n }\n } catch (err) {\n this.log?.error(\"[nostr-lora] send queue item error:\", err);\n this.emit(\n \"error\",\n err instanceof Error ? err : new Error(String(err)),\n );\n }\n\n this.items.shift();\n this.notifyUpdate();\n\n if (this.items.length > 0) {\n await sleep(\n this.INTER_PACKET_DELAY +\n Math.random() * this.INTER_PACKET_JITTER,\n );\n }\n }\n this.running = false;\n }\n}\n","import { GmPacket } from \"./protocol.js\";\nimport type { DecodedGmPacket } from \"./protocol.js\";\nimport { bytesToHex, EventEmitter, sleep } from \"./utils.js\";\nimport Defaults, { type DefaultOptions } from \"./constants.js\";\nimport type { NostrEvent } from \"./event-codec.js\";\nimport type { Logger } from \"./utils.js\";\n\nexport interface GmProtocolDeps {\n getNodeId: () => Uint8Array | null;\n getSentEvents: () => Map<string, NostrEvent>;\n sendEvent: (event: NostrEvent) => Promise<void>;\n sendGm: (\n nodeId: Uint8Array,\n eventCount: number,\n recentSince: number,\n ) => Promise<void>;\n}\n\nexport type GmProtocolEvents = {\n \"gm:receive\": [decoded: DecodedGmPacket, byteLength: number];\n};\n\nexport class GmProtocol extends EventEmitter<GmProtocolEvents> {\n private getNodeId: () => Uint8Array | null;\n private getSentEvents: () => Map<string, NostrEvent>;\n private sendEvent: (event: NostrEvent) => Promise<void>;\n private sendGm: (\n nodeId: Uint8Array,\n eventCount: number,\n recentSince: number,\n ) => Promise<void>;\n\n private backoff: Map<string, number> = new Map();\n private lastResponse: Map<string, number> = new Map();\n private log: Logger | null;\n\n BACKOFF_BASE: number;\n MAX_SHARE_EVENTS: number;\n JITTER_MIN: number;\n JITTER_MAX: number;\n\n constructor(\n deps: GmProtocolDeps,\n options: Partial<\n Pick<\n DefaultOptions,\n | \"GM_BACKOFF_BASE\"\n | \"GM_MAX_SHARE_EVENTS\"\n | \"GM_JITTER_MIN\"\n | \"GM_JITTER_MAX\"\n >\n > = {},\n logger?: Logger | null,\n ) {\n super();\n this.getNodeId = deps.getNodeId;\n this.getSentEvents = deps.getSentEvents;\n this.sendEvent = deps.sendEvent;\n this.sendGm = deps.sendGm;\n this.log = logger ?? null;\n\n this.BACKOFF_BASE = options.GM_BACKOFF_BASE ?? Defaults.GM_BACKOFF_BASE;\n this.MAX_SHARE_EVENTS = options.GM_MAX_SHARE_EVENTS ??\n Defaults.GM_MAX_SHARE_EVENTS;\n this.JITTER_MIN = options.GM_JITTER_MIN ?? Defaults.GM_JITTER_MIN;\n this.JITTER_MAX = options.GM_JITTER_MAX ?? Defaults.GM_JITTER_MAX;\n }\n\n async handlePacket(packet: Uint8Array): Promise<void> {\n const decoded = GmPacket.decode(packet);\n this.emit(\"gm:receive\", decoded, packet.length);\n\n const nodeId = this.getNodeId();\n if (!nodeId) return;\n\n const peerIdHex = bytesToHex(decoded.nodeId);\n const myIdHex = bytesToHex(nodeId);\n\n if (peerIdHex === myIdHex) return;\n\n const backoff = this.backoff.get(peerIdHex) ?? 1;\n const backoffWindow = backoff * this.BACKOFF_BASE;\n const lastTime = this.lastResponse.get(peerIdHex) ?? 0;\n const now = Date.now();\n\n if (now - lastTime < backoffWindow) {\n this.backoff.set(peerIdHex, Math.min(backoff * 2, 16));\n this.log?.debug(\n `[nostr-lora] GM from ${\n peerIdHex.substring(0, 16)\n } backed off (${backoff}x)`,\n );\n return;\n }\n\n this.backoff.set(peerIdHex, 1);\n this.lastResponse.set(peerIdHex, now);\n\n const sentEvents = this.getSentEvents();\n const toShare: NostrEvent[] = [];\n for (const event of sentEvents.values()) {\n if (event.created_at > decoded.recentSince && event.kind === 1) {\n toShare.push(event);\n if (toShare.length >= this.MAX_SHARE_EVENTS) break;\n }\n }\n\n for (const event of toShare) {\n await sleep(this.jitter());\n try {\n await this.sendEvent(event);\n this.log?.debug(\n `[nostr-lora] GM auto-share: sent event ${\n event.id.substring(0, 16)\n }`,\n );\n } catch (err) {\n this.log?.warn(\"[nostr-lora] GM auto-share failed:\", err);\n }\n }\n\n if (Math.random() < 0.5) {\n await sleep(this.jitter());\n const eventCount = sentEvents.size;\n const oldest = this.oldestTimestamp(sentEvents);\n await this.sendGm(nodeId, eventCount, oldest);\n this.log?.debug(\n `[nostr-lora] GM auto-reply sent to ${\n peerIdHex.substring(0, 16)\n }`,\n );\n }\n }\n\n private jitter(): number {\n return this.JITTER_MIN +\n Math.random() * (this.JITTER_MAX - this.JITTER_MIN);\n }\n\n private oldestTimestamp(sentEvents: Map<string, NostrEvent>): number {\n let oldest = Math.floor(Date.now() / 1000);\n for (const event of sentEvents.values()) {\n if (event.created_at < oldest) oldest = event.created_at;\n }\n return oldest;\n }\n}\n","import {\n DataPacket,\n GmPacket,\n Limits,\n PacketType,\n parsePacketType,\n RequestPacket,\n} from \"./protocol.js\";\nimport type { DecodedDataChunk, DecodedGmPacket } from \"./protocol.js\";\nimport { deserializeEvent, serializeEvent } from \"./event-codec.js\";\nimport type { NostrEvent } from \"./event-codec.js\";\nimport { Assembler } from \"./assembler.js\";\nimport { SendQueue } from \"./send-queue.js\";\nimport type { QueueSnapshot } from \"./send-queue.js\";\nimport { GmProtocol } from \"./gm-protocol.js\";\nimport { bytesToHex, EventEmitter, hexToBytes } from \"./utils.js\";\nimport Defaults, { type DefaultOptions } from \"./constants.js\";\nimport type { ConnectionManager } from \"./connection-manager.js\";\nimport type { Logger } from \"./utils.js\";\n\nexport type { ConnectionManager };\nexport type { ConnectionManagerEvents } from \"./connection-manager.js\";\n\n// ── Interfaces ────────────────────────────────────────────────────────────────\n\nexport interface TransportOptions {\n nodeId?: Uint8Array;\n /** Diagnostic logger. Defaults to `null` (silent). */\n logger?: Logger | null;\n // Timing\n interPacketDelay?: number;\n interPacketJitter?: number;\n // GM protocol\n gmBackoffBase?: number;\n gmMaxShareEvents?: number;\n gmJitterMin?: number;\n gmJitterMax?: number;\n // Transport\n requestInactivity?: number;\n requestMaxRetries?: number;\n sentChunksTtl?: number;\n dedupTimeout?: number;\n eventTimeout?: number;\n initialTtl?: number;\n}\n\nexport interface PacketReceiveInfo {\n snr: number;\n rssi: number;\n size: number;\n}\n\nexport interface TimingOptions {\n delay?: number;\n jitter?: number;\n}\n\ninterface TimerState {\n timer: ReturnType<typeof setTimeout>;\n requestCount: number;\n}\n\ninterface SentChunkEntry {\n packets: Uint8Array[];\n sentAt: number;\n}\n\n// ── Event map ─────────────────────────────────────────────────────────────────\n\nexport type LoRaTransportEvents = {\n \"event:receive\": [event: NostrEvent];\n \"event:send\": [event: NostrEvent];\n \"chunk:receive\": [decoded: DecodedDataChunk, byteLength: number];\n \"gm:receive\": [decoded: DecodedGmPacket, byteLength: number];\n \"packet:receive\": [info: PacketReceiveInfo];\n \"packet:send\": [\n decoded: DecodedDataChunk | DecodedGmPacket,\n byteLength: number,\n ];\n \"request:send\": [\n eventIdHex: string,\n missingChunks: number[],\n attempt: number,\n ];\n \"request:receive\": [prefixHex: string, missingChunks: number[]];\n \"queue:update\": [snapshot: QueueSnapshot[]];\n \"connect\": [portLabel: string];\n \"disconnect\": [];\n \"error\": [err: Error];\n};\n\n// ── LoRaTransport ─────────────────────────────────────────────────────────────\n\nexport class LoRaTransport extends EventEmitter<LoRaTransportEvents> {\n connection: ConnectionManager;\n nodeId: Uint8Array | null;\n\n private log: Logger | null;\n\n private eventTimeout: number;\n private dedupTimeout: number;\n private initialTtl: number;\n private requestInactivity: number;\n private requestMaxRetries: number;\n private sentChunksTtl: number;\n\n private assembler: Assembler;\n private queue: SendQueue;\n private gm: GmProtocol;\n private recentEventIds: Map<string, number> = new Map();\n private inactivityTimers: Map<string, TimerState> = new Map();\n private pendingTimers: Map<string, TimerState> = new Map();\n private sentEvents: Map<string, NostrEvent> = new Map();\n private sentChunks: Map<string, SentChunkEntry> = new Map();\n\n constructor(connection: ConnectionManager, options: TransportOptions = {}) {\n super();\n this.connection = connection;\n this.nodeId = options.nodeId ?? null;\n this.log = options.logger ?? null;\n\n this.eventTimeout = options.eventTimeout ?? Defaults.EVENT_TIMEOUT;\n this.dedupTimeout = options.dedupTimeout ?? Defaults.DEDUP_TIMEOUT;\n this.initialTtl = options.initialTtl ?? Defaults.INITIAL_TTL;\n this.requestInactivity = options.requestInactivity ??\n Defaults.REQUEST_INACTIVITY;\n this.requestMaxRetries = options.requestMaxRetries ??\n Defaults.REQUEST_MAX_RETRIES;\n this.sentChunksTtl = options.sentChunksTtl ?? Defaults.SENT_CHUNKS_TTL;\n\n this.assembler = new Assembler();\n\n this.queue = new SendQueue(\n async (packet) => {\n await this.connection.sendRawData(packet);\n },\n {\n INTER_PACKET_DELAY: options.interPacketDelay ??\n Defaults.INTER_PACKET_DELAY,\n INTER_PACKET_JITTER: options.interPacketJitter ??\n Defaults.INTER_PACKET_JITTER,\n },\n this.log,\n );\n\n this.queue.on(\"packet:send\", (pkt, item) => {\n if (item.type === \"data\") {\n this.emit(\"packet:send\", DataPacket.decode(pkt), pkt.length);\n }\n });\n this.queue.on(\n \"queue:update\",\n (snapshot) => this.emit(\"queue:update\", snapshot),\n );\n this.queue.on(\"error\", (err) => this.emit(\"error\", err));\n\n this.gm = new GmProtocol(\n {\n getNodeId: () => this.nodeId,\n getSentEvents: () => this.sentEvents,\n sendEvent: (e) => this.sendEvent(e),\n sendGm: (id, count, since) => this.sendGm(id, count, since),\n },\n {\n GM_BACKOFF_BASE: options.gmBackoffBase ??\n Defaults.GM_BACKOFF_BASE,\n GM_MAX_SHARE_EVENTS: options.gmMaxShareEvents ??\n Defaults.GM_MAX_SHARE_EVENTS,\n GM_JITTER_MIN: options.gmJitterMin ?? Defaults.GM_JITTER_MIN,\n GM_JITTER_MAX: options.gmJitterMax ?? Defaults.GM_JITTER_MAX,\n },\n this.log,\n );\n this.gm.on(\n \"gm:receive\",\n (decoded, byteLength) =>\n this.emit(\"gm:receive\", decoded, byteLength),\n );\n }\n\n // ── Lifecycle ─────────────────────────────────────────────────────────────\n\n /**\n * Open the connection and wire up data/lifecycle events.\n * This triggers the browser port picker for `SerialConnectionManager`.\n */\n async begin(): Promise<void> {\n this.connection.on(\"data\", (raw) => {\n this.handleRawDataPacket(raw);\n });\n this.connection.on(\"connect\", (portLabel) => {\n this.emit(\"connect\", portLabel);\n });\n this.connection.on(\"disconnect\", () => {\n this.emit(\"disconnect\");\n });\n this.connection.on(\"error\", (err) => {\n this.emit(\"error\", err);\n });\n await this.connection.open();\n }\n\n /** Close the connection. */\n async end(): Promise<void> {\n await this.connection.close();\n }\n\n // ── Public API ────────────────────────────────────────────────────────────\n\n async sendEvent(event: NostrEvent): Promise<void> {\n try {\n const { compressed } = await serializeEvent(event);\n const eventId = hexToBytes(event.id);\n\n const packets = DataPacket.createFromCompressed(\n eventId,\n event.kind,\n compressed,\n this.initialTtl,\n );\n\n const label = `EVENT kind:${event.kind} id:${\n event.id.substring(0, 8)\n }… (${packets.length} pkt)`;\n\n this.sentChunks.set(event.id, {\n packets: packets.slice(),\n sentAt: Date.now(),\n });\n this.pruneSentChunks();\n\n this.queue.enqueue(packets, label, \"data\", {\n _onSent: async () => {\n this.sentEvents.set(event.id, event);\n this.emit(\"event:send\", event);\n },\n });\n } catch (error) {\n this.emit(\n \"error\",\n error instanceof Error ? error : new Error(String(error)),\n );\n throw error;\n }\n }\n\n async sendGm(\n nodeId: Uint8Array,\n eventCount: number = 0,\n recentSince: number = 0,\n ): Promise<void> {\n const timestamp = Math.floor(Date.now() / 1000);\n const packet = GmPacket.create(\n nodeId,\n eventCount,\n recentSince,\n timestamp,\n );\n const label = `GM node:${\n bytesToHex(nodeId).substring(0, 16)\n }… events:${eventCount}`;\n\n this.queue.enqueue([packet], label, \"gm\", {\n _onSent: async () => {\n this.emit(\n \"packet:send\",\n GmPacket.decode(packet),\n packet.length,\n );\n },\n });\n }\n\n /**\n * Feed a raw radio packet into the transport.\n * Only needed when implementing a custom `ConnectionManager` that doesn't\n * use the event interface — `begin()` wires this automatically otherwise.\n */\n async handleRawDataPacket(\n data: { data: ArrayBuffer | Uint8Array; snr: number; rssi: number },\n ): Promise<void> {\n try {\n const packet = new Uint8Array(\n data.data instanceof ArrayBuffer ? data.data : data.data,\n );\n\n this.emit(\"packet:receive\", {\n snr: data.snr || 0,\n rssi: data.rssi || 0,\n size: packet.length,\n });\n\n switch (parsePacketType(packet)) {\n case PacketType.DATA:\n await this.handleDataPacket(packet);\n break;\n case PacketType.REQUEST:\n await this.handleRequestPacket(packet);\n break;\n case PacketType.GM:\n await this.handleGmPacket(packet);\n break;\n default:\n this.log?.warn(\n \"[nostr-lora] unknown packet type, flags=0x\" +\n packet[0]!.toString(16),\n );\n }\n } catch (error) {\n this.emit(\n \"error\",\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n }\n\n /** Update the inter-packet timing on the underlying send queue. */\n setTimingOptions({ delay, jitter }: TimingOptions = {}): void {\n if (delay !== undefined) this.queue.INTER_PACKET_DELAY = delay;\n if (jitter !== undefined) this.queue.INTER_PACKET_JITTER = jitter;\n }\n\n /** Number of events sent by this node (available for GM announcements). */\n get sentEventCount(): number {\n return this.sentEvents.size;\n }\n\n // ── Receive: DATA ─────────────────────────────────────────────────────────\n\n private async handleDataPacket(packet: Uint8Array): Promise<void> {\n const decoded = DataPacket.decode(packet);\n this.emit(\"chunk:receive\", decoded, packet.length);\n\n if (decoded.type === \"first\") {\n const eventIdHex = bytesToHex(decoded.eventId);\n const result = this.assembler.handleFirst(\n decoded,\n this.recentEventIds,\n );\n\n if (result.action === \"complete\") {\n this.clearInactivityTimer(eventIdHex);\n this.clearPendingTimer(\n bytesToHex(\n decoded.eventId.slice(0, Limits.EVENT_ID_PREFIX_LEN),\n ),\n );\n await this.completeSession(result.session);\n } else if (result.action === \"started\") {\n this.resetInactivityTimer(eventIdHex);\n this.clearPendingTimer(\n bytesToHex(\n decoded.eventId.slice(0, Limits.EVENT_ID_PREFIX_LEN),\n ),\n );\n }\n } else {\n const result = this.assembler.handleSubsequent(\n decoded,\n this.eventTimeout,\n );\n\n if (result.action === \"complete\") {\n this.clearInactivityTimer(bytesToHex(result.session.eventId));\n await this.completeSession(result.session);\n } else if (result.action === \"progress\") {\n this.resetInactivityTimer(result.eventIdHex);\n } else if (result.action === \"pending\") {\n this.resetPendingTimer(result.prefix);\n }\n }\n }\n\n private async completeSession(\n session: {\n eventId: Uint8Array;\n chunks: Array<Uint8Array | undefined>;\n checksum: number;\n },\n ): Promise<void> {\n const eventIdHex = bytesToHex(session.eventId);\n try {\n const event = await deserializeEvent(\n session.chunks.filter((c): c is Uint8Array => c !== undefined),\n session.checksum,\n eventIdHex,\n );\n this.recentEventIds.set(eventIdHex, Date.now());\n this.cleanupDeduplication();\n this.emit(\"event:receive\", event);\n } catch (error) {\n this.emit(\n \"error\",\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n }\n\n // ── Receive: REQUEST ──────────────────────────────────────────────────────\n\n private async handleRequestPacket(packet: Uint8Array): Promise<void> {\n const decoded = RequestPacket.decode(packet);\n const prefixHex = bytesToHex(decoded.eventIdPrefix);\n\n this.emit(\"request:receive\", prefixHex, decoded.missingChunks);\n\n let matchedId: string | null = null;\n let stored: SentChunkEntry | null = null;\n for (const [id, entry] of this.sentChunks.entries()) {\n if (id.startsWith(prefixHex)) {\n matchedId = id;\n stored = entry;\n break;\n }\n }\n\n if (!stored || !matchedId) return;\n\n const retransmit = decoded.missingChunks\n .filter((idx) => idx < stored!.packets.length)\n .map((idx) => stored!.packets[idx]!);\n\n if (retransmit.length === 0) return;\n\n const label = `RETX id:${matchedId.substring(0, 8)}… chunks:[${\n decoded.missingChunks.join(\",\")\n }]`;\n this.queue.enqueue(retransmit, label, \"data\");\n }\n\n // ── Receive: GM ───────────────────────────────────────────────────────────\n\n private async handleGmPacket(packet: Uint8Array): Promise<void> {\n await this.gm.handlePacket(packet);\n }\n\n // ── Inactivity timers: session has chunk 0 ────────────────────────────────\n\n private resetInactivityTimer(eventIdHex: string): void {\n const existing = this.inactivityTimers.get(eventIdHex);\n const requestCount = existing?.requestCount ?? 0;\n if (existing?.timer) clearTimeout(existing.timer);\n\n const timer = setTimeout(\n () => this.onChunkInactivity(eventIdHex),\n this.requestInactivity,\n );\n this.inactivityTimers.set(eventIdHex, { timer, requestCount });\n }\n\n private clearInactivityTimer(eventIdHex: string): void {\n const existing = this.inactivityTimers.get(eventIdHex);\n if (existing?.timer) clearTimeout(existing.timer);\n this.inactivityTimers.delete(eventIdHex);\n }\n\n private async onChunkInactivity(eventIdHex: string): Promise<void> {\n const timerState = this.inactivityTimers.get(eventIdHex);\n if (!timerState) return;\n\n const missingChunks = this.assembler.missingChunks(eventIdHex);\n if (!missingChunks?.length) {\n this.inactivityTimers.delete(eventIdHex);\n return;\n }\n\n timerState.requestCount++;\n\n if (timerState.requestCount > this.requestMaxRetries) {\n this.log?.warn(\n `[nostr-lora] giving up on ${\n eventIdHex.substring(0, 16)\n } after ${this.requestMaxRetries} REQUESTs`,\n );\n this.clearInactivityTimer(eventIdHex);\n this.assembler.abandonSession(eventIdHex);\n return;\n }\n\n const eventId = this.assembler.getSessionEventId(eventIdHex) ??\n hexToBytes(eventIdHex);\n const prefix = eventId.slice(0, Limits.EVENT_ID_PREFIX_LEN);\n const label = `REQ id:${eventIdHex.substring(0, 8)}… missing:[${\n missingChunks.join(\",\")\n }] (#${timerState.requestCount})`;\n\n this.queue.enqueue(\n [RequestPacket.create(prefix, missingChunks)],\n label,\n \"request\",\n );\n this.emit(\n \"request:send\",\n eventIdHex,\n missingChunks,\n timerState.requestCount,\n );\n\n timerState.timer = setTimeout(\n () => this.onChunkInactivity(eventIdHex),\n this.requestInactivity,\n );\n }\n\n // ── Inactivity timers: chunk 0 still missing ──────────────────────────────\n\n private resetPendingTimer(eventIdPrefix: Uint8Array): void {\n const prefixHex = bytesToHex(eventIdPrefix);\n const existing = this.pendingTimers.get(prefixHex);\n const requestCount = existing?.requestCount ?? 0;\n if (existing?.timer) clearTimeout(existing.timer);\n\n const timer = setTimeout(\n () => this.onPendingInactivity(prefixHex, eventIdPrefix),\n this.requestInactivity,\n );\n this.pendingTimers.set(prefixHex, { timer, requestCount });\n }\n\n private clearPendingTimer(prefixHex: string): void {\n const existing = this.pendingTimers.get(prefixHex);\n if (existing?.timer) clearTimeout(existing.timer);\n this.pendingTimers.delete(prefixHex);\n }\n\n private async onPendingInactivity(\n prefixHex: string,\n eventIdPrefix: Uint8Array,\n ): Promise<void> {\n const timerState = this.pendingTimers.get(prefixHex);\n if (!timerState) return;\n\n const missingChunks = this.assembler.missingChunksForPending(prefixHex);\n if (!missingChunks) {\n this.pendingTimers.delete(prefixHex);\n return;\n }\n\n timerState.requestCount++;\n\n if (timerState.requestCount > this.requestMaxRetries) {\n this.log?.warn(\n `[nostr-lora] giving up on pending prefix ${prefixHex} after ${this.requestMaxRetries} REQUESTs`,\n );\n this.assembler.abandonPending(prefixHex);\n this.pendingTimers.delete(prefixHex);\n return;\n }\n\n const label = `REQ prefix:${prefixHex.substring(0, 8)}… missing:[${\n missingChunks.join(\",\")\n }] (#${timerState.requestCount})`;\n this.queue.enqueue(\n [RequestPacket.create(eventIdPrefix, missingChunks)],\n label,\n \"request\",\n );\n this.emit(\n \"request:send\",\n prefixHex,\n missingChunks,\n timerState.requestCount,\n );\n\n timerState.timer = setTimeout(\n () => this.onPendingInactivity(prefixHex, eventIdPrefix),\n this.requestInactivity,\n );\n }\n\n // ── Maintenance ───────────────────────────────────────────────────────────\n\n private pruneSentChunks(): void {\n const cutoff = Date.now() - this.sentChunksTtl;\n for (const [id, entry] of this.sentChunks.entries()) {\n if (entry.sentAt < cutoff) this.sentChunks.delete(id);\n }\n }\n\n private cleanupDeduplication(): void {\n const now = Date.now();\n for (const [id, ts] of this.recentEventIds.entries()) {\n if (now - ts > this.dedupTimeout) this.recentEventIds.delete(id);\n }\n }\n}\n","import { Constants, WebSerialConnection } from \"@liamcottle/meshcore.js\";\nimport { EventEmitter } from \"./utils.js\";\nimport type {\n ConnectionManager,\n ConnectionManagerEvents,\n} from \"./connection-manager.js\";\nimport type { Logger } from \"./utils.js\";\n\nexport interface DeviceInfo {\n name: string;\n firmwareVersion: string;\n}\n\nexport interface RadioConfig {\n frequency: number;\n bandwidth: number;\n spreadingFactor: number;\n codingRate: number;\n power?: number;\n}\n\n/**\n * Web Serial connection to a MeshCore LoRa device.\n * Implements `ConnectionManager` so it can be passed directly to `LoRaTransport`.\n *\n * Key constraint: WebSerialConnection.write() has no internal queue — it calls\n * getWriter()/releaseLock() per write. Two overlapping writes crash with\n * \"WritableStream is already locked\". We must never issue a write while another\n * is in flight. `LoRaTransport`'s `SendQueue` ensures this.\n */\nexport class SerialConnectionManager\n extends EventEmitter<\n ConnectionManagerEvents & { \"deviceInfo\": [info: DeviceInfo] }\n >\n implements ConnectionManager {\n private connection: InstanceType<typeof WebSerialConnection> | null = null;\n private label: string | null = null;\n private log: Logger | null;\n\n constructor(logger?: Logger | null) {\n super();\n this.log = logger ?? null;\n }\n\n // ── ConnectionManager interface ───────────────────────────────────────────\n\n async open(): Promise<void> {\n if (!this.isWebSerialSupported()) {\n throw new Error(\n \"Web Serial is not supported in this browser. Use Chrome or Edge.\",\n );\n }\n\n try {\n this.connection = await WebSerialConnection.open();\n\n if (!this.connection) {\n throw new Error(\"No serial port selected\");\n }\n\n this.label = this.buildPortLabel(this.connection.serialPort);\n this.setupHandlers();\n } catch (error) {\n this.connection = null;\n const err = error instanceof Error\n ? error\n : new Error(String(error));\n this.emit(\"error\", err);\n throw err;\n }\n }\n\n async close(): Promise<void> {\n if (this.connection) {\n try {\n await this.connection.close();\n } catch (e) {\n this.log?.warn(\"[nostr-lora] Error closing serial port:\", e);\n }\n this.connection = null;\n this.label = null;\n this.emit(\"disconnect\");\n }\n }\n\n async sendRawData(data: Uint8Array): Promise<void> {\n if (!this.connection) {\n throw new Error(\"Not connected to device\");\n }\n try {\n const path = new Uint8Array(0);\n await this.connection.sendCommandSendRawData(path, data);\n } catch (error) {\n const err = error instanceof Error\n ? error\n : new Error(String(error));\n this.emit(\"error\", err);\n throw err;\n }\n }\n\n // ── Extra capabilities ────────────────────────────────────────────────────\n\n /** Configure the LoRa radio parameters. Must be called after `open()`. */\n async setRadioConfig(config: RadioConfig): Promise<void> {\n if (!this.connection) throw new Error(\"Not connected to device\");\n await this.connection.setRadioParams(\n config.frequency,\n config.bandwidth,\n config.spreadingFactor,\n config.codingRate,\n );\n if (config.power !== undefined) {\n await this.connection.setTxPower(config.power);\n }\n }\n\n isConnected(): boolean {\n return this.connection !== null;\n }\n\n isWebSerialSupported(): boolean {\n return typeof navigator !== \"undefined\" &&\n typeof (navigator as Navigator & { serial?: unknown }).serial !==\n \"undefined\";\n }\n\n /** The USB VID:PID or \"Serial device\" label for the connected port. */\n get portLabel(): string | null {\n return this.label;\n }\n\n // ── Private ───────────────────────────────────────────────────────────────\n\n private buildPortLabel(\n serialPort: {\n getInfo(): { usbVendorId?: number; usbProductId?: number };\n },\n ): string {\n try {\n const info = serialPort.getInfo();\n if (\n info.usbVendorId !== undefined &&\n info.usbProductId !== undefined\n ) {\n const vid = info.usbVendorId.toString(16).padStart(4, \"0\")\n .toUpperCase();\n const pid = info.usbProductId.toString(16).padStart(4, \"0\")\n .toUpperCase();\n return `USB ${vid}:${pid}`;\n }\n } catch (_) { /* ignore */ }\n return \"Serial device\";\n }\n\n private setupHandlers(): void {\n if (!this.connection) return;\n\n this.connection.once(\n Constants.ResponseCodes.DeviceInfo,\n (data: { manufacturerModel?: string; firmwareVer?: number }) => {\n const info: DeviceInfo = {\n name: data.manufacturerModel ?? \"Unknown\",\n firmwareVersion: data.firmwareVer != null\n ? String(data.firmwareVer)\n : \"Unknown\",\n };\n this.emit(\"deviceInfo\", info);\n },\n );\n\n this.connection.on(\"connected\", async () => {\n try {\n await this.connection!.sendCommandAppStart();\n } catch (e) {\n this.log?.warn(\"[nostr-lora] AppStart failed:\", e);\n }\n\n try {\n const now = Math.floor(Date.now() / 1000);\n await this.connection!.sendCommandSetDeviceTime(now);\n } catch (e) {\n this.log?.warn(\"[nostr-lora] Time sync failed:\", e);\n }\n\n this.emit(\"connect\", this.label!);\n });\n\n this.connection.on(\"disconnected\", () => {\n this.log?.log(\"[nostr-lora] Serial device disconnected\");\n this.connection = null;\n this.label = null;\n this.emit(\"disconnect\");\n });\n\n this.connection.on(\n Constants.PushCodes.RawData,\n (\n data: {\n payload: Uint8Array;\n lastSnr: number;\n lastRssi: number;\n },\n ) => {\n this.emit(\"data\", {\n data: data.payload,\n snr: data.lastSnr,\n rssi: data.lastRssi,\n });\n },\n );\n }\n}\n"],"names":["PacketType","Limits","CRC32","i","c","k","data","crc","__publicField","Compression","compressedStream","blob","decompressedStream","DataPacket","eventId","eventKind","compressed","ttl","checksum","remaining","numChunks","packets","offset","isLast","maxPayload","payloadSize","payload","totalChunks","packet","o","eventIdPrefix","chunkIndex","realChunkIndex","RequestPacket","missingChunks","bitmap","idx","GmPacket","nodeId","eventCount","recentSince","timestamp","parsePacketType","_encoder","_decoder","serializeEvent","event","canonical","deserializeEvent","chunkPayloads","expectedChecksum","eventIdHex","totalLength","sum","chunk","computedChecksum","decompressed","arr","verifyEvent","hexToBytes","hex","bytes","bytesToHex","b","matchesPrefix","id","prefix","sleep","ms","resolve","EventEmitter","fn","_a","args","err","AssemblySession","first","index","missing","Assembler","decoded","seenIds","session","eventTimeout","prefixHex","buffered","p","haveIndices","lastChunk","maxIndex","stillPending","pending","cutoff","Defaults","SendQueue","sendFn","options","logger","label","type","meta","item","pkt","GmProtocol","deps","peerIdHex","myIdHex","backoff","backoffWindow","lastTime","now","sentEvents","toShare","_b","_c","oldest","_d","LoRaTransport","connection","snapshot","e","count","since","byteLength","raw","portLabel","error","delay","jitter","result","matchedId","stored","entry","retransmit","existing","requestCount","timer","timerState","ts","SerialConnectionManager","WebSerialConnection","path","config","serialPort","info","vid","pid","Constants"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"serial-connection.js","sources":["../lib/protocol.ts","../lib/event-codec.ts","../lib/utils.ts","../lib/assembler.ts","../lib/constants.ts","../lib/send-queue.ts","../lib/gm-protocol.ts","../lib/lora-transport.ts","../lib/serial-connection.ts"],"sourcesContent":["export const PacketType = {\n DATA: 0x01,\n REQUEST: 0x02,\n GM: 0x03,\n} as const;\n\nexport type PacketTypeValue = typeof PacketType[keyof typeof PacketType];\n\nexport const Limits = {\n // MeshCore companion firmware constrains serial frames to MAX_FRAME_SIZE=172 bytes.\n // TX: cmd_frame = CMD(1) + path_len(1) + packet → packet ≤ 170\n // RX: onRawDataRecv builds push_frame = push_code(1) + snr(1) + rssi(1) +\n // reserved(1) + packet into out_frame[MAX_FRAME_SIZE+1]. The buffer-\n // overflow guard allows payload ≤ 169, BUT writeFrame() rejects any\n // frame longer than MAX_FRAME_SIZE (172). push_frame = 4 + packet,\n // so writeFrame accepts only packet ≤ 168.\n // Effective max packet size = min(170, 168) = 168.\n MAX_PACKET_SIZE: 168,\n // First chunk header: flags(1) + chunk_index(2) + event_id(32) +\n // total_chunks(2) + event_kind(2) + checksum(4) + ttl(1) = 44 bytes\n FIRST_CHUNK_HEADER: 44,\n FIRST_CHUNK_PAYLOAD: 124, // 168 - 44\n\n // Subsequent chunk header: flags(1) + event_id_prefix(14) + chunk_index(2) = 17 bytes\n SUBSEQUENT_CHUNK_HEADER: 17,\n SUBSEQUENT_CHUNK_PAYLOAD: 151, // 168 - 17\n\n EVENT_ID_PREFIX_LEN: 14, // first 14 bytes of event_id (2^112 collision resistance)\n MAX_CHUNKS: 8,\n MAX_COMPRESSED_SIZE: 1152, // 1024 content + ~128 for 64-byte sig (hex)\n} as const;\n\n// ── CRC32 ────────────────────────────────────────────────────────────────────\n\nexport class CRC32 {\n private static table: Uint32Array | null = null;\n\n private static makeTable(): void {\n if (this.table) return;\n this.table = new Uint32Array(256);\n for (let i = 0; i < 256; i++) {\n let c = i;\n for (let k = 0; k < 8; k++) {\n c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);\n }\n this.table[i] = c;\n }\n }\n\n static calculate(data: Uint8Array): number {\n this.makeTable();\n let crc = 0xFFFFFFFF;\n for (let i = 0; i < data.length; i++) {\n crc = (crc >>> 8) ^ this.table![(crc ^ data[i]!) & 0xFF]!;\n }\n return (crc ^ 0xFFFFFFFF) >>> 0;\n }\n}\n\n// ── Compression ───────────────────────────────────────────────────────────────\n\nexport class Compression {\n static async compress(data: Uint8Array): Promise<Uint8Array> {\n const stream = new Blob([data as Uint8Array<ArrayBuffer>]).stream();\n const compressedStream = stream.pipeThrough(\n new CompressionStream('deflate-raw')\n );\n const blob = await new Response(compressedStream).blob();\n return new Uint8Array(await blob.arrayBuffer());\n }\n\n static async decompress(data: Uint8Array): Promise<Uint8Array> {\n const stream = new Blob([data as Uint8Array<ArrayBuffer>]).stream();\n const decompressedStream = stream.pipeThrough(\n new DecompressionStream('deflate-raw')\n );\n const blob = await new Response(decompressedStream).blob();\n return new Uint8Array(await blob.arrayBuffer());\n }\n}\n\n// ── DataPacket ────────────────────────────────────────────────────────────────\n\nexport interface FirstChunk {\n type: 'first';\n eventId: Uint8Array;\n eventKind: number;\n totalChunks: number;\n checksum: number;\n ttl: number;\n payload: Uint8Array;\n isLast: boolean;\n}\n\nexport interface SubsequentChunk {\n type: 'subsequent';\n eventIdPrefix: Uint8Array;\n chunkIndex: number;\n payload: Uint8Array;\n isLast: boolean;\n}\n\nexport type DecodedDataChunk = FirstChunk | SubsequentChunk;\n\nexport class DataPacket {\n static createFromCompressed(\n eventId: Uint8Array,\n eventKind: number,\n compressed: Uint8Array,\n ttl: number,\n ): Uint8Array[] {\n const checksum = CRC32.calculate(compressed);\n\n let remaining = compressed.length - Limits.FIRST_CHUNK_PAYLOAD;\n let numChunks = 1;\n while (remaining > 0) {\n numChunks++;\n remaining -= Limits.SUBSEQUENT_CHUNK_PAYLOAD;\n }\n\n if (numChunks > Limits.MAX_CHUNKS) {\n throw new Error(`Event requires ${numChunks} chunks, max is ${Limits.MAX_CHUNKS}`);\n }\n\n const packets: Uint8Array[] = [];\n let offset = 0;\n\n for (let i = 0; i < numChunks; i++) {\n const isLast = i === numChunks - 1;\n const maxPayload = i === 0 ? Limits.FIRST_CHUNK_PAYLOAD : Limits.SUBSEQUENT_CHUNK_PAYLOAD;\n const payloadSize = Math.min(maxPayload, compressed.length - offset);\n const payload = compressed.slice(offset, offset + payloadSize);\n\n if (i === 0) {\n packets.push(this.encodeFirst(\n eventId, eventKind, numChunks, checksum, ttl, payload, isLast\n ));\n } else {\n packets.push(this.encodeSubsequent(\n eventId.slice(0, Limits.EVENT_ID_PREFIX_LEN),\n i,\n payload,\n isLast,\n ));\n }\n\n offset += payloadSize;\n }\n\n return packets;\n }\n\n static encodeFirst(\n eventId: Uint8Array,\n eventKind: number,\n totalChunks: number,\n checksum: number,\n ttl: number,\n payload: Uint8Array,\n isLast: boolean,\n ): Uint8Array {\n const packet = new Uint8Array(44 + payload.length);\n let o = 0;\n\n packet[o++] = (PacketType.DATA << 4) | (isLast ? 0x01 : 0x00);\n\n packet[o++] = 0x00;\n packet[o++] = 0x00;\n\n packet.set(eventId.slice(0, 32), o); o += 32;\n\n packet[o++] = (totalChunks >> 8) & 0xFF;\n packet[o++] = totalChunks & 0xFF;\n\n packet[o++] = (eventKind >> 8) & 0xFF;\n packet[o++] = eventKind & 0xFF;\n\n packet[o++] = (checksum >> 24) & 0xFF;\n packet[o++] = (checksum >> 16) & 0xFF;\n packet[o++] = (checksum >> 8) & 0xFF;\n packet[o++] = checksum & 0xFF;\n\n packet[o++] = ttl;\n\n packet.set(payload, o);\n\n return packet;\n }\n\n static encodeSubsequent(\n eventIdPrefix: Uint8Array,\n chunkIndex: number,\n payload: Uint8Array,\n isLast: boolean,\n ): Uint8Array {\n const packet = new Uint8Array(17 + payload.length);\n let o = 0;\n\n packet[o++] = (PacketType.DATA << 4) | (isLast ? 0x01 : 0x00);\n\n packet.set(eventIdPrefix.slice(0, 14), o); o += 14;\n\n packet[o++] = (chunkIndex >> 8) & 0xFF;\n packet[o++] = chunkIndex & 0xFF;\n\n packet.set(payload, o);\n\n return packet;\n }\n\n /**\n * Decode DATA packet.\n * Distinguishes first vs subsequent by peeking at bytes 1-2:\n * - First chunk: bytes 1-2 are chunk_index == 0x0000, event_id at offset 3\n * - Subsequent chunk: bytes 1-2 are part of event_id_prefix, chunk_index at offset 15\n *\n * For subsequent chunks, the first 2 bytes of event_id_prefix are statistically\n * never 0x0000 for real event IDs, so 0x0000 reliably identifies first chunks.\n */\n static decode(packet: Uint8Array): DecodedDataChunk {\n const flags = packet[0]!;\n const isLast = (flags & 0x01) !== 0;\n\n const chunkIndex = (packet[1]! << 8) | packet[2]!;\n\n if (chunkIndex === 0) {\n const eventId = packet.slice(3, 35);\n const totalChunks = (packet[35]! << 8) | packet[36]!;\n const eventKind = (packet[37]! << 8) | packet[38]!;\n const checksum = ((packet[39]! << 24) | (packet[40]! << 16) |\n (packet[41]! << 8) | packet[42]!) >>> 0;\n const ttl = packet[43]!;\n const payload = packet.slice(44);\n\n return { type: 'first', eventId, eventKind, totalChunks, checksum, ttl, payload, isLast };\n } else {\n const eventIdPrefix = packet.slice(1, 15);\n const realChunkIndex = (packet[15]! << 8) | packet[16]!;\n const payload = packet.slice(17);\n\n return { type: 'subsequent', eventIdPrefix, chunkIndex: realChunkIndex, payload, isLast };\n }\n }\n}\n\n// ── RequestPacket ─────────────────────────────────────────────────────────────\n\nexport interface DecodedRequestPacket {\n eventIdPrefix: Uint8Array;\n missingChunks: number[];\n}\n\nexport class RequestPacket {\n static create(eventIdPrefix: Uint8Array, missingChunks: number[]): Uint8Array {\n const packet = new Uint8Array(16);\n let o = 0;\n\n packet[o++] = (PacketType.REQUEST << 4);\n\n packet.set(eventIdPrefix.slice(0, Limits.EVENT_ID_PREFIX_LEN), o);\n o += Limits.EVENT_ID_PREFIX_LEN;\n\n let bitmap = 0;\n for (const idx of missingChunks) {\n if (idx < 8) bitmap |= (1 << idx);\n }\n packet[o++] = bitmap & 0xFF;\n\n return packet;\n }\n\n static decode(packet: Uint8Array): DecodedRequestPacket {\n const eventIdPrefix = packet.slice(1, 1 + Limits.EVENT_ID_PREFIX_LEN);\n const bitmap = packet[1 + Limits.EVENT_ID_PREFIX_LEN]!;\n\n const missingChunks: number[] = [];\n for (let i = 0; i < 8; i++) {\n if (bitmap & (1 << i)) missingChunks.push(i);\n }\n\n return { eventIdPrefix, missingChunks };\n }\n}\n\n// ── GmPacket ──────────────────────────────────────────────────────────────────\n\nexport interface DecodedGmPacket {\n nodeId: Uint8Array;\n timestamp: number;\n eventCount: number;\n recentSince: number;\n}\n\nexport class GmPacket {\n static create(\n nodeId: Uint8Array,\n eventCount: number,\n recentSince: number,\n timestamp: number = Math.floor(Date.now() / 1000),\n ): Uint8Array {\n const packet = new Uint8Array(21);\n let o = 0;\n\n packet[o++] = (PacketType.GM << 4);\n\n packet.set(nodeId.slice(0, 8), o); o += 8;\n\n packet[o++] = (timestamp >> 24) & 0xFF;\n packet[o++] = (timestamp >> 16) & 0xFF;\n packet[o++] = (timestamp >> 8) & 0xFF;\n packet[o++] = timestamp & 0xFF;\n\n packet[o++] = (eventCount >> 24) & 0xFF;\n packet[o++] = (eventCount >> 16) & 0xFF;\n packet[o++] = (eventCount >> 8) & 0xFF;\n packet[o++] = eventCount & 0xFF;\n\n packet[o++] = (recentSince >> 24) & 0xFF;\n packet[o++] = (recentSince >> 16) & 0xFF;\n packet[o++] = (recentSince >> 8) & 0xFF;\n packet[o++] = recentSince & 0xFF;\n\n return packet;\n }\n\n static decode(packet: Uint8Array): DecodedGmPacket {\n const nodeId = packet.slice(1, 9);\n const timestamp = ((packet[9]! << 24) | (packet[10]! << 16) | (packet[11]! << 8) | packet[12]!) >>> 0;\n const eventCount = ((packet[13]! << 24) | (packet[14]! << 16) | (packet[15]! << 8) | packet[16]!) >>> 0;\n const recentSince = ((packet[17]! << 24) | (packet[18]! << 16) | (packet[19]! << 8) | packet[20]!) >>> 0;\n return { nodeId, timestamp, eventCount, recentSince };\n }\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\n/** Read the packet type from the flags byte without fully decoding the packet. */\nexport function parsePacketType(packet: Uint8Array): number {\n if (!packet || packet.length === 0) return -1;\n return (packet[0]! >> 4) & 0x0F;\n}\n","import { verifyEvent } from 'nostr-tools';\nimport { Compression, CRC32, Limits } from './protocol.js';\n\nconst _encoder = new TextEncoder();\nconst _decoder = new TextDecoder();\n\nexport interface NostrEvent {\n id: string;\n pubkey: string;\n created_at: number;\n kind: number;\n tags: string[][];\n content: string;\n sig: string;\n}\n\nexport interface SerializedEvent {\n compressed: Uint8Array;\n checksum: number;\n}\n\nexport async function serializeEvent(event: NostrEvent): Promise<SerializedEvent> {\n if (!event.id || typeof event.id !== 'string')\n throw new Error('Event must have string id');\n if (!event.pubkey || typeof event.pubkey !== 'string')\n throw new Error('Event must have string pubkey');\n if (!event.created_at || typeof event.created_at !== 'number')\n throw new Error('Event must have numeric created_at');\n if (event.kind === undefined || typeof event.kind !== 'number')\n throw new Error('Event must have numeric kind');\n if (!Array.isArray(event.tags))\n throw new Error('Event tags must be array');\n if (typeof event.content !== 'string')\n throw new Error('Event content must be string');\n if (!event.sig || typeof event.sig !== 'string')\n throw new Error('Event must have string sig');\n\n // NIP-01 canonical wire array (index 6 carries the signature)\n const canonical = JSON.stringify([\n 0,\n event.pubkey,\n event.created_at,\n event.kind,\n event.tags,\n event.content,\n event.sig,\n ]);\n\n const compressed = await Compression.compress(_encoder.encode(canonical));\n\n if (compressed.length > Limits.MAX_COMPRESSED_SIZE) {\n throw new Error(\n `Compressed event too large: ${compressed.length} bytes (max ${Limits.MAX_COMPRESSED_SIZE})`\n );\n }\n\n const checksum = CRC32.calculate(compressed);\n return { compressed, checksum };\n}\n\nexport async function deserializeEvent(\n chunkPayloads: Uint8Array[],\n expectedChecksum: number,\n eventIdHex: string,\n): Promise<NostrEvent> {\n const totalLength = chunkPayloads.reduce((sum, c) => sum + c.length, 0);\n const compressed = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of chunkPayloads) {\n compressed.set(chunk, offset);\n offset += chunk.length;\n }\n\n const computedChecksum = CRC32.calculate(compressed);\n if (computedChecksum !== expectedChecksum) {\n throw new Error(\n `Checksum mismatch: expected 0x${expectedChecksum.toString(16)}, ` +\n `got 0x${computedChecksum.toString(16)}`\n );\n }\n\n const decompressed = await Compression.decompress(compressed);\n const arr = JSON.parse(_decoder.decode(decompressed)) as unknown[];\n\n const event: NostrEvent = {\n id: eventIdHex,\n pubkey: arr[1] as string,\n created_at: arr[2] as number,\n kind: arr[3] as number,\n tags: arr[4] as string[][],\n content: arr[5] as string,\n sig: arr[6] as string,\n };\n\n // Mandatory cryptographic signature verification — drop forged events\n if (!verifyEvent(event)) {\n throw new Error(`Invalid signature for event ${eventIdHex.substring(0, 16)}…`);\n }\n\n return event;\n}\n","export function hexToBytes(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(hex.substr(i * 2, 2), 16);\n }\n return bytes;\n}\n\nexport function bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');\n}\n\n/** True if the first `prefix.length` bytes of `id` match `prefix`. */\nexport function matchesPrefix(id: Uint8Array, prefix: Uint8Array): boolean {\n for (let i = 0; i < prefix.length; i++) {\n if (id[i] !== prefix[i]) return false;\n }\n return true;\n}\n\nexport function sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * A subset of the `console` API. Pass any object with `log`, `warn`,\n * `error`, and `debug` methods — for example `console` itself — to enable\n * internal diagnostic logging. Omit (or pass `null`) to silence all output.\n */\nexport interface Logger {\n log(message: string, ...args: unknown[]): void;\n warn(message: string, ...args: unknown[]): void;\n error(message: string, ...args: unknown[]): void;\n debug(message: string, ...args: unknown[]): void;\n}\ntype Listener<T extends unknown[]> = (...args: T) => void;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type EventMap = Record<string, any[]>;\n\nexport class EventEmitter<Events extends EventMap = EventMap> {\n private listeners: Map<string, Set<Listener<unknown[]>>> = new Map();\n\n on<K extends keyof Events & string>(event: K, fn: Listener<Events[K]>): () => void {\n if (!this.listeners.has(event)) this.listeners.set(event, new Set());\n this.listeners.get(event)!.add(fn as Listener<unknown[]>);\n return () => this.listeners.get(event)?.delete(fn as Listener<unknown[]>);\n }\n\n emit<K extends keyof Events & string>(event: K, ...args: Events[K]): void {\n for (const fn of (this.listeners.get(event) ?? [])) {\n try { fn(...args); } catch (err) { console.error(`[EventEmitter] '${event}' listener threw:`, err); }\n }\n }\n}\n","import { Limits } from './protocol.js';\nimport { hexToBytes, bytesToHex, matchesPrefix } from './utils.js';\nimport type { FirstChunk, SubsequentChunk } from './protocol.js';\n\n// ─── AssemblySession ─────────────────────────────────────────────────────────\n\n/** Holds the per-event state while chunks are being collected. Not exported. */\nclass AssemblySession {\n readonly eventId: Uint8Array;\n readonly eventKind: number;\n readonly totalChunks: number;\n readonly checksum: number;\n readonly chunks: Array<Uint8Array | undefined>;\n receivedChunks: number;\n\n constructor(first: FirstChunk) {\n this.eventId = first.eventId;\n this.eventKind = first.eventKind;\n this.totalChunks = first.totalChunks;\n this.checksum = first.checksum;\n\n this.chunks = new Array<Uint8Array | undefined>(this.totalChunks);\n this.chunks[0] = first.payload;\n this.receivedChunks = 1;\n }\n\n get isComplete(): boolean {\n return this.receivedChunks === this.totalChunks;\n }\n\n /** Returns true if this was a new chunk, false if duplicate. */\n addChunk(index: number, payload: Uint8Array): boolean {\n if (this.chunks[index] !== undefined) return false;\n this.chunks[index] = payload;\n this.receivedChunks++;\n return true;\n }\n\n missingIndices(): number[] {\n const missing: number[] = [];\n for (let i = 0; i < this.totalChunks; i++) {\n if (this.chunks[i] === undefined) missing.push(i);\n }\n return missing;\n }\n}\n\n// ─── Action types ─────────────────────────────────────────────────────────────\n\nexport interface CompleteAction { action: 'complete'; session: AssemblySession }\nexport interface StartedAction { action: 'started'; session: AssemblySession }\nexport interface ProgressAction { action: 'progress'; session: AssemblySession; eventIdHex: string }\nexport interface PendingAction { action: 'pending'; prefix: Uint8Array }\nexport interface NoneAction { action: 'none' }\n\nexport type FirstChunkResult = CompleteAction | StartedAction | NoneAction;\nexport type SubsequentChunkResult = CompleteAction | ProgressAction | PendingAction | NoneAction;\n\n// ─── Pending buffer entry ─────────────────────────────────────────────────────\n\ninterface PendingEntry {\n eventIdPrefix: Uint8Array;\n chunkIndex: number;\n payload: Uint8Array;\n isLast: boolean;\n arrivedAt: number;\n}\n\n// ─── Assembler ───────────────────────────────────────────────────────────────\n\nexport class Assembler {\n /** In-progress sessions keyed by the full event ID as a hex string. */\n sessions: Map<string, AssemblySession> = new Map();\n\n /** Subsequent chunks that arrived before their first chunk. */\n pendingSubsequent: PendingEntry[] = [];\n\n // ─── First-chunk path ───────────────────────────────────────────────────\n\n handleFirst(decoded: FirstChunk, seenIds: Map<string, number>): FirstChunkResult {\n const eventIdHex = bytesToHex(decoded.eventId);\n\n if (decoded.ttl === 0) return { action: 'none' };\n if (decoded.totalChunks > Limits.MAX_CHUNKS) return { action: 'none' };\n if (seenIds.has(eventIdHex)) return { action: 'none' };\n if (this.sessions.has(eventIdHex)) return { action: 'none' };\n\n const session = new AssemblySession(decoded);\n this.sessions.set(eventIdHex, session);\n\n this._replayPending(eventIdHex, session);\n\n if (session.isComplete) {\n this.sessions.delete(eventIdHex);\n return { action: 'complete', session };\n }\n\n return { action: 'started', session };\n }\n\n // ─── Subsequent-chunk path ──────────────────────────────────────────────\n\n handleSubsequent(decoded: SubsequentChunk, eventTimeout: number): SubsequentChunkResult {\n const eventIdHex = this._findSessionForPrefix(decoded.eventIdPrefix);\n\n if (!eventIdHex) {\n this.pendingSubsequent.push({\n eventIdPrefix: decoded.eventIdPrefix,\n chunkIndex: decoded.chunkIndex,\n payload: decoded.payload,\n isLast: decoded.isLast,\n arrivedAt: Date.now(),\n });\n this._prunePending(eventTimeout);\n return { action: 'pending', prefix: decoded.eventIdPrefix };\n }\n\n const session = this.sessions.get(eventIdHex)!;\n const added = session.addChunk(decoded.chunkIndex, decoded.payload);\n if (!added) return { action: 'none' };\n\n if (session.isComplete) {\n this.sessions.delete(eventIdHex);\n return { action: 'complete', session };\n }\n\n return { action: 'progress', session, eventIdHex };\n }\n\n // ─── Timer-driven queries ────────────────────────────────────────────────\n\n /** Returns missing chunk indices for a session, or null if it no longer exists. */\n missingChunks(eventIdHex: string): number[] | null {\n const session = this.sessions.get(eventIdHex);\n return session ? session.missingIndices() : null;\n }\n\n /** Abandon an in-progress session after max retries exceeded. */\n abandonSession(eventIdHex: string): void {\n this.sessions.delete(eventIdHex);\n }\n\n /**\n * Returns missing chunk indices for a pending-subsequent group\n * (chunk 0 still missing), or null if no buffered chunks exist for this prefix.\n */\n missingChunksForPending(prefixHex: string): number[] | null {\n const buffered = this.pendingSubsequent.filter(\n p => bytesToHex(p.eventIdPrefix) === prefixHex\n );\n if (buffered.length === 0) return null;\n\n const haveIndices = new Set(buffered.map(p => p.chunkIndex));\n\n const lastChunk = buffered.find(p => p.isLast);\n const maxIndex = Math.max(...haveIndices);\n const totalChunks = lastChunk ? lastChunk.chunkIndex + 1 : maxIndex + 1;\n\n const missing: number[] = [];\n for (let i = 0; i < totalChunks; i++) {\n if (!haveIndices.has(i)) missing.push(i);\n }\n return missing.length > 0 ? missing : null;\n }\n\n /** Abandon all pending entries for a given prefix after max retries exceeded. */\n abandonPending(prefixHex: string): void {\n this.pendingSubsequent = this.pendingSubsequent.filter(\n p => bytesToHex(p.eventIdPrefix) !== prefixHex\n );\n }\n\n /** True if a session is currently in progress for this event ID. */\n hasSession(eventIdHex: string): boolean {\n return this.sessions.has(eventIdHex);\n }\n\n /**\n * Returns the raw eventId bytes for an in-progress session, or null.\n * Used by the transport to build REQUEST prefix bytes.\n */\n getSessionEventId(eventIdHex: string): Uint8Array | null {\n return this.sessions.get(eventIdHex)?.eventId ?? null;\n }\n\n // ─── Internals ───────────────────────────────────────────────────────────\n\n private _findSessionForPrefix(prefix: Uint8Array): string | null {\n for (const [id] of this.sessions.entries()) {\n if (matchesPrefix(hexToBytes(id), prefix)) return id;\n }\n return null;\n }\n\n private _replayPending(eventIdHex: string, session: AssemblySession): void {\n const stillPending: PendingEntry[] = [];\n for (const pending of this.pendingSubsequent) {\n if (matchesPrefix(hexToBytes(eventIdHex), pending.eventIdPrefix)) {\n session.addChunk(pending.chunkIndex, pending.payload);\n } else {\n stillPending.push(pending);\n }\n }\n this.pendingSubsequent = stillPending;\n }\n\n private _prunePending(eventTimeout: number): void {\n const cutoff = Date.now() - eventTimeout;\n this.pendingSubsequent = this.pendingSubsequent.filter(\n p => p.arrivedAt > cutoff\n );\n }\n}\n","const Defaults = {\n // ── SendQueue ─────────────────────────────────────────────────────────────\n\n /** Base delay between packets within a single queue item (ms). */\n INTER_PACKET_DELAY: 2000,\n\n /** Maximum additional random jitter added on top of INTER_PACKET_DELAY (ms). */\n INTER_PACKET_JITTER: 500,\n\n // ── GmProtocol ────────────────────────────────────────────────────────────\n\n /** Per-peer minimum response interval before the first backoff multiplier kicks in (ms). */\n GM_BACKOFF_BASE: 5 * 60 * 1000,\n\n /** Maximum number of our own events to re-share in response to one GM. */\n GM_MAX_SHARE_EVENTS: 3,\n\n /** Lower bound of the random pre-share / pre-reply delay (ms). */\n GM_JITTER_MIN: 500,\n\n /** Upper bound of the random pre-share / pre-reply delay (ms). */\n GM_JITTER_MAX: 1500,\n\n // ── LoRaTransport ─────────────────────────────────────────────────────────\n\n /** How long to wait for the next chunk before declaring a gap and sending REQUEST (ms). */\n REQUEST_INACTIVITY: 30_000,\n\n /** Number of REQUEST retries before abandoning a partial event. */\n REQUEST_MAX_RETRIES: 3,\n\n /** How long to keep sent chunk packets around for potential retransmission (ms). */\n SENT_CHUNKS_TTL: 5 * 60 * 1000,\n\n /** How long before we stop deduplicating a previously-seen event ID (ms). */\n DEDUP_TIMEOUT: 15 * 60 * 1000,\n\n /** How long to wait for subsequent chunks of a partial event (ms). */\n EVENT_TIMEOUT: 30_000,\n\n /** TTL assigned to locally-created events (decremented on each re-broadcast hop). */\n INITIAL_TTL: 6,\n} as const;\n\nexport default Defaults;\n\n/** Widened (plain `number`) version of `Defaults` — use for constructor option types. */\nexport type DefaultOptions = { [K in keyof typeof Defaults]: number };\n","import { EventEmitter, sleep } from \"./utils.js\";\nimport Defaults, { type DefaultOptions } from \"./constants.js\";\nimport type { Logger } from \"./utils.js\";\n\nexport interface QueueItemMeta {\n _onSent?: () => Promise<void>;\n}\n\nexport interface QueueItem {\n id: number;\n label: string;\n type: string;\n packets: Uint8Array[];\n meta: QueueItemMeta;\n status: \"pending\" | \"sending\";\n}\n\nexport interface QueueSnapshot {\n id: number;\n label: string;\n type: string;\n packetCount: number;\n status: \"pending\" | \"sending\";\n}\n\nexport type SendQueueEvents = {\n \"packet:send\": [packet: Uint8Array, item: QueueItem];\n \"queue:update\": [snapshot: QueueSnapshot[]];\n \"error\": [err: Error];\n};\n\nexport class SendQueue extends EventEmitter<SendQueueEvents> {\n private sendFn: (packet: Uint8Array) => Promise<void>;\n private items: QueueItem[] = [];\n private running: boolean = false;\n private seq: number = 0;\n private log: Logger | null;\n\n INTER_PACKET_DELAY: number;\n INTER_PACKET_JITTER: number;\n\n constructor(\n sendFn: (packet: Uint8Array) => Promise<void>,\n options: Partial<\n Pick<DefaultOptions, \"INTER_PACKET_DELAY\" | \"INTER_PACKET_JITTER\">\n > = {},\n logger?: Logger | null,\n ) {\n super();\n if (typeof sendFn !== \"function\") {\n throw new Error(\"SendQueue requires a sendFn(packet) argument\");\n }\n this.sendFn = sendFn;\n this.log = logger ?? null;\n\n this.INTER_PACKET_DELAY = options.INTER_PACKET_DELAY ??\n Defaults.INTER_PACKET_DELAY;\n this.INTER_PACKET_JITTER = options.INTER_PACKET_JITTER ??\n Defaults.INTER_PACKET_JITTER;\n }\n\n // ─── Public API ──────────────────────────────────────────────────────────\n\n enqueue(\n packets: Uint8Array[],\n label: string,\n type: string,\n meta: QueueItemMeta = {},\n ): number {\n const id = ++this.seq;\n this.items.push({ id, label, type, packets, meta, status: \"pending\" });\n this.notifyUpdate();\n this.drain();\n return id;\n }\n\n get snapshot(): QueueSnapshot[] {\n return this.items.map((item) => ({\n id: item.id,\n label: item.label,\n type: item.type,\n packetCount: item.packets.length,\n status: item.status,\n }));\n }\n\n // ─── Internal ────────────────────────────────────────────────────────────\n\n private notifyUpdate(): void {\n this.emit(\"queue:update\", this.snapshot);\n }\n\n private drain(): void {\n if (this.running) return;\n this.running = true;\n this.run().catch((err) => {\n this.log?.error(\"[nostr-lora] send queue drain error:\", err);\n this.running = false;\n });\n }\n\n private async run(): Promise<void> {\n while (this.items.length > 0) {\n const item = this.items[0]!;\n item.status = \"sending\";\n this.notifyUpdate();\n\n try {\n for (let i = 0; i < item.packets.length; i++) {\n const pkt = item.packets[i]!;\n await this.sendFn(pkt);\n this.emit(\"packet:send\", pkt, item);\n\n if (i < item.packets.length - 1) {\n await sleep(\n this.INTER_PACKET_DELAY +\n Math.random() * this.INTER_PACKET_JITTER,\n );\n }\n }\n\n if (item.meta._onSent) {\n await item.meta._onSent();\n }\n } catch (err) {\n this.log?.error(\"[nostr-lora] send queue item error:\", err);\n this.emit(\n \"error\",\n err instanceof Error ? err : new Error(String(err)),\n );\n }\n\n this.items.shift();\n this.notifyUpdate();\n\n if (this.items.length > 0) {\n await sleep(\n this.INTER_PACKET_DELAY +\n Math.random() * this.INTER_PACKET_JITTER,\n );\n }\n }\n this.running = false;\n }\n}\n","import { GmPacket } from \"./protocol.js\";\nimport type { DecodedGmPacket } from \"./protocol.js\";\nimport { bytesToHex, EventEmitter, sleep } from \"./utils.js\";\nimport Defaults, { type DefaultOptions } from \"./constants.js\";\nimport type { NostrEvent } from \"./event-codec.js\";\nimport type { Logger } from \"./utils.js\";\n\nexport interface GmProtocolDeps {\n getNodeId: () => Uint8Array | null;\n getSentEvents: () => Map<string, NostrEvent>;\n sendEvent: (event: NostrEvent) => Promise<void>;\n sendGm: (\n nodeId: Uint8Array,\n eventCount: number,\n recentSince: number,\n ) => Promise<void>;\n}\n\nexport type GmProtocolEvents = {\n \"gm:receive\": [decoded: DecodedGmPacket, byteLength: number];\n};\n\nexport class GmProtocol extends EventEmitter<GmProtocolEvents> {\n private getNodeId: () => Uint8Array | null;\n private getSentEvents: () => Map<string, NostrEvent>;\n private sendEvent: (event: NostrEvent) => Promise<void>;\n private sendGm: (\n nodeId: Uint8Array,\n eventCount: number,\n recentSince: number,\n ) => Promise<void>;\n\n private backoff: Map<string, number> = new Map();\n private lastResponse: Map<string, number> = new Map();\n private log: Logger | null;\n\n BACKOFF_BASE: number;\n MAX_SHARE_EVENTS: number;\n JITTER_MIN: number;\n JITTER_MAX: number;\n\n constructor(\n deps: GmProtocolDeps,\n options: Partial<\n Pick<\n DefaultOptions,\n | \"GM_BACKOFF_BASE\"\n | \"GM_MAX_SHARE_EVENTS\"\n | \"GM_JITTER_MIN\"\n | \"GM_JITTER_MAX\"\n >\n > = {},\n logger?: Logger | null,\n ) {\n super();\n this.getNodeId = deps.getNodeId;\n this.getSentEvents = deps.getSentEvents;\n this.sendEvent = deps.sendEvent;\n this.sendGm = deps.sendGm;\n this.log = logger ?? null;\n\n this.BACKOFF_BASE = options.GM_BACKOFF_BASE ?? Defaults.GM_BACKOFF_BASE;\n this.MAX_SHARE_EVENTS = options.GM_MAX_SHARE_EVENTS ??\n Defaults.GM_MAX_SHARE_EVENTS;\n this.JITTER_MIN = options.GM_JITTER_MIN ?? Defaults.GM_JITTER_MIN;\n this.JITTER_MAX = options.GM_JITTER_MAX ?? Defaults.GM_JITTER_MAX;\n }\n\n async handlePacket(packet: Uint8Array): Promise<void> {\n const decoded = GmPacket.decode(packet);\n this.emit(\"gm:receive\", decoded, packet.length);\n\n const nodeId = this.getNodeId();\n if (!nodeId) return;\n\n const peerIdHex = bytesToHex(decoded.nodeId);\n const myIdHex = bytesToHex(nodeId);\n\n if (peerIdHex === myIdHex) return;\n\n const backoff = this.backoff.get(peerIdHex) ?? 1;\n const backoffWindow = backoff * this.BACKOFF_BASE;\n const lastTime = this.lastResponse.get(peerIdHex) ?? 0;\n const now = Date.now();\n\n if (now - lastTime < backoffWindow) {\n this.backoff.set(peerIdHex, Math.min(backoff * 2, 16));\n this.log?.debug(\n `[nostr-lora] GM from ${\n peerIdHex.substring(0, 16)\n } backed off (${backoff}x)`,\n );\n return;\n }\n\n this.backoff.set(peerIdHex, 1);\n this.lastResponse.set(peerIdHex, now);\n\n const sentEvents = this.getSentEvents();\n const toShare: NostrEvent[] = [];\n for (const event of sentEvents.values()) {\n if (event.created_at > decoded.recentSince && event.kind === 1) {\n toShare.push(event);\n if (toShare.length >= this.MAX_SHARE_EVENTS) break;\n }\n }\n\n for (const event of toShare) {\n await sleep(this.jitter());\n try {\n await this.sendEvent(event);\n this.log?.debug(\n `[nostr-lora] GM auto-share: sent event ${\n event.id.substring(0, 16)\n }`,\n );\n } catch (err) {\n this.log?.warn(\"[nostr-lora] GM auto-share failed:\", err);\n }\n }\n\n if (Math.random() < 0.5) {\n await sleep(this.jitter());\n const eventCount = sentEvents.size;\n const oldest = this.oldestTimestamp(sentEvents);\n await this.sendGm(nodeId, eventCount, oldest);\n this.log?.debug(\n `[nostr-lora] GM auto-reply sent to ${\n peerIdHex.substring(0, 16)\n }`,\n );\n }\n }\n\n private jitter(): number {\n return this.JITTER_MIN +\n Math.random() * (this.JITTER_MAX - this.JITTER_MIN);\n }\n\n private oldestTimestamp(sentEvents: Map<string, NostrEvent>): number {\n let oldest = Math.floor(Date.now() / 1000);\n for (const event of sentEvents.values()) {\n if (event.created_at < oldest) oldest = event.created_at;\n }\n return oldest;\n }\n}\n","import {\n DataPacket,\n GmPacket,\n Limits,\n PacketType,\n parsePacketType,\n RequestPacket,\n} from \"./protocol.js\";\nimport type { DecodedDataChunk, DecodedGmPacket } from \"./protocol.js\";\nimport { deserializeEvent, serializeEvent } from \"./event-codec.js\";\nimport type { NostrEvent } from \"./event-codec.js\";\nimport { Assembler } from \"./assembler.js\";\nimport { SendQueue } from \"./send-queue.js\";\nimport type { QueueSnapshot } from \"./send-queue.js\";\nimport { GmProtocol } from \"./gm-protocol.js\";\nimport { bytesToHex, EventEmitter, hexToBytes } from \"./utils.js\";\nimport Defaults, { type DefaultOptions } from \"./constants.js\";\nimport type { ConnectionManager } from \"./connection-manager.js\";\nimport type { Logger } from \"./utils.js\";\n\nexport type { ConnectionManager };\nexport type { ConnectionManagerEvents } from \"./connection-manager.js\";\n\n// ── Interfaces ────────────────────────────────────────────────────────────────\n\nexport interface TransportOptions {\n nodeId?: Uint8Array;\n /** Diagnostic logger. Defaults to `null` (silent). */\n logger?: Logger | null;\n // Timing\n interPacketDelay?: number;\n interPacketJitter?: number;\n // GM protocol\n gmBackoffBase?: number;\n gmMaxShareEvents?: number;\n gmJitterMin?: number;\n gmJitterMax?: number;\n // Transport\n requestInactivity?: number;\n requestMaxRetries?: number;\n sentChunksTtl?: number;\n dedupTimeout?: number;\n eventTimeout?: number;\n initialTtl?: number;\n}\n\nexport interface PacketReceiveInfo {\n snr: number;\n rssi: number;\n size: number;\n}\n\nexport interface TimingOptions {\n delay?: number;\n jitter?: number;\n}\n\ninterface TimerState {\n timer: ReturnType<typeof setTimeout>;\n requestCount: number;\n}\n\ninterface SentChunkEntry {\n packets: Uint8Array[];\n sentAt: number;\n}\n\n// ── Event map ─────────────────────────────────────────────────────────────────\n\nexport type LoRaTransportEvents = {\n \"event:receive\": [event: NostrEvent];\n \"event:send\": [event: NostrEvent];\n \"chunk:receive\": [decoded: DecodedDataChunk, byteLength: number];\n \"gm:receive\": [decoded: DecodedGmPacket, byteLength: number];\n \"packet:receive\": [info: PacketReceiveInfo];\n \"packet:send\": [\n decoded: DecodedDataChunk | DecodedGmPacket,\n byteLength: number,\n ];\n \"request:send\": [\n eventIdHex: string,\n missingChunks: number[],\n attempt: number,\n ];\n \"request:receive\": [prefixHex: string, missingChunks: number[]];\n \"queue:update\": [snapshot: QueueSnapshot[]];\n \"connect\": [portLabel: string];\n \"disconnect\": [];\n \"error\": [err: Error];\n};\n\n// ── LoRaTransport ─────────────────────────────────────────────────────────────\n\nexport class LoRaTransport extends EventEmitter<LoRaTransportEvents> {\n connection: ConnectionManager;\n nodeId: Uint8Array | null;\n\n private log: Logger | null;\n\n private eventTimeout: number;\n private dedupTimeout: number;\n private initialTtl: number;\n private requestInactivity: number;\n private requestMaxRetries: number;\n private sentChunksTtl: number;\n\n private assembler: Assembler;\n private queue: SendQueue;\n private gm: GmProtocol;\n private recentEventIds: Map<string, number> = new Map();\n private inactivityTimers: Map<string, TimerState> = new Map();\n private pendingTimers: Map<string, TimerState> = new Map();\n private sentEvents: Map<string, NostrEvent> = new Map();\n private sentChunks: Map<string, SentChunkEntry> = new Map();\n\n constructor(connection: ConnectionManager, options: TransportOptions = {}) {\n super();\n this.connection = connection;\n this.nodeId = options.nodeId ?? null;\n this.log = options.logger ?? null;\n\n this.eventTimeout = options.eventTimeout ?? Defaults.EVENT_TIMEOUT;\n this.dedupTimeout = options.dedupTimeout ?? Defaults.DEDUP_TIMEOUT;\n this.initialTtl = options.initialTtl ?? Defaults.INITIAL_TTL;\n this.requestInactivity = options.requestInactivity ??\n Defaults.REQUEST_INACTIVITY;\n this.requestMaxRetries = options.requestMaxRetries ??\n Defaults.REQUEST_MAX_RETRIES;\n this.sentChunksTtl = options.sentChunksTtl ?? Defaults.SENT_CHUNKS_TTL;\n\n this.assembler = new Assembler();\n\n this.queue = new SendQueue(\n async (packet) => {\n await this.connection.sendRawData(packet);\n },\n {\n INTER_PACKET_DELAY: options.interPacketDelay ??\n Defaults.INTER_PACKET_DELAY,\n INTER_PACKET_JITTER: options.interPacketJitter ??\n Defaults.INTER_PACKET_JITTER,\n },\n this.log,\n );\n\n this.queue.on(\"packet:send\", (pkt, item) => {\n if (item.type === \"data\") {\n this.emit(\"packet:send\", DataPacket.decode(pkt), pkt.length);\n }\n });\n this.queue.on(\n \"queue:update\",\n (snapshot) => this.emit(\"queue:update\", snapshot),\n );\n this.queue.on(\"error\", (err) => this.emit(\"error\", err));\n\n this.gm = new GmProtocol(\n {\n getNodeId: () => this.nodeId,\n getSentEvents: () => this.sentEvents,\n sendEvent: (e) => this.sendEvent(e),\n sendGm: (id, count, since) => this.sendGm(id, count, since),\n },\n {\n GM_BACKOFF_BASE: options.gmBackoffBase ??\n Defaults.GM_BACKOFF_BASE,\n GM_MAX_SHARE_EVENTS: options.gmMaxShareEvents ??\n Defaults.GM_MAX_SHARE_EVENTS,\n GM_JITTER_MIN: options.gmJitterMin ?? Defaults.GM_JITTER_MIN,\n GM_JITTER_MAX: options.gmJitterMax ?? Defaults.GM_JITTER_MAX,\n },\n this.log,\n );\n this.gm.on(\n \"gm:receive\",\n (decoded, byteLength) =>\n this.emit(\"gm:receive\", decoded, byteLength),\n );\n }\n\n // ── Lifecycle ─────────────────────────────────────────────────────────────\n\n /**\n * Open the connection and wire up data/lifecycle events.\n * This triggers the browser port picker for `SerialConnectionManager`.\n */\n async begin(): Promise<void> {\n this.connection.on(\"data\", (raw) => {\n this.handleRawDataPacket(raw);\n });\n this.connection.on(\"connect\", (portLabel) => {\n this.emit(\"connect\", portLabel);\n });\n this.connection.on(\"disconnect\", () => {\n this.emit(\"disconnect\");\n });\n this.connection.on(\"error\", (err) => {\n this.emit(\"error\", err);\n });\n await this.connection.open();\n }\n\n /** Close the connection. */\n async end(): Promise<void> {\n await this.connection.close();\n }\n\n // ── Public API ────────────────────────────────────────────────────────────\n\n async sendEvent(event: NostrEvent): Promise<void> {\n try {\n const { compressed } = await serializeEvent(event);\n const eventId = hexToBytes(event.id);\n\n const packets = DataPacket.createFromCompressed(\n eventId,\n event.kind,\n compressed,\n this.initialTtl,\n );\n\n const label = `EVENT kind:${event.kind} id:${\n event.id.substring(0, 8)\n }… (${packets.length} pkt)`;\n\n this.sentChunks.set(event.id, {\n packets: packets.slice(),\n sentAt: Date.now(),\n });\n this.pruneSentChunks();\n\n this.queue.enqueue(packets, label, \"data\", {\n _onSent: async () => {\n this.sentEvents.set(event.id, event);\n this.emit(\"event:send\", event);\n },\n });\n } catch (error) {\n this.emit(\n \"error\",\n error instanceof Error ? error : new Error(String(error)),\n );\n throw error;\n }\n }\n\n async sendGm(\n nodeId: Uint8Array,\n eventCount: number = 0,\n recentSince: number = 0,\n ): Promise<void> {\n const timestamp = Math.floor(Date.now() / 1000);\n const packet = GmPacket.create(\n nodeId,\n eventCount,\n recentSince,\n timestamp,\n );\n const label = `GM node:${\n bytesToHex(nodeId).substring(0, 16)\n }… events:${eventCount}`;\n\n this.queue.enqueue([packet], label, \"gm\", {\n _onSent: async () => {\n this.emit(\n \"packet:send\",\n GmPacket.decode(packet),\n packet.length,\n );\n },\n });\n }\n\n /**\n * Feed a raw radio packet into the transport.\n * Only needed when implementing a custom `ConnectionManager` that doesn't\n * use the event interface — `begin()` wires this automatically otherwise.\n */\n async handleRawDataPacket(\n data: { data: ArrayBuffer | Uint8Array; snr: number; rssi: number },\n ): Promise<void> {\n try {\n const packet = new Uint8Array(\n data.data instanceof ArrayBuffer ? data.data : data.data,\n );\n\n this.emit(\"packet:receive\", {\n snr: data.snr || 0,\n rssi: data.rssi || 0,\n size: packet.length,\n });\n\n switch (parsePacketType(packet)) {\n case PacketType.DATA:\n await this.handleDataPacket(packet);\n break;\n case PacketType.REQUEST:\n await this.handleRequestPacket(packet);\n break;\n case PacketType.GM:\n await this.handleGmPacket(packet);\n break;\n default:\n this.log?.warn(\n \"[nostr-lora] unknown packet type, flags=0x\" +\n packet[0]!.toString(16),\n );\n }\n } catch (error) {\n this.emit(\n \"error\",\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n }\n\n /** Update the inter-packet timing on the underlying send queue. */\n setTimingOptions({ delay, jitter }: TimingOptions = {}): void {\n if (delay !== undefined) this.queue.INTER_PACKET_DELAY = delay;\n if (jitter !== undefined) this.queue.INTER_PACKET_JITTER = jitter;\n }\n\n /** Number of events sent by this node (available for GM announcements). */\n get sentEventCount(): number {\n return this.sentEvents.size;\n }\n\n // ── Receive: DATA ─────────────────────────────────────────────────────────\n\n private async handleDataPacket(packet: Uint8Array): Promise<void> {\n const decoded = DataPacket.decode(packet);\n this.emit(\"chunk:receive\", decoded, packet.length);\n\n if (decoded.type === \"first\") {\n const eventIdHex = bytesToHex(decoded.eventId);\n const result = this.assembler.handleFirst(\n decoded,\n this.recentEventIds,\n );\n\n if (result.action === \"complete\") {\n this.clearInactivityTimer(eventIdHex);\n this.clearPendingTimer(\n bytesToHex(\n decoded.eventId.slice(0, Limits.EVENT_ID_PREFIX_LEN),\n ),\n );\n await this.completeSession(result.session);\n } else if (result.action === \"started\") {\n this.resetInactivityTimer(eventIdHex);\n this.clearPendingTimer(\n bytesToHex(\n decoded.eventId.slice(0, Limits.EVENT_ID_PREFIX_LEN),\n ),\n );\n }\n } else {\n const result = this.assembler.handleSubsequent(\n decoded,\n this.eventTimeout,\n );\n\n if (result.action === \"complete\") {\n this.clearInactivityTimer(bytesToHex(result.session.eventId));\n await this.completeSession(result.session);\n } else if (result.action === \"progress\") {\n this.resetInactivityTimer(result.eventIdHex);\n } else if (result.action === \"pending\") {\n this.resetPendingTimer(result.prefix);\n }\n }\n }\n\n private async completeSession(\n session: {\n eventId: Uint8Array;\n chunks: Array<Uint8Array | undefined>;\n checksum: number;\n },\n ): Promise<void> {\n const eventIdHex = bytesToHex(session.eventId);\n try {\n const event = await deserializeEvent(\n session.chunks.filter((c): c is Uint8Array => c !== undefined),\n session.checksum,\n eventIdHex,\n );\n this.recentEventIds.set(eventIdHex, Date.now());\n this.cleanupDeduplication();\n this.emit(\"event:receive\", event);\n } catch (error) {\n this.emit(\n \"error\",\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n }\n\n // ── Receive: REQUEST ──────────────────────────────────────────────────────\n\n private async handleRequestPacket(packet: Uint8Array): Promise<void> {\n const decoded = RequestPacket.decode(packet);\n const prefixHex = bytesToHex(decoded.eventIdPrefix);\n\n this.emit(\"request:receive\", prefixHex, decoded.missingChunks);\n\n let matchedId: string | null = null;\n let stored: SentChunkEntry | null = null;\n for (const [id, entry] of this.sentChunks.entries()) {\n if (id.startsWith(prefixHex)) {\n matchedId = id;\n stored = entry;\n break;\n }\n }\n\n if (!stored || !matchedId) return;\n\n const retransmit = decoded.missingChunks\n .filter((idx) => idx < stored!.packets.length)\n .map((idx) => stored!.packets[idx]!);\n\n if (retransmit.length === 0) return;\n\n const label = `RETX id:${matchedId.substring(0, 8)}… chunks:[${\n decoded.missingChunks.join(\",\")\n }]`;\n this.queue.enqueue(retransmit, label, \"data\");\n }\n\n // ── Receive: GM ───────────────────────────────────────────────────────────\n\n private async handleGmPacket(packet: Uint8Array): Promise<void> {\n await this.gm.handlePacket(packet);\n }\n\n // ── Inactivity timers: session has chunk 0 ────────────────────────────────\n\n private resetInactivityTimer(eventIdHex: string): void {\n const existing = this.inactivityTimers.get(eventIdHex);\n const requestCount = existing?.requestCount ?? 0;\n if (existing?.timer) clearTimeout(existing.timer);\n\n const timer = setTimeout(\n () => this.onChunkInactivity(eventIdHex),\n this.requestInactivity,\n );\n this.inactivityTimers.set(eventIdHex, { timer, requestCount });\n }\n\n private clearInactivityTimer(eventIdHex: string): void {\n const existing = this.inactivityTimers.get(eventIdHex);\n if (existing?.timer) clearTimeout(existing.timer);\n this.inactivityTimers.delete(eventIdHex);\n }\n\n private async onChunkInactivity(eventIdHex: string): Promise<void> {\n const timerState = this.inactivityTimers.get(eventIdHex);\n if (!timerState) return;\n\n const missingChunks = this.assembler.missingChunks(eventIdHex);\n if (!missingChunks?.length) {\n this.inactivityTimers.delete(eventIdHex);\n return;\n }\n\n timerState.requestCount++;\n\n if (timerState.requestCount > this.requestMaxRetries) {\n this.log?.warn(\n `[nostr-lora] giving up on ${\n eventIdHex.substring(0, 16)\n } after ${this.requestMaxRetries} REQUESTs`,\n );\n this.clearInactivityTimer(eventIdHex);\n this.assembler.abandonSession(eventIdHex);\n return;\n }\n\n const eventId = this.assembler.getSessionEventId(eventIdHex) ??\n hexToBytes(eventIdHex);\n const prefix = eventId.slice(0, Limits.EVENT_ID_PREFIX_LEN);\n const label = `REQ id:${eventIdHex.substring(0, 8)}… missing:[${\n missingChunks.join(\",\")\n }] (#${timerState.requestCount})`;\n\n this.queue.enqueue(\n [RequestPacket.create(prefix, missingChunks)],\n label,\n \"request\",\n );\n this.emit(\n \"request:send\",\n eventIdHex,\n missingChunks,\n timerState.requestCount,\n );\n\n timerState.timer = setTimeout(\n () => this.onChunkInactivity(eventIdHex),\n this.requestInactivity,\n );\n }\n\n // ── Inactivity timers: chunk 0 still missing ──────────────────────────────\n\n private resetPendingTimer(eventIdPrefix: Uint8Array): void {\n const prefixHex = bytesToHex(eventIdPrefix);\n const existing = this.pendingTimers.get(prefixHex);\n const requestCount = existing?.requestCount ?? 0;\n if (existing?.timer) clearTimeout(existing.timer);\n\n const timer = setTimeout(\n () => this.onPendingInactivity(prefixHex, eventIdPrefix),\n this.requestInactivity,\n );\n this.pendingTimers.set(prefixHex, { timer, requestCount });\n }\n\n private clearPendingTimer(prefixHex: string): void {\n const existing = this.pendingTimers.get(prefixHex);\n if (existing?.timer) clearTimeout(existing.timer);\n this.pendingTimers.delete(prefixHex);\n }\n\n private async onPendingInactivity(\n prefixHex: string,\n eventIdPrefix: Uint8Array,\n ): Promise<void> {\n const timerState = this.pendingTimers.get(prefixHex);\n if (!timerState) return;\n\n const missingChunks = this.assembler.missingChunksForPending(prefixHex);\n if (!missingChunks) {\n this.pendingTimers.delete(prefixHex);\n return;\n }\n\n timerState.requestCount++;\n\n if (timerState.requestCount > this.requestMaxRetries) {\n this.log?.warn(\n `[nostr-lora] giving up on pending prefix ${prefixHex} after ${this.requestMaxRetries} REQUESTs`,\n );\n this.assembler.abandonPending(prefixHex);\n this.pendingTimers.delete(prefixHex);\n return;\n }\n\n const label = `REQ prefix:${prefixHex.substring(0, 8)}… missing:[${\n missingChunks.join(\",\")\n }] (#${timerState.requestCount})`;\n this.queue.enqueue(\n [RequestPacket.create(eventIdPrefix, missingChunks)],\n label,\n \"request\",\n );\n this.emit(\n \"request:send\",\n prefixHex,\n missingChunks,\n timerState.requestCount,\n );\n\n timerState.timer = setTimeout(\n () => this.onPendingInactivity(prefixHex, eventIdPrefix),\n this.requestInactivity,\n );\n }\n\n // ── Maintenance ───────────────────────────────────────────────────────────\n\n private pruneSentChunks(): void {\n const cutoff = Date.now() - this.sentChunksTtl;\n for (const [id, entry] of this.sentChunks.entries()) {\n if (entry.sentAt < cutoff) this.sentChunks.delete(id);\n }\n }\n\n private cleanupDeduplication(): void {\n const now = Date.now();\n for (const [id, ts] of this.recentEventIds.entries()) {\n if (now - ts > this.dedupTimeout) this.recentEventIds.delete(id);\n }\n }\n}\n","import Constants from \"@liamcottle/meshcore.js/src/constants.js\";\nimport WebSerialConnection from \"@liamcottle/meshcore.js/src/connection/web_serial_connection.js\";\nimport { EventEmitter } from \"./utils.js\";\nimport type {\n ConnectionManager,\n ConnectionManagerEvents,\n} from \"./connection-manager.js\";\nimport type { Logger } from \"./utils.js\";\n\nexport interface DeviceInfo {\n name: string;\n firmwareVersion: string;\n}\n\nexport interface RadioConfig {\n frequency: number;\n bandwidth: number;\n spreadingFactor: number;\n codingRate: number;\n power?: number;\n}\n\n/**\n * Web Serial connection to a MeshCore LoRa device.\n * Implements `ConnectionManager` so it can be passed directly to `LoRaTransport`.\n *\n * Key constraint: WebSerialConnection.write() has no internal queue — it calls\n * getWriter()/releaseLock() per write. Two overlapping writes crash with\n * \"WritableStream is already locked\". We must never issue a write while another\n * is in flight. `LoRaTransport`'s `SendQueue` ensures this.\n */\nexport class SerialConnectionManager\n extends EventEmitter<\n ConnectionManagerEvents & { \"deviceInfo\": [info: DeviceInfo] }\n >\n implements ConnectionManager {\n private connection: InstanceType<typeof WebSerialConnection> | null = null;\n private label: string | null = null;\n private log: Logger | null;\n\n constructor(logger?: Logger | null) {\n super();\n this.log = logger ?? null;\n }\n\n // ── ConnectionManager interface ───────────────────────────────────────────\n\n async open(): Promise<void> {\n if (!this.isWebSerialSupported()) {\n throw new Error(\n \"Web Serial is not supported in this browser. Use Chrome or Edge.\",\n );\n }\n\n try {\n this.connection = await WebSerialConnection.open();\n\n if (!this.connection) {\n throw new Error(\"No serial port selected\");\n }\n\n this.label = this.buildPortLabel(this.connection.serialPort);\n this.setupHandlers();\n } catch (error) {\n this.connection = null;\n const err = error instanceof Error\n ? error\n : new Error(String(error));\n this.emit(\"error\", err);\n throw err;\n }\n }\n\n async close(): Promise<void> {\n if (this.connection) {\n try {\n await this.connection.close();\n } catch (e) {\n this.log?.warn(\"[nostr-lora] Error closing serial port:\", e);\n }\n this.connection = null;\n this.label = null;\n this.emit(\"disconnect\");\n }\n }\n\n async sendRawData(data: Uint8Array): Promise<void> {\n if (!this.connection) {\n throw new Error(\"Not connected to device\");\n }\n try {\n const path = new Uint8Array(0);\n await this.connection.sendCommandSendRawData(path, data);\n } catch (error) {\n const err = error instanceof Error\n ? error\n : new Error(String(error));\n this.emit(\"error\", err);\n throw err;\n }\n }\n\n // ── Extra capabilities ────────────────────────────────────────────────────\n\n /** Configure the LoRa radio parameters. Must be called after `open()`. */\n async setRadioConfig(config: RadioConfig): Promise<void> {\n if (!this.connection) throw new Error(\"Not connected to device\");\n await this.connection.setRadioParams(\n config.frequency,\n config.bandwidth,\n config.spreadingFactor,\n config.codingRate,\n );\n if (config.power !== undefined) {\n await this.connection.setTxPower(config.power);\n }\n }\n\n isConnected(): boolean {\n return this.connection !== null;\n }\n\n isWebSerialSupported(): boolean {\n return typeof navigator !== \"undefined\" &&\n typeof (navigator as Navigator & { serial?: unknown }).serial !==\n \"undefined\";\n }\n\n /** The USB VID:PID or \"Serial device\" label for the connected port. */\n get portLabel(): string | null {\n return this.label;\n }\n\n // ── Private ───────────────────────────────────────────────────────────────\n\n private buildPortLabel(\n serialPort: {\n getInfo(): { usbVendorId?: number; usbProductId?: number };\n },\n ): string {\n try {\n const info = serialPort.getInfo();\n if (\n info.usbVendorId !== undefined &&\n info.usbProductId !== undefined\n ) {\n const vid = info.usbVendorId.toString(16).padStart(4, \"0\")\n .toUpperCase();\n const pid = info.usbProductId.toString(16).padStart(4, \"0\")\n .toUpperCase();\n return `USB ${vid}:${pid}`;\n }\n } catch (_) { /* ignore */ }\n return \"Serial device\";\n }\n\n private setupHandlers(): void {\n if (!this.connection) return;\n\n this.connection.once(\n Constants.ResponseCodes.DeviceInfo,\n (data: { manufacturerModel?: string; firmwareVer?: number }) => {\n const info: DeviceInfo = {\n name: data.manufacturerModel ?? \"Unknown\",\n firmwareVersion: data.firmwareVer != null\n ? String(data.firmwareVer)\n : \"Unknown\",\n };\n this.emit(\"deviceInfo\", info);\n },\n );\n\n this.connection.on(\"connected\", async () => {\n try {\n await this.connection!.sendCommandAppStart();\n } catch (e) {\n this.log?.warn(\"[nostr-lora] AppStart failed:\", e);\n }\n\n try {\n const now = Math.floor(Date.now() / 1000);\n await this.connection!.sendCommandSetDeviceTime(now);\n } catch (e) {\n this.log?.warn(\"[nostr-lora] Time sync failed:\", e);\n }\n\n this.emit(\"connect\", this.label!);\n });\n\n this.connection.on(\"disconnected\", () => {\n this.log?.log(\"[nostr-lora] Serial device disconnected\");\n this.connection = null;\n this.label = null;\n this.emit(\"disconnect\");\n });\n\n this.connection.on(\n Constants.PushCodes.RawData,\n (\n data: {\n payload: Uint8Array;\n lastSnr: number;\n lastRssi: number;\n },\n ) => {\n this.emit(\"data\", {\n data: data.payload,\n snr: data.lastSnr,\n rssi: data.lastRssi,\n });\n },\n );\n }\n}\n"],"names":["PacketType","Limits","CRC32","i","c","k","data","crc","__publicField","Compression","compressedStream","blob","decompressedStream","DataPacket","eventId","eventKind","compressed","ttl","checksum","remaining","numChunks","packets","offset","isLast","maxPayload","payloadSize","payload","totalChunks","packet","o","eventIdPrefix","chunkIndex","realChunkIndex","RequestPacket","missingChunks","bitmap","idx","GmPacket","nodeId","eventCount","recentSince","timestamp","parsePacketType","_encoder","_decoder","serializeEvent","event","canonical","deserializeEvent","chunkPayloads","expectedChecksum","eventIdHex","totalLength","sum","chunk","computedChecksum","decompressed","arr","verifyEvent","hexToBytes","hex","bytes","bytesToHex","b","matchesPrefix","id","prefix","sleep","ms","resolve","EventEmitter","fn","_a","args","err","AssemblySession","first","index","missing","Assembler","decoded","seenIds","session","eventTimeout","prefixHex","buffered","p","haveIndices","lastChunk","maxIndex","stillPending","pending","cutoff","Defaults","SendQueue","sendFn","options","logger","label","type","meta","item","pkt","GmProtocol","deps","peerIdHex","myIdHex","backoff","backoffWindow","lastTime","now","sentEvents","toShare","_b","_c","oldest","_d","LoRaTransport","connection","snapshot","e","count","since","byteLength","raw","portLabel","error","delay","jitter","result","matchedId","stored","entry","retransmit","existing","requestCount","timer","timerState","ts","SerialConnectionManager","WebSerialConnection","path","config","serialPort","info","vid","pid","Constants"],"mappings":"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"}
|