@seriousme/opifex 1.5.0 → 1.6.0

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/CHANGELOG.md CHANGED
@@ -4,6 +4,19 @@
4
4
 
5
5
  ### Changed
6
6
 
7
+ ## [v1.6.0] 18-01-2025
8
+
9
+ ### Changed
10
+
11
+ - added: export TcpClient and TcpServer for both NodeJS and Deno
12
+ - added: export mqttPacket and utils
13
+
14
+ ## [v1.5.1] 16-01-2025
15
+
16
+ ### Changed
17
+
18
+ - fixed issue with QoS=1
19
+
7
20
  ## [v1.5.0] 02-01-2025
8
21
 
9
22
  ### Changed
@@ -5,7 +5,7 @@ import { AuthenticationResult } from "../server/mod.ts";
5
5
  import type { Context, TAuthenticationResult, Topic } from "../server/mod.ts";
6
6
  import { logger, LogLevel } from "../utils/mod.ts";
7
7
  import { getArgs, parseArgs } from "../utils/mod.ts";
8
- import { TcpServer } from "../node/server.ts";
8
+ import { TcpServer } from "../node/tcpServer.ts";
9
9
 
10
10
  const utf8Decoder = new TextDecoder();
11
11
  const userTable = new Map();
package/bin/demoServer.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env -S node --experimental-strip-types
2
- import { TcpServer } from "../node/server.ts";
2
+ import { TcpServer } from "../node/tcpServer.ts";
3
3
  import type { Context, TAuthenticationResult, Topic } from "../server/mod.ts";
4
4
  import { AuthenticationResult } from "../server/mod.ts";
5
5
  import { getArgs, logger, LogLevel, parseArgs } from "../utils/mod.ts";
package/bin/mqtt.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  import { DEFAULT_URL } from "../client/mod.ts";
3
3
  import { logger, LogLevel } from "../utils/mod.ts";
4
4
  import { getArgs, parseArgs } from "../utils/mod.ts";
5
- import { getFileData, TcpClient } from "../node/client.ts";
5
+ import { getFileData, TcpClient } from "../node/tcpClient.ts";
6
6
  import type { Args } from "../utils/args.ts";
7
7
 
8
8
  const client = new TcpClient();
package/client/client.ts CHANGED
@@ -173,19 +173,23 @@ export class Client {
173
173
  type: PacketType.publish,
174
174
  ...params,
175
175
  };
176
- await this.ctx.send(packet);
176
+ await this.ctx.publish(packet);
177
177
  }
178
178
 
179
179
  async subscribe(params: SubscribeParameters): Promise<void> {
180
180
  const packet: SubscribePacket = {
181
181
  type: PacketType.subscribe,
182
- id: this.ctx.store.nextId(),
182
+ id: 0, //placeholder
183
183
  ...params,
184
184
  };
185
- await this.ctx.send(packet);
185
+ await this.ctx.subscribe(packet);
186
186
  }
187
187
 
188
188
  async *messages(): AsyncGenerator<PublishPacket, void, unknown> {
189
189
  yield* this.ctx.incoming;
190
190
  }
191
+
192
+ closeMessages(reason?: string) {
193
+ this.ctx.incoming.close(reason);
194
+ }
191
195
  }
