@seriousme/opifex 1.7.1 → 1.8.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.
package/README.md CHANGED
@@ -13,8 +13,7 @@ persistence.
13
13
 
14
14
  ## Example
15
15
 
16
- A simple server example using Deno, for more elaborate examples see the Usage
17
- section below.
16
+ A simple server example using Deno.
18
17
 
19
18
  ```typescript
20
19
  import { MqttServer } from "./server/mod.ts";
@@ -26,6 +25,9 @@ for await (const conn of this.listener) {
26
25
  }
27
26
  ```
28
27
 
28
+ A more elaborate example including client and server can be found in the
29
+ [examples](/examples/) folder.
30
+
29
31
  ## Architecture
30
32
 
31
33
  1. The basis of Opifex is the MQTT packet module
@@ -86,6 +88,19 @@ inspiration.
86
88
 
87
89
  Bun (as of version 1.2) is capable of running the NodeJS version.
88
90
 
91
+ ## Exports
92
+
93
+ | Export | Description |
94
+ | ---------------------------- | ------------------------------------------------------- |
95
+ | @seriousme/opifex/tcpClient | Exports a MQTT over TCP client |
96
+ | @seriousme/opifex/tcpServer | Exports a MQTT over TCP server |
97
+ | @seriousme/opifex/server | Exports a transport agnostic MQTT server |
98
+ | @seriousme/opifex/client | Exports a transport agnostic MQTT client |
99
+ | @seriousme/opifex/client | Exports an Typescript interface for storage persistence |
100
+ | @seriousme/opifex/mqttConn | Exports MQTT connection handling |
101
+ | @seriousme/opifex/mqttPacket | Exports MQTT packet handling |
102
+ | @seriousme/opifex/utils | Exports various utilities |
103
+
89
104
  ## Naming
90
105
 
91
106
  Some MQTT servers have names like:
@@ -1,36 +1,79 @@
1
+ /**
2
+ * @module mqttConn
3
+ */
1
4
  import { type AnyPacket } from "../mqttPacket/mod.ts";
2
5
  import type { SockConn } from "../socket/socket.ts";
3
6
  import { Conn } from "../socket/socket.ts";
7
+ /**
8
+ * Common MQTT connection error messages
9
+ */
4
10
  export declare const MqttConnError: {
5
11
  readonly invalidPacket: "Invalid Packet";
6
12
  readonly packetTooLarge: "Packet too large";
7
13
  readonly UnexpectedEof: "Unexpected EOF";
8
14
  };
15
+ /**
16
+ * Interface for MQTT connection handling
17
+ */
9
18
  export interface IMqttConn extends AsyncIterable<AnyPacket> {
19
+ /** Underlying connection */
10
20
  readonly conn: Conn;
21
+ /** Whether connection is closed */
11
22
  readonly isClosed: boolean;
23
+ /** Reason for connection closure if any */
12
24
  readonly reason: string | undefined;
25
+ /** Async iterator for receiving packets */
13
26
  [Symbol.asyncIterator](): AsyncIterableIterator<AnyPacket>;
27
+ /** Send an MQTT packet */
14
28
  send(data: AnyPacket): Promise<void>;
29
+ /** Close the connection */
15
30
  close(): void;
16
31
  }
17
- /** Read MQTT packet
18
- * @throws `Error` if packet is invalid
32
+ /**
33
+ * Read a complete MQTT packet from the connection
34
+ * @param conn Connection to read from
35
+ * @param maxPacketSize Maximum allowed packet size
36
+ * @returns Decoded MQTT packet
37
+ * @throws Error if packet is invalid or too large
19
38
  */
20
39
  export declare function readPacket(conn: Conn, maxPacketSize: number): Promise<AnyPacket>;
