@seriousme/opifex 1.11.2 → 2.0.3

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 (538) hide show
  1. package/LICENSE.txt +1 -1
  2. package/README.md +102 -43
  3. package/bin/conformanceServer.ts +45 -13
  4. package/bin/demoServer.ts +38 -13
  5. package/bin/mqtt.ts +52 -30
  6. package/client/ConnectionState.ts +2 -0
  7. package/client/client.ts +57 -47
  8. package/client/context.ts +75 -21
  9. package/client/deps.ts +5 -0
  10. package/client/handlers/handleAuth.test.ts +193 -0
  11. package/client/handlers/handleAuth.ts +89 -0
  12. package/client/handlers/handleConnack.test.ts +173 -0
  13. package/client/handlers/handleConnack.ts +16 -6
  14. package/client/handlers/handleDisconnect.test.ts +22 -0
  15. package/client/handlers/handleDisconnect.ts +17 -0
  16. package/client/handlers/handlePacket.test.ts +242 -0
  17. package/client/handlers/handlePacket.ts +19 -5
  18. package/client/handlers/handlePuback.test.ts +99 -0
  19. package/client/handlers/handlePuback.ts +6 -0
  20. package/client/handlers/handlePubcomp.test.ts +112 -0
  21. package/client/handlers/handlePubcomp.ts +5 -0
  22. package/client/handlers/handlePublish.test.ts +188 -0
  23. package/client/handlers/handlePubrec.test.ts +119 -0
  24. package/client/handlers/handlePubrec.ts +5 -1
  25. package/client/handlers/handlePubrel.test.ts +136 -0
  26. package/client/handlers/handleSuback.test.ts +122 -0
  27. package/client/handlers/handleSuback.ts +3 -0
  28. package/client/handlers/handleUnsuback.test.ts +101 -0
  29. package/client/mod.ts +2 -2
  30. package/client/store/memoryStore.ts +2 -2
  31. package/client/store/store.ts +3 -1
  32. package/deno/README.md +1 -2
  33. package/deno/tcpClient.test.ts +75 -0
  34. package/deno/tcpClient.ts +16 -11
  35. package/deno/tcpServer.test.ts +22 -21
  36. package/deno/tcpServer.ts +5 -0
  37. package/deno/tlsServer.test.ts +97 -0
  38. package/deno/tlsServer.ts +42 -0
  39. package/deno/wsServer.test.ts +93 -0
  40. package/deno/wsServer.ts +73 -0
  41. package/dev_utils/generateCert.ts +95 -0
  42. package/dev_utils/mockConn.ts +36 -0
  43. package/dev_utils/mockServer.ts +28 -0
  44. package/dev_utils/mod.ts +30 -2
  45. package/dev_utils/packetHelpers.ts +404 -0
  46. package/dev_utils/test-handlers.ts +98 -0
  47. package/dev_utils/timers.ts +14 -0
  48. package/dev_utils/webStreamPair.test.ts +171 -0
  49. package/dev_utils/webStreamPair.ts +143 -0
  50. package/dist/bin/conformanceServer.js +17 -12
  51. package/dist/bin/conformanceServer.js.map +1 -1
  52. package/dist/bin/demoServer.js +16 -12
  53. package/dist/bin/demoServer.js.map +1 -1
  54. package/dist/bin/mqtt.js +19 -16
  55. package/dist/bin/mqtt.js.map +1 -1
  56. package/dist/client/ConnectionState.d.ts +2 -0
  57. package/dist/client/ConnectionState.d.ts.map +1 -1
  58. package/dist/client/ConnectionState.js +2 -0
  59. package/dist/client/ConnectionState.js.map +1 -1
  60. package/dist/client/client.d.ts +19 -22
  61. package/dist/client/client.d.ts.map +1 -1
  62. package/dist/client/client.js +34 -20
  63. package/dist/client/client.js.map +1 -1
  64. package/dist/client/context.d.ts +11 -3
  65. package/dist/client/context.d.ts.map +1 -1
  66. package/dist/client/context.js +44 -12
  67. package/dist/client/context.js.map +1 -1
  68. package/dist/client/deps.d.ts +3 -2
  69. package/dist/client/deps.d.ts.map +1 -1
  70. package/dist/client/deps.js +1 -1
  71. package/dist/client/deps.js.map +1 -1
  72. package/dist/client/handlers/handleAuth.d.ts +9 -0
  73. package/dist/client/handlers/handleAuth.d.ts.map +1 -0
  74. package/dist/client/handlers/handleAuth.js +66 -0
  75. package/dist/client/handlers/handleAuth.js.map +1 -0
  76. package/dist/client/handlers/handleConnack.d.ts +2 -2
  77. package/dist/client/handlers/handleConnack.d.ts.map +1 -1
  78. package/dist/client/handlers/handleConnack.js +16 -6
  79. package/dist/client/handlers/handleConnack.js.map +1 -1
  80. package/dist/client/handlers/handleDisconnect.d.ts +11 -0
  81. package/dist/client/handlers/handleDisconnect.d.ts.map +1 -0
  82. package/dist/client/handlers/handleDisconnect.js +15 -0
  83. package/dist/client/handlers/handleDisconnect.js.map +1 -0
  84. package/dist/client/handlers/handlePacket.d.ts.map +1 -1
  85. package/dist/client/handlers/handlePacket.js +14 -3
  86. package/dist/client/handlers/handlePacket.js.map +1 -1
  87. package/dist/client/handlers/handlePuback.d.ts.map +1 -1
  88. package/dist/client/handlers/handlePuback.js +5 -0
  89. package/dist/client/handlers/handlePuback.js.map +1 -1
  90. package/dist/client/handlers/handlePubcomp.d.ts.map +1 -1
  91. package/dist/client/handlers/handlePubcomp.js +5 -0
  92. package/dist/client/handlers/handlePubcomp.js.map +1 -1
  93. package/dist/client/handlers/handlePubrec.d.ts.map +1 -1
  94. package/dist/client/handlers/handlePubrec.js +5 -1
  95. package/dist/client/handlers/handlePubrec.js.map +1 -1
  96. package/dist/client/handlers/handleSuback.d.ts +1 -0
  97. package/dist/client/handlers/handleSuback.d.ts.map +1 -1
  98. package/dist/client/handlers/handleSuback.js +3 -0
  99. package/dist/client/handlers/handleSuback.js.map +1 -1
  100. package/dist/client/mod.d.ts +2 -2
  101. package/dist/client/mod.d.ts.map +1 -1
  102. package/dist/client/mod.js +1 -1
  103. package/dist/client/mod.js.map +1 -1
  104. package/dist/client/store/memoryStore.d.ts.map +1 -1
  105. package/dist/client/store/memoryStore.js +2 -2
  106. package/dist/client/store/memoryStore.js.map +1 -1
  107. package/dist/client/store/store.d.ts +2 -1
  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 +25 -16
  128. package/dist/mqttConn/mqttConn.d.ts.map +1 -1
  129. package/dist/mqttConn/mqttConn.js +69 -40
  130. package/dist/mqttConn/mqttConn.js.map +1 -1
  131. package/dist/mqttPacket/AuthenticationResult.d.ts.map +1 -1
  132. package/dist/mqttPacket/BitMask.d.ts +12 -11
  133. package/dist/mqttPacket/BitMask.d.ts.map +1 -1
  134. package/dist/mqttPacket/BitMask.js +10 -18
  135. package/dist/mqttPacket/BitMask.js.map +1 -1
  136. package/dist/mqttPacket/PacketType.d.ts.map +1 -1
  137. package/dist/mqttPacket/Properties.d.ts +2 -2
  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 +1 -0
  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.map +1 -1
  145. package/dist/mqttPacket/auth.d.ts +3 -5
  146. package/dist/mqttPacket/auth.d.ts.map +1 -1
  147. package/dist/mqttPacket/auth.js +31 -30
  148. package/dist/mqttPacket/auth.js.map +1 -1
  149. package/dist/mqttPacket/codec.d.ts +48 -0
  150. package/dist/mqttPacket/codec.d.ts.map +1 -0
  151. package/dist/mqttPacket/codec.js +93 -0
  152. package/dist/mqttPacket/codec.js.map +1 -0
  153. package/dist/mqttPacket/connack.d.ts +4 -5
  154. package/dist/mqttPacket/connack.d.ts.map +1 -1
  155. package/dist/mqttPacket/connack.js +43 -45
  156. package/dist/mqttPacket/connack.js.map +1 -1
  157. package/dist/mqttPacket/connect.d.ts +26 -27
  158. package/dist/mqttPacket/connect.d.ts.map +1 -1
  159. package/dist/mqttPacket/connect.js +156 -157
  160. package/dist/mqttPacket/connect.js.map +1 -1
  161. package/dist/mqttPacket/decoder.d.ts +1 -13
  162. package/dist/mqttPacket/decoder.d.ts.map +1 -1
  163. package/dist/mqttPacket/decoder.js +7 -28
  164. package/dist/mqttPacket/decoder.js.map +1 -1
  165. package/dist/mqttPacket/disconnect.d.ts +4 -5
  166. package/dist/mqttPacket/disconnect.d.ts.map +1 -1
  167. package/dist/mqttPacket/disconnect.js +41 -43
  168. package/dist/mqttPacket/disconnect.js.map +1 -1
  169. package/dist/mqttPacket/encoder.d.ts +1 -15
  170. package/dist/mqttPacket/encoder.d.ts.map +1 -1
  171. package/dist/mqttPacket/encoder.js +4 -27
  172. package/dist/mqttPacket/encoder.js.map +1 -1
  173. package/dist/mqttPacket/length.js +1 -1
  174. package/dist/mqttPacket/length.js.map +1 -1
  175. package/dist/mqttPacket/mod.d.ts +21 -103
  176. package/dist/mqttPacket/mod.d.ts.map +1 -1
  177. package/dist/mqttPacket/mod.js +5 -94
  178. package/dist/mqttPacket/mod.js.map +1 -1
  179. package/dist/mqttPacket/pingreq.d.ts +4 -5
  180. package/dist/mqttPacket/pingreq.d.ts.map +1 -1
  181. package/dist/mqttPacket/pingreq.js +11 -13
  182. package/dist/mqttPacket/pingreq.js.map +1 -1
  183. package/dist/mqttPacket/pingres.d.ts +4 -5
  184. package/dist/mqttPacket/pingres.d.ts.map +1 -1
  185. package/dist/mqttPacket/pingres.js +11 -13
  186. package/dist/mqttPacket/pingres.js.map +1 -1
  187. package/dist/mqttPacket/protocolLevels.d.ts +11 -6
  188. package/dist/mqttPacket/protocolLevels.d.ts.map +1 -1
  189. package/dist/mqttPacket/protocolLevels.js +5 -1
  190. package/dist/mqttPacket/protocolLevels.js.map +1 -1
  191. package/dist/mqttPacket/pubblishAcks.d.ts +9 -10
  192. package/dist/mqttPacket/pubblishAcks.d.ts.map +1 -1
  193. package/dist/mqttPacket/pubblishAcks.js +43 -45
  194. package/dist/mqttPacket/pubblishAcks.js.map +1 -1
  195. package/dist/mqttPacket/publish.d.ts +14 -14
  196. package/dist/mqttPacket/publish.d.ts.map +1 -1
  197. package/dist/mqttPacket/publish.js +53 -55
  198. package/dist/mqttPacket/publish.js.map +1 -1
  199. package/dist/mqttPacket/suback.d.ts +4 -5
  200. package/dist/mqttPacket/suback.d.ts.map +1 -1
  201. package/dist/mqttPacket/suback.js +38 -40
  202. package/dist/mqttPacket/suback.js.map +1 -1
  203. package/dist/mqttPacket/subscribe.d.ts +36 -7
  204. package/dist/mqttPacket/subscribe.d.ts.map +1 -1
  205. package/dist/mqttPacket/subscribe.js +90 -89
  206. package/dist/mqttPacket/subscribe.js.map +1 -1
  207. package/dist/mqttPacket/types.d.ts +1 -8
  208. package/dist/mqttPacket/types.d.ts.map +1 -1
  209. package/dist/mqttPacket/unsuback.d.ts +4 -5
  210. package/dist/mqttPacket/unsuback.d.ts.map +1 -1
  211. package/dist/mqttPacket/unsuback.js +32 -34
  212. package/dist/mqttPacket/unsuback.js.map +1 -1
  213. package/dist/mqttPacket/unsubscribe.d.ts +23 -6
  214. package/dist/mqttPacket/unsubscribe.d.ts.map +1 -1
  215. package/dist/mqttPacket/unsubscribe.js +45 -44
  216. package/dist/mqttPacket/unsubscribe.js.map +1 -1
  217. package/dist/mqttPacket/validators.d.ts +4 -16
  218. package/dist/mqttPacket/validators.d.ts.map +1 -1
  219. package/dist/mqttPacket/validators.js +5 -26
  220. package/dist/mqttPacket/validators.js.map +1 -1
  221. package/dist/node/tcpClient.d.ts +1 -1
  222. package/dist/node/tcpClient.d.ts.map +1 -1
  223. package/dist/node/tcpClient.js +12 -7
  224. package/dist/node/tcpClient.js.map +1 -1
  225. package/dist/node/tcpServer.d.ts.map +1 -1
  226. package/dist/node/tcpServer.js +4 -0
  227. package/dist/node/tcpServer.js.map +1 -1
  228. package/dist/node/tlsServer.d.ts +19 -0
  229. package/dist/node/tlsServer.d.ts.map +1 -0
  230. package/dist/node/tlsServer.js +58 -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 +44 -69
  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 +198 -24
  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 +8 -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 +29 -6
  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 +11 -9
  340. package/dist/socket/socket.d.ts.map +1 -1
  341. package/dist/socket/socket.js +67 -15
  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 +47 -17
  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 +40 -3
  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 +7 -0
  396. package/dist/web/wsClient.d.ts.map +1 -0
  397. package/dist/web/wsClient.js +22 -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 +91 -49
  409. package/mqttPacket/BitMask.ts +11 -19
  410. package/mqttPacket/Properties.ts +6 -3
  411. package/mqttPacket/ReasonCode.ts +2 -1
  412. package/mqttPacket/auth.test.ts +1 -1
  413. package/mqttPacket/auth.ts +41 -48
  414. package/mqttPacket/codec.ts +146 -0
  415. package/mqttPacket/connack.ts +57 -68
  416. package/mqttPacket/connect.test.ts +2 -2
  417. package/mqttPacket/connect.ts +197 -204
  418. package/mqttPacket/decoder.test.ts +0 -39
  419. package/mqttPacket/decoder.ts +9 -36
  420. package/mqttPacket/disconnect.ts +57 -68
  421. package/mqttPacket/encoder.ts +5 -40
  422. package/mqttPacket/length.ts +1 -1
  423. package/mqttPacket/mod.ts +48 -200
  424. package/mqttPacket/pingreq.ts +21 -27
  425. package/mqttPacket/pingres.ts +21 -27
  426. package/mqttPacket/protocolLevels.ts +12 -7
  427. package/mqttPacket/pubblishAcks.ts +68 -77
  428. package/mqttPacket/publish.ts +76 -83
  429. package/mqttPacket/suback.ts +48 -58
  430. package/mqttPacket/subscribe.ts +141 -117
  431. package/mqttPacket/types.ts +1 -9
  432. package/mqttPacket/unsuback.ts +46 -58
  433. package/mqttPacket/unsubscribe.ts +74 -63
  434. package/mqttPacket/validators.ts +5 -31
  435. package/node/socket-node-stream.test.ts +322 -0
  436. package/node/socket-node-stream.ts +174 -0
  437. package/node/tcpClient.test.ts +70 -0
  438. package/node/tcpClient.ts +13 -12
  439. package/node/tcpServer.test.ts +24 -20
  440. package/node/tcpServer.ts +6 -0
  441. package/node/tlsServer.test.ts +94 -0
  442. package/node/tlsServer.ts +81 -0
  443. package/node/wrapNodeSocket.ts +7 -30
  444. package/package.json +73 -45
  445. package/persistence/basicPersistence.ts +495 -0
  446. package/persistence/deps.ts +11 -1
  447. package/persistence/memory/memoryPersistence.ts +12 -152
  448. package/persistence/memory/memoryStorage.ts +232 -0
  449. package/persistence/mod.ts +4 -4
  450. package/persistence/persistence.test.ts +24 -0
  451. package/persistence/persistence.testing.ts +1324 -0
  452. package/persistence/persistence.ts +111 -67
  453. package/persistence/sqlite/sqliteDatabase.ts +294 -0
  454. package/persistence/sqlite/sqlitePersistence.test.ts +48 -0
  455. package/persistence/sqlite/sqlitePersistence.ts +36 -0
  456. package/persistence/sqlite/sqliteStorage.ts +324 -0
  457. package/persistence/storage.ts +84 -0
  458. package/server/config.ts +65 -0
  459. package/server/context.ts +658 -74
  460. package/server/deps.ts +25 -5
  461. package/server/handlers/completeConnect.ts +93 -0
  462. package/server/handlers/duplicate.test.ts +196 -0
  463. package/server/handlers/handleAuth.test.ts +225 -0
  464. package/server/handlers/handleAuth.ts +88 -0
  465. package/server/handlers/handleConnect.test.ts +589 -0
  466. package/server/handlers/handleConnect.ts +201 -86
  467. package/server/handlers/handleConnect.version.test.ts +108 -0
  468. package/server/handlers/handleDisconnect.test.ts +56 -0
  469. package/server/handlers/handleDisconnect.ts +3 -3
  470. package/server/handlers/handlePacket.test.ts +24 -0
  471. package/server/handlers/handlePacket.ts +30 -8
  472. package/server/handlers/handlePingreq.test.ts +29 -0
  473. package/server/handlers/handlePuback.test.ts +28 -0
  474. package/server/handlers/handlePuback.ts +5 -8
  475. package/server/handlers/handlePubcomp.test.ts +28 -0
  476. package/server/handlers/handlePubcomp.ts +5 -4
  477. package/server/handlers/handlePublish.test.ts +471 -0
  478. package/server/handlers/handlePublish.ts +189 -35
  479. package/server/handlers/handlePubrec.test.ts +28 -0
  480. package/server/handlers/handlePubrec.ts +2 -3
  481. package/server/handlers/handlePubrel.test.ts +74 -0
  482. package/server/handlers/handlePubrel.ts +12 -2
  483. package/server/handlers/handleSubscribe.test.ts +971 -0
  484. package/server/handlers/handleSubscribe.ts +158 -39
  485. package/server/handlers/handleUnsubscribe.test.ts +79 -0
  486. package/server/handlers/handleUnsubscribe.ts +29 -5
  487. package/server/handlers/keepalive.test.ts +132 -0
  488. package/server/handlers/session.persistence.test.ts +194 -0
  489. package/server/handlers/will.delivery.test.ts +221 -0
  490. package/server/mod.ts +4 -3
  491. package/server/preconnect.test.ts +151 -0
  492. package/server/server.ts +115 -17
  493. package/socket/socket.test.ts +91 -0
  494. package/socket/socket.ts +81 -31
  495. package/timer/timer.test.ts +4 -7
  496. package/timer/timer.ts +36 -0
  497. package/tools/benchmarks/receiver.ts +51 -0
  498. package/tools/benchmarks/sender.ts +48 -0
  499. package/tools/benchmarks/utils.ts +31 -0
  500. package/tools/mqtt-compat/README.md +130 -0
  501. package/tools/mqtt-compat/broker.ts +52 -0
  502. package/tools/mqtt-compat/paho-mqtt4.test.ts +379 -0
  503. package/tools/mqtt-compat/paho-mqtt5.tests.ts +887 -0
  504. package/trie/trie.test.ts +9 -1
  505. package/trie/trie.ts +57 -22
  506. package/utils/bufferedAsyncIterable.test.ts +92 -0
  507. package/utils/{BufferedAsyncIterable.ts → bufferedAsyncIterable.ts} +1 -1
  508. package/utils/deferred.test.ts +36 -0
  509. package/utils/deferred.ts +0 -1
  510. package/utils/getOrInsertPolyFill.ts +12 -0
  511. package/utils/logger.test.ts +189 -0
  512. package/utils/logger.ts +62 -7
  513. package/utils/mod.ts +15 -1
  514. package/utils/outboundTopicAliasManager.ts +100 -0
  515. package/utils/queue.test.ts +61 -0
  516. package/utils/topicFilter.test.ts +63 -0
  517. package/utils/topicFilter.ts +61 -0
  518. package/utils/utils.ts +3 -0
  519. package/utils/validators.ts +67 -0
  520. package/web/webSocketStream.ts +97 -0
  521. package/web/wrapWebSocket.ts +52 -0
  522. package/web/wsClient.test.ts +26 -0
  523. package/web/wsClient.ts +25 -0
  524. package/benchmarks/receiver.ts +0 -35
  525. package/benchmarks/sender.ts +0 -32
  526. package/benchmarks/utils.ts +0 -9
  527. package/dev_utils/dummyConn.ts +0 -110
  528. package/dist/persistence/store.d.ts +0 -13
  529. package/dist/persistence/store.d.ts.map +0 -1
  530. package/dist/persistence/store.js +0 -2
  531. package/dist/persistence/store.js.map +0 -1
  532. package/dist/utils/BufferedAsyncIterable.d.ts.map +0 -1
  533. package/dist/utils/BufferedAsyncIterable.js.map +0 -1
  534. package/examples/tcpServer.test.ts +0 -148
  535. package/persistence/memory/memoryPersistence.test.ts +0 -156
  536. package/persistence/store.ts +0 -15
  537. package/server/test/handleConnect.test.ts +0 -130
  538. package/server/test/test-handlers.ts +0 -53
