@seriousme/opifex 1.9.3 → 1.10.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.
Files changed (304) hide show
  1. package/README.md +37 -26
  2. package/bin/conformanceServer.ts +3 -2
  3. package/bin/demoServer.ts +3 -2
  4. package/bin/mqtt.ts +126 -117
  5. package/client/client.ts +35 -15
  6. package/client/context.ts +42 -22
  7. package/client/deps.ts +5 -0
  8. package/client/handlers/handleConnack.ts +17 -5
  9. package/client/handlers/handlePublish.ts +4 -1
  10. package/client/handlers/handlePubrec.ts +3 -1
  11. package/client/handlers/handlePubrel.ts +3 -1
  12. package/client/handlers/handleSuback.ts +3 -1
  13. package/client/mod.ts +8 -1
  14. package/client/store/memoryStore.ts +2 -1
  15. package/dev_utils/dummyConn.ts +17 -2
  16. package/dist/bin/conformanceServer.js +3 -2
  17. package/dist/bin/conformanceServer.js.map +1 -1
  18. package/dist/bin/demoServer.js +3 -2
  19. package/dist/bin/demoServer.js.map +1 -1
  20. package/dist/bin/mqtt.d.ts +29 -0
  21. package/dist/bin/mqtt.d.ts.map +1 -1
  22. package/dist/bin/mqtt.js +101 -96
  23. package/dist/bin/mqtt.js.map +1 -1
  24. package/dist/client/client.d.ts +7 -4
  25. package/dist/client/client.d.ts.map +1 -1
  26. package/dist/client/client.js +17 -9
  27. package/dist/client/client.js.map +1 -1
  28. package/dist/client/context.d.ts +3 -1
  29. package/dist/client/context.d.ts.map +1 -1
  30. package/dist/client/context.js +27 -13
  31. package/dist/client/context.js.map +1 -1
  32. package/dist/client/deps.d.ts +3 -2
  33. package/dist/client/deps.d.ts.map +1 -1
  34. package/dist/client/deps.js +2 -1
  35. package/dist/client/deps.js.map +1 -1
  36. package/dist/client/handlers/handleConnack.d.ts +1 -1
  37. package/dist/client/handlers/handleConnack.d.ts.map +1 -1
  38. package/dist/client/handlers/handleConnack.js +14 -5
  39. package/dist/client/handlers/handleConnack.js.map +1 -1
  40. package/dist/client/handlers/handlePublish.d.ts +1 -1
  41. package/dist/client/handlers/handlePublish.d.ts.map +1 -1
  42. package/dist/client/handlers/handlePublish.js +2 -0
  43. package/dist/client/handlers/handlePublish.js.map +1 -1
  44. package/dist/client/handlers/handlePubrec.d.ts +1 -1
  45. package/dist/client/handlers/handlePubrec.d.ts.map +1 -1
  46. package/dist/client/handlers/handlePubrec.js +1 -0
  47. package/dist/client/handlers/handlePubrec.js.map +1 -1
  48. package/dist/client/handlers/handlePubrel.d.ts +1 -1
  49. package/dist/client/handlers/handlePubrel.d.ts.map +1 -1
  50. package/dist/client/handlers/handlePubrel.js +1 -0
  51. package/dist/client/handlers/handlePubrel.js.map +1 -1
  52. package/dist/client/handlers/handleSuback.d.ts.map +1 -1
  53. package/dist/client/handlers/handleSuback.js +3 -1
  54. package/dist/client/handlers/handleSuback.js.map +1 -1
  55. package/dist/client/mod.d.ts +3 -1
  56. package/dist/client/mod.d.ts.map +1 -1
  57. package/dist/client/mod.js +2 -1
  58. package/dist/client/mod.js.map +1 -1
  59. package/dist/client/store/memoryStore.d.ts +1 -1
  60. package/dist/client/store/memoryStore.d.ts.map +1 -1
  61. package/dist/client/store/memoryStore.js.map +1 -1
  62. package/dist/mqttConn/mqttConn.d.ts +13 -8
  63. package/dist/mqttConn/mqttConn.d.ts.map +1 -1
  64. package/dist/mqttConn/mqttConn.js +27 -12
  65. package/dist/mqttConn/mqttConn.js.map +1 -1
  66. package/dist/mqttPacket/PacketType.d.ts +1 -0
  67. package/dist/mqttPacket/PacketType.d.ts.map +1 -1
  68. package/dist/mqttPacket/PacketType.js +1 -0
  69. package/dist/mqttPacket/PacketType.js.map +1 -1
  70. package/dist/mqttPacket/Properties.d.ts +157 -0
  71. package/dist/mqttPacket/Properties.d.ts.map +1 -0
  72. package/dist/mqttPacket/Properties.js +177 -0
  73. package/dist/mqttPacket/Properties.js.map +1 -0
  74. package/dist/mqttPacket/ReasonCode.d.ts +74 -0
  75. package/dist/mqttPacket/ReasonCode.d.ts.map +1 -0
  76. package/dist/mqttPacket/ReasonCode.js +230 -0
  77. package/dist/mqttPacket/ReasonCode.js.map +1 -0
  78. package/dist/mqttPacket/RetainHandling.d.ts +12 -0
  79. package/dist/mqttPacket/RetainHandling.d.ts.map +1 -0
  80. package/dist/mqttPacket/RetainHandling.js +12 -0
  81. package/dist/mqttPacket/RetainHandling.js.map +1 -0
  82. package/dist/mqttPacket/auth.d.ts +21 -0
  83. package/dist/mqttPacket/auth.d.ts.map +1 -0
  84. package/dist/mqttPacket/auth.js +34 -0
  85. package/dist/mqttPacket/auth.js.map +1 -0
  86. package/dist/mqttPacket/connack.d.ts +15 -7
  87. package/dist/mqttPacket/connack.d.ts.map +1 -1
  88. package/dist/mqttPacket/connack.js +43 -15
  89. package/dist/mqttPacket/connack.js.map +1 -1
  90. package/dist/mqttPacket/connect.d.ts +27 -8
  91. package/dist/mqttPacket/connect.d.ts.map +1 -1
  92. package/dist/mqttPacket/connect.js +106 -30
  93. package/dist/mqttPacket/connect.js.map +1 -1
  94. package/dist/mqttPacket/decoder.d.ts +42 -3
  95. package/dist/mqttPacket/decoder.d.ts.map +1 -1
  96. package/dist/mqttPacket/decoder.js +135 -4
  97. package/dist/mqttPacket/decoder.js.map +1 -1
  98. package/dist/mqttPacket/disconnect.d.ts +13 -7
  99. package/dist/mqttPacket/disconnect.d.ts.map +1 -1
  100. package/dist/mqttPacket/disconnect.js +45 -6
  101. package/dist/mqttPacket/disconnect.js.map +1 -1
  102. package/dist/mqttPacket/encoder.d.ts +57 -8
  103. package/dist/mqttPacket/encoder.d.ts.map +1 -1
  104. package/dist/mqttPacket/encoder.js +185 -13
  105. package/dist/mqttPacket/encoder.js.map +1 -1
  106. package/dist/mqttPacket/length.d.ts.map +1 -1
  107. package/dist/mqttPacket/length.js +3 -2
  108. package/dist/mqttPacket/length.js.map +1 -1
  109. package/dist/mqttPacket/mod.d.ts +56 -91
  110. package/dist/mqttPacket/mod.d.ts.map +1 -1
  111. package/dist/mqttPacket/mod.js +22 -22
  112. package/dist/mqttPacket/mod.js.map +1 -1
  113. package/dist/mqttPacket/pingreq.d.ts +4 -6
  114. package/dist/mqttPacket/pingreq.d.ts.map +1 -1
  115. package/dist/mqttPacket/pingreq.js +5 -4
  116. package/dist/mqttPacket/pingreq.js.map +1 -1
  117. package/dist/mqttPacket/pingres.d.ts +4 -6
  118. package/dist/mqttPacket/pingres.d.ts.map +1 -1
  119. package/dist/mqttPacket/pingres.js +7 -5
  120. package/dist/mqttPacket/pingres.js.map +1 -1
  121. package/dist/mqttPacket/protocolLevels.d.ts +11 -0
  122. package/dist/mqttPacket/protocolLevels.d.ts.map +1 -0
  123. package/dist/mqttPacket/protocolLevels.js +11 -0
  124. package/dist/mqttPacket/protocolLevels.js.map +1 -0
  125. package/dist/mqttPacket/pubblishAcks.d.ts +35 -0
  126. package/dist/mqttPacket/pubblishAcks.d.ts.map +1 -0
  127. package/dist/mqttPacket/pubblishAcks.js +56 -0
  128. package/dist/mqttPacket/pubblishAcks.js.map +1 -0
  129. package/dist/mqttPacket/publish.d.ts +19 -8
  130. package/dist/mqttPacket/publish.d.ts.map +1 -1
  131. package/dist/mqttPacket/publish.js +26 -7
  132. package/dist/mqttPacket/publish.js.map +1 -1
  133. package/dist/mqttPacket/suback.d.ts +14 -7
  134. package/dist/mqttPacket/suback.d.ts.map +1 -1
  135. package/dist/mqttPacket/suback.js +41 -20
  136. package/dist/mqttPacket/suback.js.map +1 -1
  137. package/dist/mqttPacket/subscribe.d.ts +24 -9
  138. package/dist/mqttPacket/subscribe.d.ts.map +1 -1
  139. package/dist/mqttPacket/subscribe.js +71 -15
  140. package/dist/mqttPacket/subscribe.js.map +1 -1
  141. package/dist/mqttPacket/types.d.ts +33 -0
  142. package/dist/mqttPacket/types.d.ts.map +1 -1
  143. package/dist/mqttPacket/unsuback.d.ts +14 -7
  144. package/dist/mqttPacket/unsuback.d.ts.map +1 -1
  145. package/dist/mqttPacket/unsuback.js +29 -6
  146. package/dist/mqttPacket/unsuback.js.map +1 -1
  147. package/dist/mqttPacket/unsubscribe.d.ts +14 -7
  148. package/dist/mqttPacket/unsubscribe.d.ts.map +1 -1
  149. package/dist/mqttPacket/unsubscribe.js +24 -8
  150. package/dist/mqttPacket/unsubscribe.js.map +1 -1
  151. package/dist/node/wrapNodeSocket.d.ts.map +1 -1
  152. package/dist/node/wrapNodeSocket.js.map +1 -1
  153. package/dist/persistence/deps.d.ts +1 -1
  154. package/dist/persistence/deps.d.ts.map +1 -1
  155. package/dist/persistence/deps.js +1 -1
  156. package/dist/persistence/deps.js.map +1 -1
  157. package/dist/server/context.d.ts +3 -1
  158. package/dist/server/context.d.ts.map +1 -1
  159. package/dist/server/context.js +8 -2
  160. package/dist/server/context.js.map +1 -1
  161. package/dist/server/deps.d.ts +2 -2
  162. package/dist/server/deps.d.ts.map +1 -1
  163. package/dist/server/deps.js +1 -1
  164. package/dist/server/deps.js.map +1 -1
  165. package/dist/server/handlers/handleConnect.d.ts +1 -1
  166. package/dist/server/handlers/handleConnect.d.ts.map +1 -1
  167. package/dist/server/handlers/handleConnect.js +9 -1
  168. package/dist/server/handlers/handleConnect.js.map +1 -1
  169. package/dist/server/handlers/handlePacket.d.ts +1 -1
  170. package/dist/server/handlers/handlePacket.d.ts.map +1 -1
  171. package/dist/server/handlers/handlePacket.js +1 -1
  172. package/dist/server/handlers/handlePacket.js.map +1 -1
  173. package/dist/server/handlers/handlePingreq.d.ts.map +1 -1
  174. package/dist/server/handlers/handlePingreq.js +1 -0
  175. package/dist/server/handlers/handlePingreq.js.map +1 -1
  176. package/dist/server/handlers/handlePublish.d.ts +2 -2
  177. package/dist/server/handlers/handlePublish.d.ts.map +1 -1
  178. package/dist/server/handlers/handlePublish.js +2 -0
  179. package/dist/server/handlers/handlePublish.js.map +1 -1
  180. package/dist/server/handlers/handlePubrec.d.ts +1 -1
  181. package/dist/server/handlers/handlePubrec.d.ts.map +1 -1
  182. package/dist/server/handlers/handlePubrec.js +1 -0
  183. package/dist/server/handlers/handlePubrec.js.map +1 -1
  184. package/dist/server/handlers/handlePubrel.d.ts +1 -1
  185. package/dist/server/handlers/handlePubrel.d.ts.map +1 -1
  186. package/dist/server/handlers/handlePubrel.js +1 -0
  187. package/dist/server/handlers/handlePubrel.js.map +1 -1
  188. package/dist/server/handlers/handleSubscribe.d.ts +1 -1
  189. package/dist/server/handlers/handleSubscribe.d.ts.map +1 -1
  190. package/dist/server/handlers/handleSubscribe.js +2 -1
  191. package/dist/server/handlers/handleSubscribe.js.map +1 -1
  192. package/dist/server/handlers/handleUnsubscribe.d.ts +1 -1
  193. package/dist/server/handlers/handleUnsubscribe.d.ts.map +1 -1
  194. package/dist/server/handlers/handleUnsubscribe.js +1 -0
  195. package/dist/server/handlers/handleUnsubscribe.js.map +1 -1
  196. package/dist/server/server.d.ts +2 -2
  197. package/dist/server/server.d.ts.map +1 -1
  198. package/dist/server/server.js +5 -3
  199. package/dist/server/server.js.map +1 -1
  200. package/dist/socket/socket.d.ts.map +1 -1
  201. package/dist/socket/socket.js +6 -1
  202. package/dist/socket/socket.js.map +1 -1
  203. package/dist/utils/assert.d.ts +0 -3
  204. package/dist/utils/assert.d.ts.map +1 -1
  205. package/dist/utils/assert.js +0 -7
  206. package/dist/utils/assert.js.map +1 -1
  207. package/dist/utils/mod.d.ts +0 -3
  208. package/dist/utils/mod.d.ts.map +1 -1
  209. package/dist/utils/mod.js +0 -2
  210. package/dist/utils/mod.js.map +1 -1
  211. package/mqttConn/mqttConn.test.ts +27 -12
  212. package/mqttConn/mqttConn.ts +45 -14
  213. package/mqttPacket/PacketType.ts +1 -0
  214. package/mqttPacket/Properties.ts +276 -0
  215. package/mqttPacket/ReasonCode.test.ts +22 -0
  216. package/mqttPacket/ReasonCode.ts +256 -0
  217. package/mqttPacket/RetainHandling.ts +11 -0
  218. package/mqttPacket/auth.test.ts +112 -0
  219. package/mqttPacket/auth.ts +72 -0
  220. package/mqttPacket/connack.test.ts +84 -64
  221. package/mqttPacket/connack.ts +84 -20
  222. package/mqttPacket/connect.test.ts +497 -18
  223. package/mqttPacket/connect.ts +167 -36
  224. package/mqttPacket/decoder.test.ts +33 -16
  225. package/mqttPacket/decoder.ts +173 -5
  226. package/mqttPacket/disconnect.test.ts +97 -18
  227. package/mqttPacket/disconnect.ts +82 -10
  228. package/mqttPacket/encoder.test.ts +32 -8
  229. package/mqttPacket/encoder.ts +236 -11
  230. package/mqttPacket/length.ts +3 -2
  231. package/mqttPacket/mod.ts +75 -32
  232. package/mqttPacket/pingreq.test.ts +52 -4
  233. package/mqttPacket/pingreq.ts +20 -7
  234. package/mqttPacket/pingres.test.ts +52 -4
  235. package/mqttPacket/pingres.ts +22 -8
  236. package/mqttPacket/properties.test.ts +193 -0
  237. package/mqttPacket/protocolLevels.ts +13 -0
  238. package/mqttPacket/pubblishAcks.ts +117 -0
  239. package/mqttPacket/publish.test.ts +212 -133
  240. package/mqttPacket/publish.ts +69 -14
  241. package/mqttPacket/publishAcks.test.ts +128 -0
  242. package/mqttPacket/suback.test.ts +71 -27
  243. package/mqttPacket/suback.ts +78 -26
  244. package/mqttPacket/subscribe.test.ts +132 -43
  245. package/mqttPacket/subscribe.ts +125 -22
  246. package/mqttPacket/types.ts +40 -0
  247. package/mqttPacket/unsuback.test.ts +80 -30
  248. package/mqttPacket/unsuback.ts +65 -10
  249. package/mqttPacket/unsubscribe.test.ts +59 -34
  250. package/mqttPacket/unsubscribe.ts +61 -14
  251. package/node/wrapNodeSocket.ts +2 -1
  252. package/package.json +2 -2
  253. package/persistence/deps.ts +1 -1
  254. package/persistence/memory/memoryPersistence.test.ts +5 -1
  255. package/server/context.ts +23 -10
  256. package/server/deps.ts +2 -0
  257. package/server/handlers/handleConnect.ts +12 -9
  258. package/server/handlers/handlePacket.ts +10 -11
  259. package/server/handlers/handlePingreq.ts +1 -0
  260. package/server/handlers/handlePublish.ts +6 -2
  261. package/server/handlers/handlePubrec.ts +3 -1
  262. package/server/handlers/handlePubrel.ts +3 -1
  263. package/server/handlers/handleSubscribe.ts +3 -6
  264. package/server/handlers/handleUnsubscribe.ts +3 -1
  265. package/server/server.ts +8 -12
  266. package/server/test/handleConnect.test.ts +2 -1
  267. package/server/test/test-handlers.ts +2 -1
  268. package/socket/socket.ts +5 -1
  269. package/utils/assert.ts +0 -18
  270. package/utils/mod.ts +0 -3
  271. package/dist/mqttPacket/puback.d.ts +0 -16
  272. package/dist/mqttPacket/puback.d.ts.map +0 -1
  273. package/dist/mqttPacket/puback.js +0 -21
  274. package/dist/mqttPacket/puback.js.map +0 -1
  275. package/dist/mqttPacket/pubcomp.d.ts +0 -17
  276. package/dist/mqttPacket/pubcomp.d.ts.map +0 -1
  277. package/dist/mqttPacket/pubcomp.js +0 -21
  278. package/dist/mqttPacket/pubcomp.js.map +0 -1
  279. package/dist/mqttPacket/pubrec.d.ts +0 -17
  280. package/dist/mqttPacket/pubrec.d.ts.map +0 -1
  281. package/dist/mqttPacket/pubrec.js +0 -21
  282. package/dist/mqttPacket/pubrec.js.map +0 -1
  283. package/dist/mqttPacket/pubrel.d.ts +0 -17
  284. package/dist/mqttPacket/pubrel.d.ts.map +0 -1
  285. package/dist/mqttPacket/pubrel.js +0 -21
  286. package/dist/mqttPacket/pubrel.js.map +0 -1
  287. package/dist/utils/args.d.ts +0 -28
  288. package/dist/utils/args.d.ts.map +0 -1
  289. package/dist/utils/args.js +0 -76
  290. package/dist/utils/args.js.map +0 -1
  291. package/dist/utils/platform.d.ts +0 -5
  292. package/dist/utils/platform.d.ts.map +0 -1
  293. package/dist/utils/platform.js +0 -8
  294. package/dist/utils/platform.js.map +0 -1
  295. package/mqttPacket/puback.test.ts +0 -55
  296. package/mqttPacket/puback.ts +0 -34
  297. package/mqttPacket/pubcomp.test.ts +0 -55
  298. package/mqttPacket/pubcomp.ts +0 -35
  299. package/mqttPacket/pubrec.test.ts +0 -55
  300. package/mqttPacket/pubrec.ts +0 -35
  301. package/mqttPacket/pubrel.test.ts +0 -55
  302. package/mqttPacket/pubrel.ts +0 -35
  303. package/utils/args.ts +0 -103
  304. package/utils/platform.ts +0 -8