40
+ /**
41
+ * MQTT Connection class implementing IMqttConn interface
42
+ */
21
43
  export declare class MqttConn implements IMqttConn {
44
+ /** Underlying connection */
22
45
  readonly conn: Conn;
46
+ /** Maximum allowed packet size */
23
47
  private readonly maxPacketSize;
48
+ /** Reason for connection closure if any */
24
49
  private _reason;
50
+ /** Whether connection is closed */
25
51
  private _isClosed;
52
+ /**
53
+ * Create new MQTT connection
54
+ * @param options Connection options
55
+ * @param options.conn Underlying socket connection
56
+ * @param options.maxPacketSize Maximum allowed packet size (default 2MB)
57
+ */
26
58
  constructor({ conn, maxPacketSize, }: {
27
59
  conn: SockConn;
28
60
  maxPacketSize?: number;
29
61
  });
62
+ /** Get reason for connection closure */
30
63
  get reason(): string | undefined;
64
+ /**
65
+ * Async iterator for receiving packets
66
+ * @yields MQTT packets
67
+ */
31
68
  [Symbol.asyncIterator](): AsyncIterableIterator<AnyPacket>;
69
+ /**
70
+ * Send an MQTT packet
71
+ * @param data Packet to send
72
+ */
32
73
  send(data: AnyPacket): Promise<void>;
74
+ /** Whether connection is closed */
33
75
  get isClosed(): boolean;
76
+ /** Close the connection */
34
77
  close(): void;
35
78
  }
36
79
  //# sourceMappingURL=mqttConn.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mqttConn.d.ts","sourceRoot":"","sources":["../../mqttConn/mqttConn.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EAKf,MAAM,sBAAsB,CAAC;AAG9B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAE3C,eAAO,MAAM,aAAa;;;;CAIhB,CAAC;AAEX,MAAM,WAAW,SAAU,SAAQ,aAAa,CAAC,SAAS,CAAC;IACzD,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAC3D,IAAI,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,KAAK,IAAI,IAAI,CAAC;CACf;AAoBD;;GAEG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,IAAI,EACV,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,SAAS,CAAC,CAmBpB;AAED,qBAAa,QAAS,YAAW,SAAS;IACxC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,SAAS,CAAS;gBAEd,EACV,IAAI,EACJ,aAAa,GACd,EAAE;QACD,IAAI,EAAE,QAAQ,CAAC;QACf,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB;IAKD,IAAI,MAAM,IAAI,MAAM,GAAG,SAAS,CAE/B;IAEM,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,qBAAqB,CAAC,SAAS,CAAC;IAkB3D,IAAI,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAW1C,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,KAAK,IAAI,IAAI;CAUd"}
