@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
package/server/context.ts CHANGED
@@ -2,169 +2,753 @@ import {
2
2
  logger,
3
3
  MqttConn,
4
4
  MQTTLevel,
5
+ OutboundTopicAliasManager,
5
6
  PacketNameByType,
6
7
  PacketType,
8
+ Timer,
7
9
  } from "./deps.ts";
8
10
  import type {
9
11
  AnyPacket,
10
- IPersistence,
11
- IStore,
12
+ ConnectPacket,
13
+ ExtPublishPacket,
14
+ PacketId,
15
+ Persistence,
12
16
  ProtocolLevel,
13
17
  PublishPacket,
18
+ PubrelPacket,
19
+ ReasonCode,
20
+ ShareName,
14
21
  SockConn,
15
- TAuthenticationResult,
16
- Timer,
17
22
  Topic,
18
23
  } from "./deps.ts";
19
24
 
25
+ import { QueueMode } from "./config.ts";
26
+ import type { Configuration } from "./config.ts";
27
+
28
+ /** Unique identifier for an MQTT client. */
20
29
  export type ClientId = string;
21
30
 
31
+ /** System topic prefix character used for internal MQTT system topics. */
22
32
  export const SysPrefix = "$";
23
33
 
34
+ /** Standard UTF-8 encoder used across the server to convert strings to byte arrays. */
24
35
  export const utf8Encoder = new TextEncoder();
25
36
 
37
+ /** Possible results from isAuthenticated and processAuth handler */
38
+ export type AuthenticatedResult = {
39
+ reasonCode: ReasonCode;
40
+ reasonString?: string;
41
+ authData?: Uint8Array;
42
+ };
43
+
44
+ export const SessionState = {
45
+ connecting: 0,
46
+ authenticating: 1,
47
+ connected: 2,
48
+ closing: 3,
49
+ disconnected: 4,
50
+ } as const;
51
+
52
+ export type SessionState = typeof SessionState[keyof typeof SessionState];
53
+
26
54
  /**
27
55
  * Handlers are hooks that the server will call
28
56
  * and let you influence the servers behaviour.
29
57
  * The following handlers can be configured:
30
- * - isAuthenticated()
31
- * - isAuthorizedToPublish()
32
- * - isAuthorizedToSubscribe()
58
+ * - preconnect()
59
+ * - isAuthenticated()
60
+ * - processAuth()
61
+ * - isAuthorizedToPublish()
62
+ * - isAuthorizedToSubscribe()
33
63
  */
