Address PR review feedback: literal types, doc comments, typed event overloads

- Change TxtType from {number} to {0 | 1 | 2} literal union
- Add @type {const} to all Constants static properties for literal + readonly output
- Add JSDoc descriptions to all major typedefs for IDE hover tooltips
- Add typed event overloads to Connection.on() for all response/push events
- Fix sendCommandSendRawData path param from any[] to number[]
- Fix sendCommandSendRawData call site wrapping Uint8Array in unnecessary array
- Add missing event payload types (AdvertPush, PathUpdatedPush, OkResponse, etc.)
This commit is contained in:
Manuel Bahamóndez-Honores 2026-02-19 06:13:56 -03:00
parent 452f5c7e58
commit 4afb51ef7b
3 changed files with 374 additions and 27 deletions

View file

@ -32,10 +32,265 @@ import RandomUtils from "../random_utils.js";
* @typedef {import("../types.js").TelemetryResponsePush} TelemetryResponsePush
* @typedef {import("../types.js").BinaryResponsePush} BinaryResponsePush
* @typedef {import("../types.js").NewAdvertPush} NewAdvertPush
* @typedef {import("../types.js").AdvertPush} AdvertPush
* @typedef {import("../types.js").PathUpdatedPush} PathUpdatedPush
* @typedef {import("../types.js").MsgWaitingPush} MsgWaitingPush
* @typedef {import("../types.js").ContactsStartResponse} ContactsStartResponse
* @typedef {import("../types.js").EndOfContactsResponse} EndOfContactsResponse
* @typedef {import("../types.js").CurrTimeResponse} CurrTimeResponse
* @typedef {import("../types.js").NoMoreMessagesResponse} NoMoreMessagesResponse
* @typedef {import("../types.js").OkResponse} OkResponse
* @typedef {import("../types.js").ErrResponse} ErrResponse
* @typedef {import("../types.js").DisabledResponse} DisabledResponse
* @typedef {import("../types.js").SignStartResponse} SignStartResponse
* @typedef {import("../types.js").SignatureResponse} SignatureResponse
*/
class Connection extends EventEmitter {
/**
* @overload
* @param {"connected"} event
* @param {() => void} callback
* @returns {void}
*/
/**
* @overload
* @param {"disconnected"} event
* @param {() => void} callback
* @returns {void}
*/
/**
* @overload
* @param {"rx"} event
* @param {(frame: Uint8Array | number[]) => void} callback
* @returns {void}
*/
/**
* ResponseCodes.Ok
* @overload
* @param {0} event
* @param {(data: OkResponse) => void} callback
* @returns {void}
*/
/**
* ResponseCodes.Err
* @overload
* @param {1} event
* @param {(data: ErrResponse) => void} callback
* @returns {void}
*/
/**
* ResponseCodes.ContactsStart
* @overload
* @param {2} event
* @param {(data: ContactsStartResponse) => void} callback
* @returns {void}
*/
/**
* ResponseCodes.Contact
* @overload
* @param {3} event
* @param {(data: Contact) => void} callback
* @returns {void}
*/
/**
* ResponseCodes.EndOfContacts
* @overload
* @param {4} event
* @param {(data: EndOfContactsResponse) => void} callback
* @returns {void}
*/
/**
* ResponseCodes.SelfInfo
* @overload
* @param {5} event
* @param {(data: SelfInfo) => void} callback
* @returns {void}
*/
/**
* ResponseCodes.Sent
* @overload
* @param {6} event
* @param {(data: SentResponse) => void} callback
* @returns {void}
*/
/**
* ResponseCodes.ContactMsgRecv
* @overload
* @param {7} event
* @param {(data: ContactMessage) => void} callback
* @returns {void}
*/
/**
* ResponseCodes.ChannelMsgRecv
* @overload
* @param {8} event
* @param {(data: ChannelMessage) => void} callback
* @returns {void}
*/
/**
* ResponseCodes.CurrTime
* @overload
* @param {9} event
* @param {(data: CurrTimeResponse) => void} callback
* @returns {void}
*/
/**
* ResponseCodes.NoMoreMessages
* @overload
* @param {10} event
* @param {(data: NoMoreMessagesResponse) => void} callback
* @returns {void}
*/
/**
* ResponseCodes.ExportContact
* @overload
* @param {11} event
* @param {(data: ExportContactResponse) => void} callback
* @returns {void}
*/
/**
* ResponseCodes.BatteryVoltage
* @overload
* @param {12} event
* @param {(data: BatteryVoltageResponse) => void} callback
* @returns {void}
*/
/**
* ResponseCodes.DeviceInfo
* @overload
* @param {13} event
* @param {(data: DeviceInfo) => void} callback
* @returns {void}
*/
/**
* ResponseCodes.PrivateKey
* @overload
* @param {14} event
* @param {(data: PrivateKeyResponse) => void} callback
* @returns {void}
*/
/**
* ResponseCodes.Disabled
* @overload
* @param {15} event
* @param {(data: DisabledResponse) => void} callback
* @returns {void}
*/
/**
* ResponseCodes.ChannelInfo
* @overload
* @param {18} event
* @param {(data: ChannelInfo) => void} callback
* @returns {void}
*/
/**
* ResponseCodes.SignStart
* @overload
* @param {19} event
* @param {(data: SignStartResponse) => void} callback
* @returns {void}
*/
/**
* ResponseCodes.Signature
* @overload
* @param {20} event
* @param {(data: SignatureResponse) => void} callback
* @returns {void}
*/
/**
* PushCodes.Advert
* @overload
* @param {0x80} event
* @param {(data: AdvertPush) => void} callback
* @returns {void}
*/
/**
* PushCodes.PathUpdated
* @overload
* @param {0x81} event
* @param {(data: PathUpdatedPush) => void} callback
* @returns {void}
*/
/**
* PushCodes.SendConfirmed
* @overload
* @param {0x82} event
* @param {(data: SendConfirmedPush) => void} callback
* @returns {void}
*/
/**
* PushCodes.MsgWaiting
* @overload
* @param {0x83} event
* @param {(data: MsgWaitingPush) => void} callback
* @returns {void}
*/
/**
* PushCodes.RawData
* @overload
* @param {0x84} event
* @param {(data: RawDataPush) => void} callback
* @returns {void}
*/
/**
* PushCodes.LoginSuccess
* @overload
* @param {0x85} event
* @param {(data: LoginSuccessPush) => void} callback
* @returns {void}
*/
/**
* PushCodes.StatusResponse
* @overload
* @param {0x87} event
* @param {(data: StatusResponsePush) => void} callback
* @returns {void}
*/
/**
* PushCodes.LogRxData
* @overload
* @param {0x88} event
* @param {(data: LogRxDataPush) => void} callback
* @returns {void}
*/
/**
* PushCodes.TraceData
* @overload
* @param {0x89} event
* @param {(data: TraceDataResult) => void} callback
* @returns {void}
*/
/**
* PushCodes.NewAdvert
* @overload
* @param {0x8A} event
* @param {(data: NewAdvertPush) => void} callback
* @returns {void}
*/
/**
* PushCodes.TelemetryResponse
* @overload
* @param {0x8B} event
* @param {(data: TelemetryResponsePush) => void} callback
* @returns {void}
*/
/**
* PushCodes.BinaryResponse
* @overload
* @param {0x8C} event
* @param {(data: BinaryResponsePush) => void} callback
* @returns {void}
*/
/**
* @param {string | number} event
* @param {Function} callback
*/
on(event, callback) {
super.on(event, callback);
}
async onConnected() {
// tell device what protocol version we support
@ -345,7 +600,7 @@ class Connection extends EventEmitter {
}
/**
* @param {Uint8Array | any[]} path
* @param {Uint8Array | number[]} path
* @param {Uint8Array} rawData
* @returns {Promise<void>}
*/
@ -2178,11 +2433,9 @@ class Connection extends EventEmitter {
}
// send raw data to repeater, for it to repeat zero hop
await this.sendCommandSendRawData([
// we set the repeater we want to ping as the path
// it should repeat our packet, and we can listen for it
contactPublicKey.subarray(0, 1),
], rawBytes);
// we set the repeater we want to ping as the path
// it should repeat our packet, and we can listen for it
await this.sendCommandSendRawData(contactPublicKey.subarray(0, 1), rawBytes);
} catch(e) {
reject(e);

View file

@ -3,18 +3,18 @@ class Constants {
static SupportedCompanionProtocolVersion = 1;
static SerialFrameTypes = {
static SerialFrameTypes = /** @type {const} */ ({
Incoming: 0x3e, // ">"
Outgoing: 0x3c, // "<"
}
})
static Ble = {
static Ble = /** @type {const} */ ({
ServiceUuid: "6E400001-B5A3-F393-E0A9-E50E24DCCA9E",
CharacteristicUuidRx: "6E400002-B5A3-F393-E0A9-E50E24DCCA9E",
CharacteristicUuidTx: "6E400003-B5A3-F393-E0A9-E50E24DCCA9E",
}
})
static CommandCodes = {
static CommandCodes = /** @type {const} */ ({
AppStart: 1,
SendTxtMsg: 2,
SendChannelTxtMsg: 3,
@ -53,9 +53,9 @@ class Constants {
SendTelemetryReq: 39,
SendBinaryReq: 50,
}
})
static ResponseCodes = {
static ResponseCodes = /** @type {const} */ ({
Ok: 0, // todo
Err: 1, // todo
ContactsStart: 2,
@ -75,9 +75,9 @@ class Constants {
ChannelInfo: 18,
SignStart: 19,
Signature: 20,
}
})
static PushCodes = {
static PushCodes = /** @type {const} */ ({
Advert: 0x80, // when companion is set to auto add contacts
PathUpdated: 0x81,
SendConfirmed: 0x82,
@ -91,41 +91,41 @@ class Constants {
NewAdvert: 0x8A, // when companion is set to manually add contacts
TelemetryResponse: 0x8B,
BinaryResponse: 0x8C,
}
})
static ErrorCodes = {
static ErrorCodes = /** @type {const} */ ({
UnsupportedCmd: 1,
NotFound: 2,
TableFull: 3,
BadState: 4,
FileIoError: 5,
IllegalArg: 6,
}
})
static AdvType = {
static AdvType = /** @type {const} */ ({
None: 0,
Chat: 1,
Repeater: 2,
Room: 3,
}
})
static SelfAdvertTypes = {
static SelfAdvertTypes = /** @type {const} */ ({
ZeroHop: 0,
Flood: 1,
}
})
static TxtTypes = {
static TxtTypes = /** @type {const} */ ({
Plain: 0,
CliData: 1,
SignedPlain: 2,
}
})
static BinaryRequestTypes = {
static BinaryRequestTypes = /** @type {const} */ ({
GetTelemetryData: 0x03, // #define REQ_TYPE_GET_TELEMETRY_DATA 0x03
GetAvgMinMax: 0x04, // #define REQ_TYPE_GET_AVG_MIN_MAX 0x04
GetAccessList: 0x05, // #define REQ_TYPE_GET_ACCESS_LIST 0x05
GetNeighbours: 0x06, // #define REQ_TYPE_GET_NEIGHBOURS 0x06
}
})
}

View file

@ -11,10 +11,11 @@
*/
/**
* @typedef {number} TxtType Text message type: 0=Plain, 1=CliData, 2=SignedPlain
* @typedef {0 | 1 | 2} TxtType Text message type: 0=Plain, 1=CliData, 2=SignedPlain
*/
/**
* Information about the connected MeshCore device/node.
* @typedef {object} SelfInfo
* @property {number} type
* @property {number} txPower
@ -32,6 +33,7 @@
*/
/**
* A known contact on the mesh network.
* @typedef {object} Contact
* @property {Uint8Array} publicKey
* @property {number} type
@ -46,6 +48,7 @@
*/
/**
* A direct message received from a contact.
* @typedef {object} ContactMessage
* @property {Uint8Array} pubKeyPrefix
* @property {number} pathLen
@ -55,6 +58,7 @@
*/
/**
* A message received on a channel.
* @typedef {object} ChannelMessage
* @property {number} channelIdx
* @property {number} pathLen
@ -64,6 +68,7 @@
*/
/**
* Channel configuration details.
* @typedef {object} ChannelInfo
* @property {number} channelIdx
* @property {string} name
@ -71,6 +76,7 @@
*/
/**
* Response after sending a message, containing delivery status and timeout estimate.
* @typedef {object} SentResponse
* @property {number} result
* @property {number} expectedAckCrc
@ -78,6 +84,7 @@
*/
/**
* Hardware and firmware information about the connected device.
* @typedef {object} DeviceInfo
* @property {number} firmwareVer
* @property {Uint8Array} reserved
@ -86,21 +93,25 @@
*/
/**
* Battery voltage reading from the device.
* @typedef {object} BatteryVoltageResponse
* @property {MilliVolts} batteryMilliVolts
*/
/**
* Exported contact as raw advert packet bytes.
* @typedef {object} ExportContactResponse
* @property {Uint8Array} advertPacketBytes
*/
/**
* Exported private key from the device.
* @typedef {object} PrivateKeyResponse
* @property {Uint8Array} privateKey
*/
/**
* Statistics from a repeater node (battery, traffic counters, air time, etc.).
* @typedef {object} RepeaterStats
* @property {MilliVolts} batt_milli_volts
* @property {number} curr_tx_queue_len
@ -121,12 +132,14 @@
*/
/**
* Result of syncing the next pending message from the device.
* @typedef {object} SyncMessageResult
* @property {ContactMessage} [contactMessage]
* @property {ChannelMessage} [channelMessage]
*/
/**
* A nearby node discovered via radio.
* @typedef {object} Neighbour
* @property {Uint8Array} publicKeyPrefix
* @property {number} heardSecondsAgo
@ -134,12 +147,14 @@
*/
/**
* List of nearby nodes and total count.
* @typedef {object} NeighboursResult
* @property {number} totalNeighboursCount
* @property {Neighbour[]} neighbours
*/
/**
* Trace path data received from a trace route request.
* @typedef {object} TraceDataResult
* @property {number} reserved
* @property {number} pathLen
@ -152,6 +167,7 @@
*/
/**
* Parsed advertisement data from a node's advert packet.
* @typedef {object} AdvertParsedData
* @property {"NONE" | "CHAT" | "REPEATER" | "ROOM" | null} type
* @property {number | null} lat
@ -162,6 +178,7 @@
*/
/**
* A single Cayenne LPP telemetry entry.
* @typedef {object} CayenneTelemetryEntry
* @property {number} channel
* @property {number} type
@ -169,12 +186,14 @@
*/
/**
* Push notification when a login to a repeater/room succeeds.
* @typedef {object} LoginSuccessPush
* @property {number} reserved
* @property {Uint8Array} pubKeyPrefix
*/
/**
* Push notification containing a status response from a repeater/room.
* @typedef {object} StatusResponsePush
* @property {number} reserved
* @property {Uint8Array} pubKeyPrefix
@ -182,6 +201,7 @@
*/
/**
* Push notification with raw data received from the radio.
* @typedef {object} RawDataPush
* @property {number} lastSnr
* @property {number} lastRssi
@ -190,12 +210,14 @@
*/
/**
* Push notification confirming a sent message was acknowledged.
* @typedef {object} SendConfirmedPush
* @property {number} ackCode
* @property {Milliseconds} roundTrip
*/
/**
* Push notification with logged received radio data.
* @typedef {object} LogRxDataPush
* @property {number} lastSnr
* @property {number} lastRssi
@ -203,6 +225,7 @@
*/
/**
* Push notification containing telemetry sensor data from a node.
* @typedef {object} TelemetryResponsePush
* @property {number} reserved
* @property {Uint8Array} pubKeyPrefix
@ -210,6 +233,7 @@
*/
/**
* Push notification containing a binary response from a node.
* @typedef {object} BinaryResponsePush
* @property {number} reserved
* @property {number} tag
@ -217,6 +241,7 @@
*/
/**
* Push notification when a new contact advertisement is received (manual add mode).
* @typedef {object} NewAdvertPush
* @property {Uint8Array} publicKey
* @property {number} type
@ -230,4 +255,73 @@
* @property {EpochSeconds} lastMod
*/
/**
* Push notification when a contact advertisement is auto-added.
* @typedef {object} AdvertPush
* @property {Uint8Array} publicKey
*/
/**
* Push notification when a contact's path is updated.
* @typedef {object} PathUpdatedPush
* @property {Uint8Array} publicKey
*/
/**
* Push notification indicating messages are waiting to be synced.
* @typedef {object} MsgWaitingPush
*/
/**
* Response indicating the start of a contacts list.
* @typedef {object} ContactsStartResponse
* @property {number} count
*/
/**
* Response indicating the end of a contacts list.
* @typedef {object} EndOfContactsResponse
* @property {EpochSeconds} mostRecentLastmod
*/
/**
* Response containing the current device time.
* @typedef {object} CurrTimeResponse
* @property {EpochSeconds} epochSecs
*/
/**
* Response indicating no more messages to sync.
* @typedef {object} NoMoreMessagesResponse
*/
/**
* OK response from the device.
* @typedef {object} OkResponse
*/
/**
* Error response from the device.
* @typedef {object} ErrResponse
* @property {number | null} errCode
*/
/**
* Response indicating the device/feature is disabled.
* @typedef {object} DisabledResponse
*/
/**
* Response indicating signing can start.
* @typedef {object} SignStartResponse
* @property {number} reserved
* @property {number} maxSignDataLen
*/
/**
* Response containing a cryptographic signature.
* @typedef {object} SignatureResponse
* @property {Uint8Array} signature
*/
export {};