@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,7 +1,18 @@
1
- import type { ClientId, Payload, QoS, Topic, TPacketType } from "./types.ts";
1
+ import type {
2
+ ClientId,
3
+ CodecOpts,
4
+ Payload,
5
+ ProtocolLevel,
6
+ ProtocolLevelNoV5,
7
+ QoS,
8
+ Topic,
9
+ TPacketType,
10
+ } from "./types.ts";
11
+ import type { ConnectProperties, WillProperties } from "./Properties.ts";
12
+ import { PropertySetType } from "./Properties.ts";
2
13
  import { PacketType } from "./PacketType.ts";
3
14
  import { BitMask } from "./BitMask.ts";
4
- import { Encoder } from "./encoder.ts";
15
+ import { Encoder, EncoderError } from "./encoder.ts";
5
16
  import {
6
17
  booleanFlag,
7
18
  Decoder,
@@ -12,10 +23,10 @@ import {
12
23
  /**
13
24
  * ConnectPacket is sent from the client to the server to initiate a connection.
14
25
  */
15
- export type ConnectPacket = {
26
+ export type ConnectPacketV4 = {
16
27
  type: TPacketType;
17
28
  protocolName?: string;
18
- protocolLevel?: number;
29
+ protocolLevel: ProtocolLevelNoV5;
19
30
  clientId?: ClientId;
20
31
  username?: string;
21
32
  password?: Uint8Array;
@@ -27,31 +38,85 @@ export type ConnectPacket = {
27
38
  };
28
39
  clean?: boolean;
29
40
  keepAlive?: number;
41
+ bridgeMode?: boolean;
30
42
  };
31
43
 
44
+ export type ConnectPacketV5 = {
45
+ type: TPacketType;
46
+ protocolName?: string;
47
+ protocolLevel: 5;
48
+ clientId?: ClientId;
49
+ username?: string;
50
+ password?: Uint8Array;
51
+ will?: {
52
+ topic: Topic;
53
+ payload: Payload;
54
+ retain?: boolean;
55
+ qos?: QoS;
56
+ properties?: WillProperties;
57
+ };
58
+ clean?: boolean;
59
+ keepAlive?: number;
60
+ bridgeMode?: boolean;
61
+ properties?: ConnectProperties;
62
+ };
63
+
64
+ export type ConnectPacket = ConnectPacketV4 | ConnectPacketV5;
65
+
32
66
  function invalidProtocolName(version: number, name: string): boolean {
33
- if (version === 4 && name !== "MQTT") {
34
- return true;
67
+ if (version === 3 && name === "MQIsdp") {
68
+ return false;
35
69
  }
36
- return false;
70
+ if (version === 4 && name === "MQTT") {
71
+ return false;
72
+ }
73
+ if (version === 5 && name == "MQTT") {
74
+ return false;
75
+ }
76
+ return true;
37
77
  }
38
78
 
39
79
  export const connect: {
40
- encode(packet: ConnectPacket): { flags: number; bytes: number[] };
41
- decode(buffer: Uint8Array, flags: number): ConnectPacket;
80
+ encode(packet: ConnectPacket, _codecOpts: CodecOpts): Uint8Array;
81
+ decode(
82
+ buffer: Uint8Array,
83
+ flags: number,
84
+ codecOpts: CodecOpts,
85
+ packetType: TPacketType,
86
+ ): ConnectPacket;
42
87
  } = {
43
- encode(packet: ConnectPacket): { flags: number; bytes: number[] } {
88
+ encode(packet: ConnectPacket, codecOpts: CodecOpts): Uint8Array {
44
89
  const flags = 0;
45
90
 
46
- const protocolLevel = 4;
47
- const protocolName = protocolLevel === 4 ? "MQTT" : "MQIsdp";
91
+ if (typeof packet.protocolLevel !== "number") {
92
+ throw new EncoderError("Protocol level must be a number");
93
+ }
94
+ const protocolLevel = packet.protocolLevel || 4;
95
+ if (protocolLevel < 3 || protocolLevel > 5) {
96
+ throw new EncoderError("Unsupported protocol level");
97
+ }
98
+ const protocolName = protocolLevel > 3 ? "MQTT" : "MQIsdp";
48
99
  const clientId = packet.clientId || "";
49
- const usernameFlag = !!packet.username;
50
- const passwordFlag = !!packet.password;
100
+ const usernameFlag = packet.username !== undefined;
101
+ if (protocolLevel === 3 && clientId === "") {
102
+ throw new EncoderError("Client id required for protocol level 3");
103
+ }
104
+ const passwordFlag = usernameFlag && packet.password !== undefined;
51
105
  const willRetain = !!packet.will?.retain;
52
106
  const willQoS = packet.will?.qos || 0;
53
- const willFlag = !!packet.will;
107
+ const willFlag = packet.will !== undefined;
108
+ if (willFlag) {
109
+ if (packet.will?.topic === undefined) {
110
+ throw new EncoderError("Will topic must be defined");
111
+ }
112
+ if (packet.will?.payload === undefined) {
113
+ throw new EncoderError("Will payload must be defined");
114
+ }
115
+ }
54
116
  const cleanSession = packet.clean !== false;
117
+ if (!cleanSession && (clientId === "")) {
118
+ throw new EncoderError("Client id required for clean session");
119
+ }
55
120
  const connectFlags = (usernameFlag ? BitMask.bit7 : 0) +
56
121
  (passwordFlag ? BitMask.bit6 : 0) +
57
122
  (willRetain ? BitMask.bit5 : 0) +
@@ -60,35 +125,64 @@ export const connect: {
60
125
  (willFlag ? BitMask.bit2 : 0) +
61
126
  (cleanSession ? BitMask.bit1 : 0);
62
127
  const keepAlive = packet.keepAlive || 0;
128
+ const bridgeMode = !!packet.bridgeMode;
129
+ const rawProtocolLevel = bridgeMode ? protocolLevel + 128 : protocolLevel;
63
130
 
64
- const encoder = new Encoder();
131
+ const encoder = new Encoder(packet.type);
65
132
  encoder
66
133
  .setUtf8String(protocolName)
67
- .setByte(protocolLevel)
134
+ .setByte(rawProtocolLevel)
68
135
  .setByte(connectFlags)
69
- .setInt16(keepAlive)
70
- .setUtf8String(clientId);
136
+ .setInt16(keepAlive);
137
+ if (packet.protocolLevel === 5) {
138
+ encoder.setProperties(
139
+ packet.properties || {},
140
+ PropertySetType.connect,
141
+ codecOpts.maxOutgoingPacketSize,
142
+ );
143
+ }
144
+ encoder.setUtf8String(clientId);
71
145
 
72
- if (packet.will) {
146
+ if (
147
+ packet.will !== undefined && packet.will.topic !== undefined &&
148
+ packet.will.payload !== undefined
149
+ ) {
150
+ if (packet.protocolLevel === 5) {
151
+ encoder.setProperties(
152
+ packet.will?.properties || {},
153
+ PropertySetType.will,
154
+ codecOpts.maxOutgoingPacketSize,
155
+ );
156
+ }
73
157
  encoder.setTopic(packet.will.topic).setByteArray(packet.will.payload);
74
158
  }
75
159
 
76
- if (packet.username) {
160
+ if (packet.username !== undefined) {
77
161
  encoder.setUtf8String(packet.username);
78
162
  }
79
163
 
80
- if (packet.password) {
164
+ if (passwordFlag && packet.password !== undefined) {
81
165
  encoder.setByteArray(packet.password);
82
166
  }
83
- return { flags, bytes: encoder.done() };
167
+ return encoder.done(flags);
84
168
  },
85
169
 
86
- decode(buffer: Uint8Array, flags: number): ConnectPacket {
87
- const decoder = new Decoder(buffer);
88
- const protocolName = decoder.getUtf8String();
89
- const protocolLevel = decoder.getByte();
170
+ decode(
171
+ buffer: Uint8Array,
172
+ flags: number,
173
+ _codecOpts: CodecOpts,
174
+ packetType: TPacketType,
175
+ ): ConnectPacket {
176
+ const decoder = new Decoder(packetType, buffer);
177
+ const protocolName = decoder.getUTF8String();
178
+ const rawProtocolLevel = decoder.getByte();
179
+ const bridgeMode = rawProtocolLevel > 128;
180
+ const protocolLevel = bridgeMode
181
+ ? rawProtocolLevel - 128
182
+ : rawProtocolLevel;
183
+
90
184
  if (invalidProtocolName(protocolLevel, protocolName)) {
91
- throw new DecoderError("Invalid protocol name");
185
+ throw new DecoderError("Invalid protocol name or level");
92
186
  }
93
187
 
94
188
  const connectFlags = decoder.getByte();
@@ -112,12 +206,24 @@ export const connect: {
112
206
  throw new DecoderError("Invalid will qos");
113
207
  }
114
208
 
115
- const keepAlive = decoder.getInt16();
116
- const clientId = decoder.getUtf8String();
209
+ if (!usernameFlag && passwordFlag) {
210
+ throw new DecoderError("Password without username");
211
+ }
117
212
 
213
+ const keepAlive = decoder.getInt16();
214
+ const isV5 = protocolLevel === 5;
215
+ let properties;
216
+ if (isV5) {
217
+ properties = decoder.getProperties(PropertySetType.connect);
218
+ }
219
+ const clientId = decoder.getUTF8String();
220
+ let willProperties;
118
221
  let willTopic;
119
222
  let willPayload;
120
223
  if (willFlag) {
224
+ if (isV5) {
225
+ willProperties = decoder.getProperties(PropertySetType.will);
226
+ }
121
227
  willTopic = decoder.getTopic();
122
228
  willPayload = decoder.getByteArray();
123
229
  }
@@ -125,7 +231,7 @@ export const connect: {
125
231
  let username;
126
232
  let password;
127
233
  if (usernameFlag) {
128
- username = decoder.getUtf8String();
234
+ username = decoder.getUTF8String();
129
235
  }
130
236
 
131
237
  if (passwordFlag) {
@@ -143,13 +249,40 @@ export const connect: {
143
249
  throw new DecoderError("Clean session must be true if clientID is empty");
144
250
  }
145
251
 
146
- return {
252
+ let bridgeModeProp = {};
253
+ if (bridgeMode) {
254
+ bridgeModeProp = { bridgeMode };
255
+ }
256
+ const commonProps = {
147
257
  type: PacketType.connect,
148
258
  protocolName: protocolName,
149
- protocolLevel,
150
259
  clientId: clientId,
151
- username: username ? username : undefined,
260
+ username,
152
261
  password: password ? password : undefined,
262
+ clean: cleanSession,
263
+ keepAlive,
264
+ ...bridgeModeProp,
265
+ };
266
+
267
+ if (isV5) {
268
+ return {
269
+ ...commonProps,
270
+ protocolLevel: 5,
271
+ will: willFlag
272
+ ? {
273
+ topic: willTopic || "",
274
+ payload: willPayload || Uint8Array.from([0]),
275
+ retain: willRetain,
276
+ qos: willQoS,
277
+ properties: willProperties,
278
+ }
279
+ : undefined,
280
+ properties,
281
+ };
282
+ }
283
+ return {
284
+ ...commonProps,
285
+ protocolLevel: protocolLevel as ProtocolLevel,
153
286
  will: willFlag
154
287
  ? {
155
288
  topic: willTopic || "",
@@ -158,8 +291,6 @@ export const connect: {
158
291
  qos: willQoS,
159
292
  }
160
293
  : undefined,
161
- clean: cleanSession,
162
- keepAlive,
163
294
  };
164
295
  },
165
296
  };
@@ -4,10 +4,11 @@ import { test } from "node:test";
4
4
  import { Decoder } from "./decoder.ts";
5
5
 
6
6
  const utf8encoder = new TextEncoder();
7
+ const packetType = 0;
7
8
 
8
9
  test("decode byte", () => {
9
10
  const byte = 127;
10
- const decoder = new Decoder(Uint8Array.from([byte]));
11
+ const decoder = new Decoder(packetType, Uint8Array.from([byte]));
11
12
  assert.deepStrictEqual(decoder.getByte(), byte);
12
13
  assert.deepStrictEqual(decoder.done(), true);
13
14
  });
@@ -16,7 +17,7 @@ test("decode Int16", () => {
16
17
  const value = 0xf0f2;
17
18
  const msb = 0xf0;
18
19
  const lsb = 0xf2;
19
- const decoder = new Decoder(Uint8Array.from([msb, lsb]));
20
+ const decoder = new Decoder(packetType, Uint8Array.from([msb, lsb]));
20
21
  assert.deepStrictEqual(decoder.getInt16(), value);
21
22
  assert.deepStrictEqual(decoder.done(), true);
22
23
  });
@@ -25,7 +26,7 @@ test("decode Int16 with remainder", () => {
25
26
  const value = 0xf0f2;
26
27
  const msb = 0xf0;
27
28
  const lsb = 0xf2;
28
- const decoder = new Decoder(Uint8Array.from([msb, lsb, 0xff]));
29
+ const decoder = new Decoder(packetType, Uint8Array.from([msb, lsb, 0xff]));
29
30
  assert.deepStrictEqual(decoder.getInt16(), value, "value is correct");
30
31
  assert.deepStrictEqual(decoder.atEnd(), false);
31
32
  });
@@ -35,6 +36,7 @@ test("decode byte array", () => {
35
36
  byteArray.fill(127);
36
37
  const len = byteArray.length;
37
38
  const decoder = new Decoder(
39
+ packetType,
38
40
  Uint8Array.from([len >> 8, len & 0xff, ...byteArray]),
39
41
  );
40
42
  assert.deepStrictEqual(decoder.getByteArray(), Uint8Array.from(byteArray));
@@ -44,7 +46,7 @@ test("decode byte array", () => {
44
46
  test("decode byte array as remainder", () => {
45
47
  const str = "hello world";
46
48
  const byteArray = utf8encoder.encode(str);
47
- const decoder = new Decoder(byteArray);
49
+ const decoder = new Decoder(packetType, byteArray);
48
50
  assert.deepStrictEqual(decoder.getRemainder(), byteArray);
49
51
  assert.deepStrictEqual(decoder.done(), true);
50
52
  });
@@ -54,8 +56,11 @@ test("decode byte array as empty remainder", () => {
54
56
  const emptyArray = Uint8Array.from([]);
55
57
  const byteArray = utf8encoder.encode(str);
56
58
  const len = byteArray.length;
57
- const decoder = new Decoder(Uint8Array.from([0x00, len, ...byteArray]));
58
- assert.deepStrictEqual(decoder.getUtf8String(), str);
59
+ const decoder = new Decoder(
60
+ packetType,
61
+ Uint8Array.from([0x00, len, ...byteArray]),
62
+ );
63
+ assert.deepStrictEqual(decoder.getUTF8String(), str);
59
64
  assert.deepStrictEqual(decoder.getRemainder(), emptyArray);
60
65
  assert.deepStrictEqual(decoder.done(), true);
61
66
  });
@@ -64,8 +69,11 @@ test("decode string", () => {
64
69
  const str = "hello world";
65
70
  const byteArray = utf8encoder.encode(str);
66
71
  const len = byteArray.length;
67
- const decoder = new Decoder(Uint8Array.from([0x00, len, ...byteArray]));
68
- assert.deepStrictEqual(decoder.getUtf8String(), str);
72
+ const decoder = new Decoder(
73
+ packetType,
74
+ Uint8Array.from([0x00, len, ...byteArray]),
75
+ );
76
+ assert.deepStrictEqual(decoder.getUTF8String(), str);
69
77
  assert.deepStrictEqual(decoder.done(), true);
70
78
  });
71
79
 
@@ -73,13 +81,16 @@ test("decode topic", () => {
73
81
  const str = "hello world";
74
82
  const byteArray = utf8encoder.encode(str);
75
83
  const len = byteArray.length;
76
- const decoder = new Decoder(Uint8Array.from([0x00, len, ...byteArray]));
84
+ const decoder = new Decoder(
85
+ packetType,
86
+ Uint8Array.from([0x00, len, ...byteArray]),
87
+ );
77
88
  assert.deepStrictEqual(decoder.getTopic(), str);
78
89
  assert.deepStrictEqual(decoder.done(), true);
79
90
  });
80
91
 
81
92
  test("Topic too short", () => {
82
- const decoder = new Decoder(Uint8Array.from([0x00, 0]));
93
+ const decoder = new Decoder(packetType, Uint8Array.from([0x00, 0]));
83
94
  assert.throws(
84
95
  () => decoder.getTopic(),
85
96
  Error,
@@ -88,7 +99,7 @@ test("Topic too short", () => {
88
99
  });
89
100
 
90
101
  test("Invalid topic", () => {
91
- const decoder = new Decoder(Uint8Array.from([0x00, 0x01, 0x00]));
102
+ const decoder = new Decoder(packetType, Uint8Array.from([0x00, 0x01, 0x00]));
92
103
  assert.throws(
93
104
  () => decoder.getTopic(),
94
105
  Error,
@@ -97,7 +108,7 @@ test("Invalid topic", () => {
97
108
  });
98
109
 
99
110
  test("Invalid topicFilter", () => {
100
- const decoder = new Decoder(Uint8Array.from([0x00, 0x01, 0x00]));
111
+ const decoder = new Decoder(packetType, Uint8Array.from([0x00, 0x01, 0x00]));
101
112
  assert.throws(
102
113
  () => decoder.getTopic(),
103
114
  Error,
@@ -109,15 +120,21 @@ test("Buffer too short", () => {
109
120
  const str = "hello world";
110
121
  const byteArray = utf8encoder.encode(str);
111
122
  const len = byteArray.length;
112
- const decoder = new Decoder(Uint8Array.from([0x00, len + 1, ...byteArray]));
113
- assert.throws(() => decoder.getUtf8String(), Error, "too short");
123
+ const decoder = new Decoder(
124
+ packetType,
125
+ Uint8Array.from([0x00, len + 1, ...byteArray]),
126
+ );
127
+ assert.throws(() => decoder.getUTF8String(), Error, "too short");
114
128
  });
115
129
 
116
130
  test("Buffer too long", () => {
117
131
  const str = "hello world";
118
132
  const byteArray = utf8encoder.encode(str);
119
133
  const len = byteArray.length;
120
- const decoder = new Decoder(Uint8Array.from([0x00, len, ...byteArray, 0]));
121
- assert.deepStrictEqual(decoder.getUtf8String(), str);
134
+ const decoder = new Decoder(
135
+ packetType,
136
+ Uint8Array.from([0x00, len, ...byteArray, 0]),
137
+ );
138
+ assert.deepStrictEqual(decoder.getUTF8String(), str);
122
139
  assert.throws(() => decoder.done(), Error, "too long");
123
140
  });
@@ -1,7 +1,32 @@
1
- import type { TBitMask, Topic, TopicFilter } from "./types.ts";
1
+ import type {
2
+ TBitMask,
3
+ Topic,
4
+ TopicFilter,
5
+ TPacketType,
6
+ TReasonCode,
7
+ UTF8StringPair,
8
+ } from "./types.ts";
2
9
  import { invalidTopic, invalidTopicFilter } from "./validators.ts";
10
+ import { isValidReasonCode } from "./ReasonCode.ts";
11
+ import type {
12
+ Mqttv5PropertyTypesNoUser,
13
+ PropsByPacketSetType,
14
+ SubscriptionIdentifiersType,
15
+ UserPropertyType,
16
+ ValidPropertyNumber,
17
+ } from "./Properties.ts";
18
+ import {
19
+ propertyByNumber,
20
+ PropertyByPropertySetType,
21
+ propertyKind,
22
+ propertyToId,
23
+ propertyToKind,
24
+ } from "./Properties.ts";
3
25
 
4
26
  const utf8Decoder = new TextDecoder("utf-8");
27
+ const userPropID = propertyToId.userProperty;
28
+ const subIdentID = propertyToId.subscriptionIdentifier;
29
+ const subIdentsID = propertyToId.subscriptionIdentifiers;
5
30
  /**
6
31
  * Checks if a specific bit flag is set in a byte using a bitmask
7
32
  * @param byte - The byte to check
@@ -48,6 +73,7 @@ export class DecoderError extends Error {
48
73
  * Decoder class for parsing MQTT packets
49
74
  */
50
75
  export class Decoder {
76
+ private packetType: TPacketType;
51
77
  private buf: Uint8Array;
52
78
  private pos: number;
53
79
  private len: number;
@@ -57,7 +83,8 @@ export class Decoder {
57
83
  * @param buf - The buffer to decode
58
84
  * @param pos - Starting position in the buffer (default: 0)
59
85
  */
60
- constructor(buf: Uint8Array, pos: number = 0) {
86
+ constructor(packetType: TPacketType, buf: Uint8Array, pos: number = 0) {
87
+ this.packetType = packetType;
61
88
  this.buf = buf;
62
89
  this.pos = pos;
63
90
  this.len = buf.length;
@@ -93,6 +120,37 @@ export class Decoder {
93
120
  return (msb << 8) | lsb;
94
121
  }
95
122
 
123
+ /**
124
+ * Gets a 4 byte integer from the buffer (for v5)
125
+ * @returns The 32 bit integer value
126
+ */
127
+ getInt32(): number {
128
+ const msb = this.getInt16();
129
+ const lsb = this.getInt16();
130
+ return (msb << 16) | lsb;
131
+ }
132
+
133
+ /**
134
+ * Gets a variable byte integer from the buffer (for v5)
135
+ * its like the length decoder in length.ts but this one works directly on the buffer
136
+ * @returns The decoded variable byte integer
137
+ * @throws {DecoderError} If malformed variable byte integer
138
+ */
139
+ getVariableByteInteger(): number {
140
+ let num = 0;
141
+ let multiplier = 1;
142
+ let byte;
143
+ do {
144
+ byte = this.getByte();
145
+ num += (byte & 127) * multiplier;
146
+ if (multiplier > 128 * 128 * 128) {
147
+ throw new DecoderError("Malformed Variable Byte Integer");
148
+ }
149
+ multiplier *= 128;
150
+ } while ((byte & 128) !== 0);
151
+ return num;
152
+ }
153
+
96
154
  /**
97
155
  * Gets a byte array from the buffer
98
156
  * @returns A subarray of the buffer
@@ -110,18 +168,28 @@ export class Decoder {
110
168
  * Gets a UTF-8 string from the buffer
111
169
  * @returns The decoded UTF-8 string
112
170
  */
113
- getUtf8String(): string {
171
+ getUTF8String(): string {
114
172
  const str = utf8Decoder.decode(this.getByteArray());
115
173
  return str;
116
174
  }
117
175
 
176
+ /**
177
+ * Gets a UTF-8 string pair from the buffer (for v5)
178
+ * @returns The decoded UTF-8 pair as [name,value]
179
+ */
180
+ getUTF8StringPair(): UTF8StringPair {
181
+ const name = utf8Decoder.decode(this.getByteArray());
182
+ const value = utf8Decoder.decode(this.getByteArray());
183
+ return [name, value];
184
+ }
185
+
118
186
  /**
119
187
  * Gets a topic from the buffer
120
188
  * @returns The decoded topic
121
189
  * @throws {DecoderError} If topic is invalid
122
190
  */
123
191
  getTopic(): Topic {
124
- const topic = this.getUtf8String();
192
+ const topic = this.getUTF8String();
125
193
  if (invalidTopic(topic)) {
126
194
  throw new DecoderError(
127
195
  "Topic must contain valid UTF-8 and contain more than 1 byte and no wildcards",
@@ -136,7 +204,7 @@ export class Decoder {
136
204
  * @throws {DecoderError} If topic filter is invalid
137
205
  */
138
206
  getTopicFilter(): TopicFilter {
139
- const topicFilter = this.getUtf8String();
207
+ const topicFilter = this.getUTF8String();
140
208
  if (invalidTopicFilter(topicFilter)) {
141
209
  throw new DecoderError(
142
210
  "Topicfilter must contain valid UTF-8 and contain more than 1 byte and valid wildcards",
@@ -156,6 +224,106 @@ export class Decoder {
156
224
  return this.buf.subarray(start, end);
157
225
  }
158
226
 
227
+ /**
228
+ * Gets a single property
229
+ * @param id
230
+ * @returns
231
+ */
232
+ private getProperty(
233
+ id: ValidPropertyNumber,
234
+ ): Mqttv5PropertyTypesNoUser {
235
+ switch (propertyToKind[id]) {
236
+ case propertyKind.boolean:
237
+ return !!this.getByte();
238
+ case propertyKind.byte:
239
+ return this.getByte();
240
+ case propertyKind.int16:
241
+ return this.getInt16();
242
+ case propertyKind.int32:
243
+ return this.getInt32();
244
+ case propertyKind.varInt:
245
+ return this.getVariableByteInteger();
246
+ case propertyKind.byteArray:
247
+ return this.getByteArray();
248
+ case propertyKind.utf8string:
249
+ return this.getUTF8String();
250
+ }
251
+ // deno-coverage-ignore
252
+ throw new DecoderError("Invalid property kind");
253
+ }
254
+
255
+ /**
256
+ * Gets all properties from the buffer
257
+ * and validates if they are allowed for this type of packet
258
+ * @param propertySetType
259
+ * @returns
260
+ * @throws {DecoderError} If properties are invalid
261
+ */
262
+ getProperties<T extends keyof PropsByPacketSetType>(
263
+ propertySetType: T,
264
+ ): PropsByPacketSetType[T] {
265
+ const allowedProps: readonly number[] =
266
+ PropertyByPropertySetType[propertySetType];
267
+ const properties = {} as PropsByPacketSetType[T];
268
+ const propLength = this.getVariableByteInteger();
269
+ const endPos = this.pos + propLength;
270
+ const userProps: UserPropertyType = [];
271
+ const subIdents: SubscriptionIdentifiersType = [];
272
+
273
+ while (this.pos < endPos) {
274
+ const id = this.getVariableByteInteger() as ValidPropertyNumber;
275
+ const label = propertyByNumber[id];
276
+ if (
277
+ id === subIdentID && allowedProps.includes(subIdentsID)
278
+ ) {
279
+ subIdents.push(this.getVariableByteInteger());
280
+ continue;
281
+ }
282
+ if (!allowedProps.includes(id)) {
283
+ throw new DecoderError(
284
+ `Property type ${label ? label : id} not allowed at byte ${this.pos}`,
285
+ );
286
+ }
287
+ if (id === userPropID) {
288
+ const stringPair = this.getUTF8StringPair();
289
+ userProps.push(stringPair);
290
+ continue;
291
+ }
292
+
293
+ const value = this.getProperty(id);
294
+
295
+ // deno-lint-ignore no-explicit-any
296
+ if ((properties as any)[label] !== undefined) {
297
+ throw new DecoderError(`Property ${label} only allowed once`);
298
+ } else {
299
+ // deno-lint-ignore no-explicit-any
300
+ (properties as any)[label] = value;
301
+ }
302
+ }
303
+ if (userProps.length > 0) {
304
+ // deno-lint-ignore no-explicit-any
305
+ (properties as any).userProperty = userProps;
306
+ }
307
+ if (subIdents.length > 0) {
308
+ // deno-lint-ignore no-explicit-any
309
+ (properties as any).subscriptionIdentifiers = subIdents;
310
+ }
311
+ return properties;
312
+ }
313
+
314
+ /**
315
+ * Gets a reason code from the buffer
316
+ * @returns The decoded reason code
317
+ * @throws {DecoderError} If reason code is invalid
318
+ */
319
+ getReasonCode(): TReasonCode {
320
+ const reasonCode = this.getByte();
321
+ if (!isValidReasonCode(this.packetType, reasonCode)) {
322
+ throw new DecoderError("Invalid reason code");
323
+ }
324
+ return reasonCode as TReasonCode;
325
+ }
326
+
159
327
  /**
160
328
  * Checks if decoder has reached the end of the buffer
161
329
  * @returns True if at end, false otherwise