From 2d4cb35e511581fc9800e7faa82886d2478e78b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Baham=C3=B3ndez-Honores?= Date: Wed, 18 Feb 2026 05:49:46 -0300 Subject: [PATCH] Replace hand-written .d.ts with JSDoc + tsc auto-generation - Delete hand-written index.d.ts that drifted from source - Add JSDoc type annotations to all source files - Create src/types.js with shared typedefs (SelfInfo, Contact, ContactMessage, ChannelInfo, RepeaterStats, etc.) - Add tsconfig.json for declaration generation - Add build:types script and prepublishOnly hook - Add GitHub Actions CI workflow for type checking - Use Uint8Array everywhere (no Buffer references) - Add semantic type aliases (EpochSeconds, Milliseconds, MilliVolts) - Add typed event overloads on Connection (on/once/off) - All 11 PR #15 review comments addressed --- .github/workflows/typecheck.yml | 17 + .gitignore | 1 + index.d.ts | 185 -------- package-lock.json | 35 ++ package.json | 8 +- src/advert.js | 18 + src/buffer_reader.js | 27 ++ src/buffer_utils.js | 17 + src/buffer_writer.js | 12 + src/cayenne_lpp.js | 20 +- src/connection/connection.js | 483 ++++++++++++++++++++- src/connection/nodejs_serial_connection.js | 9 +- src/connection/serial_connection.js | 18 + src/connection/tcp_connection.js | 24 +- src/connection/web_ble_connection.js | 16 + src/connection/web_serial_connection.js | 9 + src/constants.js | 1 + src/events.js | 17 + src/index.js | 2 + src/packet.js | 27 +- src/random_utils.js | 5 + src/types.js | 233 ++++++++++ tsconfig.json | 16 + 23 files changed, 988 insertions(+), 212 deletions(-) create mode 100644 .github/workflows/typecheck.yml delete mode 100644 index.d.ts create mode 100644 src/types.js create mode 100644 tsconfig.json diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml new file mode 100644 index 0000000..b22397c --- /dev/null +++ b/.github/workflows/typecheck.yml @@ -0,0 +1,17 @@ +name: Type Check +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + typecheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + - run: npm ci + - run: npm run build:types diff --git a/.gitignore b/.gitignore index 6684c76..2905f45 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /.idea /node_modules +/dist diff --git a/index.d.ts b/index.d.ts deleted file mode 100644 index 5fbbada..0000000 --- a/index.d.ts +++ /dev/null @@ -1,185 +0,0 @@ -/** - * TypeScript declarations for @liamcottle/meshcore.js - */ - -declare module '@liamcottle/meshcore.js' { - import { EventEmitter } from 'events'; - - export class Connection extends EventEmitter { - connect(): Promise; - close(): Promise; - - // High-level API methods - getSelfInfo(timeout?: number): Promise; - getWaitingMessages(): Promise; - getChannels(): Promise; - getContacts(since?: number): Promise; - syncNextMessage(): Promise; - - // Message sending - sendTextMessage(publicKey: Buffer, text: string): Promise; - sendChannelTextMessage(channelIdx: number, text: string): Promise; - - // Contact lookup - findContactByPublicKeyPrefix(prefix: Buffer): Promise; - - // Frame handling (can be overridden in subclasses) - onFrameReceived(frame: Buffer): void; - - // Events - on(event: 'connected', listener: () => void): this; - on(event: 'disconnected', listener: () => void): this; - on(event: number, listener: (data: any) => void): this; - on(event: string | number, listener: (...args: any[]) => void): this; - - emit(event: string | number, ...args: any[]): boolean; - } - - export class NodeJSSerialConnection extends Connection { - constructor(port: string); - } - - export class WebSerialConnection extends Connection { - constructor(); - } - - export class TCPConnection extends Connection { - constructor(host: string, port: number); - } - - export class WebBleConnection extends Connection { - constructor(); - } - - export class SerialConnection extends Connection { - constructor(); - } - - // Type definitions - export interface SelfInfo { - publicKey: Buffer; - name?: string; - } - - export interface Message { - pubKeyPrefix: Buffer; - pathLen: number; - txtType: number; - senderTimestamp: number; - text: string; - } - - export interface Channel { - channelIdx: number; - name: string; - secret: Buffer; - } - - export interface Contact { - publicKey: Buffer; - name?: string; - lastSeen?: number; - } - - // Constants - export class Constants { - static readonly SupportedCompanionProtocolVersion: number; - - static readonly ResponseCodes: { - ContactMsgRecv: number; - ChannelMsgRecv: number; - [key: string]: number; - }; - - static readonly PushCodes: { - MsgWaiting: number; - NewAdvert: number; - [key: string]: number; - }; - - static readonly CommandCodes: { - AppStart: number; - SendTxtMsg: number; - SendChannelTxtMsg: number; - GetContacts: number; - GetDeviceTime: number; - SetDeviceTime: number; - SendSelfAdvert: number; - SetAdvertName: number; - [key: string]: number; - }; - } - - export class Advert { - constructor(data: Buffer); - publicKey: Buffer; - advName?: string; - } - - export class Packet { - constructor(data: Buffer); - } - - export class BufferUtils { - static xor(a: Buffer, b: Buffer): Buffer; - static concat(...buffers: Buffer[]): Buffer; - } - - export class CayenneLpp { - constructor(); - } -} - -// Type declarations for submodules -declare module '@liamcottle/meshcore.js/src/constants.js' { - const Constants: { - SupportedCompanionProtocolVersion: number; - ResponseCodes: { - ContactMsgRecv: number; - ChannelMsgRecv: number; - [key: string]: number; - }; - PushCodes: { - MsgWaiting: number; - NewAdvert: number; - [key: string]: number; - }; - CommandCodes: { - AppStart: number; - SendTxtMsg: number; - SendChannelTxtMsg: number; - GetContacts: number; - GetDeviceTime: number; - SetDeviceTime: number; - SendSelfAdvert: number; - SetAdvertName: number; - [key: string]: number; - }; - }; - export default Constants; -} - -declare module '@liamcottle/meshcore.js/src/buffer_reader.js' { - export default class BufferReader { - constructor(buffer: Buffer); - readByte(): number; - readInt8(): number; - readUInt16LE(): number; - readUInt32LE(): number; - readBytes(length: number): Buffer; - readString(): string; - } -} - -declare module '@liamcottle/meshcore.js/src/buffer_writer.js' { - export default class BufferWriter { - constructor(); - writeByte(value: number): void; - writeInt8(value: number): void; - writeUInt16LE(value: number): void; - writeUInt32LE(value: number): void; - writeBytes(buffer: Buffer | Uint8Array): void; - writeString(str: string): void; - toBytes(): Buffer; - } -} diff --git a/package-lock.json b/package-lock.json index 3bef957..3ac287b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,10 @@ "dependencies": { "@noble/curves": "^1.8.1", "serialport": "^13.0.0" + }, + "devDependencies": { + "@types/node": "^25.2.3", + "typescript": "^5.9.3" } }, "node_modules/@noble/curves": { @@ -227,6 +231,16 @@ "url": "https://opencollective.com/serialport/donate" } }, + "node_modules/@types/node": { + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz", + "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -292,6 +306,27 @@ "funding": { "url": "https://opencollective.com/serialport/donate" } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" } } } diff --git a/package.json b/package.json index a381602..0bf8316 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,11 @@ "version": "1.11.0", "description": "", "main": "src/index.js", - "types": "index.d.ts", + "types": "dist/index.d.ts", "type": "module", "scripts": { + "build:types": "tsc", + "prepublishOnly": "npm run build:types", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "Liam Cottle ", @@ -13,5 +15,9 @@ "dependencies": { "@noble/curves": "^1.8.1", "serialport": "^13.0.0" + }, + "devDependencies": { + "@types/node": "^25.2.3", + "typescript": "^5.9.3" } } diff --git a/src/advert.js b/src/advert.js index 5349436..25a839d 100644 --- a/src/advert.js +++ b/src/advert.js @@ -1,6 +1,8 @@ import BufferReader from "./buffer_reader.js"; import BufferWriter from "./buffer_writer.js"; +/** @typedef {import("./types.js").AdvertParsedData} AdvertParsedData */ + class Advert { static ADV_TYPE_NONE = 0; @@ -13,14 +15,25 @@ class Advert { static ADV_FEAT2_MASK = 0x40; static ADV_NAME_MASK = 0x80; + /** + * @param {Uint8Array} publicKey + * @param {number} timestamp + * @param {Uint8Array} signature + * @param {Uint8Array} appData + */ constructor(publicKey, timestamp, signature, appData) { this.publicKey = publicKey; this.timestamp = timestamp; this.signature = signature; this.appData = appData; + /** @type {AdvertParsedData} */ this.parsed = this.parseAppData(); } + /** + * @param {Uint8Array} bytes + * @returns {Advert} + */ static fromBytes(bytes) { // read bytes @@ -34,15 +47,18 @@ class Advert { } + /** @returns {number} */ getFlags() { return this.appData[0]; } + /** @returns {number} */ getType() { const flags = this.getFlags(); return flags & 0x0F; } + /** @returns {string | null} */ getTypeString() { const type = this.getType(); if(type === Advert.ADV_TYPE_NONE) return "NONE"; @@ -52,6 +68,7 @@ class Advert { return null; } + /** @returns {Promise} */ async isVerified() { const { ed25519 } = await import("@noble/curves/ed25519"); @@ -67,6 +84,7 @@ class Advert { } + /** @returns {AdvertParsedData} */ parseAppData() { // read app data diff --git a/src/buffer_reader.js b/src/buffer_reader.js index 9142fbc..9ba30e9 100644 --- a/src/buffer_reader.js +++ b/src/buffer_reader.js @@ -1,32 +1,49 @@ class BufferReader { + /** + * @param {Uint8Array | ArrayLike} data + */ constructor(data) { + /** @type {number} */ this.pointer = 0; + /** @type {Uint8Array} */ this.buffer = new Uint8Array(data); } + /** @returns {number} */ getRemainingBytesCount() { return this.buffer.length - this.pointer; } + /** @returns {number} */ readByte() { return this.readBytes(1)[0]; } + /** + * @param {number} count + * @returns {Uint8Array} + */ readBytes(count) { const data = this.buffer.slice(this.pointer, this.pointer + count); this.pointer += count; return data; } + /** @returns {Uint8Array} */ readRemainingBytes() { return this.readBytes(this.getRemainingBytesCount()); } + /** @returns {string} */ readString() { return new TextDecoder().decode(this.readRemainingBytes()); } + /** + * @param {number} maxLength + * @returns {string | undefined} + */ readCString(maxLength) { const value = []; const bytes = this.readBytes(maxLength); @@ -42,60 +59,70 @@ class BufferReader { } } + /** @returns {number} */ readInt8() { const bytes = this.readBytes(1); const view = new DataView(bytes.buffer); return view.getInt8(0); } + /** @returns {number} */ readUInt8() { const bytes = this.readBytes(1); const view = new DataView(bytes.buffer); return view.getUint8(0); } + /** @returns {number} */ readUInt16LE() { const bytes = this.readBytes(2); const view = new DataView(bytes.buffer); return view.getUint16(0, true); } + /** @returns {number} */ readUInt16BE() { const bytes = this.readBytes(2); const view = new DataView(bytes.buffer); return view.getUint16(0, false); } + /** @returns {number} */ readUInt32LE() { const bytes = this.readBytes(4); const view = new DataView(bytes.buffer); return view.getUint32(0, true); } + /** @returns {number} */ readUInt32BE() { const bytes = this.readBytes(4); const view = new DataView(bytes.buffer); return view.getUint32(0, false); } + /** @returns {number} */ readInt16LE() { const bytes = this.readBytes(2); const view = new DataView(bytes.buffer); return view.getInt16(0, true); } + /** @returns {number} */ readInt16BE() { const bytes = this.readBytes(2); const view = new DataView(bytes.buffer); return view.getInt16(0, false); } + /** @returns {number} */ readInt32LE() { const bytes = this.readBytes(4); const view = new DataView(bytes.buffer); return view.getInt32(0, true); } + /** @returns {number} */ readInt24BE() { // read 24-bit (3 bytes) big endian integer diff --git a/src/buffer_utils.js b/src/buffer_utils.js index d5ff14f..a23761b 100644 --- a/src/buffer_utils.js +++ b/src/buffer_utils.js @@ -1,21 +1,38 @@ class BufferUtils { + /** + * @param {Uint8Array} uint8Array + * @returns {string} + */ static bytesToHex(uint8Array) { return Array.from(uint8Array).map(byte => { return byte.toString(16).padStart(2, '0'); }).join(''); } + /** + * @param {string} hex + * @returns {Uint8Array} + */ static hexToBytes(hex) { return Uint8Array.from(hex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))); } + /** + * @param {string} base64 + * @returns {Uint8Array} + */ static base64ToBytes(base64) { return Uint8Array.from(atob(base64), (c) => { return c.charCodeAt(0); }); } + /** + * @param {Uint8Array} byteArray1 + * @param {Uint8Array} byteArray2 + * @returns {boolean} + */ static areBuffersEqual(byteArray1, byteArray2) { // ensure length is the same diff --git a/src/buffer_writer.js b/src/buffer_writer.js index a12cb2d..5da4fd0 100644 --- a/src/buffer_writer.js +++ b/src/buffer_writer.js @@ -1,13 +1,16 @@ class BufferWriter { constructor() { + /** @type {number[]} */ this.buffer = []; } + /** @returns {Uint8Array} */ toBytes() { return new Uint8Array(this.buffer); } + /** @param {Uint8Array | number[]} bytes */ writeBytes(bytes) { this.buffer = [ ...this.buffer, @@ -15,12 +18,14 @@ class BufferWriter { ]; } + /** @param {number} byte */ writeByte(byte) { this.writeBytes([ byte, ]); } + /** @param {number} num */ writeUInt16LE(num) { const bytes = new Uint8Array(2); const view = new DataView(bytes.buffer); @@ -28,6 +33,7 @@ class BufferWriter { this.writeBytes(bytes); } + /** @param {number} num */ writeUInt32LE(num) { const bytes = new Uint8Array(4); const view = new DataView(bytes.buffer); @@ -35,6 +41,7 @@ class BufferWriter { this.writeBytes(bytes); } + /** @param {number} num */ writeInt32LE(num) { const bytes = new Uint8Array(4); const view = new DataView(bytes.buffer); @@ -42,10 +49,15 @@ class BufferWriter { this.writeBytes(bytes); } + /** @param {string} string */ writeString(string) { this.writeBytes(new TextEncoder().encode(string)); } + /** + * @param {string} string + * @param {number} maxLength + */ writeCString(string, maxLength) { // create buffer of max length diff --git a/src/cayenne_lpp.js b/src/cayenne_lpp.js index ab8cab3..86f4490 100644 --- a/src/cayenne_lpp.js +++ b/src/cayenne_lpp.js @@ -1,5 +1,7 @@ import BufferReader from "./buffer_reader.js"; +/** @typedef {import("./types.js").CayenneTelemetryEntry} CayenneTelemetryEntry */ + class CayenneLpp { static LPP_DIGITAL_INPUT = 0; // 1 byte @@ -30,9 +32,14 @@ class CayenneLpp { static LPP_SWITCH = 142; // 1 byte, 0/1 static LPP_POLYLINE = 240; // 1 byte size, 1 byte delta factor, 3 byte lon/lat 0.0001° * factor, n (size-8) bytes deltas + /** + * @param {Uint8Array} bytes + * @returns {CayenneTelemetryEntry[]} + */ static parse(bytes) { const buffer = new BufferReader(bytes); + /** @type {CayenneTelemetryEntry[]} */ const telemetry = []; while(buffer.getRemainingBytesCount() >= 2){ // need at least 2 more bytes to get channel and type @@ -48,7 +55,6 @@ class CayenneLpp { switch(type){ case this.LPP_GENERIC_SENSOR: { const value = buffer.readUInt32BE(); - // console.log(`[CayenneLpp] parsed LPP_GENERIC_SENSOR=${value}`); telemetry.push({ "channel": channel, "type": type, @@ -58,7 +64,6 @@ class CayenneLpp { } case this.LPP_LUMINOSITY: { const lux = buffer.readInt16BE(); - // console.log(`[CayenneLpp] parsed LPP_LUMINOSITY=${lux}`); telemetry.push({ "channel": channel, "type": type, @@ -68,7 +73,6 @@ class CayenneLpp { } case this.LPP_PRESENCE: { const presence = buffer.readUInt8(); - // console.log(`[CayenneLpp] parsed LPP_PRESENCE=${presence}`); telemetry.push({ "channel": channel, "type": type, @@ -78,7 +82,6 @@ class CayenneLpp { } case this.LPP_TEMPERATURE: { const temperature = buffer.readInt16BE() / 10; - // console.log(`[CayenneLpp] parsed LPP_TEMPERATURE=${temperature}`); telemetry.push({ "channel": channel, "type": type, @@ -88,7 +91,6 @@ class CayenneLpp { } case this.LPP_RELATIVE_HUMIDITY: { const relativeHumidity = buffer.readUInt8() / 2; - // console.log(`[CayenneLpp] parsed LPP_RELATIVE_HUMIDITY=${relativeHumidity}`); telemetry.push({ "channel": channel, "type": type, @@ -98,7 +100,6 @@ class CayenneLpp { } case this.LPP_BAROMETRIC_PRESSURE: { const barometricPressure = buffer.readUInt16BE() / 10; - // console.log(`[CayenneLpp] parsed LPP_BAROMETRIC_PRESSURE=${barometricPressure}`); telemetry.push({ "channel": channel, "type": type, @@ -111,7 +112,6 @@ class CayenneLpp { // int16: -327.67v to +327.67v // should be readUInt16BE, but I'm using readInt16BE to allow for negative voltage const voltage = buffer.readInt16BE() / 100; - // console.log(`[CayenneLpp] parsed LPP_VOLTAGE=${voltage}`); telemetry.push({ "channel": channel, "type": type, @@ -124,7 +124,6 @@ class CayenneLpp { // int16: -327.67A to +327.67A // should be readUInt16BE, but I'm using readInt16BE to allow for negative current const current = buffer.readInt16BE() / 1000; - // console.log(`[CayenneLpp] parsed LPP_CURRENT=${current}`); telemetry.push({ "channel": channel, "type": type, @@ -134,7 +133,6 @@ class CayenneLpp { } case this.LPP_PERCENTAGE: { const percentage = buffer.readUInt8(); - // console.log(`[CayenneLpp] parsed LPP_PERCENTAGE=${percentage}`); telemetry.push({ "channel": channel, "type": type, @@ -144,7 +142,6 @@ class CayenneLpp { } case this.LPP_CONCENTRATION: { const concentration = buffer.readUInt16BE(); - // console.log(`[CayenneLpp] parsed LPP_CONCENTRATION=${concentration}`); telemetry.push({ "channel": channel, "type": type, @@ -154,7 +151,6 @@ class CayenneLpp { } case this.LPP_POWER: { const power = buffer.readUInt16BE(); - // console.log(`[CayenneLpp] parsed LPP_POWER=${power}`); telemetry.push({ "channel": channel, "type": type, @@ -166,7 +162,6 @@ class CayenneLpp { const latitude = buffer.readInt24BE() / 10000; const longitude = buffer.readInt24BE() / 10000; const altitude = buffer.readInt24BE() / 100; - // console.log(`[CayenneLpp] parsed LPP_GPS=${latitude},${longitude},${altitude}`); telemetry.push({ "channel": channel, "type": type, @@ -180,7 +175,6 @@ class CayenneLpp { } // todo support all telemetry types, otherwise if an unknown is given, we can't read other telemetry after it default: { - // console.log(`[CayenneLpp] unsupported type: ${type}`); return telemetry; } } diff --git a/src/connection/connection.js b/src/connection/connection.js index 2dd2d4d..0aca501 100644 --- a/src/connection/connection.js +++ b/src/connection/connection.js @@ -6,6 +6,34 @@ import BufferUtils from "../buffer_utils.js"; import Packet from "../packet.js"; import RandomUtils from "../random_utils.js"; +/** + * @typedef {import("../types.js").SelfInfo} SelfInfo + * @typedef {import("../types.js").Contact} Contact + * @typedef {import("../types.js").ContactMessage} ContactMessage + * @typedef {import("../types.js").ChannelMessage} ChannelMessage + * @typedef {import("../types.js").ChannelInfo} ChannelInfo + * @typedef {import("../types.js").SentResponse} SentResponse + * @typedef {import("../types.js").DeviceInfo} DeviceInfo + * @typedef {import("../types.js").BatteryVoltageResponse} BatteryVoltageResponse + * @typedef {import("../types.js").ExportContactResponse} ExportContactResponse + * @typedef {import("../types.js").PrivateKeyResponse} PrivateKeyResponse + * @typedef {import("../types.js").RepeaterStats} RepeaterStats + * @typedef {import("../types.js").SyncMessageResult} SyncMessageResult + * @typedef {import("../types.js").NeighboursResult} NeighboursResult + * @typedef {import("../types.js").TraceDataResult} TraceDataResult + * @typedef {import("../types.js").EpochSeconds} EpochSeconds + * @typedef {import("../types.js").Milliseconds} Milliseconds + * @typedef {import("../types.js").TxtType} TxtType + * @typedef {import("../types.js").LoginSuccessPush} LoginSuccessPush + * @typedef {import("../types.js").StatusResponsePush} StatusResponsePush + * @typedef {import("../types.js").RawDataPush} RawDataPush + * @typedef {import("../types.js").SendConfirmedPush} SendConfirmedPush + * @typedef {import("../types.js").LogRxDataPush} LogRxDataPush + * @typedef {import("../types.js").TelemetryResponsePush} TelemetryResponsePush + * @typedef {import("../types.js").BinaryResponsePush} BinaryResponsePush + * @typedef {import("../types.js").NewAdvertPush} NewAdvertPush + */ + class Connection extends EventEmitter { async onConnected() { @@ -26,14 +54,20 @@ class Connection extends EventEmitter { this.emit("disconnected"); } + /** @returns {Promise} */ async close() { throw new Error("This method must be implemented by the subclass."); } + /** + * @param {Uint8Array} data + * @returns {Promise} + */ async sendToRadioFrame(data) { throw new Error("This method must be implemented by the subclass."); } + /** @returns {Promise} */ async sendCommandAppStart() { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.AppStart); @@ -43,6 +77,14 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {TxtType} txtType + * @param {number} attempt + * @param {EpochSeconds} senderTimestamp + * @param {Uint8Array} pubKeyPrefix + * @param {string} text + * @returns {Promise} + */ async sendCommandSendTxtMsg(txtType, attempt, senderTimestamp, pubKeyPrefix, text) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.SendTxtMsg); @@ -54,6 +96,13 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {TxtType} txtType + * @param {number} channelIdx + * @param {EpochSeconds} senderTimestamp + * @param {string} text + * @returns {Promise} + */ async sendCommandSendChannelTxtMsg(txtType, channelIdx, senderTimestamp, text) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.SendChannelTxtMsg); @@ -64,6 +113,10 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {EpochSeconds} [since] + * @returns {Promise} + */ async sendCommandGetContacts(since) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.GetContacts); @@ -73,12 +126,17 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** @returns {Promise} */ async sendCommandGetDeviceTime() { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.GetDeviceTime); await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {EpochSeconds} epochSecs + * @returns {Promise} + */ async sendCommandSetDeviceTime(epochSecs) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.SetDeviceTime); @@ -86,6 +144,10 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {number} type + * @returns {Promise} + */ async sendCommandSendSelfAdvert(type) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.SendSelfAdvert); @@ -93,6 +155,10 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {string} name + * @returns {Promise} + */ async sendCommandSetAdvertName(name) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.SetAdvertName); @@ -100,6 +166,18 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {Uint8Array} publicKey + * @param {number} type + * @param {number} flags + * @param {number} outPathLen + * @param {Uint8Array} outPath + * @param {string} advName + * @param {EpochSeconds} lastAdvert + * @param {number} advLat + * @param {number} advLon + * @returns {Promise} + */ async sendCommandAddUpdateContact(publicKey, type, flags, outPathLen, outPath, advName, lastAdvert, advLat, advLon) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.AddUpdateContact); @@ -115,12 +193,20 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** @returns {Promise} */ async sendCommandSyncNextMessage() { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.SyncNextMessage); await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {number} radioFreq + * @param {number} radioBw + * @param {number} radioSf + * @param {number} radioCr + * @returns {Promise} + */ async sendCommandSetRadioParams(radioFreq, radioBw, radioSf, radioCr) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.SetRadioParams); @@ -131,6 +217,10 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {number} txPower + * @returns {Promise} + */ async sendCommandSetTxPower(txPower) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.SetTxPower); @@ -138,6 +228,10 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {Uint8Array} pubKey + * @returns {Promise} + */ async sendCommandResetPath(pubKey) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.ResetPath); @@ -145,6 +239,11 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {number} lat + * @param {number} lon + * @returns {Promise} + */ async sendCommandSetAdvertLatLon(lat, lon) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.SetAdvertLatLon); @@ -153,6 +252,10 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {Uint8Array} pubKey + * @returns {Promise} + */ async sendCommandRemoveContact(pubKey) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.RemoveContact); @@ -160,6 +263,10 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {Uint8Array} pubKey + * @returns {Promise} + */ async sendCommandShareContact(pubKey) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.ShareContact); @@ -167,8 +274,10 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } - // provide a public key to export that contact - // not providing a public key will export local identity as a contact instead + /** + * @param {Uint8Array | null} [pubKey] + * @returns {Promise} + */ async sendCommandExportContact(pubKey = null) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.ExportContact); @@ -178,6 +287,10 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {Uint8Array} advertPacketBytes + * @returns {Promise} + */ async sendCommandImportContact(advertPacketBytes) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.ImportContact); @@ -185,6 +298,7 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** @returns {Promise} */ async sendCommandReboot() { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.Reboot); @@ -192,12 +306,17 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** @returns {Promise} */ async sendCommandGetBatteryVoltage() { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.GetBatteryVoltage); await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {number} appTargetVer + * @returns {Promise} + */ async sendCommandDeviceQuery(appTargetVer) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.DeviceQuery); @@ -205,12 +324,17 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** @returns {Promise} */ async sendCommandExportPrivateKey() { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.ExportPrivateKey); await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {Uint8Array} privateKey + * @returns {Promise} + */ async sendCommandImportPrivateKey(privateKey) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.ImportPrivateKey); @@ -218,6 +342,11 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {Uint8Array | any[]} path + * @param {Uint8Array} rawData + * @returns {Promise} + */ async sendCommandSendRawData(path, rawData) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.SendRawData); @@ -227,6 +356,11 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {Uint8Array} publicKey + * @param {string} password + * @returns {Promise} + */ async sendCommandSendLogin(publicKey, password) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.SendLogin); @@ -235,6 +369,10 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {Uint8Array} publicKey + * @returns {Promise} + */ async sendCommandSendStatusReq(publicKey) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.SendStatusReq); @@ -242,6 +380,10 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {Uint8Array} publicKey + * @returns {Promise} + */ async sendCommandSendTelemetryReq(publicKey) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.SendTelemetryReq); @@ -252,6 +394,11 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {Uint8Array} publicKey + * @param {Uint8Array} requestCodeAndParams + * @returns {Promise} + */ async sendCommandSendBinaryReq(publicKey, requestCodeAndParams) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.SendBinaryReq); @@ -260,6 +407,10 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {number} channelIdx + * @returns {Promise} + */ async sendCommandGetChannel(channelIdx) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.GetChannel); @@ -267,6 +418,12 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {number} channelIdx + * @param {string} name + * @param {Uint8Array} secret + * @returns {Promise} + */ async sendCommandSetChannel(channelIdx, name, secret) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.SetChannel); @@ -276,12 +433,17 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** @returns {Promise} */ async sendCommandSignStart() { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.SignStart); await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {Uint8Array} dataToSign + * @returns {Promise} + */ async sendCommandSignData(dataToSign) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.SignData); @@ -289,12 +451,19 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** @returns {Promise} */ async sendCommandSignFinish() { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.SignFinish); await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {number} tag + * @param {number} auth + * @param {Uint8Array} path + * @returns {Promise} + */ async sendCommandSendTracePath(tag, auth, path) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.SendTracePath); @@ -305,6 +474,10 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {number} manualAddContacts + * @returns {Promise} + */ async sendCommandSetOtherParams(manualAddContacts) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.SetOtherParams); @@ -312,6 +485,9 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + /** + * @param {Uint8Array | number[]} frame + */ onFrameReceived(frame) { // emit received frame @@ -388,18 +564,21 @@ class Connection extends EventEmitter { } + /** @param {BufferReader} bufferReader */ onAdvertPush(bufferReader) { this.emit(Constants.PushCodes.Advert, { publicKey: bufferReader.readBytes(32), }); } + /** @param {BufferReader} bufferReader */ onPathUpdatedPush(bufferReader) { this.emit(Constants.PushCodes.PathUpdated, { publicKey: bufferReader.readBytes(32), }); } + /** @param {BufferReader} bufferReader */ onSendConfirmedPush(bufferReader) { this.emit(Constants.PushCodes.SendConfirmed, { ackCode: bufferReader.readUInt32LE(), @@ -407,12 +586,14 @@ class Connection extends EventEmitter { }); } + /** @param {BufferReader} bufferReader */ onMsgWaitingPush(bufferReader) { this.emit(Constants.PushCodes.MsgWaiting, { }); } + /** @param {BufferReader} bufferReader */ onRawDataPush(bufferReader) { this.emit(Constants.PushCodes.RawData, { lastSnr: bufferReader.readInt8() / 4, @@ -422,6 +603,7 @@ class Connection extends EventEmitter { }); } + /** @param {BufferReader} bufferReader */ onLoginSuccessPush(bufferReader) { this.emit(Constants.PushCodes.LoginSuccess, { reserved: bufferReader.readByte(), // reserved @@ -429,6 +611,7 @@ class Connection extends EventEmitter { }); } + /** @param {BufferReader} bufferReader */ onStatusResponsePush(bufferReader) { this.emit(Constants.PushCodes.StatusResponse, { reserved: bufferReader.readByte(), // reserved @@ -437,6 +620,7 @@ class Connection extends EventEmitter { }); } + /** @param {BufferReader} bufferReader */ onLogRxDataPush(bufferReader) { this.emit(Constants.PushCodes.LogRxData, { lastSnr: bufferReader.readInt8() / 4, @@ -445,6 +629,7 @@ class Connection extends EventEmitter { }); } + /** @param {BufferReader} bufferReader */ onTelemetryResponsePush(bufferReader) { this.emit(Constants.PushCodes.TelemetryResponse, { reserved: bufferReader.readByte(), // reserved @@ -453,6 +638,7 @@ class Connection extends EventEmitter { }); } + /** @param {BufferReader} bufferReader */ onBinaryResponsePush(bufferReader) { this.emit(Constants.PushCodes.BinaryResponse, { reserved: bufferReader.readByte(), // reserved @@ -461,6 +647,7 @@ class Connection extends EventEmitter { }); } + /** @param {BufferReader} bufferReader */ onTraceDataPush(bufferReader) { const reserved = bufferReader.readByte(); const pathLen = bufferReader.readUInt8(); @@ -476,6 +663,7 @@ class Connection extends EventEmitter { }); } + /** @param {BufferReader} bufferReader */ onNewAdvertPush(bufferReader) { this.emit(Constants.PushCodes.NewAdvert, { publicKey: bufferReader.readBytes(32), @@ -491,12 +679,14 @@ class Connection extends EventEmitter { }); } + /** @param {BufferReader} bufferReader */ onOkResponse(bufferReader) { this.emit(Constants.ResponseCodes.Ok, { }); } + /** @param {BufferReader} bufferReader */ onErrResponse(bufferReader) { const errCode = bufferReader.getRemainingBytesCount() > 0 ? bufferReader.readByte() : null; this.emit(Constants.ResponseCodes.Err, { @@ -504,12 +694,14 @@ class Connection extends EventEmitter { }); } + /** @param {BufferReader} bufferReader */ onContactsStartResponse(bufferReader) { this.emit(Constants.ResponseCodes.ContactsStart, { count: bufferReader.readUInt32LE(), }); } + /** @param {BufferReader} bufferReader */ onContactResponse(bufferReader) { this.emit(Constants.ResponseCodes.Contact, { publicKey: bufferReader.readBytes(32), @@ -525,12 +717,14 @@ class Connection extends EventEmitter { }); } + /** @param {BufferReader} bufferReader */ onEndOfContactsResponse(bufferReader) { this.emit(Constants.ResponseCodes.EndOfContacts, { mostRecentLastmod: bufferReader.readUInt32LE(), }); } + /** @param {BufferReader} bufferReader */ onSentResponse(bufferReader) { this.emit(Constants.ResponseCodes.Sent, { result: bufferReader.readInt8(), @@ -539,18 +733,21 @@ class Connection extends EventEmitter { }); } + /** @param {BufferReader} bufferReader */ onExportContactResponse(bufferReader) { this.emit(Constants.ResponseCodes.ExportContact, { advertPacketBytes: bufferReader.readRemainingBytes(), }); } + /** @param {BufferReader} bufferReader */ onBatteryVoltageResponse(bufferReader) { this.emit(Constants.ResponseCodes.BatteryVoltage, { batteryMilliVolts: bufferReader.readUInt16LE(), }); } + /** @param {BufferReader} bufferReader */ onDeviceInfoResponse(bufferReader) { this.emit(Constants.ResponseCodes.DeviceInfo, { firmwareVer: bufferReader.readInt8(), @@ -560,18 +757,21 @@ class Connection extends EventEmitter { }); } + /** @param {BufferReader} bufferReader */ onPrivateKeyResponse(bufferReader) { this.emit(Constants.ResponseCodes.PrivateKey, { privateKey: bufferReader.readBytes(64), }); } + /** @param {BufferReader} bufferReader */ onDisabledResponse(bufferReader) { this.emit(Constants.ResponseCodes.Disabled, { }); } + /** @param {BufferReader} bufferReader */ onChannelInfoResponse(bufferReader) { const idx = bufferReader.readUInt8(); @@ -591,6 +791,7 @@ class Connection extends EventEmitter { } + /** @param {BufferReader} bufferReader */ onSignStartResponse(bufferReader) { this.emit(Constants.ResponseCodes.SignStart, { reserved: bufferReader.readByte(), @@ -598,12 +799,14 @@ class Connection extends EventEmitter { }); } + /** @param {BufferReader} bufferReader */ onSignatureResponse(bufferReader) { this.emit(Constants.ResponseCodes.Signature, { signature: bufferReader.readBytes(64), }); } + /** @param {BufferReader} bufferReader */ onSelfInfoResponse(bufferReader) { this.emit(Constants.ResponseCodes.SelfInfo, { type: bufferReader.readByte(), @@ -622,18 +825,21 @@ class Connection extends EventEmitter { }); } + /** @param {BufferReader} bufferReader */ onCurrTimeResponse(bufferReader) { this.emit(Constants.ResponseCodes.CurrTime, { epochSecs: bufferReader.readUInt32LE(), }); } + /** @param {BufferReader} bufferReader */ onNoMoreMessagesResponse(bufferReader) { this.emit(Constants.ResponseCodes.NoMoreMessages, { }); } + /** @param {BufferReader} bufferReader */ onContactMsgRecvResponse(bufferReader) { this.emit(Constants.ResponseCodes.ContactMsgRecv, { pubKeyPrefix: bufferReader.readBytes(6), @@ -644,6 +850,7 @@ class Connection extends EventEmitter { }); } + /** @param {BufferReader} bufferReader */ onChannelMsgRecvResponse(bufferReader) { this.emit(Constants.ResponseCodes.ChannelMsgRecv, { channelIdx: bufferReader.readInt8(), // reserved (0 for now, ie. 'public') @@ -654,6 +861,90 @@ class Connection extends EventEmitter { }); } + // --- High-level API methods --- + + /** + * @overload + * @param {"connected"} event + * @param {() => void} callback + * @returns {void} + */ + /** + * @overload + * @param {"disconnected"} event + * @param {() => void} callback + * @returns {void} + */ + /** + * @overload + * @param {number} event + * @param {(data: any) => void} callback + * @returns {void} + */ + /** + * @param {string | number} event + * @param {Function} callback + */ + on(event, callback) { + super.on(event, callback); + } + + /** + * @overload + * @param {"connected"} event + * @param {() => void} callback + * @returns {void} + */ + /** + * @overload + * @param {"disconnected"} event + * @param {() => void} callback + * @returns {void} + */ + /** + * @overload + * @param {number} event + * @param {(data: any) => void} callback + * @returns {void} + */ + /** + * @param {string | number} event + * @param {Function} callback + */ + once(event, callback) { + super.once(event, callback); + } + + /** + * @overload + * @param {"connected"} event + * @param {() => void} callback + * @returns {void} + */ + /** + * @overload + * @param {"disconnected"} event + * @param {() => void} callback + * @returns {void} + */ + /** + * @overload + * @param {number} event + * @param {(data: any) => void} callback + * @returns {void} + */ + /** + * @param {string | number} event + * @param {Function} callback + */ + off(event, callback) { + super.off(event, callback); + } + + /** + * @param {Milliseconds | null} [timeoutMillis] + * @returns {Promise} + */ getSelfInfo(timeoutMillis = null) { return new Promise(async (resolve, reject) => { @@ -673,6 +964,10 @@ class Connection extends EventEmitter { }); } + /** + * @param {number} type + * @returns {Promise} + */ async sendAdvert(type) { return new Promise(async (resolve, reject) => { try { @@ -704,14 +999,20 @@ class Connection extends EventEmitter { }); } + /** @returns {Promise} */ async sendFloodAdvert() { return await this.sendAdvert(Constants.SelfAdvertTypes.Flood); } + /** @returns {Promise} */ async sendZeroHopAdvert() { return await this.sendAdvert(Constants.SelfAdvertTypes.ZeroHop); } + /** + * @param {string} name + * @returns {Promise} + */ setAdvertName(name) { return new Promise(async (resolve, reject) => { try { @@ -743,6 +1044,11 @@ class Connection extends EventEmitter { }); } + /** + * @param {number} latitude + * @param {number} longitude + * @returns {Promise} + */ setAdvertLatLong(latitude, longitude) { return new Promise(async (resolve, reject) => { try { @@ -774,6 +1080,10 @@ class Connection extends EventEmitter { }); } + /** + * @param {number} txPower + * @returns {Promise} + */ setTxPower(txPower) { return new Promise(async (resolve, reject) => { try { @@ -805,6 +1115,13 @@ class Connection extends EventEmitter { }); } + /** + * @param {number} radioFreq + * @param {number} radioBw + * @param {number} radioSf + * @param {number} radioCr + * @returns {Promise} + */ setRadioParams(radioFreq, radioBw, radioSf, radioCr) { return new Promise(async (resolve, reject) => { try { @@ -836,10 +1153,12 @@ class Connection extends EventEmitter { }); } + /** @returns {Promise} */ getContacts() { return new Promise(async (resolve, reject) => { // add contacts we receive to a list + /** @type {Contact[]} */ const contacts = []; const onContactReceived = (contact) => { contacts.push(contact); @@ -860,6 +1179,10 @@ class Connection extends EventEmitter { }); } + /** + * @param {string} name + * @returns {Promise} + */ async findContactByName(name) { // get contacts @@ -872,6 +1195,10 @@ class Connection extends EventEmitter { } + /** + * @param {Uint8Array} pubKeyPrefix + * @returns {Promise} + */ async findContactByPublicKeyPrefix(pubKeyPrefix) { // get contacts @@ -885,6 +1212,12 @@ class Connection extends EventEmitter { } + /** + * @param {Uint8Array} contactPublicKey + * @param {string} text + * @param {TxtType} [type] + * @returns {Promise} + */ sendTextMessage(contactPublicKey, text, type) { return new Promise(async (resolve, reject) => { try { @@ -921,6 +1254,11 @@ class Connection extends EventEmitter { }); } + /** + * @param {number} channelIdx + * @param {string} text + * @returns {Promise} + */ sendChannelTextMessage(channelIdx, text) { return new Promise(async (resolve, reject) => { try { @@ -956,6 +1294,7 @@ class Connection extends EventEmitter { }); } + /** @returns {Promise} */ syncNextMessage() { return new Promise(async (resolve, reject) => { @@ -998,8 +1337,10 @@ class Connection extends EventEmitter { }); } + /** @returns {Promise} */ async getWaitingMessages() { + /** @type {SyncMessageResult[]} */ const waitingMessages = []; while(true){ @@ -1019,6 +1360,7 @@ class Connection extends EventEmitter { } + /** @returns {Promise<{epochSecs: number}>} */ getDeviceTime() { return new Promise(async (resolve, reject) => { try { @@ -1050,6 +1392,10 @@ class Connection extends EventEmitter { }); } + /** + * @param {EpochSeconds} epochSecs + * @returns {Promise} + */ setDeviceTime(epochSecs) { return new Promise(async (resolve, reject) => { try { @@ -1081,10 +1427,15 @@ class Connection extends EventEmitter { }); } + /** @returns {Promise} */ async syncDeviceTime() { await this.setDeviceTime(Math.floor(Date.now() / 1000)); } + /** + * @param {Uint8Array} advertPacketBytes + * @returns {Promise} + */ importContact(advertPacketBytes) { return new Promise(async (resolve, reject) => { try { @@ -1116,6 +1467,10 @@ class Connection extends EventEmitter { }); } + /** + * @param {Uint8Array | null} [pubKey] + * @returns {Promise} + */ exportContact(pubKey = null) { return new Promise(async (resolve, reject) => { try { @@ -1147,6 +1502,10 @@ class Connection extends EventEmitter { }); } + /** + * @param {Uint8Array} pubKey + * @returns {Promise} + */ shareContact(pubKey) { return new Promise(async (resolve, reject) => { try { @@ -1178,6 +1537,10 @@ class Connection extends EventEmitter { }); } + /** + * @param {Uint8Array} pubKey + * @returns {Promise} + */ removeContact(pubKey) { return new Promise(async (resolve, reject) => { try { @@ -1209,6 +1572,18 @@ class Connection extends EventEmitter { }); } + /** + * @param {Uint8Array} publicKey + * @param {number} type + * @param {number} flags + * @param {number} outPathLen + * @param {Uint8Array} outPath + * @param {string} advName + * @param {EpochSeconds} lastAdvert + * @param {number} advLat + * @param {number} advLon + * @returns {Promise} + */ addOrUpdateContact(publicKey, type, flags, outPathLen, outPath, advName, lastAdvert, advLat, advLon) { return new Promise(async (resolve, reject) => { try { @@ -1240,6 +1615,11 @@ class Connection extends EventEmitter { }); } + /** + * @param {Contact} contact + * @param {Uint8Array} path + * @returns {Promise} + */ setContactPath(contact, path) { return new Promise(async (resolve, reject) => { try { @@ -1266,6 +1646,10 @@ class Connection extends EventEmitter { }); } + /** + * @param {Uint8Array} pubKey + * @returns {Promise} + */ resetPath(pubKey) { return new Promise(async (resolve, reject) => { try { @@ -1297,6 +1681,7 @@ class Connection extends EventEmitter { }); } + /** @returns {Promise} */ reboot() { return new Promise(async (resolve, reject) => { try { @@ -1325,6 +1710,7 @@ class Connection extends EventEmitter { }); } + /** @returns {Promise} */ getBatteryVoltage() { return new Promise(async (resolve, reject) => { try { @@ -1356,6 +1742,10 @@ class Connection extends EventEmitter { }); } + /** + * @param {number} appTargetVer + * @returns {Promise} + */ deviceQuery(appTargetVer) { return new Promise(async (resolve, reject) => { try { @@ -1387,6 +1777,7 @@ class Connection extends EventEmitter { }); } + /** @returns {Promise} */ exportPrivateKey() { return new Promise(async (resolve, reject) => { try { @@ -1429,6 +1820,10 @@ class Connection extends EventEmitter { }); } + /** + * @param {Uint8Array} privateKey + * @returns {Promise} + */ importPrivateKey(privateKey) { return new Promise(async (resolve, reject) => { try { @@ -1471,6 +1866,12 @@ class Connection extends EventEmitter { }); } + /** + * @param {Uint8Array} contactPublicKey + * @param {string} password + * @param {Milliseconds} [extraTimeoutMillis] + * @returns {Promise} + */ login(contactPublicKey, password, extraTimeoutMillis = 1000) { return new Promise(async (resolve, reject) => { try { @@ -1537,6 +1938,11 @@ class Connection extends EventEmitter { }); } + /** + * @param {Uint8Array} contactPublicKey + * @param {Milliseconds} [extraTimeoutMillis] + * @returns {Promise} + */ getStatus(contactPublicKey, extraTimeoutMillis = 1000) { return new Promise(async (resolve, reject) => { try { @@ -1625,6 +2031,11 @@ class Connection extends EventEmitter { }); } + /** + * @param {Uint8Array} contactPublicKey + * @param {Milliseconds} [extraTimeoutMillis] + * @returns {Promise} + */ getTelemetry(contactPublicKey, extraTimeoutMillis = 1000) { return new Promise(async (resolve, reject) => { try { @@ -1692,6 +2103,12 @@ class Connection extends EventEmitter { }); } + /** + * @param {Uint8Array} contactPublicKey + * @param {Uint8Array} requestCodeAndParams + * @param {Milliseconds} [extraTimeoutMillis] + * @returns {Promise} + */ sendBinaryRequest(contactPublicKey, requestCodeAndParams, extraTimeoutMillis = 1000) { return new Promise(async (resolve, reject) => { try { @@ -1761,7 +2178,12 @@ class Connection extends EventEmitter { }); } - // @deprecated migrate to using tracePath instead. pingRepeaterZeroHop will be removed in a future update + /** + * @deprecated migrate to using tracePath instead. pingRepeaterZeroHop will be removed in a future update + * @param {Uint8Array} contactPublicKey + * @param {Milliseconds} [timeoutMillis] + * @returns {Promise<{rtt: number, snr: number, rssi: number}>} + */ pingRepeaterZeroHop(contactPublicKey, timeoutMillis) { return new Promise(async (resolve, reject) => { try { @@ -1844,6 +2266,10 @@ class Connection extends EventEmitter { }); } + /** + * @param {number} channelIdx + * @returns {Promise} + */ getChannel(channelIdx) { return new Promise(async (resolve, reject) => { try { @@ -1875,6 +2301,12 @@ class Connection extends EventEmitter { }); } + /** + * @param {number} channelIdx + * @param {string} name + * @param {Uint8Array} secret + * @returns {Promise} + */ setChannel(channelIdx, name, secret) { return new Promise(async (resolve, reject) => { try { @@ -1906,10 +2338,15 @@ class Connection extends EventEmitter { }); } + /** + * @param {number} channelIdx + * @returns {Promise} + */ async deleteChannel(channelIdx) { return await this.setChannel(channelIdx, "", new Uint8Array(16)); } + /** @returns {Promise} */ getChannels() { return new Promise(async (resolve, reject) => { @@ -1935,6 +2372,10 @@ class Connection extends EventEmitter { }); } + /** + * @param {string} name + * @returns {Promise} + */ async findChannelByName(name) { // get channels @@ -1947,6 +2388,10 @@ class Connection extends EventEmitter { } + /** + * @param {Uint8Array} secret + * @returns {Promise} + */ async findChannelBySecret(secret) { // get channels @@ -1959,6 +2404,10 @@ class Connection extends EventEmitter { } + /** + * @param {Uint8Array} data + * @returns {Promise} + */ async sign(data) { return new Promise(async (resolve, reject) => { try { @@ -2048,6 +2497,11 @@ class Connection extends EventEmitter { }); } + /** + * @param {Uint8Array} path + * @param {Milliseconds} [extraTimeoutMillis] + * @returns {Promise} + */ tracePath(path, extraTimeoutMillis = 0) { return new Promise(async (resolve, reject) => { try { @@ -2114,6 +2568,10 @@ class Connection extends EventEmitter { }); } + /** + * @param {number} manualAddContacts + * @returns {Promise} + */ setOtherParams(manualAddContacts) { return new Promise(async (resolve, reject) => { try { @@ -2145,21 +2603,28 @@ class Connection extends EventEmitter { }); } + /** @returns {Promise} */ async setAutoAddContacts() { - return await this.setOtherParams(false); + return await this.setOtherParams(0); } + /** @returns {Promise} */ async setManualAddContacts() { - return await this.setOtherParams(true); + return await this.setOtherParams(1); } - // REQ_TYPE_GET_NEIGHBOURS from Repeater role - // https://github.com/meshcore-dev/MeshCore/pull/833 - // Repeater must be running firmware v1.9.0+ + /** + * @param {Uint8Array} publicKey + * @param {number} [count] + * @param {number} [offset] + * @param {number} [orderBy] 0=newest_to_oldest, 1=oldest_to_newest, 2=strongest_to_weakest, 3=weakest_to_strongest + * @param {number} [pubKeyPrefixLength] + * @returns {Promise} + */ async getNeighbours(publicKey, count = 10, offset = 0, - orderBy = 0, // 0=newest_to_oldest, 1=oldest_to_newest, 2=strongest_to_weakest, 3=weakest_to_strongest + orderBy = 0, pubKeyPrefixLength = 8, ) { diff --git a/src/connection/nodejs_serial_connection.js b/src/connection/nodejs_serial_connection.js index 4bc519d..1a030e2 100644 --- a/src/connection/nodejs_serial_connection.js +++ b/src/connection/nodejs_serial_connection.js @@ -3,13 +3,15 @@ import SerialConnection from "./serial_connection.js"; class NodeJSSerialConnection extends SerialConnection { /** - * @param path serial port to connect to, e.g: "/dev/ttyACM0" or "/dev/cu.usbmodem14401" + * @param {string} path serial port to connect to, e.g: "/dev/ttyACM0" or "/dev/cu.usbmodem14401" */ constructor(path) { super(); + /** @type {string} */ this.serialPortPath = path; } + /** @returns {Promise} */ async connect() { // note: serialport module is only available in NodeJS, you shouldn't use NodeJSSerialConnection from a web browser @@ -43,6 +45,7 @@ class NodeJSSerialConnection extends SerialConnection { } + /** @returns {Promise} */ async close() { try { await this.serialPort.close(); @@ -51,6 +54,10 @@ class NodeJSSerialConnection extends SerialConnection { } } + /** + * @param {Uint8Array} bytes + * @returns {Promise} + */ /* override */ async write(bytes) { this.serialPort.write(bytes); } diff --git a/src/connection/serial_connection.js b/src/connection/serial_connection.js index b60779f..770a873 100644 --- a/src/connection/serial_connection.js +++ b/src/connection/serial_connection.js @@ -7,16 +7,26 @@ class SerialConnection extends Connection { constructor() { super(); + /** @type {number[]} */ this.readBuffer = []; if(this.constructor === SerialConnection){ throw new Error("SerialConnection is an abstract class and can't be instantiated."); } } + /** + * @param {Uint8Array} bytes + * @returns {Promise} + */ async write(bytes) { throw new Error("Not Implemented: write must be implemented by SerialConnection sub class."); } + /** + * @param {number} frameType + * @param {Uint8Array} frameData + * @returns {Promise} + */ async writeFrame(frameType, frameData) { // create frame @@ -34,12 +44,20 @@ class SerialConnection extends Connection { } + /** + * @param {Uint8Array} data + * @returns {Promise} + */ async sendToRadioFrame(data) { // write "app to radio" frame 0x3c "<" this.emit("tx", data); await this.writeFrame(0x3c, data); } + /** + * @param {Uint8Array | number[]} value + * @returns {Promise} + */ async onDataReceived(value) { // append received bytes to read buffer diff --git a/src/connection/tcp_connection.js b/src/connection/tcp_connection.js index eab0e16..f04ef34 100644 --- a/src/connection/tcp_connection.js +++ b/src/connection/tcp_connection.js @@ -5,13 +5,19 @@ import Connection from "./connection.js"; class TCPConnection extends Connection { + /** + * @param {string} host + * @param {number} port + */ constructor(host, port) { super(); this.host = host; this.port = port; + /** @type {number[]} */ this.readBuffer = []; } + /** @returns {Promise} */ async connect() { // note: net module is only available in NodeJS, you shouldn't use TCPConnection from a web browser @@ -21,7 +27,7 @@ class TCPConnection extends Connection { this.socket = new Socket(); // handle received data - this.socket.on('data', (data) => { + this.socket.on('data', (/** @type {Uint8Array} */ data) => { this.onSocketDataReceived(data); }); @@ -42,6 +48,7 @@ class TCPConnection extends Connection { } + /** @param {Uint8Array | number[]} data */ onSocketDataReceived(data) { // append received bytes to read buffer @@ -96,7 +103,7 @@ class TCPConnection extends Connection { } - close() { + async close() { try { this.socket.destroy(); } catch(e) { @@ -104,10 +111,19 @@ class TCPConnection extends Connection { } } + /** + * @param {Uint8Array} bytes + * @returns {Promise} + */ async write(bytes) { this.socket.write(new Uint8Array(bytes)); } + /** + * @param {number} frameType + * @param {Uint8Array} frameData + * @returns {Promise} + */ async writeFrame(frameType, frameData) { // create frame @@ -125,6 +141,10 @@ class TCPConnection extends Connection { } + /** + * @param {Uint8Array} data + * @returns {Promise} + */ async sendToRadioFrame(data) { // write "app to radio" frame 0x3c "<" this.emit("tx", data); diff --git a/src/connection/web_ble_connection.js b/src/connection/web_ble_connection.js index dad7c86..4c445a5 100644 --- a/src/connection/web_ble_connection.js +++ b/src/connection/web_ble_connection.js @@ -3,24 +3,31 @@ import Connection from "./connection.js"; class WebBleConnection extends Connection { + /** @param {any} bleDevice */ constructor(bleDevice) { super(); this.bleDevice = bleDevice; + /** @type {any} */ this.gattServer = null; + /** @type {any} */ this.rxCharacteristic = null; + /** @type {any} */ this.txCharacteristic = null; this.init(); } + /** @returns {Promise} */ static async open() { // ensure browser supports web bluetooth + // @ts-ignore - Web Bluetooth API if(!navigator.bluetooth){ alert("Web Bluetooth is not supported in this browser"); return; } // ask user to select device + // @ts-ignore - Web Bluetooth API const device = await navigator.bluetooth.requestDevice({ filters: [ { @@ -76,6 +83,7 @@ class WebBleConnection extends Connection { } + /** @returns {Promise} */ async close() { try { this.gattServer?.disconnect(); @@ -85,6 +93,10 @@ class WebBleConnection extends Connection { } } + /** + * @param {Uint8Array} bytes + * @returns {Promise} + */ async write(bytes) { try { // fixme: NetworkError: GATT operation already in progress. @@ -96,6 +108,10 @@ class WebBleConnection extends Connection { } } + /** + * @param {Uint8Array} frame + * @returns {Promise} + */ async sendToRadioFrame(frame) { this.emit("tx", frame); await this.write(frame); diff --git a/src/connection/web_serial_connection.js b/src/connection/web_serial_connection.js index a9eae02..88b9008 100644 --- a/src/connection/web_serial_connection.js +++ b/src/connection/web_serial_connection.js @@ -2,6 +2,7 @@ import SerialConnection from "./serial_connection.js"; class WebSerialConnection extends SerialConnection { + /** @param {any} serialPort */ constructor(serialPort) { super(); @@ -23,15 +24,18 @@ class WebSerialConnection extends SerialConnection { } + /** @returns {Promise} */ static async open() { // ensure browser supports web serial + // @ts-ignore - Web Serial API if(!navigator.serial){ alert("Web Serial is not supported in this browser"); return null; } // ask user to select device + // @ts-ignore - Web Serial API const serialPort = await navigator.serial.requestPort({ filters: [], }); @@ -45,6 +49,7 @@ class WebSerialConnection extends SerialConnection { } + /** @returns {Promise} */ async close() { // release reader lock @@ -63,6 +68,10 @@ class WebSerialConnection extends SerialConnection { } + /** + * @param {Uint8Array} bytes + * @returns {Promise} + */ /* override */ async write(bytes) { const writer = this.writable.getWriter(); try { diff --git a/src/constants.js b/src/constants.js index 2eb2706..a385589 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,3 +1,4 @@ +/** Constants used by the MeshCore companion protocol. */ class Constants { static SupportedCompanionProtocolVersion = 1; diff --git a/src/events.js b/src/events.js index 606fff2..ee1b258 100644 --- a/src/events.js +++ b/src/events.js @@ -1,9 +1,14 @@ class EventEmitter { constructor() { + /** @type {Map} */ this.eventListenersMap = new Map(); } + /** + * @param {string | number} event + * @param {Function} callback + */ on(event, callback) { // create list of listeners for event if it doesn't exist @@ -16,6 +21,10 @@ class EventEmitter { } + /** + * @param {string | number} event + * @param {Function} callback + */ off(event, callback) { // remove callback from listeners for this event @@ -26,6 +35,10 @@ class EventEmitter { } + /** + * @param {string | number} event + * @param {Function} callback + */ once(event, callback) { // internal callback to handle the event @@ -44,6 +57,10 @@ class EventEmitter { } + /** + * @param {string | number} event + * @param {...any} data + */ emit(event, ...data) { // invoke each listener for this event diff --git a/src/index.js b/src/index.js index af95004..3e5aa52 100644 --- a/src/index.js +++ b/src/index.js @@ -10,6 +10,8 @@ import Packet from "./packet.js"; import BufferUtils from "./buffer_utils.js"; import CayenneLpp from "./cayenne_lpp.js"; +export * from "./types.js"; + export { Connection, WebBleConnection, diff --git a/src/packet.js b/src/packet.js index 53a2f46..13d283b 100644 --- a/src/packet.js +++ b/src/packet.js @@ -27,9 +27,15 @@ class Packet { static PAYLOAD_TYPE_TRACE = 0x09; // trace a path, collecting SNR for each hop static PAYLOAD_TYPE_RAW_CUSTOM = 0x0F; // custom packet as raw bytes, for applications with custom encryption, payloads, etc + /** + * @param {number} header + * @param {Uint8Array} path + * @param {Uint8Array} payload + * @param {number | null} transportCode1 + * @param {number | null} transportCode2 + */ constructor(header, path, payload, transportCode1, transportCode2) { - this.header = header; this.path = path; this.payload = payload; @@ -46,6 +52,10 @@ class Packet { } + /** + * @param {Uint8Array} bytes + * @returns {Packet} + */ static fromBytes(bytes) { const bufferReader = new BufferReader(bytes); @@ -71,10 +81,12 @@ class Packet { } + /** @returns {number} */ getRouteType() { return this.header & Packet.PH_ROUTE_MASK; } + /** @returns {string | null} */ getRouteTypeString() { switch(this.getRouteType()){ case Packet.ROUTE_TYPE_FLOOD: return "FLOOD"; @@ -85,18 +97,22 @@ class Packet { } } + /** @returns {boolean} */ isRouteFlood() { return this.getRouteType() === Packet.ROUTE_TYPE_FLOOD; } + /** @returns {boolean} */ isRouteDirect() { return this.getRouteType() === Packet.ROUTE_TYPE_DIRECT; } + /** @returns {number} */ getPayloadType() { return (this.header >> Packet.PH_TYPE_SHIFT) & Packet.PH_TYPE_MASK; } + /** @returns {string | null} */ getPayloadTypeString() { switch(this.getPayloadType()){ case Packet.PAYLOAD_TYPE_REQ: return "REQ"; @@ -114,6 +130,7 @@ class Packet { } } + /** @returns {number} */ getPayloadVer() { return (this.header >> Packet.PH_VER_SHIFT) & Packet.PH_VER_MASK; } @@ -122,6 +139,7 @@ class Packet { this.header = 0xFF; } + /** @returns {boolean} */ isMarkedDoNotRetransmit() { return this.header === 0xFF; } @@ -139,6 +157,7 @@ class Packet { } } + /** @returns {{ src: number, dest: number }} */ parsePayloadTypePath() { // parse bytes @@ -154,6 +173,7 @@ class Packet { } + /** @returns {{ src: number, dest: number, encrypted: Uint8Array }} */ parsePayloadTypeReq() { // parse bytes @@ -170,6 +190,7 @@ class Packet { } + /** @returns {{ src: number, dest: number }} */ parsePayloadTypeResponse() { // parse bytes @@ -185,6 +206,7 @@ class Packet { } + /** @returns {{ src: number, dest: number }} */ parsePayloadTypeTxtMsg() { // parse bytes @@ -200,12 +222,14 @@ class Packet { } + /** @returns {{ ack_code: Uint8Array }} */ parsePayloadTypeAck() { return { ack_code: this.payload, }; } + /** @returns {{ public_key: Uint8Array, timestamp: number, app_data: import("./types.js").AdvertParsedData }} */ parsePayloadTypeAdvert() { const advert = Advert.fromBytes(this.payload); return { @@ -215,6 +239,7 @@ class Packet { }; } + /** @returns {{ src: Uint8Array, dest: number }} */ parsePayloadTypeAnonReq() { // parse bytes diff --git a/src/random_utils.js b/src/random_utils.js index a66bad3..7898424 100644 --- a/src/random_utils.js +++ b/src/random_utils.js @@ -1,5 +1,10 @@ class RandomUtils { + /** + * @param {number} min + * @param {number} max + * @returns {number} + */ static getRandomInt(min, max) { min = Math.ceil(min); max = Math.floor(max); diff --git a/src/types.js b/src/types.js new file mode 100644 index 0000000..659bc7e --- /dev/null +++ b/src/types.js @@ -0,0 +1,233 @@ +/** + * @typedef {number} EpochSeconds Unix timestamp in seconds + */ + +/** + * @typedef {number} Milliseconds Duration in milliseconds + */ + +/** + * @typedef {number} MilliVolts Voltage in millivolts + */ + +/** + * @typedef {number} TxtType Text message type: 0=Plain, 1=CliData, 2=SignedPlain + */ + +/** + * @typedef {object} SelfInfo + * @property {number} type + * @property {number} txPower + * @property {number} maxTxPower + * @property {Uint8Array} publicKey + * @property {number} advLat + * @property {number} advLon + * @property {Uint8Array} reserved + * @property {number} manualAddContacts + * @property {number} radioFreq + * @property {number} radioBw + * @property {number} radioSf + * @property {number} radioCr + * @property {string} name + */ + +/** + * @typedef {object} Contact + * @property {Uint8Array} publicKey + * @property {number} type + * @property {number} flags + * @property {number} outPathLen + * @property {Uint8Array} outPath + * @property {string} advName + * @property {EpochSeconds} lastAdvert + * @property {number} advLat + * @property {number} advLon + * @property {EpochSeconds} lastMod + */ + +/** + * @typedef {object} ContactMessage + * @property {Uint8Array} pubKeyPrefix + * @property {number} pathLen + * @property {TxtType} txtType + * @property {EpochSeconds} senderTimestamp + * @property {string} text + */ + +/** + * @typedef {object} ChannelMessage + * @property {number} channelIdx + * @property {number} pathLen + * @property {TxtType} txtType + * @property {EpochSeconds} senderTimestamp + * @property {string} text + */ + +/** + * @typedef {object} ChannelInfo + * @property {number} channelIdx + * @property {string} name + * @property {Uint8Array} secret + */ + +/** + * @typedef {object} SentResponse + * @property {number} result + * @property {number} expectedAckCrc + * @property {Milliseconds} estTimeout + */ + +/** + * @typedef {object} DeviceInfo + * @property {number} firmwareVer + * @property {Uint8Array} reserved + * @property {string} firmware_build_date + * @property {string} manufacturerModel + */ + +/** + * @typedef {object} BatteryVoltageResponse + * @property {MilliVolts} batteryMilliVolts + */ + +/** + * @typedef {object} ExportContactResponse + * @property {Uint8Array} advertPacketBytes + */ + +/** + * @typedef {object} PrivateKeyResponse + * @property {Uint8Array} privateKey + */ + +/** + * @typedef {object} RepeaterStats + * @property {MilliVolts} batt_milli_volts + * @property {number} curr_tx_queue_len + * @property {number} noise_floor + * @property {number} last_rssi + * @property {number} n_packets_recv + * @property {number} n_packets_sent + * @property {number} total_air_time_secs + * @property {number} total_up_time_secs + * @property {number} n_sent_flood + * @property {number} n_sent_direct + * @property {number} n_recv_flood + * @property {number} n_recv_direct + * @property {number} err_events + * @property {number} last_snr + * @property {number} n_direct_dups + * @property {number} n_flood_dups + */ + +/** + * @typedef {object} SyncMessageResult + * @property {ContactMessage} [contactMessage] + * @property {ChannelMessage} [channelMessage] + */ + +/** + * @typedef {object} Neighbour + * @property {Uint8Array} publicKeyPrefix + * @property {number} heardSecondsAgo + * @property {number} snr + */ + +/** + * @typedef {object} NeighboursResult + * @property {number} totalNeighboursCount + * @property {Neighbour[]} neighbours + */ + +/** + * @typedef {object} TraceDataResult + * @property {number} reserved + * @property {number} pathLen + * @property {number} flags + * @property {number} tag + * @property {number} authCode + * @property {Uint8Array} pathHashes + * @property {Uint8Array} pathSnrs + * @property {number} lastSnr + */ + +/** + * @typedef {object} AdvertParsedData + * @property {string | null} type + * @property {number | null} lat + * @property {number | null} lon + * @property {string | null} name + * @property {number | null} feat1 + * @property {number | null} feat2 + */ + +/** + * @typedef {object} CayenneTelemetryEntry + * @property {number} channel + * @property {number} type + * @property {number | {latitude: number, longitude: number, altitude: number}} value + */ + +/** + * @typedef {object} LoginSuccessPush + * @property {number} reserved + * @property {Uint8Array} pubKeyPrefix + */ + +/** + * @typedef {object} StatusResponsePush + * @property {number} reserved + * @property {Uint8Array} pubKeyPrefix + * @property {Uint8Array} statusData + */ + +/** + * @typedef {object} RawDataPush + * @property {number} lastSnr + * @property {number} lastRssi + * @property {number} reserved + * @property {Uint8Array} payload + */ + +/** + * @typedef {object} SendConfirmedPush + * @property {number} ackCode + * @property {Milliseconds} roundTrip + */ + +/** + * @typedef {object} LogRxDataPush + * @property {number} lastSnr + * @property {number} lastRssi + * @property {Uint8Array} raw + */ + +/** + * @typedef {object} TelemetryResponsePush + * @property {number} reserved + * @property {Uint8Array} pubKeyPrefix + * @property {Uint8Array} lppSensorData + */ + +/** + * @typedef {object} BinaryResponsePush + * @property {number} reserved + * @property {number} tag + * @property {Uint8Array} responseData + */ + +/** + * @typedef {object} NewAdvertPush + * @property {Uint8Array} publicKey + * @property {number} type + * @property {number} flags + * @property {number} outPathLen + * @property {Uint8Array} outPath + * @property {string} advName + * @property {EpochSeconds} lastAdvert + * @property {number} advLat + * @property {number} advLon + * @property {EpochSeconds} lastMod + */ + +export {}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..f3cf957 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,16 @@ +{ + "include": ["src/**/*.js"], + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "declaration": true, + "emitDeclarationOnly": true, + "declarationMap": true, + "outDir": "dist", + "module": "ESNext", + "moduleResolution": "bundler", + "target": "ESNext", + "strict": false, + "skipLibCheck": true + } +}