@seriousme/opifex 1.11.3 → 2.1.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.
Files changed (542) hide show
  1. package/README.md +99 -40
  2. package/bin/conformanceServer.ts +45 -13
  3. package/bin/demoServer.ts +38 -13
  4. package/bin/mqtt.ts +2 -2
  5. package/client/ConnectionState.ts +3 -1
  6. package/client/client.ts +88 -47
  7. package/client/context.ts +79 -27
  8. package/client/deps.ts +10 -3
  9. package/client/handlers/handleAuth.test.ts +193 -0
  10. package/client/handlers/handleAuth.ts +89 -0
  11. package/client/handlers/handleConnack.test.ts +173 -0
  12. package/client/handlers/handleConnack.ts +18 -8
  13. package/client/handlers/handleDisconnect.test.ts +22 -0
  14. package/client/handlers/handleDisconnect.ts +17 -0
  15. package/client/handlers/handlePacket.test.ts +242 -0
  16. package/client/handlers/handlePacket.ts +19 -5
  17. package/client/handlers/handlePuback.test.ts +99 -0
  18. package/client/handlers/handlePuback.ts +6 -0
  19. package/client/handlers/handlePubcomp.test.ts +112 -0
  20. package/client/handlers/handlePubcomp.ts +5 -0
  21. package/client/handlers/handlePublish.test.ts +188 -0
  22. package/client/handlers/handlePubrec.test.ts +119 -0
  23. package/client/handlers/handlePubrec.ts +5 -1
  24. package/client/handlers/handlePubrel.test.ts +136 -0
  25. package/client/handlers/handleSuback.test.ts +122 -0
  26. package/client/handlers/handleSuback.ts +3 -0
  27. package/client/handlers/handleUnsuback.test.ts +101 -0
  28. package/client/mod.ts +13 -2
  29. package/client/store/memoryStore.ts +12 -11
  30. package/client/store/store.ts +4 -2
  31. package/deno/README.md +1 -2
  32. package/deno/tcpClient.test.ts +75 -0
  33. package/deno/tcpClient.ts +36 -18
  34. package/deno/tcpServer.test.ts +22 -21
  35. package/deno/tcpServer.ts +26 -4
  36. package/deno/tlsServer.test.ts +97 -0
  37. package/deno/tlsServer.ts +58 -0
  38. package/deno/wsServer.test.ts +93 -0
  39. package/deno/wsServer.ts +96 -0
  40. package/dev_utils/generateCert.ts +95 -0
  41. package/dev_utils/mockConn.ts +36 -0
  42. package/dev_utils/mockServer.ts +28 -0
  43. package/dev_utils/mod.ts +30 -3
  44. package/dev_utils/packetHelpers.ts +404 -0
  45. package/dev_utils/test-handlers.ts +98 -0
  46. package/dev_utils/timers.ts +14 -0
  47. package/dev_utils/webStreamPair.test.ts +171 -0
  48. package/dev_utils/webStreamPair.ts +143 -0
  49. package/dist/bin/conformanceServer.js +17 -12
  50. package/dist/bin/conformanceServer.js.map +1 -1
  51. package/dist/bin/demoServer.js +16 -12
  52. package/dist/bin/demoServer.js.map +1 -1
  53. package/dist/bin/mqtt.js +2 -2
  54. package/dist/bin/mqtt.js.map +1 -1
  55. package/dist/client/ConnectionState.d.ts +3 -1
  56. package/dist/client/ConnectionState.d.ts.map +1 -1
  57. package/dist/client/ConnectionState.js +2 -0
  58. package/dist/client/ConnectionState.js.map +1 -1
  59. package/dist/client/client.d.ts +47 -21
  60. package/dist/client/client.d.ts.map +1 -1
  61. package/dist/client/client.js +57 -20
  62. package/dist/client/client.js.map +1 -1
  63. package/dist/client/context.d.ts +15 -7
  64. package/dist/client/context.d.ts.map +1 -1
  65. package/dist/client/context.js +44 -12
  66. package/dist/client/context.js.map +1 -1
  67. package/dist/client/deps.d.ts +4 -3
  68. package/dist/client/deps.d.ts.map +1 -1
  69. package/dist/client/deps.js +1 -1
  70. package/dist/client/deps.js.map +1 -1
  71. package/dist/client/handlers/handleAuth.d.ts +9 -0
  72. package/dist/client/handlers/handleAuth.d.ts.map +1 -0
  73. package/dist/client/handlers/handleAuth.js +66 -0
  74. package/dist/client/handlers/handleAuth.js.map +1 -0
  75. package/dist/client/handlers/handleConnack.d.ts +2 -2
  76. package/dist/client/handlers/handleConnack.d.ts.map +1 -1
  77. package/dist/client/handlers/handleConnack.js +16 -6
  78. package/dist/client/handlers/handleConnack.js.map +1 -1
  79. package/dist/client/handlers/handleDisconnect.d.ts +11 -0
  80. package/dist/client/handlers/handleDisconnect.d.ts.map +1 -0
  81. package/dist/client/handlers/handleDisconnect.js +15 -0
  82. package/dist/client/handlers/handleDisconnect.js.map +1 -0
  83. package/dist/client/handlers/handlePacket.d.ts.map +1 -1
  84. package/dist/client/handlers/handlePacket.js +14 -3
  85. package/dist/client/handlers/handlePacket.js.map +1 -1
  86. package/dist/client/handlers/handlePuback.d.ts.map +1 -1
  87. package/dist/client/handlers/handlePuback.js +5 -0
  88. package/dist/client/handlers/handlePuback.js.map +1 -1
  89. package/dist/client/handlers/handlePubcomp.d.ts.map +1 -1
  90. package/dist/client/handlers/handlePubcomp.js +5 -0
  91. package/dist/client/handlers/handlePubcomp.js.map +1 -1
  92. package/dist/client/handlers/handlePubrec.d.ts.map +1 -1
  93. package/dist/client/handlers/handlePubrec.js +5 -1
  94. package/dist/client/handlers/handlePubrec.js.map +1 -1
  95. package/dist/client/handlers/handleSuback.d.ts +1 -0
  96. package/dist/client/handlers/handleSuback.d.ts.map +1 -1
  97. package/dist/client/handlers/handleSuback.js +3 -0
  98. package/dist/client/handlers/handleSuback.js.map +1 -1
  99. package/dist/client/mod.d.ts +3 -2
  100. package/dist/client/mod.d.ts.map +1 -1
  101. package/dist/client/mod.js +1 -1
  102. package/dist/client/mod.js.map +1 -1
  103. package/dist/client/store/memoryStore.d.ts +3 -3
  104. package/dist/client/store/memoryStore.d.ts.map +1 -1
  105. package/dist/client/store/memoryStore.js +3 -3
  106. package/dist/client/store/memoryStore.js.map +1 -1
  107. package/dist/client/store/store.d.ts +3 -2
  108. package/dist/client/store/store.d.ts.map +1 -1
  109. package/dist/client/store/store.js +1 -1
  110. package/dist/client/store/store.js.map +1 -1
  111. package/dist/dev_utils/generateCert.d.ts +11 -0
  112. package/dist/dev_utils/generateCert.d.ts.map +1 -0
  113. package/dist/dev_utils/generateCert.js +76 -0
  114. package/dist/dev_utils/generateCert.js.map +1 -0
  115. package/dist/examples/authHandler.test.d.ts +2 -0
  116. package/dist/examples/authHandler.test.d.ts.map +1 -0
  117. package/dist/examples/authHandler.test.js +123 -0
  118. package/dist/examples/authHandler.test.js.map +1 -0
  119. package/dist/examples/mqtt.test.d.ts +2 -0
  120. package/dist/examples/mqtt.test.d.ts.map +1 -0
  121. package/dist/examples/mqtt.test.js +70 -0
  122. package/dist/examples/mqtt.test.js.map +1 -0
  123. package/dist/examples/mqtts.test.d.ts +2 -0
  124. package/dist/examples/mqtts.test.d.ts.map +1 -0
  125. package/dist/examples/mqtts.test.js +81 -0
  126. package/dist/examples/mqtts.test.js.map +1 -0
  127. package/dist/mqttConn/mqttConn.d.ts +24 -31
  128. package/dist/mqttConn/mqttConn.d.ts.map +1 -1
  129. package/dist/mqttConn/mqttConn.js +57 -36
  130. package/dist/mqttConn/mqttConn.js.map +1 -1
  131. package/dist/mqttPacket/AuthenticationResult.d.ts +4 -0
  132. package/dist/mqttPacket/AuthenticationResult.d.ts.map +1 -1
  133. package/dist/mqttPacket/AuthenticationResult.js.map +1 -1
  134. package/dist/mqttPacket/BitMask.d.ts.map +1 -1
  135. package/dist/mqttPacket/PacketType.d.ts +4 -0
  136. package/dist/mqttPacket/PacketType.d.ts.map +1 -1
  137. package/dist/mqttPacket/Properties.d.ts +4 -4
  138. package/dist/mqttPacket/Properties.d.ts.map +1 -1
  139. package/dist/mqttPacket/Properties.js.map +1 -1
  140. package/dist/mqttPacket/ReasonCode.d.ts +4 -5
  141. package/dist/mqttPacket/ReasonCode.d.ts.map +1 -1
  142. package/dist/mqttPacket/ReasonCode.js +1 -0
  143. package/dist/mqttPacket/ReasonCode.js.map +1 -1
  144. package/dist/mqttPacket/RetainHandling.d.ts +4 -0
  145. package/dist/mqttPacket/RetainHandling.d.ts.map +1 -1
  146. package/dist/mqttPacket/auth.d.ts +7 -8
  147. package/dist/mqttPacket/auth.d.ts.map +1 -1
  148. package/dist/mqttPacket/auth.js +31 -30
  149. package/dist/mqttPacket/auth.js.map +1 -1
  150. package/dist/mqttPacket/codec.d.ts +48 -0
  151. package/dist/mqttPacket/codec.d.ts.map +1 -0
  152. package/dist/mqttPacket/codec.js +93 -0
  153. package/dist/mqttPacket/codec.js.map +1 -0
  154. package/dist/mqttPacket/connack.d.ts +11 -10
  155. package/dist/mqttPacket/connack.d.ts.map +1 -1
  156. package/dist/mqttPacket/connack.js +43 -45
  157. package/dist/mqttPacket/connack.js.map +1 -1
  158. package/dist/mqttPacket/connect.d.ts +7 -7
  159. package/dist/mqttPacket/connect.d.ts.map +1 -1
  160. package/dist/mqttPacket/connect.js +156 -157
  161. package/dist/mqttPacket/connect.js.map +1 -1
  162. package/dist/mqttPacket/decoder.d.ts +6 -16
  163. package/dist/mqttPacket/decoder.d.ts.map +1 -1
  164. package/dist/mqttPacket/decoder.js +7 -28
  165. package/dist/mqttPacket/decoder.js.map +1 -1
  166. package/dist/mqttPacket/disconnect.d.ts +9 -8
  167. package/dist/mqttPacket/disconnect.d.ts.map +1 -1
  168. package/dist/mqttPacket/disconnect.js +41 -43
  169. package/dist/mqttPacket/disconnect.js.map +1 -1
  170. package/dist/mqttPacket/encoder.d.ts +7 -19
  171. package/dist/mqttPacket/encoder.d.ts.map +1 -1
  172. package/dist/mqttPacket/encoder.js +5 -28
  173. package/dist/mqttPacket/encoder.js.map +1 -1
  174. package/dist/mqttPacket/mod.d.ts +21 -103
  175. package/dist/mqttPacket/mod.d.ts.map +1 -1
  176. package/dist/mqttPacket/mod.js +5 -94
  177. package/dist/mqttPacket/mod.js.map +1 -1
  178. package/dist/mqttPacket/pingreq.d.ts +6 -6
  179. package/dist/mqttPacket/pingreq.d.ts.map +1 -1
  180. package/dist/mqttPacket/pingreq.js +11 -13
  181. package/dist/mqttPacket/pingreq.js.map +1 -1
  182. package/dist/mqttPacket/pingres.d.ts +6 -6
  183. package/dist/mqttPacket/pingres.d.ts.map +1 -1
  184. package/dist/mqttPacket/pingres.js +11 -13
  185. package/dist/mqttPacket/pingres.js.map +1 -1
  186. package/dist/mqttPacket/protocolLevels.d.ts +11 -6
  187. package/dist/mqttPacket/protocolLevels.d.ts.map +1 -1
  188. package/dist/mqttPacket/protocolLevels.js +5 -1
  189. package/dist/mqttPacket/protocolLevels.js.map +1 -1
  190. package/dist/mqttPacket/pubblishAcks.d.ts +9 -10
  191. package/dist/mqttPacket/pubblishAcks.d.ts.map +1 -1
  192. package/dist/mqttPacket/pubblishAcks.js +43 -45
  193. package/dist/mqttPacket/pubblishAcks.js.map +1 -1
  194. package/dist/mqttPacket/publish.d.ts +8 -7
  195. package/dist/mqttPacket/publish.d.ts.map +1 -1
  196. package/dist/mqttPacket/publish.js +53 -55
  197. package/dist/mqttPacket/publish.js.map +1 -1
  198. package/dist/mqttPacket/suback.d.ts +9 -8
  199. package/dist/mqttPacket/suback.d.ts.map +1 -1
  200. package/dist/mqttPacket/suback.js +38 -40
  201. package/dist/mqttPacket/suback.js.map +1 -1
  202. package/dist/mqttPacket/subscribe.d.ts +41 -10
  203. package/dist/mqttPacket/subscribe.d.ts.map +1 -1
  204. package/dist/mqttPacket/subscribe.js +90 -89
  205. package/dist/mqttPacket/subscribe.js.map +1 -1
  206. package/dist/mqttPacket/types.d.ts +2 -29
  207. package/dist/mqttPacket/types.d.ts.map +1 -1
  208. package/dist/mqttPacket/unsuback.d.ts +9 -8
  209. package/dist/mqttPacket/unsuback.d.ts.map +1 -1
  210. package/dist/mqttPacket/unsuback.js +32 -34
  211. package/dist/mqttPacket/unsuback.js.map +1 -1
  212. package/dist/mqttPacket/unsubscribe.d.ts +26 -8
  213. package/dist/mqttPacket/unsubscribe.d.ts.map +1 -1
  214. package/dist/mqttPacket/unsubscribe.js +45 -44
  215. package/dist/mqttPacket/unsubscribe.js.map +1 -1
  216. package/dist/mqttPacket/validators.d.ts +4 -16
  217. package/dist/mqttPacket/validators.d.ts.map +1 -1
  218. package/dist/mqttPacket/validators.js +5 -26
  219. package/dist/mqttPacket/validators.js.map +1 -1
  220. package/dist/node/tcpClient.d.ts +22 -2
  221. package/dist/node/tcpClient.d.ts.map +1 -1
  222. package/dist/node/tcpClient.js +29 -10
  223. package/dist/node/tcpClient.js.map +1 -1
  224. package/dist/node/tcpServer.d.ts +18 -0
  225. package/dist/node/tcpServer.d.ts.map +1 -1
  226. package/dist/node/tcpServer.js +21 -2
  227. package/dist/node/tcpServer.js.map +1 -1
  228. package/dist/node/tlsServer.d.ts +37 -0
  229. package/dist/node/tlsServer.d.ts.map +1 -0
  230. package/dist/node/tlsServer.js +73 -0
  231. package/dist/node/tlsServer.js.map +1 -0
  232. package/dist/node/wrapNodeSocket.d.ts.map +1 -1
  233. package/dist/node/wrapNodeSocket.js +6 -28
  234. package/dist/node/wrapNodeSocket.js.map +1 -1
  235. package/dist/persistence/basicPersistence.d.ts +36 -0
  236. package/dist/persistence/basicPersistence.d.ts.map +1 -0
  237. package/dist/persistence/basicPersistence.js +306 -0
  238. package/dist/persistence/basicPersistence.js.map +1 -0
  239. package/dist/persistence/deps.d.ts +3 -2
  240. package/dist/persistence/deps.d.ts.map +1 -1
  241. package/dist/persistence/deps.js +2 -1
  242. package/dist/persistence/deps.js.map +1 -1
  243. package/dist/persistence/memory/memoryPersistence.d.ts +6 -23
  244. package/dist/persistence/memory/memoryPersistence.d.ts.map +1 -1
  245. package/dist/persistence/memory/memoryPersistence.js +12 -115
  246. package/dist/persistence/memory/memoryPersistence.js.map +1 -1
  247. package/dist/persistence/memory/memoryStorage.d.ts +45 -0
  248. package/dist/persistence/memory/memoryStorage.d.ts.map +1 -0
  249. package/dist/persistence/memory/memoryStorage.js +143 -0
  250. package/dist/persistence/memory/memoryStorage.js.map +1 -0
  251. package/dist/persistence/mod.d.ts +3 -3
  252. package/dist/persistence/mod.d.ts.map +1 -1
  253. package/dist/persistence/mod.js +6 -1
  254. package/dist/persistence/mod.js.map +1 -1
  255. package/dist/persistence/persistence.d.ts +45 -70
  256. package/dist/persistence/persistence.d.ts.map +1 -1
  257. package/dist/persistence/persistence.js +1 -2
  258. package/dist/persistence/persistence.js.map +1 -1
  259. package/dist/persistence/sqlite/sqliteDatabase.d.ts +46 -0
  260. package/dist/persistence/sqlite/sqliteDatabase.d.ts.map +1 -0
  261. package/dist/persistence/sqlite/sqliteDatabase.js +215 -0
  262. package/dist/persistence/sqlite/sqliteDatabase.js.map +1 -0
  263. package/dist/persistence/sqlite/sqlitePersistence.d.ts +16 -0
  264. package/dist/persistence/sqlite/sqlitePersistence.d.ts.map +1 -0
  265. package/dist/persistence/sqlite/sqlitePersistence.js +29 -0
  266. package/dist/persistence/sqlite/sqlitePersistence.js.map +1 -0
  267. package/dist/persistence/sqlite/sqliteStorage.d.ts +36 -0
  268. package/dist/persistence/sqlite/sqliteStorage.d.ts.map +1 -0
  269. package/dist/persistence/sqlite/sqliteStorage.js +194 -0
  270. package/dist/persistence/sqlite/sqliteStorage.js.map +1 -0
  271. package/dist/persistence/storage.d.ts +38 -0
  272. package/dist/persistence/storage.d.ts.map +1 -0
  273. package/dist/persistence/storage.js +5 -0
  274. package/dist/persistence/storage.js.map +1 -0
  275. package/dist/server/config.d.ts +47 -0
  276. package/dist/server/config.d.ts.map +1 -0
  277. package/dist/server/config.js +41 -0
  278. package/dist/server/config.js.map +1 -0
  279. package/dist/server/context.d.ts +199 -25
  280. package/dist/server/context.d.ts.map +1 -1
  281. package/dist/server/context.js +432 -53
  282. package/dist/server/context.js.map +1 -1
  283. package/dist/server/deps.d.ts +5 -6
  284. package/dist/server/deps.d.ts.map +1 -1
  285. package/dist/server/deps.js +2 -2
  286. package/dist/server/deps.js.map +1 -1
  287. package/dist/server/handlers/completeConnect.d.ts +9 -0
  288. package/dist/server/handlers/completeConnect.d.ts.map +1 -0
  289. package/dist/server/handlers/completeConnect.js +74 -0
  290. package/dist/server/handlers/completeConnect.js.map +1 -0
  291. package/dist/server/handlers/handleAuth.d.ts +9 -0
  292. package/dist/server/handlers/handleAuth.d.ts.map +1 -0
  293. package/dist/server/handlers/handleAuth.js +69 -0
  294. package/dist/server/handlers/handleAuth.js.map +1 -0
  295. package/dist/server/handlers/handleConnect.d.ts +2 -4
  296. package/dist/server/handlers/handleConnect.d.ts.map +1 -1
  297. package/dist/server/handlers/handleConnect.js +140 -67
  298. package/dist/server/handlers/handleConnect.js.map +1 -1
  299. package/dist/server/handlers/handleDisconnect.d.ts +1 -1
  300. package/dist/server/handlers/handleDisconnect.d.ts.map +1 -1
  301. package/dist/server/handlers/handleDisconnect.js +3 -3
  302. package/dist/server/handlers/handleDisconnect.js.map +1 -1
  303. package/dist/server/handlers/handlePacket.d.ts.map +1 -1
  304. package/dist/server/handlers/handlePacket.js +19 -6
  305. package/dist/server/handlers/handlePacket.js.map +1 -1
  306. package/dist/server/handlers/handlePuback.d.ts +1 -3
  307. package/dist/server/handlers/handlePuback.d.ts.map +1 -1
  308. package/dist/server/handlers/handlePuback.js +2 -8
  309. package/dist/server/handlers/handlePuback.js.map +1 -1
  310. package/dist/server/handlers/handlePubcomp.d.ts +1 -1
  311. package/dist/server/handlers/handlePubcomp.d.ts.map +1 -1
  312. package/dist/server/handlers/handlePubcomp.js +2 -4
  313. package/dist/server/handlers/handlePubcomp.js.map +1 -1
  314. package/dist/server/handlers/handlePublish.d.ts +1 -1
  315. package/dist/server/handlers/handlePublish.d.ts.map +1 -1
  316. package/dist/server/handlers/handlePublish.js +149 -45
  317. package/dist/server/handlers/handlePublish.js.map +1 -1
  318. package/dist/server/handlers/handlePubrec.d.ts.map +1 -1
  319. package/dist/server/handlers/handlePubrec.js +2 -3
  320. package/dist/server/handlers/handlePubrec.js.map +1 -1
  321. package/dist/server/handlers/handlePubrel.d.ts.map +1 -1
  322. package/dist/server/handlers/handlePubrel.js +6 -2
  323. package/dist/server/handlers/handlePubrel.js.map +1 -1
  324. package/dist/server/handlers/handleSubscribe.d.ts +1 -6
  325. package/dist/server/handlers/handleSubscribe.d.ts.map +1 -1
  326. package/dist/server/handlers/handleSubscribe.js +99 -38
  327. package/dist/server/handlers/handleSubscribe.js.map +1 -1
  328. package/dist/server/handlers/handleUnsubscribe.d.ts.map +1 -1
  329. package/dist/server/handlers/handleUnsubscribe.js +19 -4
  330. package/dist/server/handlers/handleUnsubscribe.js.map +1 -1
  331. package/dist/server/mod.d.ts +3 -3
  332. package/dist/server/mod.d.ts.map +1 -1
  333. package/dist/server/mod.js +1 -1
  334. package/dist/server/mod.js.map +1 -1
  335. package/dist/server/server.d.ts +32 -9
  336. package/dist/server/server.d.ts.map +1 -1
  337. package/dist/server/server.js +84 -15
  338. package/dist/server/server.js.map +1 -1
  339. package/dist/socket/socket.d.ts +15 -9
  340. package/dist/socket/socket.d.ts.map +1 -1
  341. package/dist/socket/socket.js +66 -18
  342. package/dist/socket/socket.js.map +1 -1
  343. package/dist/timer/timer.d.ts +32 -0
  344. package/dist/timer/timer.d.ts.map +1 -1
  345. package/dist/timer/timer.js +32 -0
  346. package/dist/timer/timer.js.map +1 -1
  347. package/dist/trie/trie.d.ts +10 -5
  348. package/dist/trie/trie.d.ts.map +1 -1
  349. package/dist/trie/trie.js +61 -41
  350. package/dist/trie/trie.js.map +1 -1
  351. package/dist/utils/assert.d.ts.map +1 -1
  352. package/dist/utils/{BufferedAsyncIterable.d.ts → bufferedAsyncIterable.d.ts} +1 -1
  353. package/dist/utils/bufferedAsyncIterable.d.ts.map +1 -0
  354. package/dist/utils/{BufferedAsyncIterable.js → bufferedAsyncIterable.js} +1 -1
  355. package/dist/utils/bufferedAsyncIterable.js.map +1 -0
  356. package/dist/utils/deferred.d.ts.map +1 -1
  357. package/dist/utils/deferred.js.map +1 -1
  358. package/dist/utils/getOrInsertPolyFill.d.ts +2 -0
  359. package/dist/utils/getOrInsertPolyFill.d.ts.map +1 -0
  360. package/dist/utils/getOrInsertPolyFill.js +12 -0
  361. package/dist/utils/getOrInsertPolyFill.js.map +1 -0
  362. package/dist/utils/logger.d.ts +42 -5
  363. package/dist/utils/logger.d.ts.map +1 -1
  364. package/dist/utils/logger.js +53 -7
  365. package/dist/utils/logger.js.map +1 -1
  366. package/dist/utils/mod.d.ts +6 -1
  367. package/dist/utils/mod.d.ts.map +1 -1
  368. package/dist/utils/mod.js +5 -1
  369. package/dist/utils/mod.js.map +1 -1
  370. package/dist/utils/outboundTopicAliasManager.d.ts +52 -0
  371. package/dist/utils/outboundTopicAliasManager.d.ts.map +1 -0
  372. package/dist/utils/outboundTopicAliasManager.js +74 -0
  373. package/dist/utils/outboundTopicAliasManager.js.map +1 -0
  374. package/dist/utils/queue.d.ts.map +1 -1
  375. package/dist/utils/topicFilter.d.ts +32 -0
  376. package/dist/utils/topicFilter.d.ts.map +1 -0
  377. package/dist/utils/topicFilter.js +52 -0
  378. package/dist/utils/topicFilter.js.map +1 -0
  379. package/dist/utils/utils.d.ts +2 -0
  380. package/dist/utils/utils.d.ts.map +1 -1
  381. package/dist/utils/utils.js +2 -0
  382. package/dist/utils/utils.js.map +1 -1
  383. package/dist/utils/validators.d.ts +34 -0
  384. package/dist/utils/validators.d.ts.map +1 -0
  385. package/dist/utils/validators.js +61 -0
  386. package/dist/utils/validators.js.map +1 -0
  387. package/dist/web/webSocketStream.d.ts +29 -0
  388. package/dist/web/webSocketStream.d.ts.map +1 -0
  389. package/dist/web/webSocketStream.js +69 -0
  390. package/dist/web/webSocketStream.js.map +1 -0
  391. package/dist/web/wrapWebSocket.d.ts +12 -0
  392. package/dist/web/wrapWebSocket.d.ts.map +1 -0
  393. package/dist/web/wrapWebSocket.js +41 -0
  394. package/dist/web/wrapWebSocket.js.map +1 -0
  395. package/dist/web/wsClient.d.ts +14 -0
  396. package/dist/web/wsClient.d.ts.map +1 -0
  397. package/dist/web/wsClient.js +33 -0
  398. package/dist/web/wsClient.js.map +1 -0
  399. package/dist/web/wsClient.test.d.ts +2 -0
  400. package/dist/web/wsClient.test.d.ts.map +1 -0
  401. package/dist/web/wsClient.test.js +20 -0
  402. package/dist/web/wsClient.test.js.map +1 -0
  403. package/examples/authHandler.test.ts +164 -0
  404. package/examples/mqtt.test.ts +89 -0
  405. package/examples/mqtts.test.ts +100 -0
  406. package/examples/webSocket.test.ts +94 -0
  407. package/mqttConn/mqttConn.test.ts +9 -9
  408. package/mqttConn/mqttConn.ts +76 -60
  409. package/mqttPacket/AuthenticationResult.ts +6 -0
  410. package/mqttPacket/PacketType.ts +5 -0
  411. package/mqttPacket/Properties.ts +8 -5
  412. package/mqttPacket/ReasonCode.ts +8 -9
  413. package/mqttPacket/RetainHandling.ts +5 -0
  414. package/mqttPacket/auth.test.ts +1 -1
  415. package/mqttPacket/auth.ts +44 -51
  416. package/mqttPacket/codec.ts +147 -0
  417. package/mqttPacket/connack.ts +63 -73
  418. package/mqttPacket/connect.test.ts +2 -2
  419. package/mqttPacket/connect.ts +178 -192
  420. package/mqttPacket/decoder.test.ts +0 -39
  421. package/mqttPacket/decoder.ts +17 -46
  422. package/mqttPacket/disconnect.ts +60 -71
  423. package/mqttPacket/encoder.test.ts +2 -2
  424. package/mqttPacket/encoder.ts +13 -46
  425. package/mqttPacket/mod.ts +48 -204
  426. package/mqttPacket/pingreq.ts +22 -28
  427. package/mqttPacket/pingres.ts +22 -28
  428. package/mqttPacket/protocolLevels.ts +12 -7
  429. package/mqttPacket/pubblishAcks.ts +61 -75
  430. package/mqttPacket/publish.ts +70 -84
  431. package/mqttPacket/publishAcks.test.ts +3 -3
  432. package/mqttPacket/suback.ts +53 -68
  433. package/mqttPacket/subscribe.ts +146 -128
  434. package/mqttPacket/types.ts +2 -35
  435. package/mqttPacket/unsuback.ts +50 -61
  436. package/mqttPacket/unsubscribe.ts +77 -72
  437. package/mqttPacket/validators.ts +5 -31
  438. package/node/socket-node-stream.test.ts +322 -0
  439. package/node/socket-node-stream.ts +174 -0
  440. package/node/tcpClient.test.ts +70 -0
  441. package/node/tcpClient.ts +33 -17
  442. package/node/tcpServer.test.ts +24 -20
  443. package/node/tcpServer.ts +29 -6
  444. package/node/tlsServer.test.ts +94 -0
  445. package/node/tlsServer.ts +97 -0
  446. package/node/wrapNodeSocket.ts +7 -30
  447. package/package.json +72 -44
  448. package/persistence/basicPersistence.ts +495 -0
  449. package/persistence/deps.ts +11 -1
  450. package/persistence/memory/memoryPersistence.ts +12 -153
  451. package/persistence/memory/memoryStorage.ts +232 -0
  452. package/persistence/mod.ts +5 -10
  453. package/persistence/persistence.test.ts +24 -0
  454. package/persistence/persistence.testing.ts +1324 -0
  455. package/persistence/persistence.ts +112 -68
  456. package/persistence/sqlite/sqliteDatabase.ts +294 -0
  457. package/persistence/sqlite/sqlitePersistence.test.ts +48 -0
  458. package/persistence/sqlite/sqlitePersistence.ts +36 -0
  459. package/persistence/sqlite/sqliteStorage.ts +324 -0
  460. package/persistence/storage.ts +84 -0
  461. package/server/config.ts +65 -0
  462. package/server/context.ts +660 -76
  463. package/server/deps.ts +24 -5
  464. package/server/handlers/completeConnect.ts +93 -0
  465. package/server/handlers/duplicate.test.ts +196 -0
  466. package/server/handlers/handleAuth.test.ts +225 -0
  467. package/server/handlers/handleAuth.ts +88 -0
  468. package/server/handlers/handleConnect.test.ts +589 -0
  469. package/server/handlers/handleConnect.ts +201 -86
  470. package/server/handlers/handleConnect.version.test.ts +108 -0
  471. package/server/handlers/handleDisconnect.test.ts +56 -0
  472. package/server/handlers/handleDisconnect.ts +3 -3
  473. package/server/handlers/handlePacket.test.ts +24 -0
  474. package/server/handlers/handlePacket.ts +30 -8
  475. package/server/handlers/handlePingreq.test.ts +29 -0
  476. package/server/handlers/handlePuback.test.ts +28 -0
  477. package/server/handlers/handlePuback.ts +5 -8
  478. package/server/handlers/handlePubcomp.test.ts +28 -0
  479. package/server/handlers/handlePubcomp.ts +5 -4
  480. package/server/handlers/handlePublish.test.ts +471 -0
  481. package/server/handlers/handlePublish.ts +188 -35
  482. package/server/handlers/handlePubrec.test.ts +28 -0
  483. package/server/handlers/handlePubrec.ts +2 -3
  484. package/server/handlers/handlePubrel.test.ts +74 -0
  485. package/server/handlers/handlePubrel.ts +12 -2
  486. package/server/handlers/handleSubscribe.test.ts +971 -0
  487. package/server/handlers/handleSubscribe.ts +157 -39
  488. package/server/handlers/handleUnsubscribe.test.ts +79 -0
  489. package/server/handlers/handleUnsubscribe.ts +28 -4
  490. package/server/handlers/keepalive.test.ts +132 -0
  491. package/server/handlers/session.persistence.test.ts +194 -0
  492. package/server/handlers/will.delivery.test.ts +221 -0
  493. package/server/mod.ts +5 -4
  494. package/server/preconnect.test.ts +151 -0
  495. package/server/server.ts +117 -19
  496. package/socket/socket.test.ts +91 -0
  497. package/socket/socket.ts +85 -34
  498. package/timer/timer.test.ts +4 -7
  499. package/timer/timer.ts +36 -0
  500. package/tools/benchmarks/receiver.ts +51 -0
  501. package/tools/benchmarks/sender.ts +48 -0
  502. package/tools/benchmarks/utils.ts +31 -0
  503. package/tools/mqtt-compat/README.md +130 -0
  504. package/tools/mqtt-compat/broker.ts +52 -0
  505. package/tools/mqtt-compat/paho-mqtt4.test.ts +379 -0
  506. package/tools/mqtt-compat/paho-mqtt5.tests.ts +887 -0
  507. package/trie/trie.test.ts +9 -1
  508. package/trie/trie.ts +70 -45
  509. package/utils/bufferedAsyncIterable.test.ts +92 -0
  510. package/utils/deferred.test.ts +36 -0
  511. package/utils/deferred.ts +0 -1
  512. package/utils/getOrInsertPolyFill.ts +12 -0
  513. package/utils/logger.test.ts +189 -0
  514. package/utils/logger.ts +64 -9
  515. package/utils/mod.ts +15 -1
  516. package/utils/outboundTopicAliasManager.ts +100 -0
  517. package/utils/queue.test.ts +61 -0
  518. package/utils/topicFilter.test.ts +63 -0
  519. package/utils/topicFilter.ts +61 -0
  520. package/utils/utils.ts +3 -0
  521. package/utils/validators.ts +67 -0
  522. package/web/webSocketStream.ts +97 -0
  523. package/web/wrapWebSocket.ts +52 -0
  524. package/web/wsClient.test.ts +26 -0
  525. package/web/wsClient.ts +37 -0
  526. package/benchmarks/receiver.ts +0 -35
  527. package/benchmarks/sender.ts +0 -32
  528. package/benchmarks/utils.ts +0 -9
  529. package/dev_utils/dummyConn.ts +0 -110
  530. package/dev_utils/socketPair.ts +0 -85
  531. package/dist/persistence/store.d.ts +0 -14
  532. package/dist/persistence/store.d.ts.map +0 -1
  533. package/dist/persistence/store.js +0 -2
  534. package/dist/persistence/store.js.map +0 -1
  535. package/dist/utils/BufferedAsyncIterable.d.ts.map +0 -1
  536. package/dist/utils/BufferedAsyncIterable.js.map +0 -1
  537. package/examples/tcpServer.test.ts +0 -148
  538. package/persistence/memory/memoryPersistence.test.ts +0 -156
  539. package/persistence/store.ts +0 -16
  540. package/server/test/handleConnect.test.ts +0 -122
  541. package/server/test/test-handlers.ts +0 -53
  542. /package/utils/{BufferedAsyncIterable.ts → bufferedAsyncIterable.ts} +0 -0
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Represents the result of processing an outbound topic name,
3
+ * containing either the original topic name, a topic alias integer, or both.
4
+ */
5
+ export interface OutboundTopicAlias {
6
+ /**
7
+ * The topic name.
8
+ * Will be empty (`""`) when a valid alias mapping has already been established.
9
+ */
10
+ topicName: string;
11
+
12
+ /**
13
+ * The numeric alias assigned to the topic, if aliases are enabled.
14
+ */
15
+ topicAlias?: number;
16
+ }
17
+
18
+ /**
19
+ * Manages outbound MQTT topic aliases using a Least Recently Used (LRU) caching strategy.
20
+ */
21
+ export class OutboundTopicAliasManager {
22
+ /**
23
+ * The maximum number of topic aliases supported by the broker/client.
24
+ */
25
+ private readonly maxAliases: number;
26
+
27
+ /**
28
+ * Internal mapping from topic names to assigned aliases.
29
+ * Leverages JavaScript's Map insertion-order behavior to act as an LRU cache.
30
+ */
31
+ private readonly topicToAlias: Map<string, number>;
32
+
33
+ /**
34
+ * Creates an instance of `OutboundTopicAliasManager`.
35
+ *
36
+ * @param clientTopicAliasMaximum - The maximum number of topic aliases allowed (defaults to `0`, meaning disabled).
37
+ */
38
+ constructor(clientTopicAliasMaximum: number = 0) {
39
+ this.maxAliases = clientTopicAliasMaximum;
40
+ this.topicToAlias = new Map();
41
+ }
42
+
43
+ /**
44
+ * Processes an outbound topic name and returns the appropriate payload metadata
45
+ * (`OutboundTopcAlias`) based on alias availability and caching state.
46
+ *
47
+ * - If aliases are disabled (`maxAliases === 0`), returns only the original `topicName`.
48
+ * - If the topic is already cached (cache hit), refreshes its LRU position and returns the `topicAlias` with an empty `topicName`.
49
+ * - If the topic is not cached (cache miss), assigns a new alias or evicts the least recently used topic alias mapping.
50
+ *
51
+ * @param topic - The outbound MQTT topic name to process.
52
+ * @returns An object containing the formatted `topicName` and optional `topicAlias`.
53
+ */
54
+ public processTopic(topic: string): OutboundTopicAlias {
55
+ if (this.maxAliases === 0) {
56
+ return { topicName: topic };
57
+ }
58
+
59
+ // Cache Hit -> Refresh LRU-position in Map
60
+ if (this.topicToAlias.has(topic)) {
61
+ const alias = this.topicToAlias.get(topic)!;
62
+ this.topicToAlias.delete(topic);
63
+ this.topicToAlias.set(topic, alias);
64
+
65
+ return {
66
+ topicName: "",
67
+ topicAlias: alias,
68
+ };
69
+ }
70
+
71
+ // Cache Miss
72
+ let assignedAlias: number;
73
+
74
+ if (this.topicToAlias.size < this.maxAliases) {
75
+ // While there is space, simply assign the next alias (1, 2, 3...)
76
+ assignedAlias = this.topicToAlias.size + 1;
77
+ } else {
78
+ // Cache full: reuse the alias of the least used topic (first key in the Map)
79
+ const lruTopic = this.topicToAlias.keys().next().value;
80
+ assignedAlias = this.topicToAlias.get(lruTopic!)!;
81
+
82
+ this.topicToAlias.delete(lruTopic!);
83
+ }
84
+
85
+ // Store the new topic
86
+ this.topicToAlias.set(topic, assignedAlias);
87
+
88
+ return {
89
+ topicName: topic,
90
+ topicAlias: assignedAlias,
91
+ };
92
+ }
93
+
94
+ /**
95
+ * Clears all internal topic alias mappings and releases cached resources.
96
+ */
97
+ public destroy(): void {
98
+ this.topicToAlias.clear();
99
+ }
100
+ }
@@ -0,0 +1,61 @@
1
+ import { test } from "node:test";
2
+ import assert from "node:assert";
3
+ import { ArrayQueue } from "./queue.ts";
4
+
5
+ test("should initialize as an empty queue", () => {
6
+ const queue = new ArrayQueue<number>();
7
+ assert.strictEqual(queue.isEmpty(), true);
8
+ assert.strictEqual(queue.size(), 0);
9
+ });
10
+
11
+ test("should enqueue elements and update size correctly", () => {
12
+ const queue = new ArrayQueue<string>();
13
+
14
+ queue.enqueue("A");
15
+ assert.strictEqual(queue.isEmpty(), false);
16
+ assert.strictEqual(queue.size(), 1);
17
+
18
+ queue.enqueue("B");
19
+ assert.strictEqual(queue.size(), 2);
20
+ });
21
+
22
+ test("should dequeue elements in First-In, First-Out (FIFO) order", () => {
23
+ const queue = new ArrayQueue<number>();
24
+ queue.enqueue(10);
25
+ queue.enqueue(20);
26
+ queue.enqueue(30);
27
+
28
+ assert.strictEqual(queue.dequeue(), 10);
29
+ assert.strictEqual(queue.size(), 2);
30
+
31
+ assert.strictEqual(queue.dequeue(), 20);
32
+ assert.strictEqual(queue.size(), 1);
33
+
34
+ assert.strictEqual(queue.dequeue(), 30);
35
+ assert.strictEqual(queue.isEmpty(), true);
36
+ });
37
+
38
+ test("should return undefined when dequeuing an empty queue", () => {
39
+ const queue = new ArrayQueue<unknown>();
40
+ assert.strictEqual(queue.dequeue(), undefined);
41
+ });
42
+
43
+ test("should trigger internal cleanup (slice optimization) correctly", () => {
44
+ const queue = new ArrayQueue<number>();
45
+
46
+ // 1. Fill the queue with enough elements to exceed the threshold of 2000
47
+ const testSize = 2500;
48
+ for (let i = 0; i < testSize; i++) {
49
+ queue.enqueue(i);
50
+ }
51
+
52
+ // 2. Dequeue more than 1/4 of the total length to trigger the optimization (> 625 times)
53
+ // Dequeuing 2100 times satisfies the condition: headIndex > 2000 && headIndex > items.length / 4
54
+ for (let i = 0; i < 2100; i++) {
55
+ assert.strictEqual(queue.dequeue(), i);
56
+ }
57
+
58
+ // 3. Verify that the queue still functions correctly after internal cleanup
59
+ assert.strictEqual(queue.size(), testSize - 2100); // 400 remaining
60
+ assert.strictEqual(queue.dequeue(), 2100); // Next in line
61
+ });
@@ -0,0 +1,63 @@
1
+ import { test } from "node:test";
2
+ import assert from "node:assert";
3
+ import { topicFilterToRegExp } from "./topicFilter.ts";
4
+
5
+ test("should convert an exact topic filter without wildcards to a valid regex string", () => {
6
+ const filter = "home/livingroom/temperature";
7
+ const regex = topicFilterToRegExp(filter);
8
+
9
+ assert.strictEqual(regex.test("home/livingroom/temperature"), true);
10
+ assert.strictEqual(regex.test("home/kitchen/temperature"), false);
11
+ });
12
+
13
+ test("should handle the single-level wildcard (+) correctly", () => {
14
+ const filter = "home/+/temperature";
15
+ const regex = topicFilterToRegExp(filter);
16
+
17
+ // Should match exactly one arbitrary level
18
+ assert.strictEqual(regex.test("home/livingroom/temperature"), true);
19
+ assert.strictEqual(regex.test("home/kitchen/temperature"), true);
20
+
21
+ // Should not match if the level is missing or contains sub-levels
22
+ assert.strictEqual(regex.test("home/temperature"), false);
23
+ assert.strictEqual(regex.test("home/livingroom/closet/temperature"), false);
24
+ });
25
+
26
+ test("should handle the multi-level wildcard (#) correctly", () => {
27
+ const filter = "home/#";
28
+ const regex = topicFilterToRegExp(filter);
29
+
30
+ // Should match anything that follows the prefix (including nothing)
31
+ assert.strictEqual(regex.test("home"), true);
32
+ assert.strictEqual(regex.test("home/livingroom"), true);
33
+ assert.strictEqual(regex.test("home/livingroom/temperature"), true);
34
+ assert.strictEqual(regex.test("home/kitchen/lights/status"), true);
35
+ // should not match extensions
36
+ assert.strictEqual(regex.test("homeo/livingroom"), false);
37
+ });
38
+
39
+ test("should escape special regex characters safely", () => {
40
+ // Characters like '.' and '$' have special meanings in regex, but are literal in MQTT
41
+ const filter = "device/v1.0$/+";
42
+ const regex = topicFilterToRegExp(filter);
43
+
44
+ // Should match the exact literal characters including the dot and dollar sign
45
+ assert.strictEqual(regex.test("device/v1.0$/sensor"), true);
46
+
47
+ // Should fail if the '.' is treated as a regex wildcard (matching any character)
48
+ assert.strictEqual(regex.test("device/v1X0$/sensor"), false);
49
+ });
50
+
51
+ test("should combine both single-level (+) and multi-level (#) wildcards correctly", () => {
52
+ const filter = "clients/+/status/#";
53
+ const regex = topicFilterToRegExp(filter);
54
+
55
+ assert.strictEqual(regex.test("clients/user123/status"), true);
56
+ assert.strictEqual(regex.test("clients/user456/status/online"), true);
57
+ assert.strictEqual(
58
+ regex.test("clients/device789/status/connection/errors"),
59
+ true,
60
+ );
61
+
62
+ assert.strictEqual(regex.test("clients/status/online"), false);
63
+ });
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Converts an MQTT topic filter into a regular expression according to the MQTT specification.
3
+ *
4
+ * This utility escapes standard regex characters to prevent injection or unexpected behavior,
5
+ * and then converts the native MQTT wildcards into their regex equivalents:
6
+ * - `+` (single-level wildcard) becomes `[^/]+` (matches one or more characters except a forward slash)
7
+ * - `#` (multi-level wildcard) becomes an optional matching group `(?:/.*)?` to correctly handle parent levels.
8
+ *
9
+ * @param topicFilter - The MQTT topic filter string to convert (e.g., "home/+/temperature" or "clients/#").
10
+ * @returns A `RegExp` object configured to match topics corresponding to the filter.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const regex = topicFilterToRegExp("home/+/temperature");
15
+ * regex.test("home/livingroom/temperature"); // true
16
+ * regex.test("home/kitchen/lights"); // false
17
+ * ```
18
+ */
19
+ export function topicFilterToRegExp(topicFilter: string): RegExp {
20
+ // RegExp.escape (ES2024+) safely escapes special characters, forcing punctuation into hex formats (\xHH)
21
+ const escaped = RegExp.escape(topicFilter);
22
+
23
+ const regexStr = escaped
24
+ .replace(/\\\+/g, "[^/]+") // Replace '+' wildcard globally
25
+ .replace(/(?:\\\/)?\\x23/g, "(?:/.*)?"); // Replace '#' and its optional preceding slash (\/) globally
26
+
27
+ return new RegExp(`^${regexStr}$`);
28
+ }
29
+
30
+ /**
31
+ * Parses a Topic filter for a "shared subscription" into the share name and the regular Topic Filter
32
+ *
33
+ * @param topicFilter - The MQTT topic filter string to parse (e.g. "$share/sharename/rest/of/topic/filter")).
34
+ * @returns an object that contains the regular topicFilter and he share name (if present)
35
+ */
36
+ export function parseTopicFilter(topicFilter: string): {
37
+ topicFilter: string;
38
+ shareName: string;
39
+ } {
40
+ if (topicFilter.startsWith("$share/")) {
41
+ const nextSlashIndex = topicFilter.indexOf("/", 7);
42
+ if (nextSlashIndex !== -1) {
43
+ return {
44
+ topicFilter: topicFilter.slice(nextSlashIndex + 1),
45
+ shareName: topicFilter.slice(7, nextSlashIndex),
46
+ };
47
+ }
48
+ }
49
+ return { topicFilter, shareName: "" };
50
+ }
51
+
52
+ /** the inverse of parseTopicFilter */
53
+ export function joinTopicFilter(
54
+ topicFilter: string,
55
+ shareName: string,
56
+ ): string {
57
+ if (shareName !== "") {
58
+ return `$share/${shareName}/${topicFilter}`;
59
+ }
60
+ return topicFilter;
61
+ }
package/utils/utils.ts CHANGED
@@ -3,4 +3,7 @@
3
3
  * deserve their own dedicated files.
4
4
  */
5
5
 
6
+ import { setTimeout as delay } from "node:timers/promises";
7
+ export { delay };
8
+
6
9
  export const noop = () => {};
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Checks for too many topic levels to reduce the risk of Denial of Service
3
+ *
4
+ * @param value - The string to validate.
5
+ * @returns `true` if the string contains more / signs than allowed`.
6
+ */
7
+ export function invalidmaxTopicLevels(
8
+ value: string,
9
+ maxTopicLevels: number,
10
+ ): boolean {
11
+ let slashCount = 0;
12
+ for (const char of value) {
13
+ if (char === "/") {
14
+ slashCount++;
15
+ if (slashCount >= maxTopicLevels) return true;
16
+ }
17
+ }
18
+ return false;
19
+ }
20
+
21
+ /**
22
+ * Checks for invalid characters in an MQTT topic name.
23
+ * It cannot contain wildcards (+, #)
24
+ *
25
+ * @param value - The topic name to validate.
26
+ * @returns `true` if the topic is invalid, otherwise `false`.
27
+ */
28
+ export function invalidTopic(value: string): boolean {
29
+ return hasWildcards(value);
30
+ }
31
+
32
+ /**
33
+ * Checks for MQTT wildcards in a string.
34
+ *
35
+ * @param value - The topic name to validate.
36
+ * @returns `true` if the topic is invalid, otherwise `false`.
37
+ */
38
+ export function hasWildcards(value: string): boolean {
39
+ // Native .includes() is significantly faster than regular expressions for basic character checks
40
+ return value.includes("+") || value.includes("#");
41
+ }
42
+
43
+ /**
44
+ * Checks for invalid characters and malformed wildcards in an MQTT topic filter.
45
+ *
46
+ * Wildcard rules according to the MQTT specification:
47
+ * - '+' must occupy an entire level (e.g., 'a/+/b' is valid, 'a+/b' is invalid)
48
+ * - '#' must only appear at the very end of the string (e.g., 'a/#' is valid, 'a/#/b' is invalid)
49
+ *
50
+ * @param value - The topic filter to validate.
51
+ * @returns `true` if the topic filter is invalid, otherwise `false`.
52
+ */
53
+ export function invalidTopicFilter(value: string): boolean {
54
+ // Rule 1: A '#' wildcard must NEVER be followed by any other character
55
+ if (/#./.test(value)) {
56
+ return true;
57
+ }
58
+
59
+ // Rule 2: A '+' wildcard must be isolated by slashes (or string boundaries).
60
+ // Modern lookbehind (?<=) and lookahead (?=) make this highly readable.
61
+ const malformedPlus = /(?<=[^\/])\+|\+(?=[^\/])/.test(value);
62
+ if (malformedPlus) {
63
+ return true;
64
+ }
65
+
66
+ return false;
67
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Utility functions for wrapping standard WebSockets into standard web streams.
3
+ */
4
+
5
+ export interface WebSocketConnection<
6
+ T extends Uint8Array | string = Uint8Array | string,
7
+ > {
8
+ readable: ReadableStream<T>;
9
+ writable: WritableStream<T>;
10
+ protocol: string;
11
+ extensions: string;
12
+ }
13
+
14
+ export interface WebSocketCloseInfo {
15
+ closeCode?: number;
16
+ reason?: string;
17
+ }
18
+
19
+ export interface WebSocketStreamOptions {
20
+ protocols?: string[];
21
+ signal?: AbortSignal;
22
+ }
23
+
24
+ /**
25
+ * [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) with [Streams API](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API)
26
+ *
27
+ * @see https://web.dev/websocketstream/
28
+ */
29
+ export class WebSocketStream<
30
+ T extends Uint8Array | string = Uint8Array | string,
31
+ > {
32
+ readonly opened: Promise<WebSocketConnection<T>>;
33
+
34
+ readonly closed: Promise<WebSocketCloseInfo>;
35
+
36
+ readonly close: (closeInfo?: WebSocketCloseInfo) => void;
37
+
38
+ constructor(ws: WebSocket, options: WebSocketStreamOptions = {}) {
39
+ if (options.signal?.aborted) {
40
+ throw new DOMException("This operation was aborted", "AbortError");
41
+ }
42
+ const closeWithInfo = (
43
+ { closeCode: code, reason }: WebSocketCloseInfo = {},
44
+ ) => ws.close(code, reason);
45
+
46
+ this.opened = new Promise((resolve, reject) => {
47
+ ws.onopen = () => {
48
+ resolve({
49
+ readable: new ReadableStream<T>({
50
+ start(controller) {
51
+ ws.onmessage = ({ data }) => {
52
+ if (data instanceof ArrayBuffer) {
53
+ controller.enqueue(new Uint8Array(data) as T);
54
+ } else {
55
+ controller.enqueue(data as T);
56
+ }
57
+ };
58
+ ws.onerror = (e) => controller.error(e);
59
+ },
60
+ cancel: closeWithInfo,
61
+ }),
62
+ writable: new WritableStream<T>({
63
+ write(chunk) {
64
+ if (chunk instanceof Uint8Array) {
65
+ ws.send(chunk.buffer as ArrayBuffer);
66
+ } else {
67
+ ws.send(chunk);
68
+ }
69
+ },
70
+ abort() {
71
+ ws.close();
72
+ },
73
+ close: closeWithInfo,
74
+ }),
75
+ protocol: ws.protocol,
76
+ extensions: ws.extensions,
77
+ });
78
+ ws.removeEventListener("error", reject);
79
+ };
80
+ ws.addEventListener("error", reject);
81
+ });
82
+
83
+ this.closed = new Promise<WebSocketCloseInfo>((resolve, reject) => {
84
+ ws.onclose = ({ code, reason }) => {
85
+ resolve({ closeCode: code, reason });
86
+ ws.removeEventListener("error", reject);
87
+ };
88
+ ws.addEventListener("error", reject);
89
+ });
90
+
91
+ if (options.signal) {
92
+ options.signal.onabort = () => ws.close();
93
+ }
94
+
95
+ this.close = closeWithInfo;
96
+ }
97
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Utility functions for wrapping WebSocketStream sockets into standard web streams.
3
+ */
4
+ import { WebSocketStream } from "./webSocketStream.ts";
5
+ import type { SockConn } from "../socket/socket.ts";
6
+ import { logger } from "../utils/logger.ts";
7
+
8
+ export type HostnamePort = {
9
+ hostname?: string;
10
+ port?: string | number;
11
+ };
12
+
13
+ /**
14
+ * Closes a WebSocketStream socket if it is not already closed
15
+ * @param sock - The WebSocketStream socket to close
16
+ */
17
+ async function closer(stream: WebSocketStream) {
18
+ if (!await stream.closed) {
19
+ stream.close();
20
+ }
21
+ }
22
+
23
+ /**
24
+ * Wraps a WebSocketStream socket into a standard web streams-based socket connection
25
+ * @param ws - The WebSocket to wrap
26
+ * @returns A SockConn object
27
+ */
28
+ export async function wrapWebSocket(
29
+ ws: WebSocket,
30
+ remoteAddr?: HostnamePort,
31
+ ): Promise<SockConn> {
32
+ ws.binaryType = "arraybuffer";
33
+ const wsstream = new WebSocketStream<Uint8Array>(ws);
34
+ logger.debug("created wsstream");
35
+ const stream = await wsstream.opened;
36
+ logger.debug("wsstream is opened");
37
+ const readable = stream.readable;
38
+ const writable = stream.writable;
39
+ const { hostname = "localhost", port = 0 } = remoteAddr || {};
40
+
41
+ const conn: SockConn = {
42
+ readable: readable as ReadableStream<Uint8Array>,
43
+ writable: writable as WritableStream<Uint8Array>,
44
+ close: () => closer(wsstream),
45
+ remoteAddr: {
46
+ hostname,
47
+ port: Number(port),
48
+ transport: "tcp",
49
+ },
50
+ };
51
+ return conn;
52
+ }
@@ -0,0 +1,26 @@
1
+ import { test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { WsClient } from "./wsClient.ts";
4
+
5
+ // most client testing is already done in wsServer.test.ts
6
+ test("Web: Test createConn - unsupported protocol", async () => {
7
+ const client = new WsClient();
8
+
9
+ // Use an unsupported protocol like 'ftp://' to trigger the throw in createConn
10
+ const invalidParams = {
11
+ url: new URL("ftp://localhost:1883"),
12
+ numberOfRetries: 0,
13
+ };
14
+
15
+ // Assert that connect rejects with the specific "Unsupported protocol" error
16
+ await assert.rejects(
17
+ async () => {
18
+ await client.connect(invalidParams);
19
+ },
20
+ {
21
+ name: "Error",
22
+ message: "Unsupported protocol: ftp:",
23
+ },
24
+ "Should throw an error when an unsupported protocol is provided",
25
+ );
26
+ });
@@ -0,0 +1,37 @@
1
+ /*
2
+ * this is a WebSockets specific client
3
+ * it extends the platform agnostic Client class
4
+ * @module
5
+ */
6
+
7
+ import { Client } from "../client/client.ts";
8
+ import { logger } from "../client/deps.ts";
9
+ import type { SockConn } from "../client/deps.ts";
10
+ import { wrapWebSocket } from "./wrapWebSocket.ts";
11
+
12
+ /**
13
+ * WsClient extends the Client class to provide WebSocket based clients
14
+ * it is used by the MQTTclient to connect to the broker
15
+ * see the /examples folder for an example
16
+ */
17
+ export class WsClient extends Client {
18
+ /** create a websocket and wrap it in a SockConn */
19
+ private async establishSocket(url: string): Promise<SockConn> {
20
+ const ws = new WebSocket(url);
21
+ const conn = await wrapWebSocket(ws);
22
+ return conn;
23
+ }
24
+
25
+ /** createConn provides support for both "ws:" and "wss:" URLs */
26
+ protected override createConn(): Promise<SockConn> {
27
+ const { protocol, hostname, port: portStr, pathname } = this.connectUrl;
28
+ const port = portStr ? Number(portStr) : (protocol === "wss:" ? 443 : 80);
29
+ const path = pathname || "/mqtt";
30
+
31
+ logger.debug({ hostname, port, path });
32
+ if (protocol === "ws:" || protocol === "wss:") {
33
+ return this.establishSocket(`${protocol}//${hostname}:${port}${path}`);
34
+ }
35
+ throw new Error(`Unsupported protocol: ${protocol}`);
36
+ }
37
+ }
@@ -1,35 +0,0 @@
1
- import { exit } from "node:process";
2
- import { importClientClass } from "./utils.ts";
3
-
4
- const { TcpClient } = await importClientClass();
5
- const client = new TcpClient();
6
- client.onError = (err) => {
7
- console.log("client error", err);
8
- exit(1);
9
- };
10
-
11
- await client.connect({
12
- url: new URL("mqtt://localhost:1883"),
13
- });
14
-
15
- await client.subscribe({ subscriptions: [{ topicFilter: "test", qos: 0 }] });
16
-
17
- let counter = 0;
18
- const interval = 5000;
19
-
20
- function count() {
21
- console.log("received/s", (counter / interval) * 1000);
22
- counter = 0;
23
- }
24
-
25
- setInterval(count, interval);
26
-
27
- // // Consume using a callback
28
- // client.onPacket = async (packet) => {
29
- // counter++;
30
- // };
31
-
32
- // Consume using an AsyncIterator
33
- for await (const _ of client.messages()) {
34
- counter++;
35
- }
@@ -1,32 +0,0 @@
1
- import { exit } from "node:process";
2
- import { runAsap } from "../dev_utils/timers.ts";
3
- import { importClientClass } from "./utils.ts";
4
-
5
- const { TcpClient } = await importClientClass();
6
- const client = new TcpClient();
7
- client.onError = (err) => {
8
- console.log("client error", err);
9
- exit(1);
10
- };
11
-
12
- await client.connect({
13
- url: new URL("mqtt://localhost:1883"),
14
- });
15
-
16
- const payload = new TextEncoder().encode("payload");
17
- let sent = 0;
18
- const interval = 5000;
19
-
20
- const loop = () => {
21
- sent++;
22
- client.publish({ topic: "test", payload })
23
- .then(() => runAsap(loop));
24
- };
25
-
26
- function count() {
27
- console.log("sent/s", (sent / interval) * 1000);
28
- sent = 0;
29
- }
30
-
31
- setInterval(count, interval);
32
- loop();
@@ -1,9 +0,0 @@
1
- export async function importClientClass() {
2
- // @ts-ignore: the Deno global variable is not defined in @types/node,
3
- // therefore we need to ignore this error because this file
4
- // is meant to be used in both environments.
5
- const isDeno = typeof Deno !== "undefined";
6
- return isDeno
7
- ? (await import("../deno/tcpClient.ts"))
8
- : (await import("../node/tcpClient.ts"));
9
- }