@seriousme/opifex 1.9.4 → 1.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. package/README.md +37 -26
  2. package/bin/conformanceServer.ts +3 -2
  3. package/bin/demoServer.ts +3 -2
  4. package/bin/mqtt.ts +126 -117
  5. package/client/client.ts +35 -15
  6. package/client/context.ts +42 -22
  7. package/client/deps.ts +5 -0
  8. package/client/handlers/handleConnack.ts +17 -5
  9. package/client/handlers/handlePublish.ts +4 -1
  10. package/client/handlers/handlePubrec.ts +3 -1
  11. package/client/handlers/handlePubrel.ts +3 -1
  12. package/client/handlers/handleSuback.ts +3 -1
  13. package/client/mod.ts +8 -1
  14. package/client/store/memoryStore.ts +2 -1
  15. package/dev_utils/dummyConn.ts +17 -2
  16. package/dist/bin/conformanceServer.js +3 -2
  17. package/dist/bin/conformanceServer.js.map +1 -1
  18. package/dist/bin/demoServer.js +3 -2
  19. package/dist/bin/demoServer.js.map +1 -1
  20. package/dist/bin/mqtt.d.ts +29 -0
  21. package/dist/bin/mqtt.d.ts.map +1 -1
  22. package/dist/bin/mqtt.js +101 -96
  23. package/dist/bin/mqtt.js.map +1 -1
  24. package/dist/client/client.d.ts +7 -4
  25. package/dist/client/client.d.ts.map +1 -1
  26. package/dist/client/client.js +17 -9
  27. package/dist/client/client.js.map +1 -1
  28. package/dist/client/context.d.ts +3 -1
  29. package/dist/client/context.d.ts.map +1 -1
  30. package/dist/client/context.js +27 -13
  31. package/dist/client/context.js.map +1 -1
  32. package/dist/client/deps.d.ts +3 -2
  33. package/dist/client/deps.d.ts.map +1 -1
  34. package/dist/client/deps.js +2 -1
  35. package/dist/client/deps.js.map +1 -1
  36. package/dist/client/handlers/handleConnack.d.ts +1 -1
  37. package/dist/client/handlers/handleConnack.d.ts.map +1 -1
  38. package/dist/client/handlers/handleConnack.js +14 -5
  39. package/dist/client/handlers/handleConnack.js.map +1 -1
  40. package/dist/client/handlers/handlePublish.d.ts +1 -1
  41. package/dist/client/handlers/handlePublish.d.ts.map +1 -1
  42. package/dist/client/handlers/handlePublish.js +2 -0
  43. package/dist/client/handlers/handlePublish.js.map +1 -1
  44. package/dist/client/handlers/handlePubrec.d.ts +1 -1
  45. package/dist/client/handlers/handlePubrec.d.ts.map +1 -1
  46. package/dist/client/handlers/handlePubrec.js +1 -0
  47. package/dist/client/handlers/handlePubrec.js.map +1 -1
  48. package/dist/client/handlers/handlePubrel.d.ts +1 -1
  49. package/dist/client/handlers/handlePubrel.d.ts.map +1 -1
  50. package/dist/client/handlers/handlePubrel.js +1 -0
  51. package/dist/client/handlers/handlePubrel.js.map +1 -1
  52. package/dist/client/handlers/handleSuback.d.ts.map +1 -1
  53. package/dist/client/handlers/handleSuback.js +3 -1
  54. package/dist/client/handlers/handleSuback.js.map +1 -1
  55. package/dist/client/mod.d.ts +3 -1
  56. package/dist/client/mod.d.ts.map +1 -1
  57. package/dist/client/mod.js +2 -1
  58. package/dist/client/mod.js.map +1 -1
  59. package/dist/client/store/memoryStore.d.ts +1 -1
  60. package/dist/client/store/memoryStore.d.ts.map +1 -1
  61. package/dist/client/store/memoryStore.js.map +1 -1
  62. package/dist/mqttConn/mqttConn.d.ts +13 -8
  63. package/dist/mqttConn/mqttConn.d.ts.map +1 -1
  64. package/dist/mqttConn/mqttConn.js +27 -12
  65. package/dist/mqttConn/mqttConn.js.map +1 -1
  66. package/dist/mqttPacket/PacketType.d.ts +1 -0
  67. package/dist/mqttPacket/PacketType.d.ts.map +1 -1
  68. package/dist/mqttPacket/PacketType.js +1 -0
  69. package/dist/mqttPacket/PacketType.js.map +1 -1
  70. package/dist/mqttPacket/Properties.d.ts +157 -0
  71. package/dist/mqttPacket/Properties.d.ts.map +1 -0
  72. package/dist/mqttPacket/Properties.js +177 -0
  73. package/dist/mqttPacket/Properties.js.map +1 -0
  74. package/dist/mqttPacket/ReasonCode.d.ts +74 -0
  75. package/dist/mqttPacket/ReasonCode.d.ts.map +1 -0
  76. package/dist/mqttPacket/ReasonCode.js +230 -0
  77. package/dist/mqttPacket/ReasonCode.js.map +1 -0
  78. package/dist/mqttPacket/RetainHandling.d.ts +12 -0
  79. package/dist/mqttPacket/RetainHandling.d.ts.map +1 -0
  80. package/dist/mqttPacket/RetainHandling.js +12 -0
  81. package/dist/mqttPacket/RetainHandling.js.map +1 -0
  82. package/dist/mqttPacket/auth.d.ts +21 -0
  83. package/dist/mqttPacket/auth.d.ts.map +1 -0
  84. package/dist/mqttPacket/auth.js +34 -0
  85. package/dist/mqttPacket/auth.js.map +1 -0
  86. package/dist/mqttPacket/connack.d.ts +15 -7
  87. package/dist/mqttPacket/connack.d.ts.map +1 -1
  88. package/dist/mqttPacket/connack.js +43 -15
  89. package/dist/mqttPacket/connack.js.map +1 -1
  90. package/dist/mqttPacket/connect.d.ts +27 -8
  91. package/dist/mqttPacket/connect.d.ts.map +1 -1
  92. package/dist/mqttPacket/connect.js +106 -30
  93. package/dist/mqttPacket/connect.js.map +1 -1
  94. package/dist/mqttPacket/decoder.d.ts +42 -3
  95. package/dist/mqttPacket/decoder.d.ts.map +1 -1
  96. package/dist/mqttPacket/decoder.js +135 -4
  97. package/dist/mqttPacket/decoder.js.map +1 -1
  98. package/dist/mqttPacket/disconnect.d.ts +13 -7
  99. package/dist/mqttPacket/disconnect.d.ts.map +1 -1
  100. package/dist/mqttPacket/disconnect.js +45 -6
  101. package/dist/mqttPacket/disconnect.js.map +1 -1
  102. package/dist/mqttPacket/encoder.d.ts +57 -8
  103. package/dist/mqttPacket/encoder.d.ts.map +1 -1
  104. package/dist/mqttPacket/encoder.js +185 -13
  105. package/dist/mqttPacket/encoder.js.map +1 -1
  106. package/dist/mqttPacket/length.d.ts.map +1 -1
  107. package/dist/mqttPacket/length.js +3 -2
  108. package/dist/mqttPacket/length.js.map +1 -1
  109. package/dist/mqttPacket/mod.d.ts +56 -91
  110. package/dist/mqttPacket/mod.d.ts.map +1 -1
  111. package/dist/mqttPacket/mod.js +22 -29
  112. package/dist/mqttPacket/mod.js.map +1 -1
  113. package/dist/mqttPacket/pingreq.d.ts +4 -6
  114. package/dist/mqttPacket/pingreq.d.ts.map +1 -1
  115. package/dist/mqttPacket/pingreq.js +5 -4
  116. package/dist/mqttPacket/pingreq.js.map +1 -1
  117. package/dist/mqttPacket/pingres.d.ts +4 -6
  118. package/dist/mqttPacket/pingres.d.ts.map +1 -1
  119. package/dist/mqttPacket/pingres.js +7 -5
  120. package/dist/mqttPacket/pingres.js.map +1 -1
  121. package/dist/mqttPacket/protocolLevels.d.ts +11 -0
  122. package/dist/mqttPacket/protocolLevels.d.ts.map +1 -0
  123. package/dist/mqttPacket/protocolLevels.js +11 -0
  124. package/dist/mqttPacket/protocolLevels.js.map +1 -0
  125. package/dist/mqttPacket/pubblishAcks.d.ts +35 -0
  126. package/dist/mqttPacket/pubblishAcks.d.ts.map +1 -0
  127. package/dist/mqttPacket/pubblishAcks.js +56 -0
  128. package/dist/mqttPacket/pubblishAcks.js.map +1 -0
  129. package/dist/mqttPacket/publish.d.ts +19 -8
  130. package/dist/mqttPacket/publish.d.ts.map +1 -1
  131. package/dist/mqttPacket/publish.js +26 -7
  132. package/dist/mqttPacket/publish.js.map +1 -1
  133. package/dist/mqttPacket/suback.d.ts +14 -7
  134. package/dist/mqttPacket/suback.d.ts.map +1 -1
  135. package/dist/mqttPacket/suback.js +41 -20
  136. package/dist/mqttPacket/suback.js.map +1 -1
  137. package/dist/mqttPacket/subscribe.d.ts +24 -9
  138. package/dist/mqttPacket/subscribe.d.ts.map +1 -1
  139. package/dist/mqttPacket/subscribe.js +71 -15
  140. package/dist/mqttPacket/subscribe.js.map +1 -1
  141. package/dist/mqttPacket/types.d.ts +33 -0
  142. package/dist/mqttPacket/types.d.ts.map +1 -1
  143. package/dist/mqttPacket/unsuback.d.ts +14 -7
  144. package/dist/mqttPacket/unsuback.d.ts.map +1 -1
  145. package/dist/mqttPacket/unsuback.js +29 -6
  146. package/dist/mqttPacket/unsuback.js.map +1 -1
  147. package/dist/mqttPacket/unsubscribe.d.ts +14 -7
  148. package/dist/mqttPacket/unsubscribe.d.ts.map +1 -1
  149. package/dist/mqttPacket/unsubscribe.js +24 -8
  150. package/dist/mqttPacket/unsubscribe.js.map +1 -1
  151. package/dist/node/wrapNodeSocket.d.ts.map +1 -1
  152. package/dist/node/wrapNodeSocket.js.map +1 -1
  153. package/dist/persistence/deps.d.ts +1 -1
  154. package/dist/persistence/deps.d.ts.map +1 -1
  155. package/dist/persistence/deps.js +1 -1
  156. package/dist/persistence/deps.js.map +1 -1
  157. package/dist/server/context.d.ts +3 -1
  158. package/dist/server/context.d.ts.map +1 -1
  159. package/dist/server/context.js +8 -2
  160. package/dist/server/context.js.map +1 -1
  161. package/dist/server/deps.d.ts +2 -2
  162. package/dist/server/deps.d.ts.map +1 -1
  163. package/dist/server/deps.js +1 -1
  164. package/dist/server/deps.js.map +1 -1
  165. package/dist/server/handlers/handleConnect.d.ts +1 -1
  166. package/dist/server/handlers/handleConnect.d.ts.map +1 -1
  167. package/dist/server/handlers/handleConnect.js +9 -1
  168. package/dist/server/handlers/handleConnect.js.map +1 -1
  169. package/dist/server/handlers/handlePacket.d.ts +1 -1
  170. package/dist/server/handlers/handlePacket.d.ts.map +1 -1
  171. package/dist/server/handlers/handlePacket.js +1 -1
  172. package/dist/server/handlers/handlePacket.js.map +1 -1
  173. package/dist/server/handlers/handlePingreq.d.ts.map +1 -1
  174. package/dist/server/handlers/handlePingreq.js +1 -0
  175. package/dist/server/handlers/handlePingreq.js.map +1 -1
  176. package/dist/server/handlers/handlePublish.d.ts +2 -2
  177. package/dist/server/handlers/handlePublish.d.ts.map +1 -1
  178. package/dist/server/handlers/handlePublish.js +2 -0
  179. package/dist/server/handlers/handlePublish.js.map +1 -1
  180. package/dist/server/handlers/handlePubrec.d.ts +1 -1
  181. package/dist/server/handlers/handlePubrec.d.ts.map +1 -1
  182. package/dist/server/handlers/handlePubrec.js +1 -0
  183. package/dist/server/handlers/handlePubrec.js.map +1 -1
  184. package/dist/server/handlers/handlePubrel.d.ts +1 -1
  185. package/dist/server/handlers/handlePubrel.d.ts.map +1 -1
  186. package/dist/server/handlers/handlePubrel.js +1 -0
  187. package/dist/server/handlers/handlePubrel.js.map +1 -1
  188. package/dist/server/handlers/handleSubscribe.d.ts +1 -1
  189. package/dist/server/handlers/handleSubscribe.d.ts.map +1 -1
  190. package/dist/server/handlers/handleSubscribe.js +2 -1
  191. package/dist/server/handlers/handleSubscribe.js.map +1 -1
  192. package/dist/server/handlers/handleUnsubscribe.d.ts +1 -1
  193. package/dist/server/handlers/handleUnsubscribe.d.ts.map +1 -1
  194. package/dist/server/handlers/handleUnsubscribe.js +1 -0
  195. package/dist/server/handlers/handleUnsubscribe.js.map +1 -1
  196. package/dist/server/server.d.ts +2 -2
  197. package/dist/server/server.d.ts.map +1 -1
  198. package/dist/server/server.js +5 -3
  199. package/dist/server/server.js.map +1 -1
  200. package/dist/socket/socket.d.ts.map +1 -1
  201. package/dist/socket/socket.js +6 -1
  202. package/dist/socket/socket.js.map +1 -1
  203. package/dist/utils/assert.d.ts +0 -3
  204. package/dist/utils/assert.d.ts.map +1 -1
  205. package/dist/utils/assert.js +0 -7
  206. package/dist/utils/assert.js.map +1 -1
  207. package/dist/utils/mod.d.ts +0 -3
  208. package/dist/utils/mod.d.ts.map +1 -1
  209. package/dist/utils/mod.js +0 -2
  210. package/dist/utils/mod.js.map +1 -1
  211. package/mqttConn/mqttConn.test.ts +27 -12
  212. package/mqttConn/mqttConn.ts +45 -14
  213. package/mqttPacket/PacketType.ts +1 -0
  214. package/mqttPacket/Properties.ts +276 -0
  215. package/mqttPacket/ReasonCode.test.ts +22 -0
  216. package/mqttPacket/ReasonCode.ts +256 -0
  217. package/mqttPacket/RetainHandling.ts +11 -0
  218. package/mqttPacket/auth.test.ts +112 -0
  219. package/mqttPacket/auth.ts +72 -0
  220. package/mqttPacket/connack.test.ts +84 -64
  221. package/mqttPacket/connack.ts +84 -20
  222. package/mqttPacket/connect.test.ts +497 -18
  223. package/mqttPacket/connect.ts +167 -36
  224. package/mqttPacket/decoder.test.ts +33 -16
  225. package/mqttPacket/decoder.ts +173 -5
  226. package/mqttPacket/disconnect.test.ts +97 -18
  227. package/mqttPacket/disconnect.ts +82 -10
  228. package/mqttPacket/encoder.test.ts +32 -8
  229. package/mqttPacket/encoder.ts +236 -11
  230. package/mqttPacket/length.ts +3 -2
  231. package/mqttPacket/mod.ts +75 -39
  232. package/mqttPacket/pingreq.test.ts +52 -4
  233. package/mqttPacket/pingreq.ts +20 -7
  234. package/mqttPacket/pingres.test.ts +52 -4
  235. package/mqttPacket/pingres.ts +22 -8
  236. package/mqttPacket/properties.test.ts +193 -0
  237. package/mqttPacket/protocolLevels.ts +13 -0
  238. package/mqttPacket/pubblishAcks.ts +117 -0
  239. package/mqttPacket/publish.test.ts +212 -133
  240. package/mqttPacket/publish.ts +69 -14
  241. package/mqttPacket/publishAcks.test.ts +128 -0
  242. package/mqttPacket/suback.test.ts +71 -27
  243. package/mqttPacket/suback.ts +78 -26
  244. package/mqttPacket/subscribe.test.ts +132 -43
  245. package/mqttPacket/subscribe.ts +125 -22
  246. package/mqttPacket/types.ts +40 -0
  247. package/mqttPacket/unsuback.test.ts +80 -30
  248. package/mqttPacket/unsuback.ts +65 -10
  249. package/mqttPacket/unsubscribe.test.ts +59 -34
  250. package/mqttPacket/unsubscribe.ts +61 -14
  251. package/node/wrapNodeSocket.ts +2 -1
  252. package/package.json +2 -2
  253. package/persistence/deps.ts +1 -1
  254. package/persistence/memory/memoryPersistence.test.ts +5 -1
  255. package/server/context.ts +23 -10
  256. package/server/deps.ts +2 -0
  257. package/server/handlers/handleConnect.ts +12 -9
  258. package/server/handlers/handlePacket.ts +10 -11
  259. package/server/handlers/handlePingreq.ts +1 -0
  260. package/server/handlers/handlePublish.ts +6 -2
  261. package/server/handlers/handlePubrec.ts +3 -1
  262. package/server/handlers/handlePubrel.ts +3 -1
  263. package/server/handlers/handleSubscribe.ts +3 -6
  264. package/server/handlers/handleUnsubscribe.ts +3 -1
  265. package/server/server.ts +8 -12
  266. package/server/test/handleConnect.test.ts +2 -1
  267. package/server/test/test-handlers.ts +2 -1
  268. package/socket/socket.ts +5 -1
  269. package/utils/assert.ts +0 -18
  270. package/utils/mod.ts +0 -3
  271. package/dist/mqttPacket/puback.d.ts +0 -16
  272. package/dist/mqttPacket/puback.d.ts.map +0 -1
  273. package/dist/mqttPacket/puback.js +0 -21
  274. package/dist/mqttPacket/puback.js.map +0 -1
  275. package/dist/mqttPacket/pubcomp.d.ts +0 -17
  276. package/dist/mqttPacket/pubcomp.d.ts.map +0 -1
  277. package/dist/mqttPacket/pubcomp.js +0 -21
  278. package/dist/mqttPacket/pubcomp.js.map +0 -1
  279. package/dist/mqttPacket/pubrec.d.ts +0 -17
  280. package/dist/mqttPacket/pubrec.d.ts.map +0 -1
  281. package/dist/mqttPacket/pubrec.js +0 -21
  282. package/dist/mqttPacket/pubrec.js.map +0 -1
  283. package/dist/mqttPacket/pubrel.d.ts +0 -17
  284. package/dist/mqttPacket/pubrel.d.ts.map +0 -1
  285. package/dist/mqttPacket/pubrel.js +0 -21
  286. package/dist/mqttPacket/pubrel.js.map +0 -1
  287. package/dist/utils/args.d.ts +0 -28
  288. package/dist/utils/args.d.ts.map +0 -1
  289. package/dist/utils/args.js +0 -76
  290. package/dist/utils/args.js.map +0 -1
  291. package/dist/utils/platform.d.ts +0 -5
  292. package/dist/utils/platform.d.ts.map +0 -1
  293. package/dist/utils/platform.js +0 -8
  294. package/dist/utils/platform.js.map +0 -1
  295. package/mqttPacket/puback.test.ts +0 -55
  296. package/mqttPacket/puback.ts +0 -34
  297. package/mqttPacket/pubcomp.test.ts +0 -55
  298. package/mqttPacket/pubcomp.ts +0 -35
  299. package/mqttPacket/pubrec.test.ts +0 -55
  300. package/mqttPacket/pubrec.ts +0 -35
  301. package/mqttPacket/pubrel.test.ts +0 -55
  302. package/mqttPacket/pubrel.ts +0 -35
  303. package/utils/args.ts +0 -103
  304. package/utils/platform.ts +0 -8