@@ -0,0 +1,971 @@
1
+ import assert from "node:assert/strict";
2
+ import { test } from "node:test";
3
+ import type { PublishPacket } from "../deps.ts";
4
+ import { PacketType, ReasonCode } from "../deps.ts";
5
+ import {
6
+ addMockClient,
7
+ connect,
8
+ connect5,
9
+ disconnect,
10
+ disconnect5,
11
+ isAuthenticatedBroker,
12
+ ping,
13
+ publish,
14
+ publish5,
15
+ startMockServer,
16
+ subscribe,
17
+ subscribe5,
18
+ } from "../../dev_utils/mod.ts";
19
+ import { SqlitePersistence } from "../../persistence/sqlite/sqlitePersistence.ts";
20
+ import { receiveMessages } from "../../dev_utils/packetHelpers.ts";
21
+
22
+ const txtEncoder = new TextEncoder();
23
+
24
+ test("SUBSCRIBE returns SUBACK with matching return codes", async () => {
25
+ const { mqttConn } = startMockServer();
26
+
27
+ // Connect first
28
+ await connect(mqttConn);
29
+
30
+ // Subscribe to a topic
31
+ // subscribe() also checks packetId and QoS in subacks
32
+ await subscribe(mqttConn, [{ topicFilter: "test/topic", qos: 0 }], { id: 1 });
33
+ await disconnect(mqttConn);
34
+ });
35
+
36
+ test("SUBSCRIBE with multiple topics returns multiple return codes", async () => {
37
+ const { mqttConn } = startMockServer();
38
+
39
+ await connect(mqttConn);
40
+ // subscribe() also checks packetId and QoS in subacks
41
+ await subscribe(mqttConn, [
42
+ { topicFilter: "topic/one", qos: 0 },
43
+ { topicFilter: "topic/two", qos: 1 },
44
+ { topicFilter: "topic/three", qos: 2 },
45
+ ], { id: 2 });
46
+
47
+ await disconnect(mqttConn);
48
+ });
49
+
50
+ test("SUBSCRIBE with wildcard topics works", async () => {
51
+ const { mqttConn } = startMockServer();
52
+
53
+ await connect(mqttConn);
54
+
55
+ await subscribe(mqttConn, [
56
+ { topicFilter: "sensors/+/temperature", qos: 0 },
57
+ { topicFilter: "events/#", qos: 1 },
58
+ ], { id: 3 });
59
+
60
+ await disconnect(mqttConn);
61
+ });
62
+
63
+ test("SUBSCRIBE with missing isAuthorizedToSubscribe handler authorizes subscribe", async () => {
64
+ const { mqttConn, mqttServer } = startMockServer();
65
+ mqttServer.handlers.isAuthorizedToSubscribe = undefined;
66
+
67
+ await connect(mqttConn);
68
+
69
+ await subscribe(mqttConn, [
70
+ { topicFilter: "sensors/temperature", qos: 0 },
71
+ ], { id: 3 });
72
+
73
+ await disconnect(mqttConn);
74
+ });
75
+
76
+ test("SUBSCRIBE to unauthorized topic is rejected", async () => {
77
+ const { mqttConn } = startMockServer();
78
+
79
+ await connect(mqttConn);
80
+
81
+ const subAck = await subscribe(mqttConn, [
82
+ { topicFilter: "topic/unauthorized", qos: 0 },
83
+ ], { id: 3, checkAcks: false });
84
+
85
+ assert.deepStrictEqual(subAck.type, PacketType.suback, "Expected SUBACK");
86
+ assert.deepStrictEqual(subAck.id, 3);
87
+ assert.deepStrictEqual(
88
+ subAck.returnCodes,
89
+ [128],
90
+ );
91
+
92
+ await disconnect(mqttConn);
93
+ });
94
+ // ============================================================================
95
+ // Retained Message Tests
96
+ // ============================================================================
97
+
98
+ test("SUBSCRIBE receives retained message after SUBACK", async () => {
99
+ const { mqttConn: mqttConn1, mqttServer } = startMockServer();
100
+ const retainedPayload = "retained-value";
101
+ // First, publish a retained message (before any subscriber)
102
+ await connect(mqttConn1);
103
+
104
+ await publish(mqttConn1, "sensors/temperature", 0, {
105
+ payload: retainedPayload,
106
+ retain: true,
107
+ });
108
+ await disconnect(mqttConn1);
109
+
110
+ // Connect
111
+ const mqttConn2 = addMockClient(mqttServer);
112
+ await connect(mqttConn2);
113
+
114
+ // Subscribe to the topic with retained message
115
+ await subscribe(mqttConn2, [{ topicFilter: "sensors/temperature", qos: 0 }], {
116
+ id: 10,
117
+ });
118
+
119
+ // Then should receive the retained message
120
+ const { value: publishPkt } = await mqttConn2.next();
121
+ assert.deepStrictEqual(
122
+ publishPkt.type,
123
+ PacketType.publish,
124
+ "Expected retained PUBLISH",
125
+ );
126
+
127
+ assert.deepStrictEqual(publishPkt.topic, "sensors/temperature");
128
+ assert.deepStrictEqual(
129
+ publishPkt.payload,
130
+ txtEncoder.encode(retainedPayload),
131
+ );
132
+
133
+ await disconnect(mqttConn2);
134
+ });
135
+
136
+ test("SUBSCRIBE receives multiple retained messages matching wildcard", async () => {
137
+ const { mqttConn: mqttConn1, mqttServer } = startMockServer();
138
+ // Set up multiple retained messages
139
+ await connect(mqttConn1);
140
+ await publish(mqttConn1, "sensors/temp/living", 0, {
141
+ payload: "22",
142
+ retain: true,
143
+ });
144
+
145
+ await publish(mqttConn1, "sensors/temp/bedroom", 0, {
146
+ payload: "20",
147
+ retain: true,
148
+ });
149
+
150
+ await disconnect(mqttConn1);
151
+
152
+ // Connect the second client
153
+ const mqttConn2 = addMockClient(mqttServer);
154
+ await connect(mqttConn2);
155
+
156
+ // Subscribe with wildcard
157
+ await subscribe(mqttConn2, [{ topicFilter: "sensors/temp/#", qos: 0 }], {
158
+ id: 11,
159
+ });
160
+
161
+ // Should receive both retained messages
162
+ const messages = await receiveMessages(mqttConn2);
163
+
164
+ const topics = messages
165
+ .filter((m): m is PublishPacket => m.type === PacketType.publish)
166
+ .map((m) => m.topic)
167
+ .sort();
168
+
169
+ assert.deepStrictEqual(topics, [
170
+ "sensors/temp/bedroom",
171
+ "sensors/temp/living",
172
+ ]);
173
+ });
174
+
175
+ test("SUBSCRIBE receives multiple retained messages with different QoS", async () => {
176
+ const { mqttConn: mqttConn1, mqttServer } = startMockServer();
177
+ // Set up multiple retained messages
178
+ await connect(mqttConn1);
179
+ await publish(mqttConn1, "retained/qos0", 0, {
180
+ retain: true,
181
+ id: undefined,
182
+ });
183
+ await publish(mqttConn1, "retained/qos1", 1, {
184
+ retain: true,
185
+ id: 10,
186
+ });
187
+ await publish(mqttConn1, "retained/qos2", 2, {
188
+ retain: true,
189
+ id: 11,
190
+ });
191
+
192
+ // Subscribe
193
+ await subscribe(mqttConn1, [
194
+ { topicFilter: "+/+", qos: 2 },
195
+ ]);
196
+
197
+ // Should receive three retained messages
198
+ const messages = await receiveMessages(mqttConn1);
199
+
200
+ const topics = messages
201
+ .filter((m): m is PublishPacket => m.type === PacketType.publish)
202
+ .map((m) => m.topic)
203
+ .sort();
204
+
205
+ assert.deepStrictEqual(topics, [
206
+ "retained/qos0",
207
+ "retained/qos1",
208
+ "retained/qos2",
209
+ ]);
210
+
211
+ const mqttConn2 = addMockClient(mqttServer);
212
+ await connect(mqttConn2);
213
+ // clear retained
214
+ await publish(mqttConn2, "retained/qos0", 0, {
215
+ payload: "",
216
+ retain: true,
217
+ id: undefined,
218
+ });
219
+ await publish(mqttConn2, "retained/qos1", 1, {
220
+ payload: "",
221
+ retain: true,
222
+ id: 10,
223
+ });
224
+ await publish(mqttConn2, "retained/qos2", 2, {
225
+ payload: "",
226
+ retain: true,
227
+ id: 11,
228
+ });
229
+ // Subscribe
230
+ await subscribe(mqttConn2, [
231
+ { topicFilter: "+/+", qos: 2 },
232
+ ]);
233
+ // should receive no messages, check with ping
234
+ await ping(mqttConn2);
235
+ await disconnect(mqttConn2);
236
+ });
237
+
238
+ test("SUBSCRIBE to topic without retained message receives only SUBACK", async () => {
239
+ const { mqttConn } = startMockServer();
240
+
241
+ await connect(mqttConn);
242
+
243
+ // Subscribe to topic with no retained message
244
+ await subscribe(mqttConn, [{ topicFilter: "no/retained/here", qos: 0 }]);
245
+
246
+ // No more messages should be pending - send a ping to verify
247
+ await ping(mqttConn);
248
+ await disconnect(mqttConn);
249
+ });
250
+
251
+ test("SUBSCRIBE with wildcard does not match $ topics", async () => {
252
+ const { mqttConn: subscriber, mqttServer } = startMockServer();
253
+ mqttServer.handlers.isAuthenticated = isAuthenticatedBroker;
254
+
255
+ const wildcardTopic = "+/+";
256
+ const dollarTopic = "$TopicA/B";
257
+
258
+ // subscribe to wildcard topic
259
+ await connect(subscriber);
260
+ await subscribe(subscriber, [{ topicFilter: wildcardTopic, qos: 1 }], {
261
+ id: 30,
262
+ });
263
+
264
+ const publisher = addMockClient(mqttServer);
265
+ await connect(publisher);
266
+
267
+ await publish(publisher, dollarTopic, 1, {
268
+ payload: "dollar topic message",
269
+ retain: false,
270
+ id: 105,
271
+ });
272
+ await disconnect(publisher);
273
+
274
+ await ping(subscriber);
275
+
276
+ await disconnect(subscriber);
277
+ });
278
+
279
+ test("SUBSCRIBE receives retained messages and clearing works", async () => {
280
+ const { mqttConn: publisher1, mqttServer } = startMockServer();
281
+
282
+ const qos0topic = "fromb/qos 0";
283
+ const qos1topic = "fromb/qos 1";
284
+ const qos2topic = "fromb/qos2";
285
+ const wildcardTopic = "fromb/+";
286
+
287
+ await connect(publisher1);
288
+
289
+ await publish(publisher1, qos0topic, 0, {
290
+ payload: "qos0",
291
+ retain: true,
292
+ });
293
+ await publish(publisher1, qos1topic, 1, {
294
+ payload: "qos1",
295
+ retain: true,
296
+ id: 101,
297
+ });
298
+ await publish(publisher1, qos2topic, 2, {
299
+ payload: "qos2",
300
+ retain: true,
301
+ id: 102,
302
+ });
303
+
304
+ disconnect(publisher1);
305
+
306
+ const subscriber1 = addMockClient(mqttServer);
307
+ await connect(subscriber1);
308
+
309
+ await subscribe(subscriber1, [{ topicFilter: wildcardTopic, qos: 2 }], {
310
+ id: 20,
311
+ });
312
+
313
+ const messages = await receiveMessages(subscriber1);
314
+
315
+ const receivedTopics = messages
316
+ .filter((m): m is PublishPacket => m.type === PacketType.publish)
317
+ .map((m) => m.topic)
318
+ .sort();
319
+
320
+ assert.deepStrictEqual(
321
+ receivedTopics,
322
+ [qos0topic, qos1topic, qos2topic].sort(),
323
+ "Should receive all three retained messages",
324
+ );
325
+
326
+ const publisher2 = addMockClient(mqttServer);
327
+ await connect(publisher2);
328
+
329
+ await publish(publisher2, qos0topic, 0, {
330
+ payload: "",
331
+ retain: true,
332
+ });
333
+ await publish(publisher2, qos1topic, 1, {
334
+ payload: "",
335
+ retain: true,
336
+ id: 103,
337
+ });
338
+ await publish(publisher2, qos2topic, 2, {
339
+ payload: "",
340
+ retain: true,
341
+ id: 104,
342
+ });
343
+
344
+ await disconnect(publisher2);
345
+
346
+ const subscriber2 = addMockClient(mqttServer);
347
+
348
+ await connect(subscriber2);
349
+
350
+ await subscribe(subscriber2, [{ topicFilter: wildcardTopic, qos: 2 }], {
351
+ id: 21,
352
+ });
353
+ await ping(subscriber2);
354
+
355
+ await disconnect(subscriber2);
356
+ });
357
+
358
+ test("SUBSCRIBE redelivery on reconnect (uncompleted QoS 1/2 exchanges)", async () => {
359
+ const sqlitePersistence = new SqlitePersistence();
360
+ const { mqttConn: subscriber, mqttServer } = startMockServer({
361
+ persistence: sqlitePersistence,
362
+ });
363
+ const clientId = "myclient";
364
+ const topic1 = "TopicA/B";
365
+ const topic3 = "TopicA/C";
366
+ const wildtopic6 = "TopicA/#";
367
+
368
+ await connect(subscriber, { clean: false, clientId });
369
+ await subscribe(subscriber, [{ topicFilter: wildtopic6, qos: 2 }], {
370
+ id: 40,
371
+ });
372
+ await disconnect(subscriber);
373
+
374
+ const publisher = addMockClient(mqttServer);
375
+ await connect(publisher);
376
+
377
+ await publish(publisher, topic1, 1, {
378
+ payload: "qos 1 message",
379
+ retain: false,
380
+ id: 201,
381
+ });
382
+ await publish(publisher, topic3, 2, {
383
+ payload: "qos 2 message",
384
+ retain: false,
385
+ id: 202,
386
+ });
387
+
388
+ await disconnect(publisher);
389
+
390
+ const subscriberReconnect = addMockClient(mqttServer);
391
+
392
+ await connect(subscriberReconnect, { clean: false, clientId });
393
+
394
+ const messages = await receiveMessages(subscriberReconnect);
395
+
396
+ const receivedTopics = messages
397
+ .filter((m): m is PublishPacket => m.type === PacketType.publish)
398
+ .map((m) => m.topic)
399
+ .sort();
400
+
401
+ assert.deepStrictEqual(
402
+ receivedTopics,
403
+ [topic1, topic3].sort(),
404
+ "Should receive both uncompleted QoS 1 and QoS 2 messages upon reconnect",
405
+ );
406
+
407
+ await disconnect(subscriberReconnect);
408
+ });
409
+
410
+ // ============================================================================
411
+ // MQTT v5 Specific Tests
412
+ // ============================================================================
413
+
414
+ test("SUBSCRIBE v5 returns SUBACK with reasonCodes and handles subscriptionIdentifier", async () => {
415
+ const { mqttConn } = startMockServer();
416
+
417
+ await connect5(mqttConn);
418
+
419
+ const subAck = await subscribe5(mqttConn, [{
420
+ topicFilter: "v5/test/topic",
421
+ qos: 1,
422
+ noLocal: true,
423
+ retainAsPublished: true,
424
+ retainHandling: 0,
425
+ }], { id: 100, subscriptionIdentifier: 42 });
426
+
427
+ assert.deepStrictEqual(subAck.id, 100);
428
+ assert.deepStrictEqual(
429
+ subAck.reasonCodes,
430
+ [ReasonCode.grantedQos1],
431
+ "Expected reasonCodes matching requested QoS 1 for MQTT v5",
432
+ );
433
+
434
+ await disconnect5(mqttConn);
435
+ });
436
+
437
+ test("SUBSCRIBE v5 to unauthorized topic returns ReasonCode.notAuthorized (0x87)", async () => {
438
+ const { mqttConn } = startMockServer();
439
+
440
+ await connect5(mqttConn);
441
+
442
+ const subAck = await subscribe5(mqttConn, [{
443
+ topicFilter: "topic/unauthorized",
444
+ qos: 0,
445
+ }], {
446
+ id: 101,
447
+ checkAcks: false,
448
+ });
449
+
450
+ assert.deepStrictEqual(subAck.type, PacketType.suback, "Expected SUBACK");
451
+ assert.deepStrictEqual(subAck.id, 101);
452
+ assert.deepStrictEqual(
453
+ subAck.reasonCodes,
454
+ [ReasonCode.notAuthorized],
455
+ "Return code should be 0x87 (ReasonCode.notAuthorized) for MQTT v5",
456
+ );
457
+
458
+ await disconnect5(mqttConn);
459
+ });
460
+
461
+ test("SUBSCRIBE v5 handles mixed authorized and unauthorized topics", async () => {
462
+ const { mqttConn } = startMockServer();
463
+
464
+ await connect5(mqttConn);
465
+
466
+ const subAck = await subscribe5(mqttConn, [
467
+ { topicFilter: "topic/authorized", qos: 1 },
468
+ { topicFilter: "topic/unauthorized", qos: 2 },
469
+ ], { id: 102, checkAcks: false });
470
+
471
+ assert.deepStrictEqual(subAck.type, PacketType.suback, "Expected SUBACK");
472
+
473
+ assert.deepStrictEqual(subAck.id, 102);
474
+ assert.deepStrictEqual(
475
+ subAck.reasonCodes,
476
+ [ReasonCode.grantedQos1, ReasonCode.notAuthorized],
477
+ "Should return success code for authorized topic and 0x87 for unauthorized topic in exact order",
478
+ );
479
+
480
+ await disconnect5(mqttConn);
481
+ });
482
+
483
+ // ============================================================================
484
+ // Edge Cases & Error Handling
485
+ // ============================================================================
486
+
487
+ test("SUBSCRIBE does not trigger retained messages when all subscriptions fail authorization", async () => {
488
+ const { mqttConn: publisher, mqttServer } = startMockServer();
489
+
490
+ // Set up a retained message first
491
+ await connect(publisher);
492
+ await publish(publisher, "topic/unauthorized", 0, {
493
+ payload: "secret-retained",
494
+ retain: true,
495
+ });
496
+
497
+ await disconnect(publisher);
498
+
499
+ const subscriber = addMockClient(mqttServer);
500
+ await connect(subscriber);
501
+
502
+ const subAck = await subscribe(subscriber, [{
503
+ topicFilter: "topic/unauthorized",
504
+ qos: 0,
505
+ }], {
506
+ id: 104,
507
+ checkAcks: false,
508
+ });
509
+
510
+ // Expect SUBACK with 128 (0x80)
511
+ assert.deepStrictEqual(subAck.type, PacketType.suback);
512
+ assert.deepStrictEqual(subAck.returnCodes, [128]);
513
+
514
+ // Ping to verify no retained message was forwarded
515
+ await ping(subscriber);
516
+ await disconnect(subscriber);
517
+ });
518
+
519
+ test("SUBSCRIBE handles error thrown inside isAuthorizedToSubscribe gracefully", async () => {
520
+ const { mqttConn, mqttServer } = startMockServer();
521
+
522
+ // Force authorization handler to throw an error
523
+ mqttServer.handlers.isAuthorizedToSubscribe = () => {
524
+ throw new Error("Authorization handler internal failure");
525
+ };
526
+
527
+ await connect(mqttConn);
528
+ const subAck = await subscribe(mqttConn, [{
529
+ topicFilter: "sensors/temperature",
530
+ qos: 0,
531
+ }], {
532
+ id: 105,
533
+ checkAcks: false,
534
+ });
535
+ // Expect SUBACK with 128 (0x80)
536
+ assert.deepStrictEqual(subAck.type, PacketType.suback);
537
+ assert.deepStrictEqual(subAck.returnCodes, [128]);
538
+ });
539
+
540
+ // ============================================================================
541
+ // Wildcard Restriction Tests
542
+ // ============================================================================
543
+
544
+ test("SUBSCRIBE v4 closes connection when wildcardSubscriptionAvailable is false", async () => {
545
+ const { mqttConn } = startMockServer({
546
+ configuration: { context: { wildcardSubscriptionAvailable: false } },
547
+ });
548
+
549
+ await connect(mqttConn);
550
+
551
+ await subscribe(mqttConn, [{ topicFilter: "sensors/+", qos: 0 }], {
552
+ id: 1,
553
+ checkAcks: false,
554
+ });
555
+
556
+ await mqttConn.next();
557
+ assert.strictEqual(
558
+ mqttConn.isClosed,
559
+ true,
560
+ "Expected connection to be closed in v4",
561
+ );
562
+ });
563
+
564
+ test("SUBSCRIBE v5 returns wildcardSubscriptionsNotSupported when wildcardSubscriptionAvailable is false", async () => {
565
+ const { mqttConn } = startMockServer({
566
+ configuration: { context: { wildcardSubscriptionAvailable: false } },
567
+ });
568
+
569
+ await connect5(mqttConn);
570
+
571
+ const subAck = await subscribe5(mqttConn, [{
572
+ topicFilter: "sensors/+",
573
+ qos: 0,
574
+ }], {
575
+ id: 2,
576
+ checkAcks: false,
577
+ });
578
+
579
+ assert.strictEqual(subAck.type, PacketType.suback);
580
+ assert.strictEqual(subAck.id, 2);
581
+ assert.deepStrictEqual(
582
+ subAck.reasonCodes,
583
+ [ReasonCode.wildcardSubscriptionsNotSupported],
584
+ "Expected wildcardSubscriptionsNotSupported (0xA2) in v5",
585
+ );
586
+
587
+ await disconnect5(mqttConn);
588
+ });
589
+
590
+ // ============================================================================
591
+ // Topic Filter Validation Tests
592
+ // ============================================================================
593
+
594
+ test("SUBSCRIBE v4 closes connection on invalid topic filter", async () => {
595
+ const { mqttConn } = startMockServer();
596
+
597
+ await connect(mqttConn);
598
+
599
+ // Invalid wildcard placement
600
+ await subscribe(mqttConn, [{ topicFilter: "invalid/#/topic", qos: 0 }], {
601
+ id: 3,
602
+ checkAcks: false,
603
+ });
604
+
605
+ await mqttConn.next();
606
+ assert.strictEqual(
607
+ mqttConn.isClosed,
608
+ true,
609
+ "Expected connection to be closed in v4",
610
+ );
611
+ });
612
+
613
+ test("SUBSCRIBE v5 returns topicFilterInvalid on invalid topic filter or empty filter", async () => {
614
+ const { mqttConn } = startMockServer();
615
+
616
+ await connect5(mqttConn);
617
+
618
+ const subAck = await subscribe5(mqttConn, [
619
+ { topicFilter: "", qos: 0 },
620
+ { topicFilter: "invalid/#/topic", qos: 1 },
621
+ ], {
622
+ id: 4,
623
+ checkAcks: false,
624
+ });
625
+
626
+ assert.strictEqual(subAck.type, PacketType.suback);
627
+ assert.strictEqual(subAck.id, 4);
628
+ assert.deepStrictEqual(
629
+ subAck.reasonCodes,
630
+ [ReasonCode.topicFilterInvalid, ReasonCode.topicFilterInvalid],
631
+ "Expected topicFilterInvalid (0x8F) for invalid topic filters in v5",
632
+ );
633
+
634
+ await disconnect5(mqttConn);
635
+ });
636
+
637
+ test("SUBSCRIBE v5 returns topicFilterInvalid when topic levels exceed maxTopicLevels", async () => {
638
+ const { mqttConn } = startMockServer({
639
+ configuration: { context: { maxTopicLevels: 1 } },
640
+ });
641
+
642
+ await connect5(mqttConn);
643
+
644
+ const subAck = await subscribe5(mqttConn, [{
645
+ topicFilter: "level1/level2",
646
+ qos: 0,
647
+ }], {
648
+ id: 5,
649
+ checkAcks: false,
650
+ });
651
+
652
+ assert.strictEqual(subAck.type, PacketType.suback);
653
+ assert.deepStrictEqual(
654
+ subAck.reasonCodes,
655
+ [ReasonCode.topicFilterInvalid],
656
+ "Expected topicFilterInvalid when exceeding maxTopicLevels",
657
+ );
658
+
659
+ await disconnect5(mqttConn);
660
+ });
661
+
662
+ // ============================================================================
663
+ // MQTT v5 Retain Handling Tests
664
+ // ============================================================================
665
+
666
+ test("SUBSCRIBE v5 with retainHandling = 2 does not send retained message", async () => {
667
+ const { mqttConn: publisher, mqttServer } = startMockServer();
668
+
669
+ // Publish retained message
670
+ await connect(publisher);
671
+ await publish(publisher, "retained/handling2", 0, {
672
+ payload: "should-not-be-sent",
673
+ retain: true,
674
+ });
675
+ await disconnect(publisher);
676
+
677
+ const subscriber = addMockClient(mqttServer);
678
+ await connect5(subscriber);
679
+
680
+ // Subscribe with retainHandling = 2 (Do not send retained messages)
681
+ await subscribe5(subscriber, [{
682
+ topicFilter: "retained/handling2",
683
+ qos: 0,
684
+ retainHandling: 2,
685
+ }], { id: 10 });
686
+
687
+ // PING to verify no retained PUBLISH packet was delivered after SUBACK
688
+ await ping(subscriber);
689
+ await disconnect5(subscriber);
690
+ });
691
+
692
+ test("SUBSCRIBE v5 with retainHandling = 1 sends retained message on new sub, skips on resubscribe", async () => {
693
+ const { mqttConn: publisher, mqttServer } = startMockServer();
694
+
695
+ await connect(publisher);
696
+ // Use QoS 1 so publish completes and persists before disconnecting
697
+ await publish(publisher, "retained/handling1", 1, {
698
+ id: 1,
699
+ payload: "first-delivery-only",
700
+ retain: true,
701
+ });
702
+ await disconnect(publisher);
703
+
704
+ const subscriber = addMockClient(mqttServer);
705
+ await connect5(subscriber);
706
+
707
+ // 1. Initial subscribe with retainHandling = 1 -> Should receive retained message
708
+ await subscribe5(subscriber, [{
709
+ topicFilter: "retained/handling1",
710
+ qos: 0,
711
+ retainHandling: 1,
712
+ }], { id: 20 });
713
+
714
+ const { value: publishPkt } = await subscriber.next();
715
+ assert.strictEqual(publishPkt.type, PacketType.publish);
716
+ assert.strictEqual(publishPkt.topic, "retained/handling1");
717
+
718
+ // 2. Re-subscribe to the exact same topic with retainHandling = 1 -> Should NOT receive retained message again
719
+ await subscribe5(subscriber, [{
720
+ topicFilter: "retained/handling1",
721
+ qos: 0,
722
+ retainHandling: 1,
723
+ }], { id: 21 });
724
+
725
+ await ping(subscriber);
726
+ await disconnect5(subscriber);
727
+ });
728
+
729
+ // ============================================================================
730
+ // Shared Subscriptions & Options Tests
731
+ // ============================================================================
732
+
733
+ test("SUBSCRIBE v5 returns sharedSubscriptionsNotSupported when shared subscriptions are disabled", async () => {
734
+ const { mqttConn } = startMockServer({
735
+ configuration: { context: { sharedSubscriptionAvailable: false } },
736
+ });
737
+
738
+ await connect5(mqttConn);
739
+
740
+ const subAck = await subscribe5(mqttConn, [{
741
+ topicFilter: "$share/group/sensors/temperature",
742
+ qos: 0,
743
+ }], {
744
+ id: 200,
745
+ checkAcks: false,
746
+ });
747
+
748
+ assert.strictEqual(subAck.type, PacketType.suback);
749
+ assert.strictEqual(subAck.id, 200);
750
+ assert.deepStrictEqual(
751
+ subAck.reasonCodes,
752
+ [ReasonCode.sharedSubscriptionsNotSupported],
753
+ "Expected sharedSubscriptionsNotSupported (0x8E) in v5",
754
+ );
755
+
756
+ await disconnect5(mqttConn);
757
+ });
758
+
759
+ test("SUBSCRIBE v4 closes connection on shared subscription when shared subscriptions are disabled", async () => {
760
+ const { mqttConn } = startMockServer({
761
+ configuration: { context: { sharedSubscriptionAvailable: false } },
762
+ });
763
+
764
+ await connect(mqttConn);
765
+
766
+ await subscribe(mqttConn, [{
767
+ topicFilter: "$share/group/sensors/temperature",
768
+ qos: 0,
769
+ }], {
770
+ id: 201,
771
+ checkAcks: false,
772
+ });
773
+
774
+ await mqttConn.next();
775
+ assert.strictEqual(
776
+ mqttConn.isClosed,
777
+ true,
778
+ "Expected connection to be closed in v4",
779
+ );
780
+ });
781
+
782
+ test("SUBSCRIBE v5 returns topicFilterInvalid when noLocal is set on a shared subscription", async () => {
783
+ const { mqttConn } = startMockServer({
784
+ configuration: { context: { sharedSubscriptionAvailable: true } },
785
+ });
786
+
787
+ await connect5(mqttConn);
788
+
789
+ const subAck = await subscribe5(mqttConn, [{
790
+ topicFilter: "$share/group/sensors/temperature",
791
+ qos: 1,
792
+ noLocal: true,
793
+ }], {
794
+ id: 202,
795
+ checkAcks: false,
796
+ });
797
+
798
+ assert.strictEqual(subAck.type, PacketType.suback);
799
+ assert.strictEqual(subAck.id, 202);
800
+ assert.deepStrictEqual(
801
+ subAck.reasonCodes,
802
+ [ReasonCode.topicFilterInvalid],
803
+ "Expected topicFilterInvalid (0x8F) when noLocal is true on shared subscription",
804
+ );
805
+
806
+ await disconnect5(mqttConn);
807
+ });
808
+
809
+ test("SUBSCRIBE v4 closes connection when a shared subscription is used", async () => {
810
+ const { mqttConn } = startMockServer({
811
+ configuration: { context: { sharedSubscriptionAvailable: true } },
812
+ });
813
+
814
+ await connect(mqttConn);
815
+
816
+ await subscribe(mqttConn, [{
817
+ topicFilter: "$share/group/sensors/temperature",
818
+ qos: 1,
819
+ }], {
820
+ id: 203,
821
+ checkAcks: false,
822
+ });
823
+
824
+ await mqttConn.next();
825
+ assert.strictEqual(
826
+ mqttConn.isClosed,
827
+ true,
828
+ "Expected connection to be closed in v4",
829
+ );
830
+ });
831
+
832
+ // ============================================================================
833
+ // Additional MQTT v4 Validation Edge Cases
834
+ // ============================================================================
835
+
836
+ test("SUBSCRIBE v4 closes connection when topicFilter is empty", async () => {
837
+ const { mqttConn } = startMockServer();
838
+
839
+ await connect(mqttConn);
840
+
841
+ await subscribe(mqttConn, [{ topicFilter: "", qos: 0 }], {
842
+ id: 204,
843
+ checkAcks: false,
844
+ });
845
+
846
+ await mqttConn.next();
847
+ assert.strictEqual(
848
+ mqttConn.isClosed,
849
+ true,
850
+ "Expected connection to be closed in v4 for empty topic filter",
851
+ );
852
+ });
853
+
854
+ test("SUBSCRIBE v4 closes connection when topic levels exceed maxTopicLevels", async () => {
855
+ const { mqttConn } = startMockServer({
856
+ configuration: { context: { maxTopicLevels: 1 } },
857
+ });
858
+
859
+ await connect(mqttConn);
860
+
861
+ await subscribe(mqttConn, [{ topicFilter: "level1/level2", qos: 0 }], {
862
+ id: 205,
863
+ checkAcks: false,
864
+ });
865
+
866
+ await mqttConn.next();
867
+ assert.strictEqual(
868
+ mqttConn.isClosed,
869
+ true,
870
+ "Expected connection to be closed in v4 when exceeding maxTopicLevels",
871
+ );
872
+ });
873
+
874
+ // ============================================================================
875
+ // Subscription Flags Behavior Tests (noLocal & retainAsPublished)
876
+ // ============================================================================
877
+
878
+ test("SUBSCRIBE v5 with noLocal = true does not deliver own published messages", async () => {
879
+ const { mqttConn: client, mqttServer } = startMockServer();
880
+ mqttServer.handlers.isAuthenticated = isAuthenticatedBroker;
881
+
882
+ await connect5(client, { clientId: "nolocal-client" });
883
+
884
+ await subscribe5(client, [{
885
+ topicFilter: "nolocal/test",
886
+ qos: 1,
887
+ noLocal: true,
888
+ }], { id: 206 });
889
+
890
+ // Publish message on the same connection
891
+ await publish5(client, "nolocal/test", 0, {
892
+ payload: "self-published-msg",
893
+ id: 50,
894
+ });
895
+
896
+ // Check with PING that no PUBLISH packet is delivered back
897
+ await ping(client);
898
+
899
+ await disconnect5(client);
900
+ });
901
+
902
+ test("SUBSCRIBE v5 with retainAsPublished = false strips retain flag on delivery", async () => {
903
+ const { mqttConn: publisher, mqttServer } = startMockServer();
904
+
905
+ await connect5(publisher);
906
+
907
+ const subscriber = addMockClient(mqttServer);
908
+ await connect5(subscriber);
909
+
910
+ // Subscribe with retainAsPublished: false
911
+ await subscribe5(subscriber, [{
912
+ topicFilter: "rap/test",
913
+ qos: 1,
914
+ retainAsPublished: false,
915
+ }], { id: 207 });
916
+
917
+ // Publish with retain: true
918
+ await publish5(publisher, "rap/test", 1, {
919
+ payload: "retained-content",
920
+ retain: true,
921
+ id: 60,
922
+ });
923
+
924
+ const { value: publishPkt } = await subscriber.next();
925
+ assert.strictEqual(publishPkt.type, PacketType.publish);
926
+ assert.strictEqual(
927
+ publishPkt.retain,
928
+ false,
929
+ "Expected retain flag to be cleared on delivery when retainAsPublished is false",
930
+ );
931
+
932
+ await disconnect5(publisher);
933
+ await disconnect5(subscriber);
934
+ });
935
+
936
+ // ============================================================================
937
+ // Combined Validation and Authorization Results Test
938
+ // ============================================================================
939
+
940
+ test("SUBSCRIBE v5 handles mixed validation errors and authorization failures in strict order", async () => {
941
+ const { mqttConn } = startMockServer({
942
+ configuration: { context: { sharedSubscriptionAvailable: false } },
943
+ });
944
+
945
+ await connect5(mqttConn);
946
+
947
+ const subAck = await subscribe5(mqttConn, [
948
+ { topicFilter: "topic/authorized", qos: 1 },
949
+ { topicFilter: "invalid/#/topic", qos: 0 },
950
+ { topicFilter: "topic/unauthorized", qos: 2 },
951
+ { topicFilter: "$share/group/test", qos: 1 },
952
+ ], {
953
+ id: 208,
954
+ checkAcks: false,
955
+ });
956
+
957
+ assert.strictEqual(subAck.type, PacketType.suback);
958
+ assert.strictEqual(subAck.id, 208);
959
+ assert.deepStrictEqual(
960
+ subAck.reasonCodes,
961
+ [
962
+ ReasonCode.grantedQos1,
963
+ ReasonCode.topicFilterInvalid,
964
+ ReasonCode.notAuthorized,
965
+ ReasonCode.sharedSubscriptionsNotSupported,
966
+ ],
967
+ "Expected exact sequence of reason codes matching subscription packet order",
968
+ );
969
+
970
+ await disconnect5(mqttConn);
971
+ });