@seriousme/opifex 1.1.0 → 1.2.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/bin/mqtt.ts CHANGED
@@ -9,12 +9,12 @@ const encoder = new TextEncoder();
9
9
  const decoder = new TextDecoder();
10
10
  logger.level(LogLevel.info);
11
11
 
12
- const MQTTHelp = `MQTT.ts command line interface, available commands are:
12
+ const MQTTHelp = `MQTT command line interface, available commands are:
13
13
 
14
14
  * publish publish a message to the broker
15
15
  * subscribe subscribe for updates from the broker
16
16
 
17
- Launch 'mqtt.ts [command] --help' to know more about the commands.`;
17
+ Run 'mqtt [command] --help' to know more about the commands.`;
18
18
 
19
19
  const ConnectHelp = `
20
20
  -u/--url the URL to connect to: default is ${DEFAULT_URL}
@@ -43,13 +43,13 @@ const connectOpts = {
43
43
  },
44
44
  };
45
45
 
46
- const SubscribeHelp = `Usage: MQTT.ts subscribe <options>
46
+ const SubscribeHelp = `Usage: mqtt subscribe <options>
47
47
 
48
48
  Where options are:
49
49
  -t/--topic the topic to use
50
50
  -q/--qos the QoS (0/1/2) to use, default is 0
51
51
  ${ConnectHelp}
52
- Example: MQTT.ts subscribe -t hello`;
52
+ Example: mqtt subscribe -t hello`;
53
53
 
54
54
  const subscribeOpts = {
55
55
  string: ["topic"],
@@ -125,7 +125,7 @@ async function subscribe() {
125
125
  }
126
126
  }
127
127
 
128
- const PublishHelp = `Usage: MQTT.ts publish <options>
128
+ const PublishHelp = `Usage: mqtt publish <options>
129
129
 
130
130
  Where options are:
131
131
  -t/--topic the topic to use
@@ -133,7 +133,7 @@ Where options are:
133
133
  -q/--qos the QoS (0/1/2) to use, default is 0
134
134
  -r/--retain if the message should be retained, default is false
135
135
  ${ConnectHelp}
136
- Example: MQTT.ts publish -t hello -m world`;
136
+ Example: mqtt publish -t hello -m world`;
137
137
 
138
138
  const publishOpts = {
139
139
  string: ["topic", "message"],
@@ -0,0 +1,10 @@
1
+ export const ConnectionState = {
2
+ offline: "offline",
3
+ connecting: "connecting",
4
+ connected: "connected",
5
+ disconnecting: "disconnecting",
6
+ disconnected: "disconnected",
7
+ } as const;
8
+
9
+ export type TConnectionState =
10
+ (typeof ConnectionState)[keyof typeof ConnectionState];
package/client/context.ts CHANGED
@@ -18,17 +18,8 @@ import {
18
18
  } from "./deps.ts";
19
19
 
20
20
  import { handlePacket } from "./handlers/handlePacket.ts";
21
-
22
- export const ConnectionState = {
23
- offline: "offline",
24
- connecting: "connecting",
25
- connected: "connected",
26
- disconnecting: "disconnecting",
27
- disconnected: "disconnected",
28
- } as const;
29
-
30
- export type TConnectionState =
31
- typeof ConnectionState[keyof typeof ConnectionState];
21
+ import type { TConnectionState } from "./ConnectionState.ts";
22
+ import { ConnectionState } from "./ConnectionState.ts";
32
23
 
33
24
  export class Context {
34
25
  mqttConn?: MqttConn;
@@ -1,4 +1,5 @@
1
- import { ConnectionState, type Context } from "../context.ts";
1
+ import type { Context } from "../context.ts";
2
+ import { ConnectionState } from "../ConnectionState.ts";
2
3
  import { AuthenticationResultByNumber, type ConnackPacket } from "../deps.ts";
3
4
 
4
5
  export async function handleConnack(packet: ConnackPacket, ctx: Context) {
@@ -1,4 +1,5 @@
1
- import { ConnectionState, type Context } from "../context.ts";
1
+ import type { Context } from "../context.ts";
2
+ import { ConnectionState } from "../ConnectionState.ts";
2
3
  import { handleConnack } from "./handleConnack.ts";
3
4
  import { handlePublish } from "./handlePublish.ts";
4
5
  import { handlePuback } from "./handlePuback.ts";
package/deno/README.md CHANGED
@@ -59,6 +59,8 @@ deno run -A bin/mqtt.ts
59
59
 
60
60
  Opifex is also available on [JSR.io](https://jsr.io/@seriousme/opifex).
61
61
 
62
- #Platform Native This folder also contains code to run the server using Deno
63
- sockets instead of NodeJS sockets. You can modify the import statements in the
64
- demo server/client to use these instead.
62
+ # Platform Native
63
+
64
+ This folder also contains code to run the server using Deno sockets instead of
65
+ NodeJS sockets. You can modify the import statements in the demoServer/client to
66
+ use these instead.
package/deno.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@seriousme/opifex",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "exports": {
5
5
  "./server": "./server/mod.ts",
6
6
  "./client": "./client/mod.ts"
7
- }
7
+ },
8
+ "exclude": ["dist"]
8
9
  }
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ import { createServer } from "node:net";
3
+ import {
4
+ A as AuthenticationResult,
5
+ L as LogLevel,
6
+ l as logger,
7
+ } from "../timer-DDWVNsyG.js";
8
+ import "node:process";
9
+ import { MqttServer } from "../server/mod.js";
10
+ import {
11
+ g as getArgs,
12
+ p as parseArgs,
13
+ w as wrapNodeSocket,
14
+ } from "../wrapNodeSocket-BAJm059T.js";
15
+ import "node:stream";
16
+
17
+ class TcpServer {
18
+ mqttServer;
19
+ server;
20
+ serverOptions;
21
+ constructor(serverOptions, mqttOptions) {
22
+ this.mqttServer = new MqttServer(mqttOptions);
23
+ this.serverOptions = serverOptions;
24
+ }
25
+ start() {
26
+ this.server = createServer(
27
+ (sock) => this.mqttServer.serve(wrapNodeSocket(sock)),
28
+ );
29
+ this.server.listen(this.serverOptions.port, this.serverOptions.hostname);
30
+ }
31
+ stop() {
32
+ this.server?.close();
33
+ }
34
+ get port() {
35
+ return this.serverOptions.port;
36
+ }
37
+ }
38
+
39
+ const utf8Decoder = new TextDecoder();
40
+ const userTable = /* @__PURE__ */ new Map();
41
+ userTable.set("IoTester_1", "strong_password");
42
+ userTable.set("IoTester_2", "strong_password");
43
+ function isAuthenticated(_ctx, clientId, username, password) {
44
+ const pwd = utf8Decoder.decode(password);
45
+ logger.info(
46
+ `Verifying authentication of client '${clientId}' with username '${username}' and password '${pwd}'`,
47
+ );
48
+ return AuthenticationResult.ok;
49
+ }
50
+ function isAuthorizedToPublish(ctx, topic) {
51
+ logger.debug(
52
+ `Checking authorization of client '${ctx.store?.clientId}' to publish on topic '${topic}'`,
53
+ );
54
+ return true;
55
+ }
56
+ function isAuthorizedToSubscribe(ctx, topic) {
57
+ logger.debug(
58
+ `Checking authorization of client '${ctx.store?.clientId}' to subscribe to topic '${topic}'`,
59
+ );
60
+ return true;
61
+ }
62
+ const { _: [portNum] } = parseArgs(getArgs());
63
+ const port = Number(portNum ?? 1883);
64
+ const hostname = "::";
65
+ logger.level(LogLevel.info);
66
+ const tcpServer = new TcpServer({ port, hostname }, {
67
+ handlers: {
68
+ isAuthenticated,
69
+ isAuthorizedToPublish,
70
+ isAuthorizedToSubscribe,
71
+ },
72
+ });
73
+ tcpServer.start();
74
+ logger.info(`Server started on port ${tcpServer.port}`);
@@ -0,0 +1,240 @@
1
+ #!/usr/bin/env node
2
+ import { C as Client, D as DEFAULT_URL } from "../client-DqLTBVC2.js";
3
+ import { L as LogLevel, l as logger } from "../timer-DDWVNsyG.js";
4
+ import {
5
+ g as getArgs,
6
+ p as parseArgs,
7
+ w as wrapNodeSocket,
8
+ } from "../wrapNodeSocket-BAJm059T.js";
9
+ import "node:process";
10
+ import { readFile } from "node:fs/promises";
11
+ import { connect } from "node:net";
12
+ import * as tls from "node:tls";
13
+ import "node:stream";
14
+
15
+ async function getCaCerts(filename) {
16
+ if (!filename) {
17
+ return;
18
+ }
19
+ const caCerts = await readFile(filename, { encoding: "utf-8" });
20
+ if (caCerts === "") {
21
+ return;
22
+ }
23
+ return [caCerts];
24
+ }
25
+ class TcpClient extends Client {
26
+ async connectMQTT(hostname, port = 1883) {
27
+ logger.debug({ hostname, port });
28
+ return wrapNodeSocket(await connect({ host: hostname, port }));
29
+ }
30
+ async connectMQTTS(hostname, port = 8883, caCerts) {
31
+ const opts = {
32
+ host: hostname,
33
+ port,
34
+ secureContext: caCerts
35
+ ? tls.createSecureContext({ cert: caCerts })
36
+ : void 0,
37
+ };
38
+ logger.debug({ hostname, port, caCerts });
39
+ return wrapNodeSocket(await tls.connect(opts));
40
+ }
41
+ createConn(protocol, hostname, port, caCerts) {
42
+ if (protocol === "mqtts:") {
43
+ return this.connectMQTTS(hostname, port, caCerts);
44
+ }
45
+ if (protocol === "mqtt:") {
46
+ return this.connectMQTT(hostname, port);
47
+ }
48
+ throw `Unsupported protocol: ${protocol}`;
49
+ }
50
+ }
51
+
52
+ const client = new TcpClient();
53
+ const encoder = new TextEncoder();
54
+ const decoder = new TextDecoder();
55
+ logger.level(LogLevel.info);
56
+ const MQTTHelp = `MQTT command line interface, available commands are:
57
+
58
+ * publish publish a message to the broker
59
+ * subscribe subscribe for updates from the broker
60
+
61
+ Run 'mqtt [command] --help' to know more about the commands.`;
62
+ const ConnectHelp = `
63
+ -u/--url the URL to connect to: default is ${DEFAULT_URL}
64
+ -i/--clientId the clientId to connect to the server
65
+ -U/--username the username to connect to the server
66
+ -P/--password the password to connect to the server
67
+ -c/--certFile the path to a certFile
68
+ -n/--noClean try to resume a previous session
69
+ -h/--help this text
70
+ `;
71
+ const connectOpts = {
72
+ string: ["url", "username", "password", "certFile", "clientId"],
73
+ alias: {
74
+ u: "url",
75
+ U: "username",
76
+ P: "password",
77
+ c: "certFile",
78
+ i: "clientId",
79
+ n: "noClean",
80
+ h: "help",
81
+ },
82
+ boolean: ["noClean", "help"],
83
+ default: {
84
+ noClean: false,
85
+ },
86
+ };
87
+ const SubscribeHelp = `Usage: mqtt subscribe <options>
88
+
89
+ Where options are:
90
+ -t/--topic the topic to use
91
+ -q/--qos the QoS (0/1/2) to use, default is 0
92
+ ${ConnectHelp}
93
+ Example: mqtt subscribe -t hello`;
94
+ const subscribeOpts = {
95
+ string: ["topic"],
96
+ alias: {
97
+ t: "topic",
98
+ q: "qos",
99
+ },
100
+ default: {
101
+ qos: 0,
102
+ topic: "",
103
+ },
104
+ };
105
+ function parseQos(qosArg) {
106
+ const qos = Number(qosArg);
107
+ switch (qos) {
108
+ case 0:
109
+ return 0;
110
+ case 1:
111
+ return 1;
112
+ case 2:
113
+ return 2;
114
+ }
115
+ console.log("QoS must be between 0 and 2");
116
+ return 0;
117
+ }
118
+ async function subscribe() {
119
+ const connectArgs = parseArgs(getArgs(), connectOpts);
120
+ const caCerts = await getCaCerts(connectArgs.certFile);
121
+ const subscribeArgs = parseArgs(getArgs(), subscribeOpts);
122
+ if (connectArgs.help) {
123
+ console.log(SubscribeHelp);
124
+ return;
125
+ }
126
+ if (subscribeArgs.topic === void 0) {
127
+ console.log("Missing `topic`");
128
+ return;
129
+ }
130
+ try {
131
+ await client.connect({
132
+ url: connectArgs.url,
133
+ caCerts,
134
+ options: {
135
+ username: connectArgs.username,
136
+ password: encoder.encode(connectArgs.password),
137
+ clientId: connectArgs.clientId,
138
+ clean: !connectArgs.noClean,
139
+ keepAlive: 60,
140
+ },
141
+ });
142
+ logger.debug("Connected !");
143
+ client.subscribe({
144
+ subscriptions: [
145
+ {
146
+ topicFilter: subscribeArgs.topic,
147
+ qos: parseQos(subscribeArgs.qos),
148
+ },
149
+ ],
150
+ });
151
+ logger.debug("Subscribed!");
152
+ for await (const message of client.messages()) {
153
+ console.log(decoder.decode(message.payload));
154
+ }
155
+ } catch (err) {
156
+ if (err instanceof Error) {
157
+ logger.info(err.message);
158
+ }
159
+ }
160
+ }
161
+ const PublishHelp = `Usage: mqtt publish <options>
162
+
163
+ Where options are:
164
+ -t/--topic the topic to use
165
+ -m/--message the message to send
166
+ -q/--qos the QoS (0/1/2) to use, default is 0
167
+ -r/--retain if the message should be retained, default is false
168
+ ${ConnectHelp}
169
+ Example: mqtt publish -t hello -m world`;
170
+ const publishOpts = {
171
+ string: ["topic", "message"],
172
+ boolean: ["retain"],
173
+ alias: {
174
+ t: "topic",
175
+ m: "message",
176
+ q: "qos",
177
+ r: "retain",
178
+ },
179
+ default: {
180
+ qos: 0,
181
+ dup: false,
182
+ retain: false,
183
+ topic: void 0,
184
+ message: "",
185
+ },
186
+ };
187
+ async function publish() {
188
+ const connectArgs = parseArgs(getArgs(), connectOpts);
189
+ const caCerts = await getCaCerts(connectArgs.certFile);
190
+ const publishArgs = parseArgs(getArgs(), publishOpts);
191
+ if (connectArgs.help) {
192
+ console.log(PublishHelp);
193
+ return;
194
+ }
195
+ if (publishArgs.topic === void 0) {
196
+ console.log("Missing `topic`");
197
+ return;
198
+ }
199
+ try {
200
+ await client.connect({
201
+ url: connectArgs.url,
202
+ caCerts,
203
+ options: {
204
+ username: connectArgs.username,
205
+ password: encoder.encode(connectArgs.password),
206
+ clientId: connectArgs.clientId,
207
+ clean: !connectArgs.noClean,
208
+ },
209
+ });
210
+ logger.debug("Connected !");
211
+ await client.publish({
212
+ topic: publishArgs.topic,
213
+ payload: encoder.encode(publishArgs.message),
214
+ retain: publishArgs.retain,
215
+ qos: parseQos(publishArgs.qos),
216
+ });
217
+ logger.debug("Published!");
218
+ client.disconnect();
219
+ logger.debug("Disconnected !");
220
+ } catch (err) {
221
+ if (err instanceof Error) {
222
+ logger.info(err.message);
223
+ }
224
+ }
225
+ }
226
+ function processArgs() {
227
+ const { _: [cmd] } = parseArgs(getArgs());
228
+ switch (cmd) {
229
+ case "publish":
230
+ publish();
231
+ break;
232
+ case "subscribe":
233
+ subscribe();
234
+ break;
235
+ default:
236
+ console.log(MQTTHelp);
237
+ break;
238
+ }
239
+ }
240
+ processArgs();
@@ -0,0 +1,3 @@
1
+ export { C as Client, D as DEFAULT_URL } from "../client-DqLTBVC2.js";
2
+ import "../timer-DDWVNsyG.js";
3
+ import "node:process";