34
64
  export type Handlers = {
65
+ /**
66
+ * Default preconnect handler that unconditionally permits all connections.
67
+ * @param {SockConn} conn - The connection context.
68
+ * @returns {boolean} false will close the connection
69
+ */
70
+ preconnect?(
71
+ conn: SockConn,
72
+ ): boolean | Promise<boolean>;
73
+
74
+ /**
75
+ * Hook to authenticate a client connection attempt.
76
+ * @param {Context} ctx - The connection context.
77
+ * @param {ClientId} clientId - The client identifier.
78
+ * @param {string} username - The username provided by the client.
79
+ * @param {Uint8Array} password - The password bytes provided by the client.
80
+ * @param {ConnectPacket} connectPacket - The raw connect packet
81
+ * @returns {AuthResult} The result of the authentication attempt.
82
+ */
35
83
  isAuthenticated?(
36
84
  ctx: Context,
37
85
  clientId: ClientId,
38
86
  username: string,
39
87
  password: Uint8Array,
40
- ): TAuthenticationResult;
41
- isAuthorizedToPublish?(ctx: Context, topic: Topic): boolean;
42
- isAuthorizedToSubscribe?(ctx: Context, topic: Topic): boolean;
88
+ connectPacket: ConnectPacket,
89
+ ): AuthenticatedResult | Promise<AuthenticatedResult>;
90
+
91
+ /**
92
+ * Hook to authenticate a client connection attempt.
93
+ * @param {Context} ctx - The connection context.
94
+ * @param {ClientId} clientId - The client identifier.
95
+ * @param {string} authMethod - The authentication method provided by the client.
96
+ * @param {Uint8Array} authData- The authentication data provided by the client.
97
+ * @returns {AuthenticationResult} The result of the authentication attempt.
98
+ */
99
+ processAuth?(
100
+ ctx: Context,
101
+ clientId: ClientId,
102
+ authMethod: string,
103
+ authData: Uint8Array,
104
+ ): AuthenticatedResult | Promise<AuthenticatedResult>;
105
+
106
+ /**
107
+ * Hook to authorize a message publication to a specific topic.
108
+ * @param {Context} ctx - The connection context.
109
+ * @param {Topic} topic - The topic the client wants to publish to.
110
+ * @returns {boolean} True if the client is authorized to publish, false otherwise.
111
+ */
112
+ isAuthorizedToPublish?(
113
+ ctx: Context,
114
+ topic: Topic,
115
+ ): boolean | Promise<boolean>;
116
+
117
+ /**
118
+ * Hook to authorize a subscription request to a specific topic.
119
+ * @param {Context} ctx - The connection context.
120
+ * @param {Topic} topic - The topic filter the client wants to subscribe to.
121
+ * @param {ShareName} shareName - The name of the share in case of shared subscriptions
122
+ * @returns {boolean} True if the client is authorized to subscribe, false otherwise.
123
+ */
124
+ isAuthorizedToSubscribe?(
125
+ ctx: Context,
126
+ topic: Topic,
127
+ shareName: ShareName,
128
+ ): boolean | Promise<boolean>;
43
129
  };
44
130
 
45
131
  /**
46
- * The Context class is used to maintain state of a MQTT connection
47
- * It handles:
48
- * - connect/disconnect including broadcasting of these events
49
- * - publish
50
- * - persistence
51
- * - the will
132
+ * V5 options that can be passed by handleConnect
52
133
  */
134
+ export type ConnectOptions = {
135
+ sessionExpiryInterval?: number | undefined;
136
+ willDelayInterval?: number | undefined;
137
+ topicAliasMaximum?: number | undefined;
138
+ maximumOutgoingPacketSize?: number | undefined;
139
+ receiveMaximum?: number | undefined;
140
+ assignedClientIdentifier?: string | undefined;
141
+ keepAlive?: number | undefined;
142
+ };
53
143
 