1
+ {"version":3,"file":"mqttConn.d.ts","sourceRoot":"","sources":["../../mqttConn/mqttConn.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,KAAK,SAAS,EAKf,MAAM,sBAAsB,CAAC;AAG9B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAE3C;;GAEG;AACH,eAAO,MAAM,aAAa;;;;CAIhB,CAAC;AAEX;;GAEG;AACH,MAAM,WAAW,SAAU,SAAQ,aAAa,CAAC,SAAS,CAAC;IACzD,4BAA4B;IAC5B,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,mCAAmC;IACnC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,2CAA2C;IAC3C,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,2CAA2C;IAC3C,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAC3D,0BAA0B;IAC1B,IAAI,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,2BAA2B;IAC3B,KAAK,IAAI,IAAI,CAAC;CACf;AAgCD;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,IAAI,EACV,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,SAAS,CAAC,CAmBpB;AAED;;GAEG;AACH,qBAAa,QAAS,YAAW,SAAS;IACxC,4BAA4B;IAC5B,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,kCAAkC;IAClC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,2CAA2C;IAC3C,OAAO,CAAC,OAAO,CAAiC;IAChD,mCAAmC;IACnC,OAAO,CAAC,SAAS,CAAS;IAE1B;;;;;OAKG;gBACS,EACV,IAAI,EACJ,aAAa,GACd,EAAE;QACD,IAAI,EAAE,QAAQ,CAAC;QACf,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB;IAKD,wCAAwC;IACxC,IAAI,MAAM,IAAI,MAAM,GAAG,SAAS,CAE/B;IAED;;;OAGG;IACI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,qBAAqB,CAAC,SAAS,CAAC;IAkBjE;;;OAGG;IACG,IAAI,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAW1C,mCAAmC;IACnC,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,2BAA2B;IAC3B,KAAK,IAAI,IAAI;CAUd"}
@@ -1,11 +1,23 @@
1
+ /**
2
+ * @module mqttConn
3
+ */
1
4
  import { decodePayload, encode, getLengthDecoder, } from "../mqttPacket/mod.js";
2
5
  import { assert } from "../utils/mod.js";
3
6
  import { Conn } from "../socket/socket.js";
7
+ /**
8
+ * Common MQTT connection error messages
9
+ */
4
10
  export const MqttConnError = {
5
11
  invalidPacket: "Invalid Packet",
6
12
  packetTooLarge: "Packet too large",
7
13
  UnexpectedEof: "Unexpected EOF",
8
14
  };
15
+ /**
16
+ * Read a single byte from the connection
17
+ * @param conn Connection to read from
18
+ * @returns Single byte as number
19
+ * @throws Error if EOF reached unexpectedly
20
+ */
9
21
  async function readByte(conn) {
10
22
  const buf = new Uint8Array(1);
11
23
  const bytesRead = await conn.read(buf);
@@ -13,6 +25,12 @@ async function readByte(conn) {
13
25
  assert(bytesRead !== 0, MqttConnError.UnexpectedEof);
14
26
  return buf[0];
15
27
  }
28
+ /**
29
+ * Read exact number of bytes into buffer
30
+ * @param conn Connection to read from
31
+ * @param buf Buffer to read into
32
+ * @throws Error if EOF reached unexpectedly
33
+ */
16
34
  async function readFull(conn, buf) {
17
35
  let bytesRead = 0;
18
36
  while (bytesRead < buf.length) {
@@ -22,8 +40,12 @@ async function readFull(conn, buf) {
22
40
  bytesRead += read;
23
41
  }
24
42
  }
25
- /** Read MQTT packet
26
- * @throws `Error` if packet is invalid
43
+ /**
44
+ * Read a complete MQTT packet from the connection
45
+ * @param conn Connection to read from
46
+ * @param maxPacketSize Maximum allowed packet size
47
+ * @returns Decoded MQTT packet
48
+ * @throws Error if packet is invalid or too large
27
49
  */
28
50
  export async function readPacket(conn, maxPacketSize) {
29
51
  // fixed header is 1 byte of type + flags
@@ -44,18 +66,36 @@ export async function readPacket(conn, maxPacketSize) {
44
66
  assert(packet !== null, MqttConnError.UnexpectedEof);
45
67
  return packet;
46
68
  }
69
+ /**
70
+ * MQTT Connection class implementing IMqttConn interface
71
+ */
47
72
  export class MqttConn {
73
+ /** Underlying connection */
48
74
  conn;
75
+ /** Maximum allowed packet size */
49
76
  maxPacketSize;
77
+ /** Reason for connection closure if any */
50
78
  _reason = undefined;
79
+ /** Whether connection is closed */
51
80
  _isClosed = false;
81
+ /**
82
+ * Create new MQTT connection
83
+ * @param options Connection options
84
+ * @param options.conn Underlying socket connection
85
+ * @param options.maxPacketSize Maximum allowed packet size (default 2MB)
86
+ */
52
87
  constructor({ conn, maxPacketSize, }) {
53
88
  this.conn = new Conn(conn);
54
89
  this.maxPacketSize = maxPacketSize || 2 * 1024 * 1024;
55
90
  }
91
+ /** Get reason for connection closure */
56
92
  get reason() {
57
93
  return this._reason;
58
94
  }
95
+ /**
96
+ * Async iterator for receiving packets
97
+ * @yields MQTT packets
98
+ */
59
99
  async *[Symbol.asyncIterator]() {
60
100
  while (!this._isClosed) {
61
101
  try {
@@ -74,6 +114,10 @@ export class MqttConn {
74
114
  }
75
115
  }
76
116
  }
117
+ /**
118
+ * Send an MQTT packet
119
+ * @param data Packet to send
120
+ */
77
121
  async send(data) {
78
122
  try {
79
123
  await this.conn.write(encode(data));
@@ -85,9 +129,11 @@ export class MqttConn {
85
129
  this.close();
86
130
  }
87
131
  }
132
+ /** Whether connection is closed */
88
133
  get isClosed() {
89
134
  return this._isClosed;
90
135
  }
136
+ /** Close the connection */
91
137
  close() {
92
138
  if (this.isClosed)
93
139
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"mqttConn.js","sourceRoot":"","sources":["../../mqttConn/mqttConn.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mqttConn.js","sourceRoot":"","sources":["../../mqttConn/mqttConn.ts"],"names":[],"mappings":"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"}
@@ -1,24 +1,81 @@
1
+ /**
2
+ * @module persistence
3
+ * @description Module for handling MQTT message persistence and client management
4
+ */
1
5
  import type { ClientId, PublishPacket, QoS, Topic } from "./deps.ts";
2
6
  import type { IStore } from "./store.ts";
7
+ /**
8
+ * Handler function type for processing publish packets
9
+ * @callback Handler
10
+ * @param {PublishPacket} packet - The MQTT publish packet to handle
11
+ */
3
12
  export type Handler = (packet: PublishPacket) => void;
13
+ /**
14
+ * Store type for retained messages mapped by topic
15
+ * @typedef {Map<Topic, PublishPacket>} RetainStore
16
+ */
4
17
  export type RetainStore = Map<Topic, PublishPacket>;
18
+ /**
19
+ * Client type containing message store and packet handler
20
+ * @typedef {Object} Client
21
+ * @property {IStore} store - The client's message store
22
+ * @property {Handler} handler - The client's packet handler function
23
+ */
5
24
  export type Client = {
6
25
  store: IStore;
7
26
  handler: Handler;
8
27
  };
9
28
  /**
10
- * IPersistence is the interface for various types of Persistence
11
- * to store messages and subscriptions
29
+ * Interface for persistence implementations to store messages and subscriptions
12
30
  * @interface IPersistence
13
31
  */
14
32
  export interface IPersistence {
33
+ /**
34
+ * Map of connected clients by client ID
35
+ * @type {Map<ClientId, Client>}
36
+ */
15
37
  clientList: Map<ClientId, Client>;
38
+ /**
39
+ * Map of retained messages by topic
40
+ * @type {RetainStore}
41
+ */
16
42
  retained: RetainStore;
43
+ /**
44
+ * Register a new client with the persistence layer
45
+ * @param {ClientId} clientId - Unique identifier for the client
46
+ * @param {Handler} handler - Packet handler function for the client
47
+ * @param {boolean} clean - Whether to start with a clean session
48
+ * @returns {IStore} The client's message store
49
+ */
17
50
  registerClient(clientId: ClientId, handler: Handler, clean: boolean): IStore;
51
+ /**
52
+ * Remove a client from the persistence layer
53
+ * @param {ClientId} clientId - ID of client to deregister
54
+ */
18
55
  deregisterClient(clientId: ClientId): void;
56
+ /**
57
+ * Publish a message to all subscribed clients
58
+ * @param {Topic} topic - Topic to publish to
59
+ * @param {PublishPacket} packet - Packet containing the message
60
+ */
19
61
  publish(topic: Topic, packet: PublishPacket): void;
62
+ /**
63
+ * Subscribe a client to a topic
64
+ * @param {IStore} store - Client's message store
65
+ * @param {Topic} topic - Topic to subscribe to
66
+ * @param {QoS} qos - Quality of Service level
67
+ */
20
68
  subscribe(store: IStore, topic: Topic, qos: QoS): void;
69
+ /**
70
+ * Unsubscribe a client from a topic
71
+ * @param {IStore} store - Client's message store
72
+ * @param {Topic} topic - Topic to unsubscribe from
73
+ */
21
74
  unsubscribe(store: IStore, topic: Topic): void;
75
+ /**
76
+ * Send retained messages to a client
77
+ * @param {ClientId} clientId - ID of client to send retained messages to
78
+ */
22
79
  handleRetained(clientId: ClientId): void;
23
80
  }
24
81
  //# sourceMappingURL=persistence.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../../persistence/persistence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;AAEtD,MAAM,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;AAEpD,MAAM,MAAM,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAEzD;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAClC,QAAQ,EAAE,WAAW,CAAC;IACtB,cAAc,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IAC7E,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3C,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;IACnD,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC;IACvD,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAC/C,cAAc,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;CAC1C"}
1
+ {"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../../persistence/persistence.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC;;;;GAIG;AACH,MAAM,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;AAEtD;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,MAAM,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAEzD;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,UAAU,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAElC;;;OAGG;IACH,QAAQ,EAAE,WAAW,CAAC;IAEtB;;;;;;OAMG;IACH,cAAc,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IAE7E;;;OAGG;IACH,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAE3C;;;;OAIG;IACH,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;IAEnD;;;;;OAKG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC;IAEvD;;;;OAIG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAE/C;;;OAGG;IACH,cAAc,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;CAC1C"}
@@ -0,0 +1,149 @@
1
+ import { test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { TcpClient } from "@seriousme/opifex/tcpClient";
4
+ import { TcpServer } from "@seriousme/opifex/tcpServer";
5
+ import { logger, LogLevel } from "@seriousme/opifex/utils";
6
+ import type { PublishPacket, QoS } from "@seriousme/opifex/mqttPacket";
7
+
8
+ logger.level(LogLevel.info);
9
+ export function sleep(ms: number): Promise<unknown> {
10
+ return new Promise((r) => setTimeout(r, ms));
11
+ }
12
+
13
+ test("Test pubSub using client and server", async function () {
14
+ const server = new TcpServer({ port: 0 }, {});
15
+ server.start();
16
+
17
+ assert.deepStrictEqual(
18
+ server.port !== undefined,
19
+ true,
20
+ "server runs a a random port",
21
+ );
22
+ logger.verbose({ port: server.port, address: server.address });
23
+
24
+ const params = {
25
+ url: new URL(`mqtt://${server.address}:${server.port}`),
26
+ numberOfRetries: 0,
27
+ };
28
+
29
+ const client = new TcpClient();
30
+
31
+ await client.connect(params);
32
+ assert(true, "Client connected to server");
33
+
34
+ const publishSet: { topic: string; qos: QoS }[] = [
35
+ { topic: "t0@q0", qos: 0 },
36
+ { topic: "t1@q0", qos: 0 },
37
+ { topic: "t2@q0", qos: 0 },
38
+ { topic: "t0@q1", qos: 1 },
39
+ { topic: "t1@q1", qos: 1 },
40
+ { topic: "t2@q1", qos: 1 },
41
+ { topic: "t0@q2", qos: 2 },
42
+ { topic: "t1@q2", qos: 2 },
43
+ { topic: "t2@q2", qos: 2 },
44
+ ];
45
+
46
+ const subscriptions = publishSet.map((item) => ({
47
+ topicFilter: item.topic,
48
+ qos: item.qos,
49
+ }));
50
+ await client.subscribe({
51
+ subscriptions,
52
+ });
53
+
54
+ // the IIFE ensures message reception runs in parallel
55
+ logger.verbose(`Start receiving`);
56
+ const received: PublishPacket[] = [];
57
+ (async function () {
58
+ for await (const item of client.messages()) {
59
+ logger.verbose(`Receiving: ${item.topic} -- ${item.qos}`);
60
+ received.push(item);
61
+ }
62
+ })();
63
+ // end of IIFE
64
+ for (const item of publishSet) {
65
+ logger.verbose(`Publishing: ${item.topic} -- ${item.qos}`);
66
+ await client.publish({
67
+ topic: item.topic,
68
+ qos: item.qos,
69
+ payload: new Uint8Array([0x01]),
70
+ });
71
+ }
72
+
73
+ await sleep(100);
74
+ logger.verbose(`Disconnect client`);
75
+ //client.closeMessages();
76
+ await client.disconnect();
77
+
78
+ logger.verbose(`Check completeness`);
79
+ for (const item of publishSet) {
80
+ const found = received.find((f) =>
81
+ f.topic == item.topic && f.qos === item.qos
82
+ );
83
+ logger.verbose(`Found: ${item.topic} -- ${item.qos}`);
84
+ assert(found, `${item.topic} -- ${item.qos}`);
85
+ }
86
+
87
+ logger.verbose(`Stop server`);
88
+ server.stop();
89
+ });
90
+
91
+ test("Test subscription persistence after reconnect", async function () {
92
+ // Start server
93
+ const server = new TcpServer({ port: 0 }, {});
94
+ server.start();
95
+
96
+ const params = {
97
+ url: new URL(`mqtt://${server.address}:${server.port}`),
98
+ numberOfRetries: 0,
99
+ };
100
+
101
+ const client = new TcpClient();
102
+ const testTopic = "test/topic";
103
+ const received: PublishPacket[] = [];
104
+
105
+ // First connection and subscription
106
+ await client.connect(params);
107
+ await client.subscribe({
108
+ subscriptions: [{
109
+ topicFilter: testTopic,
110
+ qos: 0,
111
+ }],
112
+ });
113
+
114
+ // Start receiving messages
115
+ (async function () {
116
+ for await (const item of client.messages()) {
117
+ received.push(item);
118
+ }
119
+ })();
120
+
121
+ // Disconnect client
122
+ await client.disconnect();
123
+ await sleep(100);
124
+
125
+ // Reconnect client
126
+ await client.connect(params);
127
+ await sleep(100);
128
+
129
+ // Publish test message
130
+ await client.publish({
131
+ topic: testTopic,
132
+ qos: 0,
133
+ payload: new Uint8Array([0x01]),
134
+ });
135
+
136
+ await sleep(100);
137
+ logger.verbose(`Disconnect client`);
138
+ await client.disconnect();
139
+
140
+ // Verify message was received
141
+ assert.equal(received.length, 1, "Should receive one message");
142
+ assert.equal(
143
+ received[0].topic,
144
+ testTopic,
145
+ "Should receive message on subscribed topic",
146
+ );
147
+
148
+ server.stop();
149
+ });
@@ -1,3 +1,7 @@
1
+ /**
2
+ * @module mqttConn
3
+ */
4
+
1
5
  import {
2
6
  type AnyPacket,
3
7
  decodePayload,
@@ -10,21 +14,39 @@ import { assert } from "../utils/mod.ts";
10
14
  import type { SockConn } from "../socket/socket.ts";
11
15
  import { Conn } from "../socket/socket.ts";
12
16
 
17
+ /**
18
+ * Common MQTT connection error messages
19
+ */
13
20
  export const MqttConnError = {
14
21
  invalidPacket: "Invalid Packet",
15
22
  packetTooLarge: "Packet too large",
16
23
  UnexpectedEof: "Unexpected EOF",
17
24
  } as const;
18
25
 
26
+ /**
27
+ * Interface for MQTT connection handling
28
+ */
19
29
  export interface IMqttConn extends AsyncIterable<AnyPacket> {
30
+ /** Underlying connection */
20
31
  readonly conn: Conn;
32
+ /** Whether connection is closed */
21
33
  readonly isClosed: boolean;
34
+ /** Reason for connection closure if any */
22
35
  readonly reason: string | undefined;
36
+ /** Async iterator for receiving packets */
23
37
  [Symbol.asyncIterator](): AsyncIterableIterator<AnyPacket>;
38
+ /** Send an MQTT packet */
24
39
  send(data: AnyPacket): Promise<void>;
40
+ /** Close the connection */
25
41
  close(): void;
26
42
  }
27
43
 
44
+ /**
45
+ * Read a single byte from the connection
46
+ * @param conn Connection to read from
47
+ * @returns Single byte as number
48
+ * @throws Error if EOF reached unexpectedly
49
+ */
28
50
  async function readByte(conn: Conn): Promise<number> {
29
51
  const buf = new Uint8Array(1);
30
52
  const bytesRead = await conn.read(buf);
@@ -33,6 +55,12 @@ async function readByte(conn: Conn): Promise<number> {
33
55
  return buf[0];
34
56
  }
35
57
 
58
+ /**
59
+ * Read exact number of bytes into buffer
60
+ * @param conn Connection to read from
61
+ * @param buf Buffer to read into
62
+ * @throws Error if EOF reached unexpectedly
63
+ */
36
64
  async function readFull(conn: Conn, buf: Uint8Array): Promise<void> {
37
65
  let bytesRead = 0;
38
66
  while (bytesRead < buf.length) {
@@ -43,8 +71,12 @@ async function readFull(conn: Conn, buf: Uint8Array): Promise<void> {
43
71
  }
44
72
  }
45
73
 
46
- /** Read MQTT packet
47
- * @throws `Error` if packet is invalid
74
+ /**
75
+ * Read a complete MQTT packet from the connection
76
+ * @param conn Connection to read from
77
+ * @param maxPacketSize Maximum allowed packet size
78
+ * @returns Decoded MQTT packet
79
+ * @throws Error if packet is invalid or too large
48
80
  */
49
81
  export async function readPacket(
50
82
  conn: Conn,
@@ -70,12 +102,25 @@ export async function readPacket(
70
102
  return packet;
71
103
  }
72
104
 
105
+ /**
106
+ * MQTT Connection class implementing IMqttConn interface
107
+ */
73
108
  export class MqttConn implements IMqttConn {
109
+ /** Underlying connection */
74
110
  readonly conn: Conn;
111
+ /** Maximum allowed packet size */
75
112
  private readonly maxPacketSize: number;
113
+ /** Reason for connection closure if any */
76
114
  private _reason: string | undefined = undefined;
115
+ /** Whether connection is closed */
77
116
  private _isClosed = false;
78
117
 
118
+ /**
119
+ * Create new MQTT connection
120
+ * @param options Connection options
121
+ * @param options.conn Underlying socket connection
122
+ * @param options.maxPacketSize Maximum allowed packet size (default 2MB)
123
+ */
79
124
  constructor({
80
125
  conn,
81
126
  maxPacketSize,
@@ -87,10 +132,15 @@ export class MqttConn implements IMqttConn {
87
132
  this.maxPacketSize = maxPacketSize || 2 * 1024 * 1024;
88
133
  }
89
134
 
135
+ /** Get reason for connection closure */
90
136
  get reason(): string | undefined {
91
137
  return this._reason;
92
138
  }
93
139
 
140
+ /**
141
+ * Async iterator for receiving packets
142
+ * @yields MQTT packets
143
+ */
94
144
  async *[Symbol.asyncIterator](): AsyncIterableIterator<AnyPacket> {
95
145
  while (!this._isClosed) {
96
146
  try {
@@ -109,6 +159,10 @@ export class MqttConn implements IMqttConn {
109
159
  }
110
160
  }
111
161
 
162
+ /**
163
+ * Send an MQTT packet
164
+ * @param data Packet to send
165
+ */
112
166
  async send(data: AnyPacket): Promise<void> {
113
167
  try {
114
168
  await this.conn.write(encode(data));
@@ -120,10 +174,12 @@ export class MqttConn implements IMqttConn {
120
174
  }
121
175
  }
122
176
 
177
+ /** Whether connection is closed */
123
178
  get isClosed(): boolean {
124
179
  return this._isClosed;
125
180
  }
126
181
 
182
+ /** Close the connection */
127
183
  close(): void {
128
184
  if (this.isClosed) return;
129
185
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seriousme/opifex",
3
- "version": "1.7.1",
3
+ "version": "1.8.1",
4
4
  "description": "MQTT client & server for Deno & NodeJS",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -9,7 +9,8 @@
9
9
  "build": "npx -p typescript tsc --build",
10
10
  "format": "deno fmt",
11
11
  "preversion": "npm run test",
12
- "postversion": "jq '( { name:.name , version:.version, license:.license } + .deno)' package.json > deno.json"
12
+ "postversion": "jq '( { name:.name , version:.version, license:.license } + .deno)' package.json > deno.json",
13
+ "postpostversion": "deno install"
13
14
  },
14
15
  "keywords": [
15
16
  "MQTT",
@@ -31,21 +32,25 @@
31
32
  "dist/**/*.d.ts.map"
32
33
  ],
33
34
  "exports": {
35
+ "./tcpClient": "./dist/node/tcpClient.js",
36
+ "./tcpServer": "./dist/node/tcpServer.js",
34
37
  "./server": "./dist/server/mod.js",
35
38
  "./client": "./dist/client/mod.js",
39
+ "./persistence": "./dist/persistence/persistence.js",
40
+ "./mqttConn": "./dist/mqttConn/mqttConn.js",
36
41
  "./mqttPacket": "./dist/mqttPacket/mod.js",
37
- "./utils": "./dist/utils/mod.js",
38
- "./tcpClient": "./dist/node/tcpClient.js",
39
- "./tcpServer": "./dist/node/tcpServer.js"
42
+ "./utils": "./dist/utils/mod.js"
40
43
  },
41
44
  "deno": {
42
45
  "exports": {
46
+ "./tcpClient": "./deno/tcpClient.ts",
47
+ "./tcpServer": "./deno/tcpServer.ts",
43
48
  "./server": "./server/mod.ts",
44
49
  "./client": "./client/mod.ts",
50
+ "./persistence": "./persistence/persistence.ts",
51
+ "./mqttConn": "./mqttConn/mqttConn.ts",
45
52
  "./mqttPacket": "./mqttPacket/mod.ts",
46
- "./utils": "./utils/mod.ts",
47
- "./tcpClient": "./deno/tcpClient.ts",
48
- "./tcpServer": "./deno/tcpServer.ts"
53
+ "./utils": "./utils/mod.ts"
49
54
  },
50
55
  "exclude": [
51
56
  "dist"
@@ -1,24 +1,88 @@
1
+ /**
2
+ * @module persistence
3
+ * @description Module for handling MQTT message persistence and client management
4
+ */
1
5
  import type { ClientId, PublishPacket, QoS, Topic } from "./deps.ts";
2
6
  import type { IStore } from "./store.ts";
3
7
 
8
+ /**
9
+ * Handler function type for processing publish packets
10
+ * @callback Handler
11
+ * @param {PublishPacket} packet - The MQTT publish packet to handle
12
+ */
4
13
  export type Handler = (packet: PublishPacket) => void;
5
14
 
15
+ /**
16
+ * Store type for retained messages mapped by topic
17
+ * @typedef {Map<Topic, PublishPacket>} RetainStore
18
+ */
6
19
  export type RetainStore = Map<Topic, PublishPacket>;
7
20
 
21
+ /**
22
+ * Client type containing message store and packet handler
23
+ * @typedef {Object} Client
24
+ * @property {IStore} store - The client's message store
25
+ * @property {Handler} handler - The client's packet handler function
26
+ */
8
27
  export type Client = { store: IStore; handler: Handler };
9
28
 
10
29
  /**
11
- * IPersistence is the interface for various types of Persistence
12
- * to store messages and subscriptions
30
+ * Interface for persistence implementations to store messages and subscriptions
13
31
  * @interface IPersistence
14
32
  */
15
33
  export interface IPersistence {
34
+ /**
35
+ * Map of connected clients by client ID
36
+ * @type {Map<ClientId, Client>}
37
+ */
16
38
  clientList: Map<ClientId, Client>;
39
+
40
+ /**
41
+ * Map of retained messages by topic
42
+ * @type {RetainStore}
43
+ */
17
44
  retained: RetainStore;
45
+
46
+ /**
47
+ * Register a new client with the persistence layer
48
+ * @param {ClientId} clientId - Unique identifier for the client
49
+ * @param {Handler} handler - Packet handler function for the client
50
+ * @param {boolean} clean - Whether to start with a clean session
51
+ * @returns {IStore} The client's message store
52
+ */
18
53
  registerClient(clientId: ClientId, handler: Handler, clean: boolean): IStore;
54
+
55
+ /**
56
+ * Remove a client from the persistence layer
57
+ * @param {ClientId} clientId - ID of client to deregister
58
+ */
19
59
  deregisterClient(clientId: ClientId): void;
60
+
61
+ /**
62
+ * Publish a message to all subscribed clients
63
+ * @param {Topic} topic - Topic to publish to
64
+ * @param {PublishPacket} packet - Packet containing the message
65
+ */
20
66
  publish(topic: Topic, packet: PublishPacket): void;
67
+
68
+ /**
69
+ * Subscribe a client to a topic
70
+ * @param {IStore} store - Client's message store
71
+ * @param {Topic} topic - Topic to subscribe to
72
+ * @param {QoS} qos - Quality of Service level
73
+ */
21
74
  subscribe(store: IStore, topic: Topic, qos: QoS): void;
75
+
76
+ /**
77
+ * Unsubscribe a client from a topic
78
+ * @param {IStore} store - Client's message store
79
+ * @param {Topic} topic - Topic to unsubscribe from
80
+ */
22
81
  unsubscribe(store: IStore, topic: Topic): void;
82
+
83
+ /**
84
+ * Send retained messages to a client
85
+ * @param {ClientId} clientId - ID of client to send retained messages to
86
+ */
23
87
  handleRetained(clientId: ClientId): void;
24
88
  }