@@ -1,38 +1,99 @@
1
- import { PacketType } from "./PacketType.ts";
2
1
  import assert from "node:assert/strict";
3
2
  import { test } from "node:test";
3
+ import { decode, encode, MQTTLevel, PacketType, ReasonCode } from "./mod.ts";
4
+ import type { CodecOpts } from "./mod.ts";
4
5
 
5
- import { decode, encode } from "./mod.ts";
6
+ const codecOptsV4: CodecOpts = {
7
+ protocolLevel: MQTTLevel.v4,
8
+ maxIncomingPacketSize: 0xffff,
9
+ maxOutgoingPacketSize: 0xffff,
10
+ };
6
11
 
7
- test("encode Disconnect", () => {
12
+ const codecOptsV5: CodecOpts = {
13
+ protocolLevel: MQTTLevel.v5,
14
+ maxIncomingPacketSize: 0xffff,
15
+ maxOutgoingPacketSize: 0xffff,
16
+ };
17
+
18
+ test("encode/decode Disconnect V4", () => {
19
+ const packet = {
20
+ type: PacketType.disconnect,
21
+ protocolLevel: MQTTLevel.v4,
22
+ };
23
+ const encoded = encode({
24
+ type: PacketType.disconnect,
25
+ protocolLevel: MQTTLevel.v4,
26
+ }, codecOptsV4);
8
27
  assert.deepStrictEqual(
9
- encode({
10
- type: PacketType.disconnect,
11
- }),
28
+ encoded,
12
29
  Uint8Array.from([
13
30
  // fixedHeader
14
31
  224, // packetType + flags
15
32
  0, // remainingLength
16
33
  ]),
17
34
  );
35
+ const decoded = decode(encoded, codecOptsV4);
36
+ assert.deepStrictEqual(
37
+ decoded,
38
+ packet,
39
+ );
18
40
  });
19
41
 
20
- test("decode Disconnect", () => {
21
- assert.deepStrictEqual(
22
- decode(
23
- Uint8Array.from([
24
- // fixedHeader
25
- 224, // packetType + flags
26
- 0, // remainingLength
27
- ]),
28
- ),
29
- {
30
- type: PacketType.disconnect,
42
+ test("encode/decode Disconnect V5", () => {
43
+ const packet = {
44
+ type: PacketType.disconnect,
45
+ protocolLevel: MQTTLevel.v5,
46
+ reasonCode: ReasonCode.success,
47
+ properties: {
48
+ reasonString: "reason",
31
49
  },
50
+ };
51
+
52
+ const encoded = encode(packet, codecOptsV5);
53
+ assert.deepStrictEqual(
54
+ encoded,
55
+ Uint8Array.from([
56
+ // fixedHeader
57
+ 224, // packetType + flags
58
+ 11, // remaining length
59
+ 0, // reasoncode
60
+ 9, // property length
61
+ 31, //reason string
62
+ 0, // string MSB
63
+ 6, // string LSB
64
+ 114, // r
65
+ 101, // e
66
+ 97, // a
67
+ 115, // s
68
+ 111, // o
69
+ 110, // n
70
+ ]),
71
+ );
72
+ const decoded = decode(encoded, codecOptsV5);
73
+ assert.deepStrictEqual(decoded, packet);
74
+ });
75
+
76
+ test("encode/decode Short Disconnect V5", () => {
77
+ const packet = {
78
+ type: PacketType.disconnect,
79
+ protocolLevel: MQTTLevel.v5,
80
+ reasonCode: ReasonCode.success,
81
+ };
82
+
83
+ const encoded = encode(packet, codecOptsV5);
84
+ assert.deepStrictEqual(
85
+ encoded,
86
+ Uint8Array.from([
87
+ // fixedHeader
88
+ 224, // packetType + flags
89
+ 0, // remainingLength
90
+ ]),
32
91
  );
92
+ const decoded = decode(encoded, codecOptsV5);
93
+ assert.deepStrictEqual(decoded, packet);
33
94
  });
34
95
 
35
- test("decode invalid Disconnect", () => {
96
+ test("decode invalid Disconnect v4", () => {
36
97
  assert.throws(
37
98
  () =>
38
99
  decode(
@@ -43,8 +104,26 @@ test("decode invalid Disconnect", () => {
43
104
  0,
44
105
  0,
45
106
  ]),
107
+ codecOptsV4,
46
108
  ),
47
109
  Error,
48
110
  "too long",
49
111
  );
50
112
  });
113
+
114
+ test("decode invalid Disconnect V5, invalid reasonCode", () => {
115
+ assert.throws(
116
+ () =>
117
+ decode(
118
+ Uint8Array.from([
119
+ // fixedHeader
120
+ 224, // packetType + flags
121
+ 2, // remainingLength
122
+ ReasonCode.grantedQos1, // reason code
123
+ 0, // properties
124
+ ]),
125
+ codecOptsV5,
126
+ ),
127
+ /Invalid reason code/,
128
+ );
129
+ });
@@ -1,28 +1,100 @@
1
1
  import { PacketType } from "./PacketType.ts";
2
- import { isEmptyBuf } from "./decoder.ts";
3
- import type { TPacketType } from "./types.ts";
2
+ import { Decoder, hasEmptyFlags, isEmptyBuf } from "./decoder.ts";
3
+ import type {
4
+ CodecOpts,
5
+ ProtocolLevelNoV5,
6
+ TPacketType,
7
+ TReasonCode,
8
+ } from "./types.ts";
9
+ import type { DisconnectProperties } from "./Properties.ts";
10
+ import { Encoder } from "./encoder.ts";
11
+ import { ReasonCode } from "./ReasonCode.ts";
4
12
 
5
13
  /**
6
14
  * DisconnectPacket is the final control packet sent from the client to the server.
7
15
  * It indicates that the client is disconnecting cleanly.
8
16
  */
9
- export type DisconnectPacket = {
17
+ export type DisconnectPacketv4 = {
10
18
  type: TPacketType;
19
+ protocolLevel: ProtocolLevelNoV5;
11
20
  };
12
21
 
22
+ export type DisconnectPacketv5 = {
23
+ type: TPacketType;
24
+ protocolLevel: 5;
25
+ reasonCode: TReasonCode;
26
+ properties?: DisconnectProperties;
27
+ };
28
+
29
+ export type DisconnectPacket = DisconnectPacketv4 | DisconnectPacketv5;
30
+
31
+ const DISCONNECT_PACKET = new Uint8Array([PacketType.disconnect << 4, 0]);
32
+
13
33
  export const disconnect: {
14
- encode(_packet: DisconnectPacket): { flags: number; bytes: number[] };
15
- decode(buffer: Uint8Array, _flags: number): DisconnectPacket;
34
+ encode(_packet: DisconnectPacket, _codecOpts: CodecOpts): Uint8Array;
35
+ decode(
36
+ buffer: Uint8Array,
37
+ _flags: number,
38
+ codecOpts: CodecOpts,
39
+ packetType: TPacketType,
40
+ ): DisconnectPacket;
16
41
  } = {
17
- encode(_packet: DisconnectPacket): { flags: number; bytes: number[] } {
18
- const flags = 0;
19
- return { flags, bytes: [] };
42
+ encode(packet: DisconnectPacket, codecOpts: CodecOpts): Uint8Array {
43
+ if (packet.protocolLevel !== 5) {
44
+ return DISCONNECT_PACKET;
45
+ }
46
+ const reasonCode = packet.reasonCode || ReasonCode.normalDisconnection;
47
+ const encoder = new Encoder(packet.type);
48
+ // see MQTT v5 3.14.2.1
49
+ // if remaining length is less than 1 the value of 0x00 (normal disconnect) is used.
50
+ if (
51
+ reasonCode === ReasonCode.normalDisconnection &&
52
+ Object.keys(packet.properties || {}).length === 0
53
+ ) {
54
+ return encoder.done(0);
55
+ }
56
+ encoder
57
+ .setReasonCode(reasonCode);
58
+ if (packet.properties) {
59
+ encoder.setProperties(
60
+ packet.properties,
61
+ packet.type,
62
+ codecOpts.maxOutgoingPacketSize,
63
+ );
64
+ }
65
+ return encoder.done(0);
20
66
  },
21
67
 
22
- decode(buffer: Uint8Array, _flags: number): DisconnectPacket {
23
- isEmptyBuf(buffer);
68
+ decode(
69
+ buffer: Uint8Array,
70
+ flags: number,
71
+ codecOpts,
72
+ packetType: TPacketType,
73
+ ): DisconnectPacket {
74
+ hasEmptyFlags(flags);
75
+ if (codecOpts.protocolLevel !== 5) {
76
+ isEmptyBuf(buffer);
77
+ return {
78
+ type: PacketType.disconnect,
79
+ protocolLevel: codecOpts.protocolLevel,
80
+ };
81
+ }
82
+ const decoder = new Decoder(packetType, buffer);
83
+ if (decoder.atEnd()) {
84
+ return {
85
+ type: PacketType.disconnect,
86
+ protocolLevel: 5,
87
+ reasonCode: 0,
88
+ };
89
+ }
90
+ const reasonCode = decoder.getReasonCode();
91
+ const properties = decoder.getProperties(PacketType.disconnect);
92
+ decoder.done();
24
93
  return {
25
94
  type: PacketType.disconnect,
95
+ protocolLevel: 5,
96
+ reasonCode,
97
+ properties,
26
98
  };
27
99
  },
28
100
  };
@@ -2,38 +2,62 @@ import assert from "node:assert/strict";
2
2
  import { test } from "node:test";
3
3
 
4
4
  import { Encoder } from "./encoder.ts";
5
+ import { encodeLength } from "./length.ts";
6
+ import { PacketType, ReasonCode } from "./mod.ts";
5
7
 
6
8
  const utf8Encoder = new TextEncoder();
7
9
 
8
10
  test("encode byte", () => {
9
11
  const byte = 127;
10
- const encoder = new Encoder();
12
+ const encoder = new Encoder(0);
11
13
  encoder.setByte(byte);
12
- assert.deepStrictEqual(encoder.done(), [byte]);
14
+ assert.deepStrictEqual(encoder.done(0), new Uint8Array([0, 1, byte]));
13
15
  });
14
16
 
15
17
  test("encode byte array", () => {
16
18
  const byteArray = new Array(300);
17
19
  byteArray.fill(127);
18
20
  const len = byteArray.length;
19
- const encoder = new Encoder();
21
+ const payload = [len >> 8, len & 0xff, ...byteArray];
22
+ const encodedLength = encodeLength(payload.length);
23
+ const packet = [0, ...encodedLength, ...payload];
24
+ const encoder = new Encoder(0);
20
25
  encoder.setByteArray(Uint8Array.from(byteArray));
21
- assert.deepStrictEqual(encoder.done(), [len >> 8, len & 0xff, ...byteArray]);
26
+ assert.deepStrictEqual(encoder.done(0), new Uint8Array(packet));
22
27
  });
23
28
 
24
29
  test("encode byte array as remainder", () => {
25
30
  const str = "hello world";
26
31
  const byteArray = utf8Encoder.encode(str);
27
- const encoder = new Encoder();
32
+ const encodedLength = encodeLength(byteArray.length);
33
+ const packet = [0, ...encodedLength, ...byteArray];
34
+ const encoder = new Encoder(0);
28
35
  encoder.setRemainder(byteArray);
29
- assert.deepStrictEqual(encoder.done(), [...byteArray]);
36
+ assert.deepStrictEqual(encoder.done(0), new Uint8Array(packet));
30
37
  });
31
38
 
32
39
  test("encode string", () => {
33
40
  const str = "hello world";
34
41
  const byteArray = utf8Encoder.encode(str);
35
42
  const len = byteArray.length;
36
- const encoder = new Encoder();
43
+ const payload = [len >> 8, len & 0xff, ...byteArray];
44
+ const encodedLength = encodeLength(payload.length);
45
+ const packet = [0, ...encodedLength, ...payload];
46
+ const encoder = new Encoder(0);
37
47
  encoder.setUtf8String(str);
38
- assert.deepStrictEqual(encoder.done(), [0x00, len, ...byteArray]);
48
+ assert.deepStrictEqual(encoder.done(0), new Uint8Array(packet));
49
+ });
50
+
51
+ test("encode reasonCode", () => {
52
+ const reasonCode = ReasonCode.success;
53
+ const encoder = new Encoder(PacketType.connack);
54
+ encoder.setReasonCode(reasonCode);
55
+ assert.deepStrictEqual(encoder.done(0), new Uint8Array([32, 1, reasonCode]));
56
+ });
57
+
58
+ test("encode invalid reasonCode", () => {
59
+ const encoder = new Encoder(PacketType.connack);
60
+ assert.throws(() => {
61
+ encoder.setReasonCode(ReasonCode.noMatchingSubscribers);
62
+ }, /Reason code 16 not allowed for packet type 2/);
39
63
  });
@@ -1,7 +1,33 @@
1
- const utf8Encoder = new TextEncoder();
2
- import type { Topic, TopicFilter } from "./types.ts";
1
+ import type {
2
+ Topic,
3
+ TopicFilter,
4
+ TPacketType,
5
+ TReasonCode,
6
+ UTF8StringPair,
7
+ } from "./types.ts";
8
+
9
+ import type {
10
+ Mqttv5PropertyTypes,
11
+ PropsByPacketSetType,
12
+ SubscriptionIdentifiersType,
13
+ TPropertySetType,
14
+ UserPropertyType,
15
+ ValidPropertyNumber,
16
+ } from "./Properties.ts";
17
+
18
+ import {
19
+ propertyByNumber,
20
+ PropertyByPropertySetType,
21
+ propertyKind,
22
+ propertyToId,
23
+ propertyToKind,
24
+ } from "./Properties.ts";
25
+
26
+ import { encodeLength } from "./length.ts";
3
27
  import { invalidTopic, invalidTopicFilter } from "./validators.ts";
28
+ import { isValidReasonCode } from "./ReasonCode.ts";
4
29
 
30
+ const utf8Encoder = new TextEncoder();
5
31
  /**
6
32
  * Custom error class for encoding operations
7
33
  */
@@ -15,15 +41,51 @@ export class EncoderError extends Error {
15
41
  /**
16
42
  * Encoder class for MQTT packet encoding
17
43
  */
44
+ type bufValue = number[] | Uint8Array;
18
45
  export class Encoder {
19
- /** Internal buffer to store encoded bytes */
20
- private buffer: number[];
46
+ /** Internal bufferlist to store items */
47
+ private buffers: bufValue[];
48
+
49
+ /** Number of bytes in the buffer */
50
+ private numBytes: number;
51
+
52
+ /** Packet type */
53
+ private packetType: TPacketType;
54
+
55
+ /** marker to rewind to */
56
+ private marker: number;
21
57
 
22
58
  /**
23
59
  * Creates a new Encoder instance
24
60
  */
25
- constructor() {
26
- this.buffer = [];
61
+ constructor(packetType: TPacketType) {
62
+ this.buffers = [];
63
+ this.numBytes = 0;
64
+ this.packetType = packetType;
65
+ this.marker = 0;
66
+ }
67
+
68
+ encodedSize() {
69
+ return this.numBytes;
70
+ }
71
+
72
+ setMarker() {
73
+ this.marker = this.buffers.length;
74
+ }
75
+
76
+ rewindToMarker() {
77
+ while (this.buffers.length > this.marker) {
78
+ const buf = this.buffers.pop();
79
+ this.numBytes -= buf?.length || 0;
80
+ }
81
+ }
82
+ /**
83
+ * @param value
84
+ */
85
+ addArray(value: number[] | Uint8Array): this {
86
+ this.buffers.push(value);
87
+ this.numBytes += value.length;
88
+ return this;
27
89
  }
28
90
 
29
91
  /**
@@ -32,7 +94,8 @@ export class Encoder {
32
94
  * @returns The encoder instance for chaining
33
95
  */
34
96
  setByte(value: number): this {
35
- this.buffer.push(value);
97
+ this.buffers.push([value]);
98
+ this.numBytes++;
36
99
  return this;
37
100
  }
38
101
 
@@ -47,6 +110,27 @@ export class Encoder {
47
110
  return this;
48
111
  }
49
112
 
113
+ /**
114
+ * adds a 4 byte integer from the buffer (for v5)
115
+ * @param value - 32-bit integer to encode and add
116
+ * @returns The encoder instance for chaining
117
+ */
118
+ setInt32(value: number): this {
119
+ this.setInt16(value >> 16);
120
+ this.setInt16(value & 0xffff);
121
+ return this;
122
+ }
123
+
124
+ /**
125
+ * adds a variable byte integer to the buffer (for v5)
126
+ * @param value - integer to encode and add
127
+ * @returns The encoder instance for chaining
128
+ */
129
+ setVariableByteInteger(value: number): this {
130
+ this.addArray(encodeLength(value));
131
+ return this;
132
+ }
133
+
50
134
  /**
51
135
  * Adds a byte array to the buffer with length prefix
52
136
  * @param value - Byte array to add
@@ -58,7 +142,7 @@ export class Encoder {
58
142
  throw new EncoderError("More than 0xffff bytes of data");
59
143
  }
60
144
  this.setInt16(value.length);
61
- this.buffer.push(...value);
145
+ this.addArray(value);
62
146
  return this;
63
147
  }
64
148
 
@@ -72,6 +156,17 @@ export class Encoder {
72
156
  return this;
73
157
  }
74
158
 
159
+ /**
160
+ * Adds a UTF-8 string pair from the buffer (for v5)
161
+ * @param stringPair - UTF8StringPair
162
+ * @returns The encoder instance for chaining
163
+ */
164
+ setUtf8StringPair([name, value]: UTF8StringPair): this {
165
+ this.setUtf8String(name);
166
+ this.setUtf8String(value);
167
+ return this;
168
+ }
169
+
75
170
  /**
76
171
  * Adds an MQTT topic string to the buffer
77
172
  * @param value - Topic string to add
@@ -110,7 +205,125 @@ export class Encoder {
110
205
  * @returns The encoder instance for chaining
111
206
  */
112
207
  setRemainder(value: Uint8Array | number[]): this {
113
- this.buffer.push(...value);
208
+ this.addArray(value);
209
+ return this;
210
+ }
211
+
212
+ /**
213
+ * add lenght and buffers from this
214
+ * @param encoder - the encoder to add it to
215
+ */
216
+ addToEncoder(encoder: Encoder) {
217
+ encoder.setVariableByteInteger(this.numBytes);
218
+ for (const buffer of this.buffers) {
219
+ encoder.addArray(buffer);
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Encode a single property
225
+ * @param id - the id of the property
226
+ * @param value the value of the property
227
+ * @returns
228
+ */
229
+ private setProperty(
230
+ id: ValidPropertyNumber,
231
+ value: Mqttv5PropertyTypes,
232
+ ) {
233
+ const kind = propertyToKind[id];
234
+ if (kind === propertyKind.utf8StringPairs) {
235
+ if (!Array.isArray(value)) {
236
+ throw new EncoderError("userProperty must be an array");
237
+ }
238
+ for (const item of value as UserPropertyType) {
239
+ if (!Array.isArray(item)) {
240
+ throw new EncoderError("userProperty item must be an array");
241
+ }
242
+ this.setVariableByteInteger(id);
243
+ this.setUtf8StringPair(item);
244
+ }
245
+ return;
246
+ }
247
+
248
+ if (kind === propertyKind.varIntArray) {
249
+ if (!Array.isArray(value)) {
250
+ throw new EncoderError("Subscription Identifiers must be an array");
251
+ }
252
+ for (const item of value as SubscriptionIdentifiersType) {
253
+ this.setVariableByteInteger(propertyToId["subscriptionIdentifier"]);
254
+ this.setVariableByteInteger(item);
255
+ }
256
+ return;
257
+ }
258
+
259
+ this.setVariableByteInteger(id);
260
+ switch (kind) {
261
+ case propertyKind.boolean:
262
+ this.setByte(!!value === true ? 1 : 0);
263
+ break;
264
+ case propertyKind.byte:
265
+ this.setByte(value as number);
266
+ break;
267
+ case propertyKind.int16:
268
+ this.setInt16(value as number);
269
+ break;
270
+ case propertyKind.int32:
271
+ this.setInt32(value as number);
272
+ break;
273
+ case propertyKind.varInt:
274
+ this.setVariableByteInteger(value as number);
275
+ break;
276
+ case propertyKind.byteArray:
277
+ this.setByteArray(value as Uint8Array);
278
+ break;
279
+ case propertyKind.utf8string:
280
+ this.setUtf8String(value as string);
281
+ break;
282
+ }
283
+ }
284
+
285
+ /**
286
+ * Encode a set of properties respecting potential size limits
287
+ * @param props - the properties to encode
288
+ * @param propertySetType - the type of packet to encode for
289
+ * @param maximumPacketSize - the maximum packet size to be consumed by properties
290
+ */
291
+ setProperties<T extends TPropertySetType>(
292
+ props: PropsByPacketSetType[T],
293
+ propertySetType: TPropertySetType,
294
+ maximumPacketSize: number,
295
+ ) {
296
+ const propsEncoder = new Encoder(0);
297
+ const allowedProps = PropertyByPropertySetType[propertySetType];
298
+ const maxSize = maximumPacketSize - 1; // propertyLength takes at least a byte
299
+
300
+ for (const id of allowedProps) {
301
+ const label = propertyByNumber[id] as keyof PropsByPacketSetType[T];
302
+ const value = props[label];
303
+ if (value !== undefined && value !== null) {
304
+ if (
305
+ id === propertyToId.reasonString || id === propertyToId.userProperty
306
+ ) {
307
+ propsEncoder.setMarker();
308
+ propsEncoder.setProperty(id, value as Mqttv5PropertyTypes);
309
+ if (propsEncoder.encodedSize() > maxSize) {
310
+ propsEncoder.rewindToMarker();
311
+ }
312
+ } else {
313
+ propsEncoder.setProperty(id, value as Mqttv5PropertyTypes);
314
+ }
315
+ }
316
+ }
317
+ propsEncoder.addToEncoder(this);
318
+ }
319
+
320
+ setReasonCode(reasonCode: TReasonCode) {
321
+ if (!isValidReasonCode(this.packetType, reasonCode)) {
322
+ throw new EncoderError(
323
+ `Reason code ${reasonCode} not allowed for packet type ${this.packetType}`,
324
+ );
325
+ }
326
+ this.setByte(reasonCode);
114
327
  return this;
115
328
  }
116
329
 
@@ -118,7 +331,19 @@ export class Encoder {
118
331
  * Returns the final encoded buffer
119
332
  * @returns Array containing all encoded bytes
120
333
  */
121
- done(): number[] {
122
- return this.buffer;
334
+ done(flags: number): Uint8Array {
335
+ const packetType = this.packetType;
336
+ const encodedLength = encodeLength(this.numBytes);
337
+ const totalLength = 1 + encodedLength.length + this.numBytes;
338
+ const result = new Uint8Array(totalLength);
339
+ let pos = 0;
340
+ result.set([packetType << 4 | flags], pos++);
341
+ result.set(encodedLength, pos);
342
+ pos += encodedLength.length;
343
+ for (const buffer of this.buffers) {
344
+ result.set(buffer, pos);
345
+ pos += buffer.length;
346
+ }
347
+ return result;
123
348
  }
124
349
  }
@@ -33,8 +33,9 @@ export function decodeLength(
33
33
  start: number,
34
34
  ): { length: number; numLengthBytes: number } {
35
35
  const decode = getLengthDecoder();
36
- const lenBuf = buf.subarray(start);
37
- for (const byte of lenBuf) {
36
+ const size = buf.length;
37
+ for (let i = start; i < size; i++) {
38
+ const byte = buf[i];
38
39
  const { done, length, numLengthBytes } = decode(byte);
39
40
  if (done) {
40
41
  return { length, numLengthBytes };