package/README.md CHANGED
@@ -1,15 +1,49 @@
1
+ [![Nodejs CI](https://github.com/seriousme/opifex/actions/workflows/nodejs-ci.yml/badge.svg)](https://github.com/seriousme/opifex/actions/workflows/nodejs-ci.yml)
1
2
  [![Deno CI](https://github.com/seriousme/opifex/actions/workflows/deno-ci.yml/badge.svg)](https://github.com/seriousme/opifex/actions/workflows/deno-ci.yml)
2
3
  [![CodeQL](https://github.com/seriousme/opifex/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/seriousme/opifex/actions/workflows/codeql-analysis.yml)
3
4
 
4
5
  # Opifex
5
6
 
6
7
  Opifex aims to provide a MQTT server and MQTT client in Typescript to be used
7
- with [Deno](https://deno.land), [NodeJS](https://nodejs.org) or
8
+ with [NodeJS](https://nodejs.org), [Deno](https://deno.land) or
8
9
  [Bun](https://bun.sh) It has _no_ third party dependencies, it only relies on
9
10
  built in modules.
10
11
 
11
- Its a work in progress, only does MQTT 3.1.1 and currently only has memory based
12
- persistence.
12
+ # Compatibility
13
+
14
+ The following MQTT versions are supported:
15
+
16
+ | | MQTT 3.1 (v3) | MQTT 3.11 (v4) | MQTT 5.0 (v5) |
17
+ | -------------- | ------------- | -------------- | ------------- |
18
+ | Packet/encoder | ✅ | ✅ | ✅ |
19
+ | Client | ✅ | ✅ | ✅ partially |
20
+ | Server | ❌ | ✅ | ❌ |
21
+
22
+ Client and server currently only have memory based persistence, but one can
23
+ provide its own persistence. (see [#Architecture] )
24
+
25
+ ## Usage
26
+
27
+ The most easy way to use this project is to just use the demo server
28
+ (demoServer) and/or the demo client (mqtt).
29
+
30
+ - [NodeJS/Bun](node/README.md)
31
+ - [Deno](deno/README.md)
32
+
33
+ If you want to change the behaviour of the server and/or the client beyond what
34
+ can be done with CLI options then the next step is to clone the demo server
35
+ and/or the client scripts and modify them to your liking.
36
+
37
+ If you want to port the platform independent client and server libs to other
38
+ types of transport (e.g. Unix sockets or websocketstream) then its recommended
39
+ to clone and modify the platform specific code in `/node` or `/deno` as well.
40
+
41
+ If you want to port the platform independent client and server libs to another
42
+ platform then the platform specific code in `/node` or `/deno` might serve as
43
+ inspiration.
44
+
45
+ Bun (as of version 1.2) and Deno are both capable of running the NodeJS version,
46
+ but for historic reasons `/deno` still exists.
13
47
 
14
48
  ## Example
15
49
 
@@ -65,29 +99,6 @@ A more elaborate example including client and server can be found in the
65
99
  7. The demo client opens a platform specific socket and passes the resulting
66
100
  platform independent streams to the client module.
67
101
 
68
- ## Usage
69
-
70
- The most easy way to use this project is to just use the demo server
71
- (demoServer) and/or the demo client (mqtt). There are separate usage
72
- instructions for:
73
-
74
- - [Deno](deno/README.md)
75
- - [NodeJS](node/README.md)
76
-
77
- If you want to change the behaviour of the server and/or the client beyond what
78
- can be done with CLI options then the next step is to clone the demo server
79
- and/or the client scripts and modify them to your liking.
80
-
81
- If you want to port the platform independent client and server libs to other
82
- types of transport (e.g. Unix sockets or websocketstream) then its recommended
83
- to clone and modify the platform specific code in `/node` or `/deno` as well.
84
-
85
- If you want to port the platform independent client and server libs to another
86
- platform then the platform specific code in `/node` or `/deno` might serve as
87
- inspiration.
88
-
89
- Bun (as of version 1.2) is capable of running the NodeJS version.
90
-
91
102
  ## Exports
92
103
 
93
104
  | Export | Description |
@@ -5,10 +5,11 @@
5
5
  * MQTT Server implementation with authentication and authorization
6
6
  * @module mqttServer
7
7
  */
8
+ import { parseArgs } from "node:util";
8
9
  import { TcpServer } from "../node/tcpServer.ts";
9
10
  import type { Context, TAuthenticationResult, Topic } from "../server/mod.ts";
10
11
  import { AuthenticationResult } from "../server/mod.ts";
11
- import { getArgs, logger, LogLevel, parseArgs } from "../utils/mod.ts";
12
+ import { logger, LogLevel } from "../utils/mod.ts";
12
13
 
13
14
  /**
14
15
  * UTF-8 decoder for parsing passwords
@@ -101,7 +102,7 @@ function isAuthorizedToSubscribe(ctx: Context, topic: Topic): boolean {
101
102
  /**
102
103
  * Parse command line arguments and start server
103
104
  */
104
- const { _: [portNum] } = parseArgs(getArgs());
105
+ const { positionals: [portNum] } = parseArgs({ allowPositionals: true });
105
106
  const port = Number(portNum ?? 1883);
106
107
  const hostname = "::";
107
108
  logger.level(LogLevel.info);
package/bin/demoServer.ts CHANGED
@@ -4,10 +4,11 @@
4
4
  * MQTT Server implementation with authentication and authorization
5
5
  * @module mqttServer
6
6
  */
7
+ import { parseArgs } from "node:util";
7
8
  import { TcpServer } from "../node/tcpServer.ts";
8
9
  import type { Context, TAuthenticationResult, Topic } from "../server/mod.ts";
9
10
  import { AuthenticationResult } from "../server/mod.ts";
10
- import { getArgs, logger, LogLevel, parseArgs } from "../utils/mod.ts";
11
+ import { logger, LogLevel } from "../utils/mod.ts";
11
12
 
12
13
  /**
13
14
  * UTF-8 decoder for parsing passwords
@@ -100,7 +101,7 @@ function isAuthorizedToSubscribe(ctx: Context, topic: Topic): boolean {
100
101
  /**
101
102
  * Parse command line arguments and start server
102
103
  */
103
- const { _: [portNum] } = parseArgs(getArgs());
104
+ const { positionals: [portNum] } = parseArgs({ allowPositionals: true });
104
105
  const port = Number(portNum ?? 1883);
105
106
  const hostname = "::";
106
107
  logger.level(LogLevel.info);
package/bin/mqtt.ts CHANGED
@@ -24,16 +24,24 @@
24
24
  * # Subscribe to a topic
25
25
  * mqtt subscribe -t topic
26
26
  *
27
- * @requires node
27
+ * @requires node:util
28
28
  * @requires ../client/mod.ts
29
29
  * @requires ../node/tcpClient.ts
30
30
  * @requires ../utils/mod.ts
31
31
  */
32
- import { DEFAULT_URL } from "../client/mod.ts";
32
+
33
+ import { parseArgs } from "node:util";
34
+ import type { ParseArgsConfig } from "node:util";
35
+ import {
36
+ DEFAULT_KEEPALIVE,
37
+ DEFAULT_PROTOCOLLEVEL,
38
+ DEFAULT_URL,
39
+ } from "../client/mod.ts";
40
+ import type { ProtocolLevel } from "../client/mod.ts";
33
41
  import { getFileData, TcpClient } from "../node/tcpClient.ts";
34
- import { getArgs, logger, LogLevel, parseArgs } from "../utils/mod.ts";
35
- import type { Args } from "../utils/mod.ts";
42
+ import { logger, LogLevel } from "../utils/mod.ts";
36
43
 
44
+ type Args = Record<string, string | boolean>;
37
45
  const client = new TcpClient();
38
46
  const encoder = new TextEncoder();
39
47
  const decoder = new TextDecoder();
@@ -52,59 +60,64 @@ const ConnectHelp = `
52
60
  -U/--username the username to connect to the server
53
61
  -P/--password the password to connect to the server
54
62
  -C/--caFile the path to a CA certificate file
63
+ -V/--mqttVersion the MQTT version to use (3,4 or 5): default is ${DEFAULT_PROTOCOLLEVEL}
55
64
  -c/--certFile the path to a certificate file
56
65
  -k/--keyFile the path to a key file
66
+ -K/--keepAlive the keep alive of the client (in seconds): default is ${DEFAULT_KEEPALIVE}
57
67
  -n/--noClean try to resume a previous session
58
68
  -h/--help this text
59
69
  `;
60
70
 
61
71
  const connectOpts = {
62
- string: [
63
- "url",
64
- "username",
65
- "password",
66
- "caFile",
67
- "certFile",
68
- "keyFile",
69
- "clientId",
70
- ],
71
- alias: {
72
- u: "url",
73
- U: "username",
74
- P: "password",
75
- C: "caFile",
76
- c: "certFile",
77
- k: "keyFile",
78
- i: "clientId",
79
- n: "noClean",
80
- h: "help",
81
- },
82
- boolean: ["noClean", "help"],
83
- default: {
84
- noClean: false,
85
- clientId: `Opifex-${crypto.randomUUID()}`,
72
+ url: { type: "string", short: "u", default: DEFAULT_URL },
73
+ username: { type: "string", short: "U" },
74
+ password: { type: "string", short: "P" },
75
+ caFile: { type: "string", short: "C" },
76
+ certFile: { type: "string", short: "c" },
77
+ keyFile: { type: "string", short: "k" },
78
+ keepAlive: {
79
+ type: "string",
80
+ short: "K",
81
+ default: DEFAULT_KEEPALIVE.toString(),
86
82
  },
87
- };
88
-
89
- const SubscribeHelp = `Usage: mqtt subscribe <options>
90
-
91
- Where options are:
92
- -t/--topic the topic to use
93
- -q/--qos the QoS (0/1/2) to use, default is 0
94
- ${ConnectHelp}
95
- Example: mqtt subscribe -t hello`;
96
-
97
- const subscribeOpts = {
98
- string: ["topic"],
99
- alias: {
100
- t: "topic",
101
- q: "qos",
83
+ clientId: {
84
+ type: "string",
85
+ short: "i",
86
+ default: `Opifex-${crypto.randomUUID()}`,
102
87
  },
103
- default: {
104
- qos: 0,
105
- topic: "",
88
+ noClean: { type: "boolean", short: "n", default: false },
89
+ mqttVersion: {
90
+ type: "string",
91
+ short: "V",
92
+ default: DEFAULT_PROTOCOLLEVEL?.toString(),
106
93
  },
107
- };
94
+ help: { type: "boolean", short: "h", default: false },
95
+ } as const;
96
+
97
+ async function connect(connectArgs: Args) {
98
+ const {
99
+ caCerts,
100
+ cert,
101
+ key,
102
+ } = await getTLSdata(connectArgs);
103
+ await client.connect({
104
+ url: new URL(connectArgs.url as string),
105
+ caCerts,
106
+ cert,
107
+ key,
108
+ options: {
109
+ username: connectArgs.username as string,
110
+ password: encodePwd(connectArgs.password as string),
111
+ clientId: connectArgs.clientId as string,
112
+ clean: !connectArgs.noClean,
113
+ keepAlive: Number(connectArgs.keepAlive),
114
+ protocolLevel: parseInt(
115
+ connectArgs.mqttVersion as string,
116
+ ) as ProtocolLevel,
117
+ },
118
+ });
119
+ logger.debug("Connected !");
120
+ }
108
121
 
109
122
  function parseQos(qosArg: string | number) {
110
123
  const qos = Number(qosArg);
@@ -122,11 +135,21 @@ function parseQos(qosArg: string | number) {
122
135
  return 0;
123
136
  }
124
137
 
138
+ function safeParseArgs(config: ParseArgsConfig) {
139
+ try {
140
+ return parseArgs(config);
141
+ } catch (err: unknown) {
142
+ if (err instanceof Error) {
143
+ console.error(err.message);
144
+ }
145
+ }
146
+ }
147
+
125
148
  async function getTLSdata(connectArgs: Args) {
126
- const caFileData = await getFileData(connectArgs.caFile);
149
+ const caFileData = await getFileData(connectArgs.caFile as string);
127
150
  const caCerts = caFileData ? [caFileData] : undefined;
128
- const cert = await getFileData(connectArgs.certFile);
129
- const key = await getFileData(connectArgs.keyFile);
151
+ const cert = await getFileData(connectArgs.certFile as string);
152
+ const key = await getFileData(connectArgs.keyFile as string);
130
153
  return {
131
154
  caCerts,
132
155
  cert,
@@ -134,43 +157,48 @@ async function getTLSdata(connectArgs: Args) {
134
157
  };
135
158
  }
136
159
 
160
+ function encodePwd(password: string | undefined) {
161
+ if (typeof password === "string") {
162
+ return encoder.encode(password);
163
+ }
164
+ return undefined;
165
+ }
166
+
167
+ const SubscribeHelp = `Usage: mqtt subscribe <options>
168
+
169
+ Where options are:
170
+ -t/--topic the topic to use
171
+ -q/--qos the QoS (0/1/2) to use, default is 0
172
+ ${ConnectHelp}
173
+ Example: mqtt subscribe -t hello`;
174
+
175
+ const subscribeOpts = {
176
+ topic: { type: "string", short: "t" },
177
+ qos: { type: "string", short: "q", default: "0" },
178
+ } as const;
179
+
137
180
  async function subscribe() {
138
- const connectArgs = parseArgs(getArgs(), connectOpts);
139
- const {
140
- caCerts,
141
- cert,
142
- key,
143
- } = await getTLSdata(connectArgs);
144
- const subscribeArgs = parseArgs(getArgs(), subscribeOpts);
145
- if (connectArgs.help) {
181
+ const res = safeParseArgs({
182
+ options: { ...connectOpts, ...subscribeOpts },
183
+ allowPositionals: true,
184
+ });
185
+ if (!res) return;
186
+ const subscribeArgs = res.values as Record<string, string | boolean>;
187
+ if (subscribeArgs.help) {
146
188
  console.log(SubscribeHelp);
147
189
  return;
148
190
  }
149
- if (subscribeArgs.topic === undefined) {
191
+ if (typeof subscribeArgs.topic !== "string") {
150
192
  console.log("Missing `topic`");
151
193
  return;
152
194
  }
153
195
  try {
154
- await client.connect({
155
- url: connectArgs.url,
156
- caCerts,
157
- cert,
158
- key,
159
- options: {
160
- username: connectArgs.username,
161
- password: encoder.encode(connectArgs.password),
162
- clientId: connectArgs.clientId,
163
- clean: !connectArgs.noClean,
164
- keepAlive: 60,
165
- },
166
- });
167
- logger.debug("Connected !");
168
-
196
+ await connect(subscribeArgs);
169
197
  client.subscribe({
170
198
  subscriptions: [
171
199
  {
172
200
  topicFilter: subscribeArgs.topic,
173
- qos: parseQos(subscribeArgs.qos),
201
+ qos: parseQos(subscribeArgs.qos as string),
174
202
  },
175
203
  ],
176
204
  });
@@ -198,58 +226,34 @@ ${ConnectHelp}
198
226
  Example: mqtt publish -t hello -m world`;
199
227
 
200
228
  const publishOpts = {
201
- string: ["topic", "message"],
202
- boolean: ["retain"],
203
- alias: {
204
- t: "topic",
205
- m: "message",
206
- q: "qos",
207
- r: "retain",
208
- },
209
- default: {
210
- qos: 0,
211
- dup: false,
212
- retain: false,
213
- topic: undefined,
214
- message: "",
215
- },
216
- };
229
+ topic: { type: "string", short: "t" },
230
+ message: { type: "string", short: "m" },
231
+ qos: { type: "string", short: "q", default: "0" },
232
+ retain: { type: "boolean", short: "r", default: false },
233
+ } as const;
217
234
 
218
235
  async function publish() {
219
- const connectArgs = parseArgs(getArgs(), connectOpts);
220
- const {
221
- caCerts,
222
- cert,
223
- key,
224
- } = await getTLSdata(connectArgs);
225
- const publishArgs = parseArgs(getArgs(), publishOpts);
226
- if (connectArgs.help) {
236
+ const res = safeParseArgs({
237
+ options: { ...connectOpts, ...publishOpts },
238
+ allowPositionals: true,
239
+ });
240
+ if (!res) return;
241
+ const publishArgs = res.values as Record<string, string | boolean>;
242
+ if (publishArgs.help) {
227
243
  console.log(PublishHelp);
228
244
  return;
229
245
  }
230
- if (publishArgs.topic === undefined) {
246
+ if (typeof publishArgs.topic !== "string") {
231
247
  console.log("Missing `topic`");
232
248
  return;
233
249
  }
234
250
  try {
235
- await client.connect({
236
- url: connectArgs.url,
237
- caCerts,
238
- cert,
239
- key,
240
- options: {
241
- username: connectArgs.username,
242
- password: encoder.encode(connectArgs.password),
243
- clientId: connectArgs.clientId,
244
- clean: !connectArgs.noClean,
245
- },
246
- });
247
- logger.debug("Connected !");
251
+ await connect(publishArgs);
248
252
  await client.publish({
249
253
  topic: publishArgs.topic,
250
- payload: encoder.encode(publishArgs.message),
251
- retain: publishArgs.retain,
252
- qos: parseQos(publishArgs.qos),
254
+ payload: encoder.encode(publishArgs.message as string),
255
+ retain: publishArgs.retain as boolean,
256
+ qos: parseQos(publishArgs.qos as string),
253
257
  });
254
258
  logger.debug("Published!");
255
259
  client.disconnect();
@@ -263,7 +267,12 @@ async function publish() {
263
267
  }
264
268
 
265
269
  function processArgs() {
266
- const { _: [cmd] } = parseArgs(getArgs());
270
+ const res = safeParseArgs({
271
+ strict: false,
272
+ allowPositionals: true,
273
+ });
274
+ const positionals = res !== undefined ? res.positionals : [];
275
+ const cmd = positionals[0];
267
276
  switch (cmd) {
268
277
  case "publish":
269
278
  publish();
package/client/client.ts CHANGED
@@ -1,13 +1,17 @@
1
1
  import {
2
- type ConnectPacket,
3
2
  Deferred,
4
3
  logger,
5
4
  MemoryStore,
5
+ MQTTLevel,
6
6
  PacketType,
7
- type PublishPacket,
8
- type SockConn,
9
- type SubscribePacket,
10
- type TAuthenticationResult,
7
+ } from "./deps.ts";
8
+
9
+ import type {
10
+ ConnectPacket,
11
+ PublishPacket,
12
+ SockConn,
13
+ SubscribePacket,
14
+ TAuthenticationResult,
11
15
  } from "./deps.ts";
12
16
 
13
17
  import { Context } from "./context.ts";
@@ -23,7 +27,7 @@ function generateClientId(prefix: string): string {
23
27
 
24
28
  type ConnectOptions = Omit<
25
29
  ConnectPacket,
26
- "type" | "protocolName" | "protocolLevel"
30
+ "type" | "protocolName"
27
31
  >;
28
32
 
29
33
  /** ConnectParameters define how to connect */
@@ -37,9 +41,15 @@ export type ConnectParameters = {
37
41
  };
38
42
 
39
43
  /** PublishParameters define how a message should be published */
40
- export type PublishParameters = Omit<PublishPacket, "type" | "id">;
44
+ export type PublishParameters = Omit<
45
+ PublishPacket,
46
+ "type" | "id" | "protocolLevel"
47
+ >;
41
48
  /** SubscribeParameters define how to subscribe to a topic */
42
- export type SubscribeParameters = Omit<SubscribePacket, "type" | "id">;
49
+ export type SubscribeParameters = Omit<
50
+ SubscribePacket,
51
+ "type" | "id" | "protocolLevel"
52
+ >;
43
53
 
44
54
  /**
45
55
  * Implements exponential backoff sleep with optional randomization
@@ -62,7 +72,8 @@ function backOffSleep(random: boolean, attempt: number): Promise<void> {
62
72
 
63
73
  /** the default MQTT URL to connect to */
64
74
  export const DEFAULT_URL = "mqtt://localhost:1883/";
65
- const DEFAULT_KEEPALIVE = 60; // 60 seconds
75
+ export const DEFAULT_PROTOCOLLEVEL = MQTTLevel.v4;
76
+ export const DEFAULT_KEEPALIVE = 60; // 60 seconds
66
77
  const DEFAULT_RETRIES = 3; // on first connect
67
78
  const CLIENTID_PREFIX = "opifex"; // on first connect
68
79
 
@@ -80,6 +91,7 @@ export class Client {
80
91
  protected numberOfRetries = DEFAULT_RETRIES;
81
92
  protected url: URL = new URL(DEFAULT_URL);
82
93
  protected keepAlive = DEFAULT_KEEPALIVE;
94
+ protected protocolLevel = DEFAULT_PROTOCOLLEVEL;
83
95
  protected autoReconnect = true;
84
96
  private caCerts?: string[];
85
97
  private cert?: string;
@@ -127,12 +139,13 @@ export class Client {
127
139
  if (!this.connectPacket) {
128
140
  return;
129
141
  }
142
+
130
143
  let isReconnect = false;
131
144
  let attempt = 1;
132
145
  let lastMessage = new Error();
133
146
  let tryConnect = true;
134
147
  while (tryConnect) {
135
- logger.debug(`${isReconnect ? "re" : ""}connecting`);
148
+ logger.debug(`${isReconnect ? "re" : ""}connecting, attempt ${attempt}`);
136
149
  try {
137
150
  const conn = await this.createConn(
138
151
  this.url.protocol,
@@ -153,13 +166,17 @@ export class Client {
153
166
  } catch (err) {
154
167
  if (err instanceof Error) {
155
168
  lastMessage = err;
169
+ logger.debug({ lastMessage });
156
170
  }
157
- logger.debug(lastMessage);
158
- if (!isReconnect && attempt > this.numberOfRetries) {
159
- tryConnect = false;
160
- } else {
161
- await backOffSleep(true, attempt++);
171
+ }
172
+
173
+ if (tryConnect && (isReconnect || attempt < this.numberOfRetries)) {
174
+ await backOffSleep(true, attempt);
175
+ if (!isReconnect) {
176
+ attempt++;
162
177
  }
178
+ } else {
179
+ tryConnect = false;
163
180
  }
164
181
  }
165
182
 
@@ -183,6 +200,7 @@ export class Client {
183
200
  {
184
201
  keepAlive: this.keepAlive,
185
202
  clientId: this.clientId,
203
+ protocolLevel: this.protocolLevel,
186
204
  },
187
205
  params?.options,
188
206
  );
@@ -212,6 +230,7 @@ export class Client {
212
230
  async publish(params: PublishParameters): Promise<void> {
213
231
  const packet: PublishPacket = {
214
232
  type: PacketType.publish,
233
+ protocolLevel: this.ctx.protocolLevel,
215
234
  ...params,
216
235
  };
217
236
  await this.ctx.publish(packet);
@@ -226,6 +245,7 @@ export class Client {
226
245
  const packet: SubscribePacket = {
227
246
  type: PacketType.subscribe,
228
247
  id: 0, //placeholder
248
+ protocolLevel: this.ctx.protocolLevel,
229
249
  ...params,
230
250
  };
231
251
  await this.ctx.subscribe(packet);