@@ -25,7 +25,7 @@ export async function handlePacket(
25
25
  ctx: Context,
26
26
  packet: AnyPacket,
27
27
  ): Promise<void> {
28
- logger.debug({ received: JSON.stringify(packet, null, 2) });
28
+ logger.debug({ received: PacketNameByType[packet.type], packet });
29
29
  if (ctx.connectionState !== ConnectionState.connected) {
30
30
  if (packet.type === PacketType.connack) {
31
31
  handleConnack(packet as ConnackPacket, ctx);
@@ -1,6 +1,6 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { test } from "node:test";
3
- import { TcpServer } from "./server.ts";
3
+ import { TcpServer } from "./tcpServer.ts";
4
4
 
5
5
  test("Deno testServer", () => {
6
6
  const server = new TcpServer({ port: 0 }, {});
@@ -29,4 +29,11 @@ export class TcpServer {
29
29
  }
30
30
  return undefined;
31
31
  }
32
+
33
+ get address() {
34
+ if (this.listener.addr.transport === "tcp") {
35
+ return this.listener.addr.hostname;
36
+ }
37
+ return undefined;
38
+ }
32
39
  }
package/deno.json CHANGED
@@ -1,9 +1,13 @@
1
1
  {
2
2
  "name": "@seriousme/opifex",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "exports": {
5
5
  "./server": "./server/mod.ts",
6
- "./client": "./client/mod.ts"
6
+ "./client": "./client/mod.ts",
7
+ "./mqttPacket": "./mqttPacket/mod.ts",
8
+ "./utils": "./utils/mod.ts,",
9
+ "./tcpClient": "./deno/tcpClient.ts",
10
+ "./tcpServer": "./deno/tcpServer.ts"
7
11
  },
8
12
  "exclude": [
9
13
  "dist"
@@ -3,7 +3,7 @@
3
3
  import { AuthenticationResult } from "../server/mod.js";
4
4
  import { logger, LogLevel } from "../utils/mod.js";
5
5
  import { getArgs, parseArgs } from "../utils/mod.js";
6
- import { TcpServer } from "../node/server.js";
6
+ import { TcpServer } from "../node/tcpServer.js";
7
7
  const utf8Decoder = new TextDecoder();
8
8
  const userTable = new Map();
9
9
  userTable.set("IoTester_1", "strong_password");
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env -S node --experimental-strip-types
2
- import { TcpServer } from "../node/server.js";
2
+ import { TcpServer } from "../node/tcpServer.js";
3
3
  import { AuthenticationResult } from "../server/mod.js";
4
4
  import { getArgs, logger, LogLevel, parseArgs } from "../utils/mod.js";
5
5
  const utf8Decoder = new TextDecoder();
package/dist/bin/mqtt.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { DEFAULT_URL } from "../client/mod.js";
3
3
  import { logger, LogLevel } from "../utils/mod.js";
4
4
  import { getArgs, parseArgs } from "../utils/mod.js";
5
- import { getFileData, TcpClient } from "../node/client.js";
5
+ import { getFileData, TcpClient } from "../node/tcpClient.js";
6
6
  const client = new TcpClient();
7
7
  const encoder = new TextEncoder();
8
8
  const decoder = new TextDecoder();
@@ -113,17 +113,20 @@ export class Client {
113
113
  type: PacketType.publish,
114
114
  ...params,
115
115
  };
116
- await this.ctx.send(packet);
116
+ await this.ctx.publish(packet);
117
117
  }
118
118
  async subscribe(params) {
119
119
  const packet = {
120
120
  type: PacketType.subscribe,
121
- id: this.ctx.store.nextId(),
121
+ id: 0, //placeholder
122
122
  ...params,
123
123
  };
124
- await this.ctx.send(packet);
124
+ await this.ctx.subscribe(packet);
125
125
  }
126
126
  async *messages() {
127
127
  yield* this.ctx.incoming;
128
128
  }
129
+ closeMessages(reason) {
130
+ this.ctx.incoming.close(reason);
131
+ }
129
132
  }
@@ -9,7 +9,7 @@ import { handleSuback } from "./handleSuback.js";
9
9
  import { handleUnsuback } from "./handleUnsuback.js";
10
10
  import { logger, PacketNameByType, PacketType } from "../deps.js";
11
11
  export async function handlePacket(ctx, packet) {
12
- logger.debug({ received: JSON.stringify(packet, null, 2) });
12
+ logger.debug({ received: PacketNameByType[packet.type], packet });
13
13
  if (ctx.connectionState !== ConnectionState.connected) {
14
14
  if (packet.type === PacketType.connack) {
15
15
  handleConnack(packet, ctx);
@@ -17,6 +17,24 @@ export class TcpServer {
17
17
  this.server?.close();
18
18
  }
19
19
  get port() {
20
- return this.serverOptions.port;
20
+ const address = this.server?.address();
21
+ if (typeof address === "object") {
22
+ return address?.port;
23
+ }
24
+ return this.serverOptions?.port;
25
+ }
26
+ get address() {
27
+ const addressResult = this.server?.address();
28
+ if (typeof addressResult === "object") {
29
+ const address = addressResult?.address;
30
+ if (address === "::") {
31
+ return "localhost";
32
+ }
33
+ if (address?.includes(":")) {
34
+ return `[${address}]`;
35
+ }
36
+ return address;
37
+ }
38
+ return addressResult;
21
39
  }
22
40
  }
@@ -21,10 +21,11 @@ export async function handlePublish(ctx, packet) {
21
21
  if (packet.id !== undefined) {
22
22
  // qos 1
23
23
  if (qos === 1) {
24
+ const id = packet.id; // retain the id
24
25
  ctx.persistence.publish(packet.topic, packet);
25
26
  await ctx.send({
26
27
  type: PacketType.puback,
27
- id: packet.id,
28
+ id,
28
29
  });
29
30
  return;
30
31
  }
@@ -1 +1 @@
1
- {"root":["../bin/conformanceServer.ts","../bin/demoServer.ts","../bin/mqtt.ts"],"version":"5.7.2"}
1
+ {"root":["../bin/conformanceServer.ts","../bin/demoServer.ts","../bin/mqtt.ts"],"version":"5.7.3"}
@@ -0,0 +1,94 @@
1
+ import { test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { TcpClient } from "./tcpClient.ts";
4
+ import { TcpServer } from "./tcpServer.ts";
5
+ import { logger, LogLevel } from "../utils/mod.ts";
6
+ import type { PublishPacket, QoS } from "../mqttPacket/mod.ts";
7
+
8
+ logger.level(LogLevel.info);
9
+ export function sleep(ms: number): Promise<unknown> {
10
+ return new Promise((r) => setTimeout(r, ms));
11
+ }
12
+ export async function serverTest() {
13
+ const server = new TcpServer({ port: 0 }, {});
14
+ server.start();
15
+ await sleep(1000);
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 pubSub using client and server", async function () {
92
+ await serverTest();
93
+ logger.verbose("End of test");
94
+ });
@@ -31,6 +31,25 @@ export class TcpServer {
31
31
  }
32
32
 
33
33
  get port() {
34
- return this.serverOptions.port;
34
+ const address = this.server?.address();
35
+ if (typeof address === "object") {
36
+ return address?.port;
37
+ }
38
+ return this.serverOptions?.port;
39
+ }
40
+
41
+ get address() {
42
+ const addressResult = this.server?.address();
43
+ if (typeof addressResult === "object") {
44
+ const address = addressResult?.address;
45
+ if (address === "::") {
46
+ return "localhost";
47
+ }
48
+ if (address?.includes(":")) {
49
+ return `[${address}]`;
50
+ }
51
+ return address;
52
+ }
53
+ return addressResult;
35
54
  }
36
55
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seriousme/opifex",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "MQTT client & server for Deno & NodeJS",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -23,12 +23,20 @@
23
23
  },
24
24
  "exports": {
25
25
  "./server": "./dist/server/mod.js",
26
- "./client": "./dist/client/mod.js"
26
+ "./client": "./dist/client/mod.js",
27
+ "./mqttPacket": "./dist/mqttPacket/mod.js",
28
+ "./utils": "./dist/utils/mod.js,",
29
+ "./tcpClient": "./dist/node/tcpClient.js",
30
+ "./tcpServer": "./dist/node/tcpServer.js"
27
31
  },
28
32
  "deno": {
29
33
  "exports": {
30
34
  "./server": "./server/mod.ts",
31
- "./client": "./client/mod.ts"
35
+ "./client": "./client/mod.ts",
36
+ "./mqttPacket": "./mqttPacket/mod.ts",
37
+ "./utils": "./utils/mod.ts,",
38
+ "./tcpClient": "./deno/tcpClient.ts",
39
+ "./tcpServer": "./deno/tcpServer.ts"
32
40
  },
33
41
  "exclude": [
34
42
  "dist"
@@ -28,10 +28,11 @@ export async function handlePublish(
28
28
  if (packet.id !== undefined) {
29
29
  // qos 1
30
30
  if (qos === 1) {
31
+ const id = packet.id; // retain the id
31
32
  ctx.persistence.publish(packet.topic, packet);
32
33
  await ctx.send({
33
34
  type: PacketType.puback,
34
- id: packet.id,
35
+ id,
35
36
  });
36
37
  return;
37
38
  }
@@ -1,14 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { test } from "node:test";
3
- import { TcpServer } from "./server.ts";
4
-
5
- test("testServer", () => {
6
- const server = new TcpServer({ port: 0 }, {});
7
- server.start();
8
- assert.deepStrictEqual(
9
- server.port !== undefined,
10
- true,
11
- "server runs a a random port",
12
- );
13
- server.stop();
14
- });
File without changes
File without changes
File without changes