144
+ /**
145
+ * Per-connection server context that manages the lifecycle, packets,
146
+ * timers, and persistence interactions for an individual client connection.
147
+ */
54
148
  export class Context {
55
- connected: boolean;
149
+ /** the client id is set after succesful connect */
150
+ clientId: ClientId | null = null;
151
+
152
+ /** Indicates the current state of the client */
153
+ state: SessionState = SessionState.disconnected;
154
+
155
+ /** Indicates whether the client asked for a clean session */
156
+ cleanSession = false;
157
+
158
+ /** Indicates whether the client is considered a broker and allowed to use $SYS topics etc */
159
+ isBroker = false;
160
+
161
+ /** The MQTT protocol level version determined during the connection handshake. */
56
162
  protocolLevel: ProtocolLevel;
163
+
164
+ /** The underlying raw socket connection. */
57
165
  conn: SockConn;
166
+
167
+ /** The wrapped MQTT connection wrapper handling frame reading and writing. */
58
168
  mqttConn: MqttConn;
59
- persistence: IPersistence;
169
+
170
+ /** The persistence layer instance used by the server. */
171
+ persistence: Persistence;
172
+
173
+ /** The configured authentication and authorization lifecycle hooks. */
60
174
  handlers: Handlers;
175
+
176
+ /** Configuration data */
177
+ config: Configuration;
178
+
179
+ /** Global registry mapping active client identifiers to their respective connection context. */
61
180
  static clientList: Map<ClientId, Context> = new Map();
62
- store?: IStore;
63
- will?: PublishPacket | undefined;
181
+
182
+ /** The optional Will packet configured by the client to be published if disconnected unexpectedly. */
183
+ will?: ExtPublishPacket | undefined;
184
+
185
+ /** The Keep Alive timer tracking the client activity timeout. */
64
186
  timer?: Timer;
65
187
 
66
- constructor(persistence: IPersistence, conn: SockConn, handlers: Handlers) {
188
+ /** Timer to keep track of when a V5 session ends */
189
+ sessionEndsTimer?: Timer;
190
+
191
+ /** Timer to handle V5 delayed will delivery */
192
+ willTimer?: Timer;
193
+
194
+ /**
195
+ * Timer enforcing a deadline for the client to send a CONNECT packet
196
+ * after establishing a socket connection.
197
+ */
198
+ preconnectTimer?: Timer;
199
+
200
+ /**
201
+ * max number of inflight packets for this client,
202
+ * in V5 client can set this lower than maxInflightMessages
203
+ * theoretical max is maxInt
204
+ */
205
+ receiveMaximum = 0xFFFF;
206
+
207
+ /**
208
+ * inflighPackets, how many publish packets have been sent and are waiting for ack
209
+ */
210
+ inflightPackets = 0;
211
+
212
+ /**
213
+ * set of Id's of publish packets waiting to be send to the client
214
+ */
215
+ queuedPacketIds: Set<PacketId> = new Set();
216
+
217
+ /**
218
+ * V5,max topicAliases ,0 = none
219
+ */
220
+ outgoingMaxTopicAlias = 0;
221
+ incomingMaxTopicAlias = 0;
222
+
223
+ /** V5 server topic aliases */
224
+ incomingTopicAliases: Map<number, Topic> = new Map();
225
+
226
+ /** V5 client topic aliases */
227
+ outgoingTopicAliasManager: undefined | OutboundTopicAliasManager;
228
+
229
+ connectOptions: undefined | ConnectOptions = undefined;
230
+ //
231
+ /**
232
+ * Initializes a new instance of the connection Context.
233
+ */
234
+ constructor(
235
+ configuration: Configuration, // all settings
236
+ persistence: Persistence, // The server persistence layer implementation.
237
+ conn: SockConn, // The underlying socket connection.
238
+ handlers: Handlers, // The validation handlers
239
+ ) {
240
+ this.config = configuration;
67
241
  this.persistence = persistence;
68
- this.connected = false;
69
242
  this.conn = conn;
70
- this.mqttConn = new MqttConn({ conn });
243
+ const cfg = this.config.context;
244
+ this.mqttConn = new MqttConn({
245
+ conn,
246
+ maxIncomingPacketSize: cfg.maximumIncomingPacketSize,
247
+ maxOutgoingPacketSize: cfg.maximumOutgoingPacketSize,
248
+ });
71
249
  this.handlers = handlers;
72
250
  this.protocolLevel = MQTTLevel.unknown;
251
+ this.initializePreconnectTimer(cfg.preconnectTimeoutMs);
73
252
  }
74
253
 
75
- async send(packet: AnyPacket): Promise<void> {
76
- logger.debug("Sending", PacketNameByType[packet.type]);
77
- logger.debug(JSON.stringify(packet, null, 2));
78
- await this.mqttConn.send(packet);
254
+ /**
255
+ * Configures and starts the preconnect timer to enforce connection deadlines.
256
+ */
257
+ private initializePreconnectTimer(preconnectTimeoutMs: number): void {
258
+ this.preconnectTimer = new Timer(() => {
259
+ if (this.state !== SessionState.connected) {
260
+ logger.warn(
261
+ `Preconnect timeout: client at ${this.mqttConn.remoteAddress} failed to connect within ${
262
+ preconnectTimeoutMs / 1000
263
+ } seconds`,
264
+ );
265
+ this.close(false);
266
+ }
267
+ }, preconnectTimeoutMs);
79
268
  }
80
269
 
81
- connect(clientId: string, clean: boolean): void {
82
- logger.debug("Connecting", clientId);
83
- const existingSession = Context.clientList.get(clientId);
84
- if (existingSession) {
85
- existingSession.close(false);
86
- }
87
- this.store = this.persistence.registerClient(
88
- clientId,
89
- this.doPublish.bind(this),
90
- clean,
270
+ /**
271
+ * Transmits an MQTT packet to the client.
272
+ */
273
+ async send(packet: AnyPacket): Promise<boolean> {
274
+ const cfg = this.config.context;
275
+ logger.verbose(
276
+ `ctx.send: Sending packet of type ${
277
+ PacketNameByType[packet.type]
278
+ } to client ${this.clientId!}`,
91
279
  );
280
+ logger.debug("ctx.send", () => JSON.stringify(packet, null, 2));
281
+ // strip reasonString from properties unless allowed by config
282
+ if ("properties" in packet && packet.properties) {
283
+ const props = packet.properties;
284
+ if (
285
+ "reasonString" in props &&
286
+ props.reasonString &&
287
+ !cfg.provideReasonStrings
288
+ ) {
289
+ props.reasonString = undefined;
290
+ }
291
+ }
292
+ // send the packet
293
+ const result = await this.mqttConn.send(packet);
294
+ if (result.sent === true) {
295
+ return true;
296
+ }
297
+ if (result.reason === "connectionClosed") {
298
+ // mqttCon signaled that the connection was closed
299
+ this.close();
300
+ return false;
301
+ }
302
+ if (result.reason === "packetTooLarge") {
303
+ if (packet.type === PacketType.publish) {
304
+ const pubPacket = packet as PublishPacket;
305
+ // just ignore qos 0
306
+ if (pubPacket.qos === 0) {
307
+ return false;
308
+ }
309
+ //
310
+ this.persistence.deletePendingOutgoingPacket(
311
+ this.clientId!,
312
+ pubPacket.id!,
313
+ );
314
+ return false;
315
+ }
316
+ // control packets that are too large means closing the connection
317
+ this.close();
318
+ return false;
319
+ }
320
+ return false;
321
+ }
92
322
 
93
- this.connected = true;
94
- this.broadcast("$SYS/connect/clients", clientId);
95
- logger.debug("Connected", clientId);
323
+ /**
324
+ * Dispatch the next publish packet in queue (if exists),
325
+ * when a ack packet on an already sent publish arrives
326
+ */
327
+ async processAck(packetId: PacketId): Promise<boolean> {
328
+ const packetExists = await this.persistence.deletePendingOutgoingPacket(
329
+ this.clientId!,
330
+ packetId,
331
+ );
332
+ logger.debug("processAck:", packetId, packetExists);
333
+ if (packetExists) {
334
+ this.inflightPackets--;
335
+ const queue = this.queuedPacketIds;
336
+ if (queue.size > 0) {
337
+ const id = queue.keys().next().value;
338
+ const packet = await this.persistence.getPendingOutgoingPacket(
339
+ this.clientId!,
340
+ id!,
341
+ );
342
+ if (packet) {
343
+ await this.dispatch(packet);
344
+ } else {
345
+ queue.delete(id!);
346
+ }
347
+ }
348
+ }
349
+ return packetExists;
96
350
  }
97
351
 
98
- doPublish(packet: PublishPacket): void {
352
+ /**
353
+ * Dispatch publish packets to client
354
+ */
355
+ async dispatch(extPacket: ExtPublishPacket): Promise<void> {
356
+ logger.debug("ctx.dispatch:", extPacket.id);
357
+ const cfg = this.config.context;
358
+ // Fast-path short-circuit if client is no longer connected
359
+ if (this.state !== SessionState.connected || this.mqttConn.isClosed) {
360
+ return;
361
+ }
362
+
363
+ // get the publish packet back from the envelope
364
+ const { expiresAtMs, ...rest } = extPacket;
365
+ const packet = rest as PublishPacket;
366
+
99
367
  const qos = packet.qos || 0;
100
- if (qos === 0) {
101
- packet.id = 0;
102
- this.send(packet);
368
+ // filter out expired packets
369
+ const packetExpired = expiresAtMs ? Date.now() > expiresAtMs : false;
370
+ if (packetExpired) {
371
+ // qos 0 we can just forget the packet
372
+ // qos 1 & 2 we need to remove the packet from persistence
373
+ if (qos !== 0) {
374
+ await this.persistence.deletePendingOutgoingPacket(
375
+ this.clientId!,
376
+ packet.id!,
377
+ );
378
+ }
103
379
  return;
104
380
  }
105
- if (this.store) {
106
- packet.id = this.store.nextId();
107
- this.store.pendingOutgoing.set(packet.id, packet);
108
- this.send(packet);
381
+
382
+ // handle queuing
383
+ if (this.inflightPackets === this.receiveMaximum) {
384
+ // client is full
385
+ // qos 0 we can just forget the packet
386
+ if (qos === 0) {
387
+ return;
388
+ }
389
+ const queue = this.queuedPacketIds;
390
+ if (queue.size === cfg.maxQueuedMessages) {
391
+ // queue is also full
392
+ if (cfg.queueStrategy === QueueMode.DiscardOldest) {
393
+ // remove the oldest packet in queue
394
+ const oldest = queue.keys().next().value;
395
+ if (oldest !== undefined) {
396
+ if (
397
+ await this.persistence.deletePendingOutgoingPacket(
398
+ this.clientId!,
399
+ oldest,
400
+ )
401
+ ) {
402
+ queue.delete(oldest);
403
+ }
404
+ }
405
+ queue.add(packet.id!);
406
+ }
407
+ // if queueMode = DiscardNewest => just drop the packet.
408
+ return;
409
+ }
410
+ // queue is not full, add the packet
411
+ queue.add(packet.id!);
412
+ return;
413
+ }
414
+
415
+ // update inflight
416
+ if (qos > 0) {
417
+ this.inflightPackets++;
418
+ }
419
+
420
+ if (this.protocolLevel) {
421
+ packet.protocolLevel = this.protocolLevel;
422
+ }
423
+ if (packet.protocolLevel === 5) {
424
+ // V5 specifics
425
+ if (this.outgoingMaxTopicAlias > 0 && this.outgoingTopicAliasManager) {
426
+ if (!packet.properties) {
427
+ packet.properties = {};
428
+ }
429
+ const { topicName, topicAlias } = this.outgoingTopicAliasManager
430
+ .processTopic(packet.topic);
431
+ packet.topic = topicName;
432
+ packet.properties.topicAlias = topicAlias;
433
+ }
434
+ }
435
+ const sent = await this.send(packet);
436
+ // if packet was sent update its dup value
437
+ if (qos > 0 && sent && packet.dup !== true) {
438
+ await this.persistence.updatePendingOutgoingPacket(
439
+ this.clientId!,
440
+ packet.id!,
441
+ true,
442
+ );
109
443
  }
110
444
  }
111
445
 
112
- clean(clientId: string) {
113
- this.persistence.deregisterClient(clientId);
446
+ /**
447
+ * Helper to setup the timers
448
+ */
449
+ private setupConnectionTimers(
450
+ keepAliveSeconds: number | undefined,
451
+ sessionExpiryInterval?: number,
452
+ willDelayInterval?: number,
453
+ ) {
454
+ const cfg = this.config.context;
455
+ const isProtocolV5 = this.protocolLevel === 5;
456
+ const clientId = this.clientId;
457
+
458
+ let keepAlive = keepAliveSeconds || 0;
459
+ if (isProtocolV5 && cfg.serverKeepAlive !== undefined) {
460
+ keepAlive = cfg.serverKeepAlive;
461
+ }
462
+
463
+ if (keepAlive > 0) {
464
+ logger.debug("Setting keepalive to", keepAlive * 1500, "ms");
465
+ this.timer = new Timer(() => {
466
+ this.close();
467
+ }, keepAlive * 1500);
468
+ }
469
+
470
+ if (isProtocolV5 && sessionExpiryInterval && clientId) {
471
+ this.sessionEndsTimer = new Timer(
472
+ () => {
473
+ this.persistence.deregisterClient(clientId);
474
+ },
475
+ sessionExpiryInterval * 1000,
476
+ true, // do not start the timer yet.
477
+ );
478
+ }
479
+
480
+ if (isProtocolV5 && willDelayInterval) {
481
+ this.willTimer = new Timer(
482
+ () => {
483
+ this.handleWill();
484
+ },
485
+ willDelayInterval * 1000,
486
+ true, // do not start the timer yet.
487
+ );
488
+ }
114
489
  }
115
490
 
116
- close(executewill = true): void {
117
- if (this.connected) {
118
- logger.info(
119
- `Closing ${this.store?.clientId} while mqttConn is ${
120
- this.mqttConn.isClosed ? "" : "not "
121
- }closed because of "${this.mqttConn.reason || "normal disconnect"}"`,
491
+ /**
492
+ * process V5 connectOptions
493
+ */
494
+ private applyConnectOptions(opts: ConnectOptions) {
495
+ const cfg = this.config.context;
496
+
497
+ // Topic Alias Maximum
498
+ if (opts.topicAliasMaximum !== undefined) {
499
+ this.outgoingMaxTopicAlias = Math.min(
500
+ opts.topicAliasMaximum,
501
+ cfg.topicAliasMaximum,
122
502
  );
123
- this.connected = false;
503
+ if (this.outgoingMaxTopicAlias > 0) {
504
+ this.outgoingTopicAliasManager = new OutboundTopicAliasManager(
505
+ this.outgoingMaxTopicAlias,
506
+ );
507
+ }
508
+ }
509
+
510
+ // Maximum Packet Size
511
+ if (opts.maximumOutgoingPacketSize !== undefined && this.mqttConn) {
512
+ this.mqttConn.codecOpts.maxOutgoingPacketSize = Math.min(
513
+ opts.maximumOutgoingPacketSize,
514
+ cfg.maximumOutgoingPacketSize,
515
+ );
516
+ }
517
+
518
+ // Receive Maximum
519
+ const maxInflight = cfg.maxInflightMessages;
520
+ this.receiveMaximum = Math.min(
521
+ opts.receiveMaximum ?? maxInflight,
522
+ maxInflight,
523
+ );
524
+ }
525
+
526
+ /**
527
+ * Finalizes the client connection state, registers the client in persistence,
528
+ * kicks out existing duplicate sessions, and broadcasts the client connection event.
529
+ */
530
+ prepareConnect(
531
+ packet: ConnectPacket,
532
+ clientId: string,
533
+ connectOpts: ConnectOptions = {},
534
+ ): void {
535
+ logger.verbose("ctx:connect connecting", clientId);
536
+ this.state = SessionState.connecting;
537
+ const cfg = this.config.context;
538
+ // configure protocol and state
539
+ this.clientId = clientId;
540
+ this.cleanSession = packet.clean || false;
541
+ this.protocolLevel = packet.protocolLevel;
542
+ this.incomingMaxTopicAlias = cfg.topicAliasMaximum;
543
+ this.connectOptions = connectOpts;
544
+
545
+ this.applyConnectOptions(connectOpts);
546
+
547
+ if (this.mqttConn) {
548
+ this.mqttConn.codecOpts.protocolLevel = this.protocolLevel;
549
+ }
550
+
551
+ if (packet.will) {
552
+ this.will = {
553
+ type: PacketType.publish,
554
+ protocolLevel: 5,
555
+ ...packet.will,
556
+ };
557
+ }
558
+ }
559
+
560
+ async connect(): Promise<boolean> {
561
+ const connectOpts = this.connectOptions || {};
562
+ const clientId = this.clientId!;
563
+ // Cleanup preconnect timer
564
+ if (this.preconnectTimer) {
565
+ this.preconnectTimer.clear();
566
+ }
567
+
568
+ // Cleanup previous sessions and timers
569
+ const existingActiveSession = Context.clientList.get(clientId);
570
+ if (existingActiveSession) {
571
+ logger.verbose(
572
+ `ctx:connect: Existing session with ${clientId} exists, closing existing session`,
573
+ );
574
+ if (this.sessionEndsTimer) this.sessionEndsTimer.clear();
575
+ if (this.willTimer) this.willTimer.clear();
576
+ await existingActiveSession.close(false);
577
+ }
578
+
579
+ if (this.cleanSession) {
580
+ logger.verbose(
581
+ `ctx:connect: Clean session requested for ${clientId}, deregistering existing state`,
582
+ );
583
+ await this.persistence.deregisterClient(clientId);
584
+ }
585
+
586
+ // Register client in persistence & clientList
587
+ logger.verbose("ctx:connect: Registering client", clientId);
588
+ const { existingSession } = await this.persistence.registerClient(
589
+ clientId,
590
+ this.dispatch.bind(this),
591
+ );
592
+
593
+ this.state = SessionState.connected;
594
+ Context.clientList.set(clientId, this);
595
+
596
+ // Start the timers
597
+ this.setupConnectionTimers(
598
+ connectOpts.keepAlive,
599
+ connectOpts.sessionExpiryInterval,
600
+ connectOpts.willDelayInterval,
601
+ );
602
+
603
+ // Announce client connected
604
+ logger.verbose("ctx:connect: Broadcasting client connection", clientId);
605
+ await this.broadcast("$SYS/connect/clients", clientId);
606
+
607
+ const remoteAddress = this.mqttConn.remoteAddress !== "unknown"
608
+ ? ` from ${this.mqttConn.remoteAddress}`
609
+ : "";
610
+ logger.info("Connected", clientId, remoteAddress);
611
+
612
+ return existingSession;
613
+ }
614
+
615
+ /**
616
+ * Proces -inbound- publication requests
617
+ */
618
+ async publish(packet: ExtPublishPacket) {
619
+ logger.verbose(
620
+ `ctx:publish processing incoming publish for topic "${packet.topic}"`,
621
+ );
622
+ await this.persistence.publish(this.clientId!, packet);
623
+ }
624
+
625
+ /**
626
+ * Explicitly removes a client session and its stored state from the persistence layer.
627
+ */
628
+ async clean() {
629
+ if (this.clientId) {
630
+ await this.persistence.deregisterClient(this.clientId);
631
+ }
632
+ await this.close(false);
633
+ }
634
+
635
+ /**
636
+ * Tears down the connection context, terminates the underlying socket,
637
+ * clears internal timers, triggers the Will packet execution if requested,
638
+ * and announces the client disconnection.
639
+ * If executewill=true triggers the registered Will packet logic.
640
+ */
641
+ async close(executewill = true): Promise<void> {
642
+ logger.debug("server closing context while state =", this.state);
643
+ if (this.state === SessionState.closing) {
644
+ return;
645
+ }
646
+ if (this.preconnectTimer) {
647
+ this.preconnectTimer.clear();
648
+ }
649
+
650
+ if (this.state === SessionState.connected) {
651
+ this.state = SessionState.closing;
652
+ if (this.cleanSession && !this.sessionEndsTimer) {
653
+ // [MQTT-3.1.2-6] State data associated with this Session MUST NOT be reused in any subsequent Session
654
+ if (this.clientId) {
655
+ await this.persistence.deregisterClient(this.clientId);
656
+ }
657
+ }
124
658
  if (typeof this.timer === "object") {
125
659
  this.timer.clear();
126
660
  }
127
- if (this.store) {
128
- this.broadcast("$SYS/disconnect/clients", this.store.clientId);
129
- }
130
661
  if (executewill) {
131
- this.handleWill();
662
+ if (this.willTimer) {
663
+ this.willTimer.reset();
664
+ } else {
665
+ await this.handleWill();
666
+ }
132
667
  }
133
- } else {
134
- logger.info(
135
- `closing connection from ${this.mqttConn.remoteAddress} because of "${
136
- this.mqttConn.reason || "normal disconnect"
137
- }"`,
138
- );
668
+ if (this.clientId) {
669
+ await this.persistence.disconnectClient(this.clientId);
670
+ void this.broadcast("$SYS/disconnect/clients", this.clientId);
671
+ Context.clientList.delete(this.clientId);
672
+ }
673
+ }
674
+ if (this.sessionEndsTimer) {
675
+ // delayed deregistration, start the timer
676
+ this.sessionEndsTimer.reset();
139
677
  }
678
+
679
+ logger.info(
680
+ `closing connection from ${
681
+ this.clientId || this.mqttConn.remoteAddress
682
+ } because of "${this.mqttConn.reason || "normal disconnect"}"`,
683
+ );
140
684
  if (!this.mqttConn.isClosed) {
141
685
  this.mqttConn.close();
142
686
  }
687
+ this.state = SessionState.closing;
143
688
  }
144
689
 
145
- private handleWill() {
690
+ /**
691
+ * Evaluates authorization and triggers the distribution of the client's Will packet.
692
+ */
693
+ private async handleWill() {
146
694
  if (this.will) {
147
695
  if (
148
696
  !this.will.topic.startsWith(SysPrefix) &&
149
697
  this.handlers.isAuthorizedToPublish &&
150
- this.handlers.isAuthorizedToPublish(this, this.will.topic)
698
+ await this.handlers.isAuthorizedToPublish(this, this.will.topic)
151
699
  ) {
152
- this.persistence.publish(this.will.topic, this.will);
700
+ logger.debug("ctx.handleWill:", this.will);
701
+ await this.publish(this.will);
153
702
  }
154
703
  }
155
704
  }
156
705
 
157
- broadcast(topic: Topic, payload: string, retain = false): void {
158
- const packet: PublishPacket = {
706
+ /**
707
+ * Utility method to generate and publish an internal system message payload to the persistence engine.
708
+ */
709
+ async broadcast(
710
+ topic: Topic, // The topic filter string to publish to.
711
+ payload: string, // The plain-text message string to encode.
712
+ retain = false, // Specifies if the message should be retained.
713
+ ): Promise<void> {
714
+ const packet: ExtPublishPacket = {
159
715
  type: PacketType.publish,
160
- protocolLevel: this.protocolLevel,
716
+ protocolLevel: 5,
161
717
  topic,
162
718
  retain,
163
719
  payload: utf8Encoder.encode(payload),
164
720
  };
165
- if (packet.retain === true) {
166
- this.persistence.retained.set(packet.topic, packet);
721
+ await this.publish(packet);
722
+ }
723
+
724
+ /**
725
+ * utility method to redeliver packets that were cached
726
+ * called from handleConnect()
727
+ */
728
+ async handleRedelivery() {
729
+ // redeliver inflight data
730
+ const clientId = this.clientId!;
731
+ const p = this.persistence;
732
+
733
+ logger.verbose("ctx:handleRedelivery for", this.clientId);
734
+ for await (const packet of p.listPendingOutgoingPackets(clientId)) {
735
+ if (this.state !== SessionState.connected) {
736
+ break;
737
+ }
738
+ packet.dup = true;
739
+ await this.dispatch(packet);
740
+ }
741
+ // we only need to resend QoS2 PubRel acks
742
+ for await (const packetId of p.listPendingAcks(clientId)) {
743
+ if (this.state !== SessionState.connected) {
744
+ break;
745
+ }
746
+ const pubrel: PubrelPacket = {
747
+ protocolLevel: this.protocolLevel,
748
+ type: PacketType.pubrel,
749
+ id: packetId,
750
+ };
751
+ await this.send(pubrel);
167
752
  }
168
- this.persistence.publish(packet.topic, packet);
169
753
  }
170
754
  }