@moqtap/codec 0.2.3 → 0.3.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 (299) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +100 -99
  3. package/dist/{chunk-RZHAPEXO.js → chunk-2A57DRXV.js} +36 -10
  4. package/dist/{chunk-73MWUKSJ.js → chunk-2UTVXWFF.js} +190 -102
  5. package/dist/{chunk-JTVCOMBI.cjs → chunk-3XKO6PCR.cjs} +290 -183
  6. package/dist/{chunk-ACQ4CUJP.cjs → chunk-4BDEU3D4.cjs} +237 -158
  7. package/dist/{chunk-4N4INRE7.cjs → chunk-4HBKHNU2.cjs} +144 -110
  8. package/dist/{chunk-2ZT6CVSE.js → chunk-4IVZZOY3.js} +269 -162
  9. package/dist/{chunk-GLBQDG4L.cjs → chunk-5O764MBD.cjs} +508 -117
  10. package/dist/chunk-7KBAWOBA.js +1212 -0
  11. package/dist/{chunk-JSQM2MG3.js → chunk-7QOPNSDF.js} +44 -12
  12. package/dist/{chunk-SYHW3FLI.cjs → chunk-7RE6KSIY.cjs} +28 -8
  13. package/dist/{chunk-YG5KJESI.js → chunk-AY6IAHUM.js} +28 -8
  14. package/dist/{chunk-EFM5T7OM.js → chunk-BLQDWOSG.js} +36 -10
  15. package/dist/{chunk-UYXTY6ZQ.cjs → chunk-E65QKRSN.cjs} +20 -12
  16. package/dist/{chunk-BISI45MN.cjs → chunk-EAHE3IOG.cjs} +44 -12
  17. package/dist/{chunk-PZBPH2D7.js → chunk-GCBFHZW6.js} +216 -137
  18. package/dist/{chunk-GXEW4COZ.cjs → chunk-H5NSL4MO.cjs} +36 -10
  19. package/dist/chunk-HZHSO7EW.cjs +1212 -0
  20. package/dist/{chunk-KZCYQ7ED.cjs → chunk-IILNSDEQ.cjs} +69 -41
  21. package/dist/{chunk-4XYGE53S.cjs → chunk-J3XCPBW3.cjs} +36 -10
  22. package/dist/{chunk-6AEHWULA.cjs → chunk-JKOEJZ5B.cjs} +28 -8
  23. package/dist/{chunk-RUPCJTC3.cjs → chunk-JL3NEW3O.cjs} +23 -1
  24. package/dist/{chunk-G7GI7LJA.js → chunk-LORZMJJB.js} +36 -10
  25. package/dist/{chunk-DUBCL3WF.cjs → chunk-LV5C6MEC.cjs} +36 -10
  26. package/dist/{chunk-ENURAVHI.cjs → chunk-MYXAD5JC.cjs} +44 -12
  27. package/dist/{chunk-FXZ2MYKJ.js → chunk-MZ7HUCGJ.js} +20 -12
  28. package/dist/{chunk-S74DWJE2.cjs → chunk-OKG2HT25.cjs} +277 -88
  29. package/dist/{chunk-BGWGVINJ.cjs → chunk-PKEOQSSZ.cjs} +144 -110
  30. package/dist/{chunk-2GODRVT5.js → chunk-Q46ZURKX.js} +325 -48
  31. package/dist/{chunk-UR6JKS56.js → chunk-Q7IGWV3P.js} +41 -9
  32. package/dist/{chunk-XSNX2IUJ.js → chunk-RW4UG7CN.js} +121 -87
  33. package/dist/{chunk-DWK5ZQZ4.js → chunk-RZTLQAKF.js} +28 -8
  34. package/dist/{chunk-UNS34PTA.cjs → chunk-S6IPIN66.cjs} +44 -12
  35. package/dist/{chunk-YIG2C3LF.cjs → chunk-SDWRXXOO.cjs} +214 -126
  36. package/dist/{chunk-CEH7ESNC.js → chunk-SHMA4PBY.js} +121 -87
  37. package/dist/{chunk-MC7W6PMI.js → chunk-U3ZVLT5L.js} +483 -135
  38. package/dist/{chunk-VJUUZ4W2.cjs → chunk-UJU63EFY.cjs} +374 -97
  39. package/dist/{chunk-7L43FE6P.js → chunk-VOU7PNHI.js} +23 -1
  40. package/dist/{chunk-E6E3NQYU.js → chunk-WRHGCVYN.js} +44 -12
  41. package/dist/{chunk-FPISPJ5N.cjs → chunk-XNWYLVGU.cjs} +529 -181
  42. package/dist/{chunk-U3IPZYPX.js → chunk-XS77RBY7.js} +228 -39
  43. package/dist/{chunk-JGG7CMSZ.cjs → chunk-XZE5R57R.cjs} +144 -110
  44. package/dist/{chunk-77MNXX3M.js → chunk-Y6KCSM3V.js} +121 -87
  45. package/dist/{chunk-TLYNOOQP.cjs → chunk-YTPZZI2M.cjs} +41 -9
  46. package/dist/{chunk-7DUBLRXC.js → chunk-YWRCSQBA.js} +44 -12
  47. package/dist/{chunk-MRJWLGYH.js → chunk-Z3VKP3PJ.js} +69 -41
  48. package/dist/{chunk-E6P74GDT.js → chunk-Z6U26IRI.js} +464 -73
  49. package/dist/{codec-FvGCxIhr.d.ts → codec-25u4_1HS.d.ts} +4 -4
  50. package/dist/{codec-DRcsBCsb.d.ts → codec-3Ppy_5Gd.d.ts} +4 -4
  51. package/dist/{codec-BdVMRMOI.d.ts → codec-BB0U8MF2.d.cts} +4 -4
  52. package/dist/{codec-B2cH-f5V.d.cts → codec-BHHfBTnV.d.ts} +4 -4
  53. package/dist/{codec-B5K73Bdj.d.ts → codec-BN6jk0Td.d.cts} +4 -4
  54. package/dist/{codec-R8GrCPt2.d.ts → codec-BU0gaSZm.d.ts} +4 -4
  55. package/dist/{codec-Bq-tLtfP.d.ts → codec-C-Yk2RNa.d.ts} +4 -4
  56. package/dist/codec-CFWQY9Lj.d.cts +18 -0
  57. package/dist/{codec-CPlK3Pi5.d.ts → codec-COy4uuPO.d.cts} +4 -4
  58. package/dist/codec-CewFCsEy.d.ts +18 -0
  59. package/dist/{codec-KXHvXZVb.d.cts → codec-Ck7ZwFF1.d.cts} +4 -4
  60. package/dist/{codec-DZhZSIii.d.cts → codec-D2QVSH9D.d.ts} +4 -4
  61. package/dist/{codec-BwFgpeQu.d.ts → codec-DDUBJGn0.d.cts} +4 -4
  62. package/dist/{codec-BFeliJFc.d.ts → codec-DNfml-se.d.ts} +4 -4
  63. package/dist/{codec-C4n1PS09.d.cts → codec-DoOGjjDq.d.cts} +4 -4
  64. package/dist/{codec-Bap882Fh.d.cts → codec-J7ekOSMc.d.ts} +4 -4
  65. package/dist/{codec-SKBeHQk7.d.ts → codec-R9TJCq-E.d.cts} +4 -4
  66. package/dist/{codec-DNUIgLqc.d.cts → codec-ZwV-Njt6.d.ts} +4 -4
  67. package/dist/{codec-HgBgHkFI.d.cts → codec-j819z_hL.d.cts} +4 -4
  68. package/dist/{codec-DY4QH364.d.cts → codec-w8z2j_uo.d.cts} +4 -4
  69. package/dist/{codec-CPvPq0sb.d.cts → codec-wtt6WF1K.d.ts} +4 -4
  70. package/dist/{codec-BUMjz0J4.d.cts → codec-z6_2PSzi.d.cts} +4 -4
  71. package/dist/draft10-session.cjs +2 -2
  72. package/dist/draft10-session.d.cts +6 -6
  73. package/dist/draft10-session.d.ts +6 -6
  74. package/dist/draft10-session.js +1 -1
  75. package/dist/draft10.cjs +8 -5
  76. package/dist/draft10.d.cts +14 -12
  77. package/dist/draft10.d.ts +14 -12
  78. package/dist/draft10.js +16 -13
  79. package/dist/draft11-session.cjs +2 -2
  80. package/dist/draft11-session.d.cts +6 -6
  81. package/dist/draft11-session.d.ts +6 -6
  82. package/dist/draft11-session.js +1 -1
  83. package/dist/draft11.cjs +8 -5
  84. package/dist/draft11.d.cts +14 -12
  85. package/dist/draft11.d.ts +14 -12
  86. package/dist/draft11.js +16 -13
  87. package/dist/draft12-session.cjs +2 -2
  88. package/dist/draft12-session.d.cts +6 -6
  89. package/dist/draft12-session.d.ts +6 -6
  90. package/dist/draft12-session.js +1 -1
  91. package/dist/draft12.cjs +8 -5
  92. package/dist/draft12.d.cts +16 -14
  93. package/dist/draft12.d.ts +16 -14
  94. package/dist/draft12.js +16 -13
  95. package/dist/draft13-session.cjs +2 -2
  96. package/dist/draft13-session.d.cts +6 -6
  97. package/dist/draft13-session.d.ts +6 -6
  98. package/dist/draft13-session.js +1 -1
  99. package/dist/draft13.cjs +8 -5
  100. package/dist/draft13.d.cts +16 -14
  101. package/dist/draft13.d.ts +16 -14
  102. package/dist/draft13.js +16 -13
  103. package/dist/draft14-session.cjs +2 -2
  104. package/dist/draft14-session.d.cts +6 -6
  105. package/dist/draft14-session.d.ts +6 -6
  106. package/dist/draft14-session.js +1 -1
  107. package/dist/draft14.cjs +14 -5
  108. package/dist/draft14.d.cts +14 -12
  109. package/dist/draft14.d.ts +14 -12
  110. package/dist/draft14.js +13 -4
  111. package/dist/draft15-session.cjs +2 -2
  112. package/dist/draft15-session.d.cts +6 -6
  113. package/dist/draft15-session.d.ts +6 -6
  114. package/dist/draft15-session.js +1 -1
  115. package/dist/draft15.cjs +8 -5
  116. package/dist/draft15.d.cts +14 -12
  117. package/dist/draft15.d.ts +14 -12
  118. package/dist/draft15.js +16 -13
  119. package/dist/draft16-session.cjs +2 -2
  120. package/dist/draft16-session.d.cts +6 -6
  121. package/dist/draft16-session.d.ts +6 -6
  122. package/dist/draft16-session.js +1 -1
  123. package/dist/draft16.cjs +8 -5
  124. package/dist/draft16.d.cts +14 -12
  125. package/dist/draft16.d.ts +14 -12
  126. package/dist/draft16.js +16 -13
  127. package/dist/draft17-session.cjs +2 -2
  128. package/dist/draft17-session.d.cts +7 -7
  129. package/dist/draft17-session.d.ts +7 -7
  130. package/dist/draft17-session.js +1 -1
  131. package/dist/draft17.cjs +8 -5
  132. package/dist/draft17.d.cts +11 -9
  133. package/dist/draft17.d.ts +11 -9
  134. package/dist/draft17.js +16 -13
  135. package/dist/draft7-session.cjs +2 -2
  136. package/dist/draft7-session.d.cts +6 -5
  137. package/dist/draft7-session.d.ts +6 -5
  138. package/dist/draft7-session.js +1 -1
  139. package/dist/draft7.cjs +71 -14
  140. package/dist/draft7.d.cts +53 -134
  141. package/dist/draft7.d.ts +53 -134
  142. package/dist/draft7.js +76 -19
  143. package/dist/draft8-session.cjs +2 -2
  144. package/dist/draft8-session.d.cts +6 -6
  145. package/dist/draft8-session.d.ts +6 -6
  146. package/dist/draft8-session.js +1 -1
  147. package/dist/draft8.cjs +8 -5
  148. package/dist/draft8.d.cts +14 -12
  149. package/dist/draft8.d.ts +14 -12
  150. package/dist/draft8.js +7 -4
  151. package/dist/draft9-session.cjs +2 -2
  152. package/dist/draft9-session.d.cts +6 -6
  153. package/dist/draft9-session.d.ts +6 -6
  154. package/dist/draft9-session.js +1 -1
  155. package/dist/draft9.cjs +8 -5
  156. package/dist/draft9.d.cts +14 -12
  157. package/dist/draft9.d.ts +14 -12
  158. package/dist/draft9.js +7 -4
  159. package/dist/index.cjs +52 -74
  160. package/dist/index.d.cts +41 -60
  161. package/dist/index.d.ts +41 -60
  162. package/dist/index.js +40 -62
  163. package/dist/{session-types-CJIFbTPd.d.ts → session-types-C7UONex5.d.cts} +28 -27
  164. package/dist/{session-types-Cbq8IGCP.d.cts → session-types-X-IYEGoG.d.ts} +28 -27
  165. package/dist/session.cjs +46 -13
  166. package/dist/session.d.cts +28 -9
  167. package/dist/session.d.ts +28 -9
  168. package/dist/session.js +46 -13
  169. package/dist/{types-DxSFvL2b.d.cts → types--Q7_DcvP.d.cts} +43 -39
  170. package/dist/{types-DxSFvL2b.d.ts → types--Q7_DcvP.d.ts} +43 -39
  171. package/dist/{types-B0FT5Qs2.d.ts → types-0IWbU9Z_.d.cts} +36 -36
  172. package/dist/{types-B0FT5Qs2.d.cts → types-0IWbU9Z_.d.ts} +36 -36
  173. package/dist/{types-gQsr0AAA.d.cts → types-B3g5G9KP.d.cts} +36 -36
  174. package/dist/{types-gQsr0AAA.d.ts → types-B3g5G9KP.d.ts} +36 -36
  175. package/dist/{types-Pme7sua0.d.cts → types-BFpFNM_m.d.cts} +57 -32
  176. package/dist/{types-Pme7sua0.d.ts → types-BFpFNM_m.d.ts} +57 -32
  177. package/dist/{types-8gUGeMgs.d.ts → types-Br7fm2mf.d.cts} +48 -26
  178. package/dist/{types-8gUGeMgs.d.cts → types-Br7fm2mf.d.ts} +48 -26
  179. package/dist/{types-BeAVNU-F.d.cts → types-C2qKTRR_.d.cts} +50 -45
  180. package/dist/{types-BeAVNU-F.d.ts → types-C2qKTRR_.d.ts} +50 -45
  181. package/dist/{types-DKpsfD3i.d.cts → types-DEaypVsD.d.cts} +115 -55
  182. package/dist/{types-DKpsfD3i.d.ts → types-DEaypVsD.d.ts} +115 -55
  183. package/dist/types-Dl4IaAHR.d.cts +282 -0
  184. package/dist/types-Dl4IaAHR.d.ts +282 -0
  185. package/dist/{types-CDa7F5ct.d.cts → types-DrQpTW1G.d.cts} +58 -50
  186. package/dist/{types-CDa7F5ct.d.ts → types-DrQpTW1G.d.ts} +58 -50
  187. package/dist/types-DuIO7cWK.d.cts +25 -0
  188. package/dist/types-DuIO7cWK.d.ts +25 -0
  189. package/dist/{types-B9A2g05r.d.cts → types-DuxfOYiO.d.cts} +47 -31
  190. package/dist/{types-B9A2g05r.d.ts → types-DuxfOYiO.d.ts} +47 -31
  191. package/dist/{types-D1dI_oTX.d.cts → types-jV1yDDBA.d.cts} +36 -36
  192. package/dist/{types-D1dI_oTX.d.ts → types-jV1yDDBA.d.ts} +36 -36
  193. package/package.json +192 -192
  194. package/src/core/buffer-reader.ts +130 -130
  195. package/src/core/buffer-writer.ts +100 -100
  196. package/src/core/errors.ts +1 -1
  197. package/src/core/hex.ts +21 -21
  198. package/src/core/session-types.ts +61 -59
  199. package/src/core/types.ts +18 -361
  200. package/src/drafts/draft07/codec.ts +1058 -938
  201. package/src/drafts/draft07/data-streams.ts +240 -240
  202. package/src/drafts/draft07/index.ts +124 -89
  203. package/src/drafts/draft07/messages.ts +76 -42
  204. package/src/drafts/draft07/rules.ts +107 -101
  205. package/src/drafts/draft07/session-fsm.ts +315 -287
  206. package/src/drafts/draft07/session.ts +8 -6
  207. package/src/drafts/draft07/types.ts +406 -72
  208. package/src/drafts/draft07/varint.ts +51 -51
  209. package/src/drafts/draft08/codec.ts +985 -953
  210. package/src/drafts/draft08/data-streams.ts +363 -359
  211. package/src/drafts/draft08/index.ts +10 -9
  212. package/src/drafts/draft08/messages.ts +59 -59
  213. package/src/drafts/draft08/rules.ts +73 -73
  214. package/src/drafts/draft08/session-fsm.ts +335 -303
  215. package/src/drafts/draft08/session.ts +6 -7
  216. package/src/drafts/draft08/types.ts +380 -384
  217. package/src/drafts/draft09/codec.ts +977 -945
  218. package/src/drafts/draft09/data-streams.ts +336 -332
  219. package/src/drafts/draft09/index.ts +10 -9
  220. package/src/drafts/draft09/messages.ts +59 -59
  221. package/src/drafts/draft09/rules.ts +73 -73
  222. package/src/drafts/draft09/session-fsm.ts +335 -303
  223. package/src/drafts/draft09/session.ts +6 -7
  224. package/src/drafts/draft09/types.ts +380 -384
  225. package/src/drafts/draft10/codec.ts +977 -945
  226. package/src/drafts/draft10/data-streams.ts +336 -332
  227. package/src/drafts/draft10/index.ts +10 -9
  228. package/src/drafts/draft10/messages.ts +59 -59
  229. package/src/drafts/draft10/rules.ts +73 -73
  230. package/src/drafts/draft10/session-fsm.ts +335 -303
  231. package/src/drafts/draft10/session.ts +6 -7
  232. package/src/drafts/draft10/types.ts +380 -384
  233. package/src/drafts/draft11/codec.ts +1054 -988
  234. package/src/drafts/draft11/data-streams.ts +316 -269
  235. package/src/drafts/draft11/index.ts +10 -9
  236. package/src/drafts/draft11/messages.ts +60 -59
  237. package/src/drafts/draft11/rules.ts +73 -73
  238. package/src/drafts/draft11/session-fsm.ts +359 -333
  239. package/src/drafts/draft11/session.ts +6 -7
  240. package/src/drafts/draft11/types.ts +385 -381
  241. package/src/drafts/draft12/codec.ts +1178 -1122
  242. package/src/drafts/draft12/data-streams.ts +324 -275
  243. package/src/drafts/draft12/index.ts +10 -9
  244. package/src/drafts/draft12/messages.ts +67 -66
  245. package/src/drafts/draft12/rules.ts +79 -79
  246. package/src/drafts/draft12/session-fsm.ts +384 -358
  247. package/src/drafts/draft12/session.ts +6 -7
  248. package/src/drafts/draft12/types.ts +424 -419
  249. package/src/drafts/draft13/codec.ts +1296 -1206
  250. package/src/drafts/draft13/data-streams.ts +326 -275
  251. package/src/drafts/draft13/index.ts +10 -9
  252. package/src/drafts/draft13/messages.ts +73 -68
  253. package/src/drafts/draft13/rules.ts +81 -81
  254. package/src/drafts/draft13/session-fsm.ts +392 -366
  255. package/src/drafts/draft13/session.ts +6 -7
  256. package/src/drafts/draft13/types.ts +447 -438
  257. package/src/drafts/draft14/codec.ts +1347 -1043
  258. package/src/drafts/draft14/data-streams.ts +831 -813
  259. package/src/drafts/draft14/index.ts +17 -9
  260. package/src/drafts/draft14/messages.ts +65 -65
  261. package/src/drafts/draft14/rules.ts +81 -81
  262. package/src/drafts/draft14/session-fsm.ts +415 -405
  263. package/src/drafts/draft14/session.ts +6 -7
  264. package/src/drafts/draft14/types.ts +451 -381
  265. package/src/drafts/draft15/codec.ts +1138 -978
  266. package/src/drafts/draft15/data-streams.ts +842 -794
  267. package/src/drafts/draft15/index.ts +10 -9
  268. package/src/drafts/draft15/messages.ts +54 -53
  269. package/src/drafts/draft15/rules.ts +67 -67
  270. package/src/drafts/draft15/session-fsm.ts +319 -299
  271. package/src/drafts/draft15/session.ts +6 -7
  272. package/src/drafts/draft15/types.ts +357 -339
  273. package/src/drafts/draft16/codec.ts +1242 -966
  274. package/src/drafts/draft16/data-streams.ts +835 -789
  275. package/src/drafts/draft16/index.ts +10 -9
  276. package/src/drafts/draft16/messages.ts +56 -55
  277. package/src/drafts/draft16/rules.ts +69 -69
  278. package/src/drafts/draft16/session-fsm.ts +319 -299
  279. package/src/drafts/draft16/session.ts +6 -7
  280. package/src/drafts/draft16/types.ts +386 -357
  281. package/src/drafts/draft17/codec.ts +1209 -971
  282. package/src/drafts/draft17/data-streams.ts +969 -758
  283. package/src/drafts/draft17/index.ts +9 -7
  284. package/src/drafts/draft17/messages.ts +43 -42
  285. package/src/drafts/draft17/rules.ts +58 -58
  286. package/src/drafts/draft17/session-fsm.ts +248 -216
  287. package/src/drafts/draft17/session.ts +5 -5
  288. package/src/drafts/draft17/types.ts +337 -313
  289. package/src/index.ts +85 -274
  290. package/src/session.ts +62 -23
  291. package/dist/chunk-FZDIEHQL.cjs +0 -1042
  292. package/dist/chunk-HDWJ2GDE.js +0 -24
  293. package/dist/chunk-KNDYS3DK.cjs +0 -24
  294. package/dist/chunk-N2KBF5Q2.js +0 -1042
  295. package/dist/types-QNXoxC9Y.d.cts +0 -236
  296. package/dist/types-QNXoxC9Y.d.ts +0 -236
  297. package/src/drafts/draft07/announce-fsm.ts +0 -2
  298. package/src/drafts/draft07/parameters.ts +0 -12
  299. package/src/drafts/draft07/subscription-fsm.ts +0 -3
@@ -1,1206 +1,1296 @@
1
- import { bytesToHex, hexToBytes } from "../../core/hex.js";
2
- import { BufferReader } from "../../core/buffer-reader.js";
3
- import { BufferWriter } from "../../core/buffer-writer.js";
4
- import type { BaseCodec, DecodeResult } from "../../core/types.js";
5
- import { DecodeError } from "../../core/types.js";
6
- import {
7
- MESSAGE_ID_MAP,
8
- MSG_ANNOUNCE,
9
- MSG_ANNOUNCE_CANCEL,
10
- MSG_ANNOUNCE_ERROR,
11
- MSG_ANNOUNCE_OK,
12
- MSG_CLIENT_SETUP,
13
- MSG_FETCH,
14
- MSG_FETCH_CANCEL,
15
- MSG_FETCH_ERROR,
16
- MSG_FETCH_OK,
17
- MSG_GOAWAY,
18
- MSG_MAX_REQUEST_ID,
19
- MSG_PUBLISH,
20
- MSG_PUBLISH_ERROR,
21
- MSG_PUBLISH_OK,
22
- MSG_REQUESTS_BLOCKED,
23
- MSG_SERVER_SETUP,
24
- MSG_SUBSCRIBE,
25
- MSG_SUBSCRIBE_DONE,
26
- MSG_SUBSCRIBE_ERROR,
27
- MSG_SUBSCRIBE_NAMESPACE,
28
- MSG_SUBSCRIBE_NAMESPACE_ERROR,
29
- MSG_SUBSCRIBE_NAMESPACE_OK,
30
- MSG_SUBSCRIBE_OK,
31
- MSG_SUBSCRIBE_UPDATE,
32
- MSG_TRACK_STATUS,
33
- MSG_TRACK_STATUS_ERROR,
34
- MSG_TRACK_STATUS_OK,
35
- MSG_UNANNOUNCE,
36
- MSG_UNSUBSCRIBE,
37
- MSG_UNSUBSCRIBE_NAMESPACE,
38
- PARAM_AUTHORIZATION_TOKEN,
39
- PARAM_DELIVERY_TIMEOUT,
40
- SETUP_PARAM_MAX_REQUEST_ID,
41
- SETUP_PARAM_PATH,
42
- } from "./messages.js";
43
- import type {
44
- DatagramObject,
45
- Draft13DataStream,
46
- Draft13Fetch,
47
- Draft13Message,
48
- Draft13Params,
49
- Draft13SetupParams,
50
- FetchObjectPayload,
51
- FetchStream,
52
- JoiningFetch,
53
- LargestLocation,
54
- ObjectPayload,
55
- StandaloneFetch,
56
- SubgroupStream,
57
- UnknownParam,
58
- } from "./types.js";
59
-
60
- const textEncoder = /* @__PURE__ */ new TextEncoder();
61
- const textDecoder = /* @__PURE__ */ new TextDecoder();
62
-
63
- // ─── Setup Parameter Encoding/Decoding (even/odd convention) ────────────────────
64
-
65
- function encodeSetupParams(params: Draft13SetupParams, w: BufferWriter): void {
66
- let count = 0;
67
- if (params.path !== undefined) count++;
68
- if (params.max_request_id !== undefined) count++;
69
- if (params.unknown) count += params.unknown.length;
70
-
71
- w.writeVarInt(count);
72
-
73
- if (params.path !== undefined) {
74
- w.writeVarInt(SETUP_PARAM_PATH);
75
- const encoded = textEncoder.encode(params.path);
76
- w.writeVarInt(encoded.byteLength);
77
- w.writeBytes(encoded);
78
- }
79
- if (params.max_request_id !== undefined) {
80
- w.writeVarInt(SETUP_PARAM_MAX_REQUEST_ID);
81
- w.writeVarInt(params.max_request_id);
82
- }
83
- if (params.unknown) {
84
- for (const u of params.unknown) {
85
- const id = BigInt(u.id);
86
- w.writeVarInt(id);
87
- if (id % 2n === 0n) {
88
- const raw = hexToBytes(u.raw_hex);
89
- const tmpReader = new BufferReader(raw);
90
- w.writeVarInt(tmpReader.readVarInt());
91
- } else {
92
- const raw = hexToBytes(u.raw_hex);
93
- w.writeVarInt(raw.byteLength);
94
- w.writeBytes(raw);
95
- }
96
- }
97
- }
98
- }
99
-
100
- function decodeSetupParams(r: BufferReader): Draft13SetupParams {
101
- const count = Number(r.readVarInt());
102
- const result: Draft13SetupParams = {};
103
- const unknown: UnknownParam[] = [];
104
-
105
- for (let i = 0; i < count; i++) {
106
- const paramType = r.readVarInt();
107
- if (paramType % 2n === 0n) {
108
- const value = r.readVarInt();
109
- if (paramType === SETUP_PARAM_MAX_REQUEST_ID) {
110
- result.max_request_id = value;
111
- } else {
112
- const tmpW = new BufferWriter(16);
113
- tmpW.writeVarInt(value);
114
- const raw = tmpW.finish();
115
- unknown.push({
116
- id: `0x${paramType.toString(16)}`,
117
- length: raw.byteLength,
118
- raw_hex: bytesToHex(raw),
119
- });
120
- }
121
- } else {
122
- const length = Number(r.readVarInt());
123
- const bytes = r.readBytes(length);
124
- if (paramType === SETUP_PARAM_PATH) {
125
- result.path = textDecoder.decode(bytes);
126
- } else {
127
- unknown.push({ id: `0x${paramType.toString(16)}`, length, raw_hex: bytesToHex(bytes) });
128
- }
129
- }
130
- }
131
-
132
- if (unknown.length > 0) result.unknown = unknown;
133
- return result;
134
- }
135
-
136
- // ─── Version-Specific Parameter Encoding/Decoding ───────────────────────────────
137
- // Uses even/odd convention: even type → bare varint value, odd type → length-prefixed blob
138
-
139
- function encodeParams(params: Draft13Params, w: BufferWriter): void {
140
- let count = params.unknown ? params.unknown.length : 0;
141
- if (params.authorization_token !== undefined) count++;
142
- if (params.delivery_timeout !== undefined) count++;
143
- w.writeVarInt(count);
144
-
145
- if (params.authorization_token !== undefined) {
146
- // AUTHORIZATION_TOKEN = 0x01 (odd → length-prefixed)
147
- w.writeVarInt(PARAM_AUTHORIZATION_TOKEN);
148
- const at = params.authorization_token;
149
- const tmpW = new BufferWriter(64);
150
- tmpW.writeVarInt(at.alias_type);
151
- tmpW.writeVarInt(at.token_type);
152
- const tokenBytes = textEncoder.encode(at.token_value);
153
- tmpW.writeBytes(tokenBytes);
154
- const raw = tmpW.finish();
155
- w.writeVarInt(raw.byteLength);
156
- w.writeBytes(raw);
157
- }
158
- if (params.delivery_timeout !== undefined) {
159
- // DELIVERY_TIMEOUT = 0x03 (odd → length-prefixed varint)
160
- w.writeVarInt(PARAM_DELIVERY_TIMEOUT);
161
- const tmpW = new BufferWriter(16);
162
- tmpW.writeVarInt(params.delivery_timeout);
163
- const raw = tmpW.finish();
164
- w.writeVarInt(raw.byteLength);
165
- w.writeBytes(raw);
166
- }
167
- if (params.unknown) {
168
- for (const u of params.unknown) {
169
- const id = BigInt(u.id);
170
- w.writeVarInt(id);
171
- if (id % 2n === 0n) {
172
- // Even → bare varint
173
- const raw = hexToBytes(u.raw_hex);
174
- const tmpReader = new BufferReader(raw);
175
- w.writeVarInt(tmpReader.readVarInt());
176
- } else {
177
- // Odd → length-prefixed
178
- const raw = hexToBytes(u.raw_hex);
179
- w.writeVarInt(raw.byteLength);
180
- w.writeBytes(raw);
181
- }
182
- }
183
- }
184
- }
185
-
186
- function decodeParams(r: BufferReader): Draft13Params {
187
- const count = Number(r.readVarInt());
188
- const result: Draft13Params = {};
189
- const unknown: UnknownParam[] = [];
190
-
191
- for (let i = 0; i < count; i++) {
192
- const paramType = r.readVarInt();
193
-
194
- if (paramType % 2n === 0n) {
195
- // Even type → bare varint value
196
- const value = r.readVarInt();
197
- const tmpW = new BufferWriter(16);
198
- tmpW.writeVarInt(value);
199
- const raw = tmpW.finish();
200
- unknown.push({
201
- id: `0x${paramType.toString(16)}`,
202
- length: raw.byteLength,
203
- raw_hex: bytesToHex(raw),
204
- });
205
- } else {
206
- // Odd type → length-prefixed blob
207
- const length = Number(r.readVarInt());
208
- const startOff = r.offset;
209
-
210
- if (paramType === PARAM_AUTHORIZATION_TOKEN) {
211
- const alias_type = r.readVarInt();
212
- const token_type = r.readVarInt();
213
- const tokenBytesLen = length - (r.offset - startOff);
214
- const tokenBytes = r.readBytes(tokenBytesLen);
215
- result.authorization_token = {
216
- alias_type,
217
- token_type,
218
- token_value: textDecoder.decode(tokenBytes),
219
- };
220
- } else if (paramType === PARAM_DELIVERY_TIMEOUT) {
221
- const blob = r.readBytes(length);
222
- const tmpReader = new BufferReader(blob);
223
- result.delivery_timeout = tmpReader.readVarInt();
224
- } else {
225
- const bytes = r.readBytes(length);
226
- unknown.push({ id: `0x${paramType.toString(16)}`, length, raw_hex: bytesToHex(bytes) });
227
- }
228
- }
229
- }
230
-
231
- if (unknown.length > 0) result.unknown = unknown;
232
- return result;
233
- }
234
-
235
- // ─── Payload Encoders ──────────────────────────────────────────────────────────
236
-
237
- function encodeClientSetupPayload(
238
- msg: Draft13Message & { type: "client_setup" },
239
- w: BufferWriter,
240
- ): void {
241
- w.writeVarInt(msg.supported_versions.length);
242
- for (const v of msg.supported_versions) w.writeVarInt(v);
243
- encodeSetupParams(msg.parameters, w);
244
- }
245
-
246
- function encodeServerSetupPayload(
247
- msg: Draft13Message & { type: "server_setup" },
248
- w: BufferWriter,
249
- ): void {
250
- w.writeVarInt(msg.selected_version);
251
- encodeSetupParams(msg.parameters, w);
252
- }
253
-
254
- function encodeSubscribePayload(
255
- msg: Draft13Message & { type: "subscribe" },
256
- w: BufferWriter,
257
- ): void {
258
- w.writeVarInt(msg.request_id);
259
- w.writeTuple(msg.track_namespace);
260
- w.writeString(msg.track_name);
261
- w.writeUint8(msg.subscriber_priority);
262
- w.writeVarInt(msg.group_order);
263
- w.writeVarInt(msg.forward);
264
- w.writeVarInt(msg.filter_type);
265
- const ft = Number(msg.filter_type);
266
- if (ft === 3 || ft === 4) {
267
- w.writeVarInt(msg.start_group!);
268
- w.writeVarInt(msg.start_object!);
269
- }
270
- if (ft === 4) {
271
- w.writeVarInt(msg.end_group!);
272
- }
273
- encodeParams(msg.parameters, w);
274
- }
275
-
276
- function encodeSubscribeOkPayload(
277
- msg: Draft13Message & { type: "subscribe_ok" },
278
- w: BufferWriter,
279
- ): void {
280
- w.writeVarInt(msg.request_id);
281
- w.writeVarInt(msg.track_alias);
282
- w.writeVarInt(msg.expires);
283
- w.writeVarInt(msg.group_order);
284
- w.writeVarInt(msg.content_exists);
285
- if (msg.content_exists === 1n && msg.largest_location) {
286
- w.writeVarInt(msg.largest_location.group);
287
- w.writeVarInt(msg.largest_location.object);
288
- }
289
- encodeParams(msg.parameters, w);
290
- }
291
-
292
- function encodeSubscribeErrorPayload(
293
- msg: Draft13Message & { type: "subscribe_error" },
294
- w: BufferWriter,
295
- ): void {
296
- w.writeVarInt(msg.request_id);
297
- w.writeVarInt(msg.error_code);
298
- w.writeString(msg.reason_phrase);
299
- }
300
-
301
- function encodeSubscribeUpdatePayload(
302
- msg: Draft13Message & { type: "subscribe_update" },
303
- w: BufferWriter,
304
- ): void {
305
- w.writeVarInt(msg.request_id);
306
- w.writeVarInt(msg.start_group);
307
- w.writeVarInt(msg.start_object);
308
- w.writeVarInt(msg.end_group);
309
- w.writeUint8(msg.subscriber_priority);
310
- w.writeVarInt(msg.forward);
311
- encodeParams(msg.parameters, w);
312
- }
313
-
314
- function encodeSubscribeDonePayload(
315
- msg: Draft13Message & { type: "subscribe_done" },
316
- w: BufferWriter,
317
- ): void {
318
- w.writeVarInt(msg.request_id);
319
- w.writeVarInt(msg.status_code);
320
- w.writeVarInt(msg.stream_count);
321
- w.writeString(msg.reason_phrase);
322
- }
323
-
324
- function encodeUnsubscribePayload(
325
- msg: Draft13Message & { type: "unsubscribe" },
326
- w: BufferWriter,
327
- ): void {
328
- w.writeVarInt(msg.request_id);
329
- }
330
-
331
- function encodeAnnouncePayload(msg: Draft13Message & { type: "announce" }, w: BufferWriter): void {
332
- w.writeVarInt(msg.request_id);
333
- w.writeTuple(msg.track_namespace);
334
- encodeParams(msg.parameters, w);
335
- }
336
-
337
- function encodeAnnounceOkPayload(
338
- msg: Draft13Message & { type: "announce_ok" },
339
- w: BufferWriter,
340
- ): void {
341
- w.writeVarInt(msg.request_id);
342
- }
343
-
344
- function encodeAnnounceErrorPayload(
345
- msg: Draft13Message & { type: "announce_error" },
346
- w: BufferWriter,
347
- ): void {
348
- w.writeVarInt(msg.request_id);
349
- w.writeVarInt(msg.error_code);
350
- w.writeString(msg.reason_phrase);
351
- }
352
-
353
- function encodeUnannouncePayload(
354
- msg: Draft13Message & { type: "unannounce" },
355
- w: BufferWriter,
356
- ): void {
357
- w.writeTuple(msg.track_namespace);
358
- }
359
-
360
- function encodeAnnounceCancelPayload(
361
- msg: Draft13Message & { type: "announce_cancel" },
362
- w: BufferWriter,
363
- ): void {
364
- w.writeTuple(msg.track_namespace);
365
- w.writeVarInt(msg.error_code);
366
- w.writeString(msg.reason_phrase);
367
- }
368
-
369
- // Draft-13: subscribe_namespace (renamed from subscribe_announces, same wire format)
370
- function encodeSubscribeNamespacePayload(
371
- msg: Draft13Message & { type: "subscribe_namespace" },
372
- w: BufferWriter,
373
- ): void {
374
- w.writeVarInt(msg.request_id);
375
- w.writeTuple(msg.track_namespace_prefix);
376
- encodeParams(msg.parameters, w);
377
- }
378
-
379
- function encodeSubscribeNamespaceOkPayload(
380
- msg: Draft13Message & { type: "subscribe_namespace_ok" },
381
- w: BufferWriter,
382
- ): void {
383
- w.writeVarInt(msg.request_id);
384
- }
385
-
386
- function encodeSubscribeNamespaceErrorPayload(
387
- msg: Draft13Message & { type: "subscribe_namespace_error" },
388
- w: BufferWriter,
389
- ): void {
390
- w.writeVarInt(msg.request_id);
391
- w.writeVarInt(msg.error_code);
392
- w.writeString(msg.reason_phrase);
393
- }
394
-
395
- function encodeUnsubscribeNamespacePayload(
396
- msg: Draft13Message & { type: "unsubscribe_namespace" },
397
- w: BufferWriter,
398
- ): void {
399
- w.writeTuple(msg.track_namespace_prefix);
400
- }
401
-
402
- function encodePublishPayload(msg: Draft13Message & { type: "publish" }, w: BufferWriter): void {
403
- w.writeVarInt(msg.request_id);
404
- w.writeTuple(msg.track_namespace);
405
- w.writeString(msg.track_name);
406
- w.writeVarInt(msg.track_alias);
407
- w.writeVarInt(msg.group_order);
408
- w.writeVarInt(msg.content_exists);
409
- if (msg.content_exists === 1n && msg.largest_location) {
410
- w.writeVarInt(msg.largest_location.group);
411
- w.writeVarInt(msg.largest_location.object);
412
- }
413
- w.writeVarInt(msg.forward);
414
- encodeParams(msg.parameters, w);
415
- }
416
-
417
- function encodePublishOkPayload(
418
- msg: Draft13Message & { type: "publish_ok" },
419
- w: BufferWriter,
420
- ): void {
421
- w.writeVarInt(msg.request_id);
422
- w.writeVarInt(msg.forward);
423
- w.writeUint8(msg.subscriber_priority);
424
- w.writeVarInt(msg.group_order);
425
- w.writeVarInt(msg.filter_type);
426
- const ft = Number(msg.filter_type);
427
- if (ft === 3 || ft === 4) {
428
- w.writeVarInt(msg.start_group!);
429
- w.writeVarInt(msg.start_object!);
430
- }
431
- if (ft === 4) {
432
- w.writeVarInt(msg.end_group!);
433
- }
434
- encodeParams(msg.parameters, w);
435
- }
436
-
437
- function encodePublishErrorPayload(
438
- msg: Draft13Message & { type: "publish_error" },
439
- w: BufferWriter,
440
- ): void {
441
- w.writeVarInt(msg.request_id);
442
- w.writeVarInt(msg.error_code);
443
- w.writeString(msg.reason_phrase);
444
- }
445
-
446
- function encodeFetchPayload(msg: Draft13Message & { type: "fetch" }, w: BufferWriter): void {
447
- w.writeVarInt(msg.request_id);
448
- w.writeUint8(msg.subscriber_priority);
449
- w.writeVarInt(msg.group_order);
450
- w.writeVarInt(msg.fetch_type);
451
- const ft = Number(msg.fetch_type);
452
- if (ft === 1 && msg.standalone) {
453
- w.writeTuple(msg.standalone.track_namespace);
454
- w.writeString(msg.standalone.track_name);
455
- w.writeVarInt(msg.standalone.start_group);
456
- w.writeVarInt(msg.standalone.start_object);
457
- w.writeVarInt(msg.standalone.end_group);
458
- w.writeVarInt(msg.standalone.end_object);
459
- } else if ((ft === 2 || ft === 3) && msg.joining) {
460
- w.writeVarInt(msg.joining.joining_subscribe_id);
461
- w.writeVarInt(msg.joining.joining_start);
462
- }
463
- encodeParams(msg.parameters, w);
464
- }
465
-
466
- function encodeFetchOkPayload(msg: Draft13Message & { type: "fetch_ok" }, w: BufferWriter): void {
467
- w.writeVarInt(msg.request_id);
468
- w.writeVarInt(msg.group_order);
469
- w.writeVarInt(msg.end_of_track);
470
- w.writeVarInt(msg.end_location.group);
471
- w.writeVarInt(msg.end_location.object);
472
- encodeParams(msg.parameters, w);
473
- }
474
-
475
- function encodeFetchErrorPayload(
476
- msg: Draft13Message & { type: "fetch_error" },
477
- w: BufferWriter,
478
- ): void {
479
- w.writeVarInt(msg.request_id);
480
- w.writeVarInt(msg.error_code);
481
- w.writeString(msg.reason_phrase);
482
- }
483
-
484
- function encodeFetchCancelPayload(
485
- msg: Draft13Message & { type: "fetch_cancel" },
486
- w: BufferWriter,
487
- ): void {
488
- w.writeVarInt(msg.request_id);
489
- }
490
-
491
- // Draft-13: track_status is now SUBSCRIBE-like
492
- function encodeTrackStatusPayload(
493
- msg: Draft13Message & { type: "track_status" },
494
- w: BufferWriter,
495
- ): void {
496
- w.writeVarInt(msg.request_id);
497
- w.writeTuple(msg.track_namespace);
498
- w.writeString(msg.track_name);
499
- w.writeUint8(msg.subscriber_priority);
500
- w.writeVarInt(msg.group_order);
501
- w.writeVarInt(msg.forward);
502
- w.writeVarInt(msg.filter_type);
503
- const ft = Number(msg.filter_type);
504
- if (ft === 3 || ft === 4) {
505
- w.writeVarInt(msg.start_group!);
506
- w.writeVarInt(msg.start_object!);
507
- }
508
- if (ft === 4) {
509
- w.writeVarInt(msg.end_group!);
510
- }
511
- encodeParams(msg.parameters, w);
512
- }
513
-
514
- // Draft-13: track_status_ok is SUBSCRIBE_OK-like
515
- function encodeTrackStatusOkPayload(
516
- msg: Draft13Message & { type: "track_status_ok" },
517
- w: BufferWriter,
518
- ): void {
519
- w.writeVarInt(msg.request_id);
520
- w.writeVarInt(msg.track_alias);
521
- w.writeVarInt(msg.expires);
522
- w.writeVarInt(msg.group_order);
523
- w.writeVarInt(msg.content_exists);
524
- if (msg.content_exists === 1n && msg.largest_location) {
525
- w.writeVarInt(msg.largest_location.group);
526
- w.writeVarInt(msg.largest_location.object);
527
- }
528
- encodeParams(msg.parameters, w);
529
- }
530
-
531
- // Draft-13: track_status_error
532
- function encodeTrackStatusErrorPayload(
533
- msg: Draft13Message & { type: "track_status_error" },
534
- w: BufferWriter,
535
- ): void {
536
- w.writeVarInt(msg.request_id);
537
- w.writeVarInt(msg.error_code);
538
- w.writeString(msg.reason_phrase);
539
- }
540
-
541
- function encodeGoAwayPayload(msg: Draft13Message & { type: "goaway" }, w: BufferWriter): void {
542
- w.writeString(msg.new_session_uri);
543
- }
544
-
545
- function encodeMaxRequestIdPayload(
546
- msg: Draft13Message & { type: "max_request_id" },
547
- w: BufferWriter,
548
- ): void {
549
- w.writeVarInt(msg.request_id);
550
- }
551
-
552
- function encodeRequestsBlockedPayload(
553
- msg: Draft13Message & { type: "requests_blocked" },
554
- w: BufferWriter,
555
- ): void {
556
- w.writeVarInt(msg.maximum_request_id);
557
- }
558
-
559
- // ─── Payload Decoders ──────────────────────────────────────────────────────────
560
-
561
- function decodeClientSetupPayload(r: BufferReader): Draft13Message {
562
- const numVersions = Number(r.readVarInt());
563
- if (numVersions === 0) {
564
- throw new DecodeError("CONSTRAINT_VIOLATION", "supported_versions must not be empty", r.offset);
565
- }
566
- const supported_versions: bigint[] = [];
567
- for (let i = 0; i < numVersions; i++) supported_versions.push(r.readVarInt());
568
- const parameters = decodeSetupParams(r);
569
- return { type: "client_setup", supported_versions, parameters };
570
- }
571
-
572
- function decodeServerSetupPayload(r: BufferReader): Draft13Message {
573
- const selected_version = r.readVarInt();
574
- const parameters = decodeSetupParams(r);
575
- return { type: "server_setup", selected_version, parameters };
576
- }
577
-
578
- function decodeSubscribePayload(r: BufferReader): Draft13Message {
579
- const request_id = r.readVarInt();
580
- const track_namespace = r.readTuple();
581
- const track_name = r.readString();
582
- const subscriber_priority = r.readUint8();
583
- const group_order = r.readVarInt();
584
- const forward = r.readVarInt();
585
- const filter_type = r.readVarInt();
586
- const ft = Number(filter_type);
587
- if (ft < 1 || ft > 4) {
588
- throw new DecodeError("CONSTRAINT_VIOLATION", `Invalid filter_type: ${ft}`, r.offset);
589
- }
590
- let start_group: bigint | undefined;
591
- let start_object: bigint | undefined;
592
- let end_group: bigint | undefined;
593
- if (ft === 3 || ft === 4) {
594
- start_group = r.readVarInt();
595
- start_object = r.readVarInt();
596
- }
597
- if (ft === 4) {
598
- end_group = r.readVarInt();
599
- }
600
- const parameters = decodeParams(r);
601
- const msg: Record<string, unknown> = {
602
- type: "subscribe",
603
- request_id,
604
- track_namespace,
605
- track_name,
606
- subscriber_priority,
607
- group_order,
608
- forward,
609
- filter_type,
610
- parameters,
611
- };
612
- if (start_group !== undefined) msg.start_group = start_group;
613
- if (start_object !== undefined) msg.start_object = start_object;
614
- if (end_group !== undefined) msg.end_group = end_group;
615
- return msg as unknown as Draft13Message;
616
- }
617
-
618
- function decodeSubscribeOkPayload(r: BufferReader): Draft13Message {
619
- const request_id = r.readVarInt();
620
- const track_alias = r.readVarInt();
621
- const expires = r.readVarInt();
622
- const group_order = r.readVarInt();
623
- const content_exists = r.readVarInt();
624
- let largest_location: LargestLocation | undefined;
625
- if (content_exists === 1n) {
626
- const group = r.readVarInt();
627
- const object = r.readVarInt();
628
- largest_location = { group, object };
629
- }
630
- const parameters = decodeParams(r);
631
- const msg: Record<string, unknown> = {
632
- type: "subscribe_ok",
633
- request_id,
634
- track_alias,
635
- expires,
636
- group_order,
637
- content_exists,
638
- parameters,
639
- };
640
- if (largest_location) msg.largest_location = largest_location;
641
- return msg as unknown as Draft13Message;
642
- }
643
-
644
- function decodeSubscribeErrorPayload(r: BufferReader): Draft13Message {
645
- const request_id = r.readVarInt();
646
- const error_code = r.readVarInt();
647
- const reason_phrase = r.readString();
648
- return { type: "subscribe_error", request_id, error_code, reason_phrase };
649
- }
650
-
651
- function decodeSubscribeUpdatePayload(r: BufferReader): Draft13Message {
652
- const request_id = r.readVarInt();
653
- const start_group = r.readVarInt();
654
- const start_object = r.readVarInt();
655
- const end_group = r.readVarInt();
656
- const subscriber_priority = r.readUint8();
657
- const forward = r.readVarInt();
658
- const parameters = decodeParams(r);
659
- return {
660
- type: "subscribe_update",
661
- request_id,
662
- start_group,
663
- start_object,
664
- end_group,
665
- subscriber_priority,
666
- forward,
667
- parameters,
668
- };
669
- }
670
-
671
- function decodeSubscribeDonePayload(r: BufferReader): Draft13Message {
672
- const request_id = r.readVarInt();
673
- const status_code = r.readVarInt();
674
- const stream_count = r.readVarInt();
675
- const reason_phrase = r.readString();
676
- return { type: "subscribe_done", request_id, status_code, stream_count, reason_phrase };
677
- }
678
-
679
- function decodeUnsubscribePayload(r: BufferReader): Draft13Message {
680
- const request_id = r.readVarInt();
681
- return { type: "unsubscribe", request_id };
682
- }
683
-
684
- function decodeAnnouncePayload(r: BufferReader): Draft13Message {
685
- const request_id = r.readVarInt();
686
- const track_namespace = r.readTuple();
687
- const parameters = decodeParams(r);
688
- return { type: "announce", request_id, track_namespace, parameters };
689
- }
690
-
691
- function decodeAnnounceOkPayload(r: BufferReader): Draft13Message {
692
- const request_id = r.readVarInt();
693
- return { type: "announce_ok", request_id };
694
- }
695
-
696
- function decodeAnnounceErrorPayload(r: BufferReader): Draft13Message {
697
- const request_id = r.readVarInt();
698
- const error_code = r.readVarInt();
699
- const reason_phrase = r.readString();
700
- return { type: "announce_error", request_id, error_code, reason_phrase };
701
- }
702
-
703
- function decodeUnannouncePayload(r: BufferReader): Draft13Message {
704
- const track_namespace = r.readTuple();
705
- return { type: "unannounce", track_namespace };
706
- }
707
-
708
- function decodeAnnounceCancelPayload(r: BufferReader): Draft13Message {
709
- const track_namespace = r.readTuple();
710
- const error_code = r.readVarInt();
711
- const reason_phrase = r.readString();
712
- return { type: "announce_cancel", track_namespace, error_code, reason_phrase };
713
- }
714
-
715
- // Draft-13: subscribe_namespace (renamed from subscribe_announces)
716
- function decodeSubscribeNamespacePayload(r: BufferReader): Draft13Message {
717
- const request_id = r.readVarInt();
718
- const track_namespace_prefix = r.readTuple();
719
- const parameters = decodeParams(r);
720
- return { type: "subscribe_namespace", request_id, track_namespace_prefix, parameters };
721
- }
722
-
723
- function decodeSubscribeNamespaceOkPayload(r: BufferReader): Draft13Message {
724
- const request_id = r.readVarInt();
725
- return { type: "subscribe_namespace_ok", request_id };
726
- }
727
-
728
- function decodeSubscribeNamespaceErrorPayload(r: BufferReader): Draft13Message {
729
- const request_id = r.readVarInt();
730
- const error_code = r.readVarInt();
731
- const reason_phrase = r.readString();
732
- return { type: "subscribe_namespace_error", request_id, error_code, reason_phrase };
733
- }
734
-
735
- function decodeUnsubscribeNamespacePayload(r: BufferReader): Draft13Message {
736
- const track_namespace_prefix = r.readTuple();
737
- return { type: "unsubscribe_namespace", track_namespace_prefix };
738
- }
739
-
740
- function decodePublishPayload(r: BufferReader): Draft13Message {
741
- const request_id = r.readVarInt();
742
- const track_namespace = r.readTuple();
743
- const track_name = r.readString();
744
- const track_alias = r.readVarInt();
745
- const group_order = r.readVarInt();
746
- const content_exists = r.readVarInt();
747
- let largest_location: LargestLocation | undefined;
748
- if (content_exists === 1n) {
749
- const group = r.readVarInt();
750
- const object = r.readVarInt();
751
- largest_location = { group, object };
752
- }
753
- const forward = r.readVarInt();
754
- const parameters = decodeParams(r);
755
- const msg: Record<string, unknown> = {
756
- type: "publish",
757
- request_id,
758
- track_namespace,
759
- track_name,
760
- track_alias,
761
- group_order,
762
- content_exists,
763
- forward,
764
- parameters,
765
- };
766
- if (largest_location) msg.largest_location = largest_location;
767
- return msg as unknown as Draft13Message;
768
- }
769
-
770
- function decodePublishOkPayload(r: BufferReader): Draft13Message {
771
- const request_id = r.readVarInt();
772
- const forward = r.readVarInt();
773
- const subscriber_priority = r.readUint8();
774
- const group_order = r.readVarInt();
775
- const filter_type = r.readVarInt();
776
- const ft = Number(filter_type);
777
- let start_group: bigint | undefined;
778
- let start_object: bigint | undefined;
779
- let end_group: bigint | undefined;
780
- if (ft === 3 || ft === 4) {
781
- start_group = r.readVarInt();
782
- start_object = r.readVarInt();
783
- }
784
- if (ft === 4) {
785
- end_group = r.readVarInt();
786
- }
787
- const parameters = decodeParams(r);
788
- const msg: Record<string, unknown> = {
789
- type: "publish_ok",
790
- request_id,
791
- forward,
792
- subscriber_priority,
793
- group_order,
794
- filter_type,
795
- parameters,
796
- };
797
- if (start_group !== undefined) msg.start_group = start_group;
798
- if (start_object !== undefined) msg.start_object = start_object;
799
- if (end_group !== undefined) msg.end_group = end_group;
800
- return msg as unknown as Draft13Message;
801
- }
802
-
803
- function decodePublishErrorPayload(r: BufferReader): Draft13Message {
804
- const request_id = r.readVarInt();
805
- const error_code = r.readVarInt();
806
- const reason_phrase = r.readString();
807
- return { type: "publish_error", request_id, error_code, reason_phrase };
808
- }
809
-
810
- function decodeFetchPayload(r: BufferReader): Draft13Message {
811
- const request_id = r.readVarInt();
812
- const subscriber_priority = r.readUint8();
813
- const group_order = r.readVarInt();
814
- const fetch_type = r.readVarInt();
815
- const ft = Number(fetch_type);
816
-
817
- if (ft < 1 || ft > 3) {
818
- throw new DecodeError("CONSTRAINT_VIOLATION", `Invalid fetch_type: ${ft}`, r.offset);
819
- }
820
-
821
- let standalone: StandaloneFetch | undefined;
822
- let joining: JoiningFetch | undefined;
823
-
824
- if (ft === 1) {
825
- const track_namespace = r.readTuple();
826
- const track_name = r.readString();
827
- const start_group = r.readVarInt();
828
- const start_object = r.readVarInt();
829
- const end_group = r.readVarInt();
830
- const end_object = r.readVarInt();
831
- standalone = { track_namespace, track_name, start_group, start_object, end_group, end_object };
832
- } else {
833
- const joining_subscribe_id = r.readVarInt();
834
- const joining_start = r.readVarInt();
835
- joining = { joining_subscribe_id, joining_start };
836
- }
837
-
838
- const parameters = decodeParams(r);
839
- return {
840
- type: "fetch",
841
- request_id,
842
- subscriber_priority,
843
- group_order,
844
- fetch_type,
845
- standalone,
846
- joining,
847
- parameters,
848
- } as Draft13Fetch;
849
- }
850
-
851
- function decodeFetchOkPayload(r: BufferReader): Draft13Message {
852
- const request_id = r.readVarInt();
853
- const group_order = r.readVarInt();
854
- const end_of_track = r.readVarInt();
855
- const group = r.readVarInt();
856
- const object = r.readVarInt();
857
- const parameters = decodeParams(r);
858
- return {
859
- type: "fetch_ok",
860
- request_id,
861
- group_order,
862
- end_of_track,
863
- end_location: { group, object },
864
- parameters,
865
- };
866
- }
867
-
868
- function decodeFetchErrorPayload(r: BufferReader): Draft13Message {
869
- const request_id = r.readVarInt();
870
- const error_code = r.readVarInt();
871
- const reason_phrase = r.readString();
872
- return { type: "fetch_error", request_id, error_code, reason_phrase };
873
- }
874
-
875
- function decodeFetchCancelPayload(r: BufferReader): Draft13Message {
876
- const request_id = r.readVarInt();
877
- return { type: "fetch_cancel", request_id };
878
- }
879
-
880
- // Draft-13: track_status is SUBSCRIBE-like
881
- function decodeTrackStatusPayload(r: BufferReader): Draft13Message {
882
- const request_id = r.readVarInt();
883
- const track_namespace = r.readTuple();
884
- const track_name = r.readString();
885
- const subscriber_priority = r.readUint8();
886
- const group_order = r.readVarInt();
887
- const forward = r.readVarInt();
888
- const filter_type = r.readVarInt();
889
- const ft = Number(filter_type);
890
- if (ft < 1 || ft > 4) {
891
- throw new DecodeError("CONSTRAINT_VIOLATION", `Invalid filter_type: ${ft}`, r.offset);
892
- }
893
- let start_group: bigint | undefined;
894
- let start_object: bigint | undefined;
895
- let end_group: bigint | undefined;
896
- if (ft === 3 || ft === 4) {
897
- start_group = r.readVarInt();
898
- start_object = r.readVarInt();
899
- }
900
- if (ft === 4) {
901
- end_group = r.readVarInt();
902
- }
903
- const parameters = decodeParams(r);
904
- const msg: Record<string, unknown> = {
905
- type: "track_status",
906
- request_id,
907
- track_namespace,
908
- track_name,
909
- subscriber_priority,
910
- group_order,
911
- forward,
912
- filter_type,
913
- parameters,
914
- };
915
- if (start_group !== undefined) msg.start_group = start_group;
916
- if (start_object !== undefined) msg.start_object = start_object;
917
- if (end_group !== undefined) msg.end_group = end_group;
918
- return msg as unknown as Draft13Message;
919
- }
920
-
921
- // Draft-13: track_status_ok is SUBSCRIBE_OK-like
922
- function decodeTrackStatusOkPayload(r: BufferReader): Draft13Message {
923
- const request_id = r.readVarInt();
924
- const track_alias = r.readVarInt();
925
- const expires = r.readVarInt();
926
- const group_order = r.readVarInt();
927
- const content_exists = r.readVarInt();
928
- let largest_location: LargestLocation | undefined;
929
- if (content_exists === 1n) {
930
- const group = r.readVarInt();
931
- const object = r.readVarInt();
932
- largest_location = { group, object };
933
- }
934
- const parameters = decodeParams(r);
935
- const msg: Record<string, unknown> = {
936
- type: "track_status_ok",
937
- request_id,
938
- track_alias,
939
- expires,
940
- group_order,
941
- content_exists,
942
- parameters,
943
- };
944
- if (largest_location) msg.largest_location = largest_location;
945
- return msg as unknown as Draft13Message;
946
- }
947
-
948
- // Draft-13: track_status_error
949
- function decodeTrackStatusErrorPayload(r: BufferReader): Draft13Message {
950
- const request_id = r.readVarInt();
951
- const error_code = r.readVarInt();
952
- const reason_phrase = r.readString();
953
- return { type: "track_status_error", request_id, error_code, reason_phrase };
954
- }
955
-
956
- function decodeGoAwayPayload(r: BufferReader): Draft13Message {
957
- const new_session_uri = r.readString();
958
- return { type: "goaway", new_session_uri };
959
- }
960
-
961
- function decodeMaxRequestIdPayload(r: BufferReader): Draft13Message {
962
- const request_id = r.readVarInt();
963
- return { type: "max_request_id", request_id };
964
- }
965
-
966
- function decodeRequestsBlockedPayload(r: BufferReader): Draft13Message {
967
- const maximum_request_id = r.readVarInt();
968
- return { type: "requests_blocked", maximum_request_id };
969
- }
970
-
971
- // ─── Payload dispatch tables ───────────────────────────────────────────────────
972
-
973
- const payloadDecoders: ReadonlyMap<bigint, (r: BufferReader) => Draft13Message> = new Map([
974
- [MSG_CLIENT_SETUP, decodeClientSetupPayload],
975
- [MSG_SERVER_SETUP, decodeServerSetupPayload],
976
- [MSG_SUBSCRIBE, decodeSubscribePayload],
977
- [MSG_SUBSCRIBE_OK, decodeSubscribeOkPayload],
978
- [MSG_SUBSCRIBE_ERROR, decodeSubscribeErrorPayload],
979
- [MSG_SUBSCRIBE_UPDATE, decodeSubscribeUpdatePayload],
980
- [MSG_SUBSCRIBE_DONE, decodeSubscribeDonePayload],
981
- [MSG_UNSUBSCRIBE, decodeUnsubscribePayload],
982
- [MSG_ANNOUNCE, decodeAnnouncePayload],
983
- [MSG_ANNOUNCE_OK, decodeAnnounceOkPayload],
984
- [MSG_ANNOUNCE_ERROR, decodeAnnounceErrorPayload],
985
- [MSG_UNANNOUNCE, decodeUnannouncePayload],
986
- [MSG_ANNOUNCE_CANCEL, decodeAnnounceCancelPayload],
987
- [MSG_SUBSCRIBE_NAMESPACE, decodeSubscribeNamespacePayload],
988
- [MSG_SUBSCRIBE_NAMESPACE_OK, decodeSubscribeNamespaceOkPayload],
989
- [MSG_SUBSCRIBE_NAMESPACE_ERROR, decodeSubscribeNamespaceErrorPayload],
990
- [MSG_UNSUBSCRIBE_NAMESPACE, decodeUnsubscribeNamespacePayload],
991
- [MSG_PUBLISH, decodePublishPayload],
992
- [MSG_PUBLISH_OK, decodePublishOkPayload],
993
- [MSG_PUBLISH_ERROR, decodePublishErrorPayload],
994
- [MSG_FETCH, decodeFetchPayload],
995
- [MSG_FETCH_OK, decodeFetchOkPayload],
996
- [MSG_FETCH_ERROR, decodeFetchErrorPayload],
997
- [MSG_FETCH_CANCEL, decodeFetchCancelPayload],
998
- [MSG_TRACK_STATUS, decodeTrackStatusPayload],
999
- [MSG_TRACK_STATUS_OK, decodeTrackStatusOkPayload],
1000
- [MSG_TRACK_STATUS_ERROR, decodeTrackStatusErrorPayload],
1001
- [MSG_GOAWAY, decodeGoAwayPayload],
1002
- [MSG_MAX_REQUEST_ID, decodeMaxRequestIdPayload],
1003
- [MSG_REQUESTS_BLOCKED, decodeRequestsBlockedPayload],
1004
- ]);
1005
-
1006
- // ─── Public API ────────────────────────────────────────────────────────────────
1007
-
1008
- /**
1009
- * Encode a draft-13 control message.
1010
- * Most messages use type(varint) + length(uint16 BE) + payload.
1011
- * PUBLISH family uses type(varint) + length(varint) + payload.
1012
- */
1013
- export function encodeMessage(message: Draft13Message): Uint8Array {
1014
- const typeId = MESSAGE_ID_MAP.get(message.type);
1015
- if (typeId === undefined) throw new Error(`Unknown message type: ${message.type}`);
1016
-
1017
- const payloadWriter = new BufferWriter();
1018
- encodePayload(message, payloadWriter);
1019
- const payload = payloadWriter.finishView();
1020
-
1021
- const writer = new BufferWriter(payload.byteLength + 16);
1022
- writer.writeVarInt(typeId);
1023
-
1024
- if (payload.byteLength > 0xffff) {
1025
- throw new Error(`Payload too large for 16-bit length: ${payload.byteLength}`);
1026
- }
1027
- writer.writeUint8((payload.byteLength >> 8) & 0xff);
1028
- writer.writeUint8(payload.byteLength & 0xff);
1029
-
1030
- writer.writeBytes(payload);
1031
- return writer.finish();
1032
- }
1033
-
1034
- function encodePayload(msg: Draft13Message, w: BufferWriter): void {
1035
- switch (msg.type) {
1036
- case "client_setup":
1037
- return encodeClientSetupPayload(msg, w);
1038
- case "server_setup":
1039
- return encodeServerSetupPayload(msg, w);
1040
- case "subscribe":
1041
- return encodeSubscribePayload(msg, w);
1042
- case "subscribe_ok":
1043
- return encodeSubscribeOkPayload(msg, w);
1044
- case "subscribe_error":
1045
- return encodeSubscribeErrorPayload(msg, w);
1046
- case "subscribe_update":
1047
- return encodeSubscribeUpdatePayload(msg, w);
1048
- case "subscribe_done":
1049
- return encodeSubscribeDonePayload(msg, w);
1050
- case "unsubscribe":
1051
- return encodeUnsubscribePayload(msg, w);
1052
- case "announce":
1053
- return encodeAnnouncePayload(msg, w);
1054
- case "announce_ok":
1055
- return encodeAnnounceOkPayload(msg, w);
1056
- case "announce_error":
1057
- return encodeAnnounceErrorPayload(msg, w);
1058
- case "unannounce":
1059
- return encodeUnannouncePayload(msg, w);
1060
- case "announce_cancel":
1061
- return encodeAnnounceCancelPayload(msg, w);
1062
- case "subscribe_namespace":
1063
- return encodeSubscribeNamespacePayload(msg, w);
1064
- case "subscribe_namespace_ok":
1065
- return encodeSubscribeNamespaceOkPayload(msg, w);
1066
- case "subscribe_namespace_error":
1067
- return encodeSubscribeNamespaceErrorPayload(msg, w);
1068
- case "unsubscribe_namespace":
1069
- return encodeUnsubscribeNamespacePayload(msg, w);
1070
- case "publish":
1071
- return encodePublishPayload(msg, w);
1072
- case "publish_ok":
1073
- return encodePublishOkPayload(msg, w);
1074
- case "publish_error":
1075
- return encodePublishErrorPayload(msg, w);
1076
- case "fetch":
1077
- return encodeFetchPayload(msg, w);
1078
- case "fetch_ok":
1079
- return encodeFetchOkPayload(msg, w);
1080
- case "fetch_error":
1081
- return encodeFetchErrorPayload(msg, w);
1082
- case "fetch_cancel":
1083
- return encodeFetchCancelPayload(msg, w);
1084
- case "track_status":
1085
- return encodeTrackStatusPayload(msg, w);
1086
- case "track_status_ok":
1087
- return encodeTrackStatusOkPayload(msg, w);
1088
- case "track_status_error":
1089
- return encodeTrackStatusErrorPayload(msg, w);
1090
- case "goaway":
1091
- return encodeGoAwayPayload(msg, w);
1092
- case "max_request_id":
1093
- return encodeMaxRequestIdPayload(msg, w);
1094
- case "requests_blocked":
1095
- return encodeRequestsBlockedPayload(msg, w);
1096
- default: {
1097
- const _exhaustive: never = msg;
1098
- throw new Error(`Unhandled message type: ${(_exhaustive as Draft13Message).type}`);
1099
- }
1100
- }
1101
- }
1102
-
1103
- /**
1104
- * Decode a draft-13 control message from bytes.
1105
- */
1106
- export function decodeMessage(bytes: Uint8Array): DecodeResult<Draft13Message> {
1107
- try {
1108
- const reader = new BufferReader(bytes);
1109
- const typeId = reader.readVarInt();
1110
-
1111
- // Read 16-bit big-endian payload length
1112
- const lenHi = reader.readUint8();
1113
- const lenLo = reader.readUint8();
1114
- const payloadLength = (lenHi << 8) | lenLo;
1115
-
1116
- const payloadBytes = reader.readBytes(payloadLength);
1117
- const payloadReader = new BufferReader(payloadBytes);
1118
-
1119
- const decoder = payloadDecoders.get(typeId);
1120
- if (!decoder) {
1121
- return {
1122
- ok: false,
1123
- error: new DecodeError(
1124
- "UNKNOWN_MESSAGE_TYPE",
1125
- `Unknown message type ID: 0x${typeId.toString(16)}`,
1126
- 0,
1127
- ),
1128
- };
1129
- }
1130
-
1131
- const message = decoder(payloadReader);
1132
- return { ok: true, value: message, bytesRead: reader.offset };
1133
- } catch (e) {
1134
- if (e instanceof DecodeError) return { ok: false, error: e };
1135
- throw e;
1136
- }
1137
- }
1138
-
1139
- // ─── Data Stream Encoding/Decoding ─────────────────────────────────────────────
1140
-
1141
- export { encodeSubgroupStream, encodeDatagram, encodeFetchStream, decodeSubgroupStream, decodeDatagram, decodeFetchStream, decodeDataStream } from "./data-streams.js";
1142
- import { encodeSubgroupStream, encodeDatagram, encodeFetchStream, decodeSubgroupStream, decodeDatagram, decodeFetchStream, decodeDataStream } from "./data-streams.js";
1143
-
1144
- export function createStreamDecoder(): TransformStream<Uint8Array, Draft13Message> {
1145
- let buffer = new Uint8Array(0);
1146
- let offset = 0;
1147
- return new TransformStream<Uint8Array, Draft13Message>({
1148
- transform(chunk, controller) {
1149
- if (offset > 0) {
1150
- buffer = buffer.subarray(offset);
1151
- offset = 0;
1152
- }
1153
- const newBuffer = new Uint8Array(buffer.length + chunk.length);
1154
- newBuffer.set(buffer, 0);
1155
- newBuffer.set(chunk, buffer.length);
1156
- buffer = newBuffer;
1157
- while (offset < buffer.length) {
1158
- const result = decodeMessage(buffer.subarray(offset));
1159
- if (!result.ok) {
1160
- if (result.error.code === "UNEXPECTED_END") break;
1161
- controller.error(result.error);
1162
- return;
1163
- }
1164
- controller.enqueue(result.value);
1165
- offset += result.bytesRead;
1166
- }
1167
- },
1168
- flush(controller) {
1169
- if (offset < buffer.length)
1170
- controller.error(new DecodeError("UNEXPECTED_END", "Stream ended with incomplete data", 0));
1171
- },
1172
- });
1173
- }
1174
-
1175
- // ─── Codec Factory ─────────────────────────────────────────────────────────────
1176
-
1177
- export interface Draft13Codec extends BaseCodec<Draft13Message> {
1178
- readonly draft: "draft-ietf-moq-transport-13";
1179
- encodeSubgroupStream(stream: SubgroupStream): Uint8Array;
1180
- encodeDatagram(dg: DatagramObject): Uint8Array;
1181
- encodeFetchStream(stream: FetchStream): Uint8Array;
1182
- decodeSubgroupStream(bytes: Uint8Array): DecodeResult<SubgroupStream>;
1183
- decodeDatagram(bytes: Uint8Array): DecodeResult<DatagramObject>;
1184
- decodeFetchStream(bytes: Uint8Array): DecodeResult<FetchStream>;
1185
- decodeDataStream(
1186
- streamType: "subgroup" | "datagram" | "fetch",
1187
- bytes: Uint8Array,
1188
- ): DecodeResult<Draft13DataStream>;
1189
- createStreamDecoder(): TransformStream<Uint8Array, Draft13Message>;
1190
- }
1191
-
1192
- export function createDraft13Codec(): Draft13Codec {
1193
- return {
1194
- draft: "draft-ietf-moq-transport-13",
1195
- encodeMessage,
1196
- decodeMessage,
1197
- encodeSubgroupStream,
1198
- encodeDatagram,
1199
- encodeFetchStream,
1200
- decodeSubgroupStream,
1201
- decodeDatagram,
1202
- decodeFetchStream,
1203
- decodeDataStream,
1204
- createStreamDecoder,
1205
- };
1206
- }
1
+ import { BufferReader } from '../../core/buffer-reader.js'
2
+ import { BufferWriter } from '../../core/buffer-writer.js'
3
+ import { bytesToHex, hexToBytes } from '../../core/hex.js'
4
+ import type { BaseCodec, DecodeResult } from '../../core/types.js'
5
+ import { DecodeError } from '../../core/types.js'
6
+ import {
7
+ decodeDatagram,
8
+ decodeDataStream,
9
+ decodeFetchStream,
10
+ decodeSubgroupStream,
11
+ encodeDatagram,
12
+ encodeFetchStream,
13
+ encodeSubgroupStream,
14
+ } from './data-streams.js'
15
+ import {
16
+ MESSAGE_ID_MAP,
17
+ MSG_ANNOUNCE,
18
+ MSG_ANNOUNCE_CANCEL,
19
+ MSG_ANNOUNCE_ERROR,
20
+ MSG_ANNOUNCE_OK,
21
+ MSG_CLIENT_SETUP,
22
+ MSG_FETCH,
23
+ MSG_FETCH_CANCEL,
24
+ MSG_FETCH_ERROR,
25
+ MSG_FETCH_OK,
26
+ MSG_GOAWAY,
27
+ MSG_MAX_REQUEST_ID,
28
+ MSG_PUBLISH,
29
+ MSG_PUBLISH_ERROR,
30
+ MSG_PUBLISH_OK,
31
+ MSG_REQUESTS_BLOCKED,
32
+ MSG_SERVER_SETUP,
33
+ MSG_SUBSCRIBE,
34
+ MSG_SUBSCRIBE_DONE,
35
+ MSG_SUBSCRIBE_ERROR,
36
+ MSG_SUBSCRIBE_NAMESPACE,
37
+ MSG_SUBSCRIBE_NAMESPACE_ERROR,
38
+ MSG_SUBSCRIBE_NAMESPACE_OK,
39
+ MSG_SUBSCRIBE_OK,
40
+ MSG_SUBSCRIBE_UPDATE,
41
+ MSG_TRACK_STATUS,
42
+ MSG_TRACK_STATUS_ERROR,
43
+ MSG_TRACK_STATUS_OK,
44
+ MSG_UNANNOUNCE,
45
+ MSG_UNSUBSCRIBE,
46
+ MSG_UNSUBSCRIBE_NAMESPACE,
47
+ PARAM_AUTHORIZATION_TOKEN,
48
+ PARAM_DELIVERY_TIMEOUT,
49
+ PARAM_MAX_CACHE_DURATION,
50
+ SETUP_PARAM_AUTHORIZATION_TOKEN,
51
+ SETUP_PARAM_MAX_AUTH_TOKEN_CACHE_SIZE,
52
+ SETUP_PARAM_MAX_REQUEST_ID,
53
+ SETUP_PARAM_PATH,
54
+ } from './messages.js'
55
+ import type {
56
+ AuthorizationToken,
57
+ DatagramObject,
58
+ Draft13DataStream,
59
+ Draft13Fetch,
60
+ Draft13Message,
61
+ Draft13Params,
62
+ Draft13SetupParams,
63
+ FetchStream,
64
+ JoiningFetch,
65
+ LargestLocation,
66
+ StandaloneFetch,
67
+ SubgroupStream,
68
+ UnknownParam,
69
+ } from './types.js'
70
+
71
+ const textEncoder = /* @__PURE__ */ new TextEncoder()
72
+ const textDecoder = /* @__PURE__ */ new TextDecoder()
73
+
74
+ // ─── Authorization Token Encoding/Decoding ─────────────────────────────────────
75
+ // alias_type: 0=DELETE, 1=REGISTER, 2=USE_ALIAS, 3=USE_VALUE
76
+
77
+ function encodeAuthorizationToken(at: AuthorizationToken, w: BufferWriter): void {
78
+ const tmpW = new BufferWriter(64)
79
+ tmpW.writeVarInt(at.alias_type)
80
+ const aliasType = Number(at.alias_type)
81
+ if (aliasType === 0 || aliasType === 1 || aliasType === 2) {
82
+ // DELETE, REGISTER, USE_ALIAS have token_alias
83
+ tmpW.writeVarInt(at.token_alias ?? 0n)
84
+ }
85
+ if (aliasType === 1 || aliasType === 3) {
86
+ // REGISTER, USE_VALUE have token_type + token_value
87
+ tmpW.writeVarInt(at.token_type ?? 0n)
88
+ const tokenBytes = textEncoder.encode(at.token_value ?? '')
89
+ tmpW.writeBytes(tokenBytes)
90
+ }
91
+ const raw = tmpW.finish()
92
+ w.writeVarInt(raw.byteLength)
93
+ w.writeBytes(raw)
94
+ }
95
+
96
+ function decodeAuthorizationToken(r: BufferReader, length: number): AuthorizationToken {
97
+ const startOff = r.offset
98
+ const alias_type = r.readVarInt()
99
+ const aliasType = Number(alias_type)
100
+ const result: Record<string, unknown> = { alias_type }
101
+ if (aliasType === 0 || aliasType === 1 || aliasType === 2) {
102
+ result.token_alias = r.readVarInt()
103
+ }
104
+ if (aliasType === 1 || aliasType === 3) {
105
+ result.token_type = r.readVarInt()
106
+ const tokenBytesLen = length - (r.offset - startOff)
107
+ const tokenBytes = r.readBytes(tokenBytesLen)
108
+ result.token_value = textDecoder.decode(tokenBytes)
109
+ }
110
+ return result as unknown as AuthorizationToken
111
+ }
112
+
113
+ // ─── Setup Parameter Encoding/Decoding (even/odd convention) ────────────────────
114
+
115
+ function encodeSetupParams(params: Draft13SetupParams, w: BufferWriter): void {
116
+ let count = 0
117
+ if (params.path !== undefined) count++
118
+ if (params.max_request_id !== undefined) count++
119
+ if (params.authorization_token !== undefined) count++
120
+ if (params.max_auth_token_cache_size !== undefined) count++
121
+ if (params.unknown) count += params.unknown.length
122
+
123
+ w.writeVarInt(count)
124
+
125
+ if (params.path !== undefined) {
126
+ w.writeVarInt(SETUP_PARAM_PATH)
127
+ const encoded = textEncoder.encode(params.path)
128
+ w.writeVarInt(encoded.byteLength)
129
+ w.writeBytes(encoded)
130
+ }
131
+ if (params.max_request_id !== undefined) {
132
+ w.writeVarInt(SETUP_PARAM_MAX_REQUEST_ID)
133
+ w.writeVarInt(params.max_request_id)
134
+ }
135
+ if (params.authorization_token !== undefined) {
136
+ w.writeVarInt(SETUP_PARAM_AUTHORIZATION_TOKEN)
137
+ encodeAuthorizationToken(params.authorization_token, w)
138
+ }
139
+ if (params.max_auth_token_cache_size !== undefined) {
140
+ w.writeVarInt(SETUP_PARAM_MAX_AUTH_TOKEN_CACHE_SIZE)
141
+ w.writeVarInt(params.max_auth_token_cache_size)
142
+ }
143
+ if (params.unknown) {
144
+ for (const u of params.unknown) {
145
+ const id = BigInt(u.id)
146
+ w.writeVarInt(id)
147
+ if (id % 2n === 0n) {
148
+ const raw = hexToBytes(u.raw_hex)
149
+ const tmpReader = new BufferReader(raw)
150
+ w.writeVarInt(tmpReader.readVarInt())
151
+ } else {
152
+ const raw = hexToBytes(u.raw_hex)
153
+ w.writeVarInt(raw.byteLength)
154
+ w.writeBytes(raw)
155
+ }
156
+ }
157
+ }
158
+ }
159
+
160
+ function decodeSetupParams(r: BufferReader): Draft13SetupParams {
161
+ const count = Number(r.readVarInt())
162
+ const result: Draft13SetupParams = {}
163
+ const unknown: UnknownParam[] = []
164
+
165
+ for (let i = 0; i < count; i++) {
166
+ const paramType = r.readVarInt()
167
+ if (paramType % 2n === 0n) {
168
+ const value = r.readVarInt()
169
+ if (paramType === SETUP_PARAM_MAX_REQUEST_ID) {
170
+ result.max_request_id = value
171
+ } else if (paramType === SETUP_PARAM_MAX_AUTH_TOKEN_CACHE_SIZE) {
172
+ result.max_auth_token_cache_size = value
173
+ } else {
174
+ const tmpW = new BufferWriter(16)
175
+ tmpW.writeVarInt(value)
176
+ const raw = tmpW.finish()
177
+ unknown.push({
178
+ id: `0x${paramType.toString(16)}`,
179
+ length: raw.byteLength,
180
+ raw_hex: bytesToHex(raw),
181
+ })
182
+ }
183
+ } else {
184
+ const length = Number(r.readVarInt())
185
+ if (paramType === SETUP_PARAM_PATH) {
186
+ const bytes = r.readBytes(length)
187
+ result.path = textDecoder.decode(bytes)
188
+ } else if (paramType === SETUP_PARAM_AUTHORIZATION_TOKEN) {
189
+ result.authorization_token = decodeAuthorizationToken(r, length)
190
+ } else {
191
+ const bytes = r.readBytes(length)
192
+ unknown.push({
193
+ id: `0x${paramType.toString(16)}`,
194
+ length,
195
+ raw_hex: bytesToHex(bytes),
196
+ })
197
+ }
198
+ }
199
+ }
200
+
201
+ if (unknown.length > 0) result.unknown = unknown
202
+ return result
203
+ }
204
+
205
+ // ─── Version-Specific Parameter Encoding/Decoding ───────────────────────────────
206
+ // Uses even/odd convention: even type → bare varint value, odd type → length-prefixed blob
207
+
208
+ function encodeParams(params: Draft13Params, w: BufferWriter): void {
209
+ let count = params.unknown ? params.unknown.length : 0
210
+ if (params.delivery_timeout !== undefined) count++
211
+ if (params.authorization_token !== undefined) count++
212
+ if (params.max_cache_duration !== undefined) count++
213
+ w.writeVarInt(count)
214
+
215
+ if (params.delivery_timeout !== undefined) {
216
+ // DELIVERY_TIMEOUT = 0x02 (even → bare varint)
217
+ w.writeVarInt(PARAM_DELIVERY_TIMEOUT)
218
+ w.writeVarInt(params.delivery_timeout)
219
+ }
220
+ if (params.authorization_token !== undefined) {
221
+ // AUTHORIZATION_TOKEN = 0x03 (odd → length-prefixed)
222
+ w.writeVarInt(PARAM_AUTHORIZATION_TOKEN)
223
+ encodeAuthorizationToken(params.authorization_token, w)
224
+ }
225
+ if (params.max_cache_duration !== undefined) {
226
+ // MAX_CACHE_DURATION = 0x04 (even bare varint)
227
+ w.writeVarInt(PARAM_MAX_CACHE_DURATION)
228
+ w.writeVarInt(params.max_cache_duration)
229
+ }
230
+ if (params.unknown) {
231
+ for (const u of params.unknown) {
232
+ const id = BigInt(u.id)
233
+ w.writeVarInt(id)
234
+ if (id % 2n === 0n) {
235
+ // Even bare varint
236
+ const raw = hexToBytes(u.raw_hex)
237
+ const tmpReader = new BufferReader(raw)
238
+ w.writeVarInt(tmpReader.readVarInt())
239
+ } else {
240
+ // Odd → length-prefixed
241
+ const raw = hexToBytes(u.raw_hex)
242
+ w.writeVarInt(raw.byteLength)
243
+ w.writeBytes(raw)
244
+ }
245
+ }
246
+ }
247
+ }
248
+
249
+ function decodeParams(r: BufferReader): Draft13Params {
250
+ const count = Number(r.readVarInt())
251
+ const result: Draft13Params = {}
252
+ const unknown: UnknownParam[] = []
253
+
254
+ for (let i = 0; i < count; i++) {
255
+ const paramType = r.readVarInt()
256
+
257
+ if (paramType % 2n === 0n) {
258
+ // Even type → bare varint value
259
+ const value = r.readVarInt()
260
+ if (paramType === PARAM_DELIVERY_TIMEOUT) {
261
+ result.delivery_timeout = value
262
+ } else if (paramType === PARAM_MAX_CACHE_DURATION) {
263
+ result.max_cache_duration = value
264
+ } else {
265
+ const tmpW = new BufferWriter(16)
266
+ tmpW.writeVarInt(value)
267
+ const raw = tmpW.finish()
268
+ unknown.push({
269
+ id: `0x${paramType.toString(16)}`,
270
+ length: raw.byteLength,
271
+ raw_hex: bytesToHex(raw),
272
+ })
273
+ }
274
+ } else {
275
+ // Odd type → length-prefixed blob
276
+ const length = Number(r.readVarInt())
277
+
278
+ if (paramType === PARAM_AUTHORIZATION_TOKEN) {
279
+ result.authorization_token = decodeAuthorizationToken(r, length)
280
+ } else {
281
+ const bytes = r.readBytes(length)
282
+ unknown.push({
283
+ id: `0x${paramType.toString(16)}`,
284
+ length,
285
+ raw_hex: bytesToHex(bytes),
286
+ })
287
+ }
288
+ }
289
+ }
290
+
291
+ if (unknown.length > 0) result.unknown = unknown
292
+ return result
293
+ }
294
+
295
+ // ─── Payload Encoders ──────────────────────────────────────────────────────────
296
+
297
+ function encodeClientSetupPayload(
298
+ msg: Draft13Message & { type: 'client_setup' },
299
+ w: BufferWriter,
300
+ ): void {
301
+ w.writeVarInt(msg.supported_versions.length)
302
+ for (const v of msg.supported_versions) w.writeVarInt(v)
303
+ encodeSetupParams(msg.parameters, w)
304
+ }
305
+
306
+ function encodeServerSetupPayload(
307
+ msg: Draft13Message & { type: 'server_setup' },
308
+ w: BufferWriter,
309
+ ): void {
310
+ w.writeVarInt(msg.selected_version)
311
+ encodeSetupParams(msg.parameters, w)
312
+ }
313
+
314
+ function encodeSubscribePayload(
315
+ msg: Draft13Message & { type: 'subscribe' },
316
+ w: BufferWriter,
317
+ ): void {
318
+ w.writeVarInt(msg.request_id)
319
+ w.writeTuple(msg.track_namespace)
320
+ w.writeString(msg.track_name)
321
+ w.writeUint8(msg.subscriber_priority)
322
+ w.writeUint8(msg.group_order)
323
+ w.writeVarInt(msg.forward)
324
+ w.writeVarInt(msg.filter_type)
325
+ const ft = Number(msg.filter_type)
326
+ if (ft === 3 || ft === 4) {
327
+ w.writeVarInt(msg.start_group!)
328
+ w.writeVarInt(msg.start_object!)
329
+ }
330
+ if (ft === 4) {
331
+ w.writeVarInt(msg.end_group!)
332
+ }
333
+ encodeParams(msg.parameters, w)
334
+ }
335
+
336
+ function encodeSubscribeOkPayload(
337
+ msg: Draft13Message & { type: 'subscribe_ok' },
338
+ w: BufferWriter,
339
+ ): void {
340
+ w.writeVarInt(msg.request_id)
341
+ w.writeVarInt(msg.track_alias)
342
+ w.writeVarInt(msg.expires)
343
+ w.writeUint8(msg.group_order)
344
+ w.writeVarInt(msg.content_exists)
345
+ if (msg.content_exists === 1n && msg.largest_location) {
346
+ w.writeVarInt(msg.largest_location.group)
347
+ w.writeVarInt(msg.largest_location.object)
348
+ }
349
+ encodeParams(msg.parameters, w)
350
+ }
351
+
352
+ function encodeSubscribeErrorPayload(
353
+ msg: Draft13Message & { type: 'subscribe_error' },
354
+ w: BufferWriter,
355
+ ): void {
356
+ w.writeVarInt(msg.request_id)
357
+ w.writeVarInt(msg.error_code)
358
+ w.writeString(msg.reason_phrase)
359
+ }
360
+
361
+ function encodeSubscribeUpdatePayload(
362
+ msg: Draft13Message & { type: 'subscribe_update' },
363
+ w: BufferWriter,
364
+ ): void {
365
+ w.writeVarInt(msg.request_id)
366
+ w.writeVarInt(msg.start_group)
367
+ w.writeVarInt(msg.start_object)
368
+ w.writeVarInt(msg.end_group)
369
+ w.writeUint8(msg.subscriber_priority)
370
+ w.writeVarInt(msg.forward)
371
+ encodeParams(msg.parameters, w)
372
+ }
373
+
374
+ function encodeSubscribeDonePayload(
375
+ msg: Draft13Message & { type: 'subscribe_done' },
376
+ w: BufferWriter,
377
+ ): void {
378
+ w.writeVarInt(msg.request_id)
379
+ w.writeVarInt(msg.status_code)
380
+ w.writeVarInt(msg.stream_count)
381
+ w.writeString(msg.reason_phrase)
382
+ }
383
+
384
+ function encodeUnsubscribePayload(
385
+ msg: Draft13Message & { type: 'unsubscribe' },
386
+ w: BufferWriter,
387
+ ): void {
388
+ w.writeVarInt(msg.request_id)
389
+ }
390
+
391
+ function encodeAnnouncePayload(msg: Draft13Message & { type: 'announce' }, w: BufferWriter): void {
392
+ w.writeVarInt(msg.request_id)
393
+ w.writeTuple(msg.track_namespace)
394
+ encodeParams(msg.parameters, w)
395
+ }
396
+
397
+ function encodeAnnounceOkPayload(
398
+ msg: Draft13Message & { type: 'announce_ok' },
399
+ w: BufferWriter,
400
+ ): void {
401
+ w.writeVarInt(msg.request_id)
402
+ }
403
+
404
+ function encodeAnnounceErrorPayload(
405
+ msg: Draft13Message & { type: 'announce_error' },
406
+ w: BufferWriter,
407
+ ): void {
408
+ w.writeVarInt(msg.request_id)
409
+ w.writeVarInt(msg.error_code)
410
+ w.writeString(msg.reason_phrase)
411
+ }
412
+
413
+ function encodeUnannouncePayload(
414
+ msg: Draft13Message & { type: 'unannounce' },
415
+ w: BufferWriter,
416
+ ): void {
417
+ w.writeTuple(msg.track_namespace)
418
+ }
419
+
420
+ function encodeAnnounceCancelPayload(
421
+ msg: Draft13Message & { type: 'announce_cancel' },
422
+ w: BufferWriter,
423
+ ): void {
424
+ w.writeTuple(msg.track_namespace)
425
+ w.writeVarInt(msg.error_code)
426
+ w.writeString(msg.reason_phrase)
427
+ }
428
+
429
+ // Draft-13: subscribe_namespace (renamed from subscribe_announces, same wire format)
430
+ function encodeSubscribeNamespacePayload(
431
+ msg: Draft13Message & { type: 'subscribe_namespace' },
432
+ w: BufferWriter,
433
+ ): void {
434
+ w.writeVarInt(msg.request_id)
435
+ w.writeTuple(msg.track_namespace_prefix)
436
+ encodeParams(msg.parameters, w)
437
+ }
438
+
439
+ function encodeSubscribeNamespaceOkPayload(
440
+ msg: Draft13Message & { type: 'subscribe_namespace_ok' },
441
+ w: BufferWriter,
442
+ ): void {
443
+ w.writeVarInt(msg.request_id)
444
+ }
445
+
446
+ function encodeSubscribeNamespaceErrorPayload(
447
+ msg: Draft13Message & { type: 'subscribe_namespace_error' },
448
+ w: BufferWriter,
449
+ ): void {
450
+ w.writeVarInt(msg.request_id)
451
+ w.writeVarInt(msg.error_code)
452
+ w.writeString(msg.reason_phrase)
453
+ }
454
+
455
+ function encodeUnsubscribeNamespacePayload(
456
+ msg: Draft13Message & { type: 'unsubscribe_namespace' },
457
+ w: BufferWriter,
458
+ ): void {
459
+ w.writeTuple(msg.track_namespace_prefix)
460
+ }
461
+
462
+ function encodePublishPayload(msg: Draft13Message & { type: 'publish' }, w: BufferWriter): void {
463
+ w.writeVarInt(msg.request_id)
464
+ w.writeTuple(msg.track_namespace)
465
+ w.writeString(msg.track_name)
466
+ w.writeVarInt(msg.track_alias)
467
+ w.writeUint8(msg.group_order)
468
+ w.writeVarInt(msg.content_exists)
469
+ if (msg.content_exists === 1n && msg.largest_location) {
470
+ w.writeVarInt(msg.largest_location.group)
471
+ w.writeVarInt(msg.largest_location.object)
472
+ }
473
+ w.writeVarInt(msg.forward)
474
+ encodeParams(msg.parameters, w)
475
+ }
476
+
477
+ function encodePublishOkPayload(
478
+ msg: Draft13Message & { type: 'publish_ok' },
479
+ w: BufferWriter,
480
+ ): void {
481
+ w.writeVarInt(msg.request_id)
482
+ w.writeVarInt(msg.forward)
483
+ w.writeUint8(msg.subscriber_priority)
484
+ w.writeUint8(msg.group_order)
485
+ w.writeVarInt(msg.filter_type)
486
+ const ft = Number(msg.filter_type)
487
+ if (ft === 3 || ft === 4) {
488
+ w.writeVarInt(msg.start_group!)
489
+ w.writeVarInt(msg.start_object!)
490
+ }
491
+ if (ft === 4) {
492
+ w.writeVarInt(msg.end_group!)
493
+ }
494
+ encodeParams(msg.parameters, w)
495
+ }
496
+
497
+ function encodePublishErrorPayload(
498
+ msg: Draft13Message & { type: 'publish_error' },
499
+ w: BufferWriter,
500
+ ): void {
501
+ w.writeVarInt(msg.request_id)
502
+ w.writeVarInt(msg.error_code)
503
+ w.writeString(msg.reason_phrase)
504
+ }
505
+
506
+ function encodeFetchPayload(msg: Draft13Message & { type: 'fetch' }, w: BufferWriter): void {
507
+ w.writeVarInt(msg.request_id)
508
+ w.writeUint8(msg.subscriber_priority)
509
+ w.writeUint8(msg.group_order)
510
+ w.writeVarInt(msg.fetch_type)
511
+ const ft = Number(msg.fetch_type)
512
+ if (ft === 1 && msg.standalone) {
513
+ w.writeTuple(msg.standalone.track_namespace)
514
+ w.writeString(msg.standalone.track_name)
515
+ w.writeVarInt(msg.standalone.start_group)
516
+ w.writeVarInt(msg.standalone.start_object)
517
+ w.writeVarInt(msg.standalone.end_group)
518
+ w.writeVarInt(msg.standalone.end_object)
519
+ } else if ((ft === 2 || ft === 3) && msg.joining) {
520
+ w.writeVarInt(msg.joining.joining_subscribe_id)
521
+ w.writeVarInt(msg.joining.joining_start)
522
+ }
523
+ encodeParams(msg.parameters, w)
524
+ }
525
+
526
+ function encodeFetchOkPayload(msg: Draft13Message & { type: 'fetch_ok' }, w: BufferWriter): void {
527
+ w.writeVarInt(msg.request_id)
528
+ w.writeUint8(msg.group_order)
529
+ w.writeVarInt(msg.end_of_track)
530
+ w.writeVarInt(msg.end_location.group)
531
+ w.writeVarInt(msg.end_location.object)
532
+ encodeParams(msg.parameters, w)
533
+ }
534
+
535
+ function encodeFetchErrorPayload(
536
+ msg: Draft13Message & { type: 'fetch_error' },
537
+ w: BufferWriter,
538
+ ): void {
539
+ w.writeVarInt(msg.request_id)
540
+ w.writeVarInt(msg.error_code)
541
+ w.writeString(msg.reason_phrase)
542
+ }
543
+
544
+ function encodeFetchCancelPayload(
545
+ msg: Draft13Message & { type: 'fetch_cancel' },
546
+ w: BufferWriter,
547
+ ): void {
548
+ w.writeVarInt(msg.request_id)
549
+ }
550
+
551
+ // Draft-13: track_status is now SUBSCRIBE-like
552
+ function encodeTrackStatusPayload(
553
+ msg: Draft13Message & { type: 'track_status' },
554
+ w: BufferWriter,
555
+ ): void {
556
+ w.writeVarInt(msg.request_id)
557
+ w.writeTuple(msg.track_namespace)
558
+ w.writeString(msg.track_name)
559
+ w.writeUint8(msg.subscriber_priority)
560
+ w.writeUint8(msg.group_order)
561
+ w.writeVarInt(msg.forward)
562
+ w.writeVarInt(msg.filter_type)
563
+ const ft = Number(msg.filter_type)
564
+ if (ft === 3 || ft === 4) {
565
+ w.writeVarInt(msg.start_group!)
566
+ w.writeVarInt(msg.start_object!)
567
+ }
568
+ if (ft === 4) {
569
+ w.writeVarInt(msg.end_group!)
570
+ }
571
+ encodeParams(msg.parameters, w)
572
+ }
573
+
574
+ // Draft-13: track_status_ok is SUBSCRIBE_OK-like
575
+ function encodeTrackStatusOkPayload(
576
+ msg: Draft13Message & { type: 'track_status_ok' },
577
+ w: BufferWriter,
578
+ ): void {
579
+ w.writeVarInt(msg.request_id)
580
+ w.writeVarInt(msg.track_alias)
581
+ w.writeVarInt(msg.expires)
582
+ w.writeUint8(msg.group_order)
583
+ w.writeVarInt(msg.content_exists)
584
+ if (msg.content_exists === 1n && msg.largest_location) {
585
+ w.writeVarInt(msg.largest_location.group)
586
+ w.writeVarInt(msg.largest_location.object)
587
+ }
588
+ encodeParams(msg.parameters, w)
589
+ }
590
+
591
+ // Draft-13: track_status_error
592
+ function encodeTrackStatusErrorPayload(
593
+ msg: Draft13Message & { type: 'track_status_error' },
594
+ w: BufferWriter,
595
+ ): void {
596
+ w.writeVarInt(msg.request_id)
597
+ w.writeVarInt(msg.error_code)
598
+ w.writeString(msg.reason_phrase)
599
+ }
600
+
601
+ function encodeGoAwayPayload(msg: Draft13Message & { type: 'goaway' }, w: BufferWriter): void {
602
+ w.writeString(msg.new_session_uri)
603
+ }
604
+
605
+ function encodeMaxRequestIdPayload(
606
+ msg: Draft13Message & { type: 'max_request_id' },
607
+ w: BufferWriter,
608
+ ): void {
609
+ w.writeVarInt(msg.request_id)
610
+ }
611
+
612
+ function encodeRequestsBlockedPayload(
613
+ msg: Draft13Message & { type: 'requests_blocked' },
614
+ w: BufferWriter,
615
+ ): void {
616
+ w.writeVarInt(msg.maximum_request_id)
617
+ }
618
+
619
+ // ─── Payload Decoders ──────────────────────────────────────────────────────────
620
+
621
+ function decodeClientSetupPayload(r: BufferReader): Draft13Message {
622
+ const numVersions = Number(r.readVarInt())
623
+ if (numVersions === 0) {
624
+ throw new DecodeError('CONSTRAINT_VIOLATION', 'supported_versions must not be empty', r.offset)
625
+ }
626
+ const supported_versions: bigint[] = []
627
+ for (let i = 0; i < numVersions; i++) supported_versions.push(r.readVarInt())
628
+ const parameters = decodeSetupParams(r)
629
+ return { type: 'client_setup', supported_versions, parameters }
630
+ }
631
+
632
+ function decodeServerSetupPayload(r: BufferReader): Draft13Message {
633
+ const selected_version = r.readVarInt()
634
+ const parameters = decodeSetupParams(r)
635
+ return { type: 'server_setup', selected_version, parameters }
636
+ }
637
+
638
+ function decodeSubscribePayload(r: BufferReader): Draft13Message {
639
+ const request_id = r.readVarInt()
640
+ const track_namespace = r.readTuple()
641
+ const track_name = r.readString()
642
+ const subscriber_priority = r.readUint8()
643
+ const group_order = r.readUint8()
644
+ const forward = r.readVarInt()
645
+ const filter_type = r.readVarInt()
646
+ const ft = Number(filter_type)
647
+ if (ft < 1 || ft > 4) {
648
+ throw new DecodeError('CONSTRAINT_VIOLATION', `Invalid filter_type: ${ft}`, r.offset)
649
+ }
650
+ let start_group: bigint | undefined
651
+ let start_object: bigint | undefined
652
+ let end_group: bigint | undefined
653
+ if (ft === 3 || ft === 4) {
654
+ start_group = r.readVarInt()
655
+ start_object = r.readVarInt()
656
+ }
657
+ if (ft === 4) {
658
+ end_group = r.readVarInt()
659
+ }
660
+ const parameters = decodeParams(r)
661
+ const msg: Record<string, unknown> = {
662
+ type: 'subscribe',
663
+ request_id,
664
+ track_namespace,
665
+ track_name,
666
+ subscriber_priority,
667
+ group_order,
668
+ forward,
669
+ filter_type,
670
+ parameters,
671
+ }
672
+ if (start_group !== undefined) msg.start_group = start_group
673
+ if (start_object !== undefined) msg.start_object = start_object
674
+ if (end_group !== undefined) msg.end_group = end_group
675
+ return msg as unknown as Draft13Message
676
+ }
677
+
678
+ function decodeSubscribeOkPayload(r: BufferReader): Draft13Message {
679
+ const request_id = r.readVarInt()
680
+ const track_alias = r.readVarInt()
681
+ const expires = r.readVarInt()
682
+ const group_order = r.readUint8()
683
+ const content_exists = r.readVarInt()
684
+ let largest_location: LargestLocation | undefined
685
+ if (content_exists === 1n) {
686
+ const group = r.readVarInt()
687
+ const object = r.readVarInt()
688
+ largest_location = { group, object }
689
+ }
690
+ const parameters = decodeParams(r)
691
+ const msg: Record<string, unknown> = {
692
+ type: 'subscribe_ok',
693
+ request_id,
694
+ track_alias,
695
+ expires,
696
+ group_order,
697
+ content_exists,
698
+ parameters,
699
+ }
700
+ if (largest_location) msg.largest_location = largest_location
701
+ return msg as unknown as Draft13Message
702
+ }
703
+
704
+ function decodeSubscribeErrorPayload(r: BufferReader): Draft13Message {
705
+ const request_id = r.readVarInt()
706
+ const error_code = r.readVarInt()
707
+ const reason_phrase = r.readString()
708
+ return { type: 'subscribe_error', request_id, error_code, reason_phrase }
709
+ }
710
+
711
+ function decodeSubscribeUpdatePayload(r: BufferReader): Draft13Message {
712
+ const request_id = r.readVarInt()
713
+ const start_group = r.readVarInt()
714
+ const start_object = r.readVarInt()
715
+ const end_group = r.readVarInt()
716
+ const subscriber_priority = r.readUint8()
717
+ const forward = r.readVarInt()
718
+ const parameters = decodeParams(r)
719
+ return {
720
+ type: 'subscribe_update',
721
+ request_id,
722
+ start_group,
723
+ start_object,
724
+ end_group,
725
+ subscriber_priority,
726
+ forward,
727
+ parameters,
728
+ }
729
+ }
730
+
731
+ function decodeSubscribeDonePayload(r: BufferReader): Draft13Message {
732
+ const request_id = r.readVarInt()
733
+ const status_code = r.readVarInt()
734
+ const stream_count = r.readVarInt()
735
+ const reason_phrase = r.readString()
736
+ return {
737
+ type: 'subscribe_done',
738
+ request_id,
739
+ status_code,
740
+ stream_count,
741
+ reason_phrase,
742
+ }
743
+ }
744
+
745
+ function decodeUnsubscribePayload(r: BufferReader): Draft13Message {
746
+ const request_id = r.readVarInt()
747
+ return { type: 'unsubscribe', request_id }
748
+ }
749
+
750
+ function decodeAnnouncePayload(r: BufferReader): Draft13Message {
751
+ const request_id = r.readVarInt()
752
+ const track_namespace = r.readTuple()
753
+ const parameters = decodeParams(r)
754
+ return { type: 'announce', request_id, track_namespace, parameters }
755
+ }
756
+
757
+ function decodeAnnounceOkPayload(r: BufferReader): Draft13Message {
758
+ const request_id = r.readVarInt()
759
+ return { type: 'announce_ok', request_id }
760
+ }
761
+
762
+ function decodeAnnounceErrorPayload(r: BufferReader): Draft13Message {
763
+ const request_id = r.readVarInt()
764
+ const error_code = r.readVarInt()
765
+ const reason_phrase = r.readString()
766
+ return { type: 'announce_error', request_id, error_code, reason_phrase }
767
+ }
768
+
769
+ function decodeUnannouncePayload(r: BufferReader): Draft13Message {
770
+ const track_namespace = r.readTuple()
771
+ return { type: 'unannounce', track_namespace }
772
+ }
773
+
774
+ function decodeAnnounceCancelPayload(r: BufferReader): Draft13Message {
775
+ const track_namespace = r.readTuple()
776
+ const error_code = r.readVarInt()
777
+ const reason_phrase = r.readString()
778
+ return { type: 'announce_cancel', track_namespace, error_code, reason_phrase }
779
+ }
780
+
781
+ // Draft-13: subscribe_namespace (renamed from subscribe_announces)
782
+ function decodeSubscribeNamespacePayload(r: BufferReader): Draft13Message {
783
+ const request_id = r.readVarInt()
784
+ const track_namespace_prefix = r.readTuple()
785
+ const parameters = decodeParams(r)
786
+ return {
787
+ type: 'subscribe_namespace',
788
+ request_id,
789
+ track_namespace_prefix,
790
+ parameters,
791
+ }
792
+ }
793
+
794
+ function decodeSubscribeNamespaceOkPayload(r: BufferReader): Draft13Message {
795
+ const request_id = r.readVarInt()
796
+ return { type: 'subscribe_namespace_ok', request_id }
797
+ }
798
+
799
+ function decodeSubscribeNamespaceErrorPayload(r: BufferReader): Draft13Message {
800
+ const request_id = r.readVarInt()
801
+ const error_code = r.readVarInt()
802
+ const reason_phrase = r.readString()
803
+ return {
804
+ type: 'subscribe_namespace_error',
805
+ request_id,
806
+ error_code,
807
+ reason_phrase,
808
+ }
809
+ }
810
+
811
+ function decodeUnsubscribeNamespacePayload(r: BufferReader): Draft13Message {
812
+ const track_namespace_prefix = r.readTuple()
813
+ return { type: 'unsubscribe_namespace', track_namespace_prefix }
814
+ }
815
+
816
+ function decodePublishPayload(r: BufferReader): Draft13Message {
817
+ const request_id = r.readVarInt()
818
+ const track_namespace = r.readTuple()
819
+ const track_name = r.readString()
820
+ const track_alias = r.readVarInt()
821
+ const group_order = r.readUint8()
822
+ const content_exists = r.readVarInt()
823
+ let largest_location: LargestLocation | undefined
824
+ if (content_exists === 1n) {
825
+ const group = r.readVarInt()
826
+ const object = r.readVarInt()
827
+ largest_location = { group, object }
828
+ }
829
+ const forward = r.readVarInt()
830
+ const parameters = decodeParams(r)
831
+ const msg: Record<string, unknown> = {
832
+ type: 'publish',
833
+ request_id,
834
+ track_namespace,
835
+ track_name,
836
+ track_alias,
837
+ group_order,
838
+ content_exists,
839
+ forward,
840
+ parameters,
841
+ }
842
+ if (largest_location) msg.largest_location = largest_location
843
+ return msg as unknown as Draft13Message
844
+ }
845
+
846
+ function decodePublishOkPayload(r: BufferReader): Draft13Message {
847
+ const request_id = r.readVarInt()
848
+ const forward = r.readVarInt()
849
+ const subscriber_priority = r.readUint8()
850
+ const group_order = r.readUint8()
851
+ const filter_type = r.readVarInt()
852
+ const ft = Number(filter_type)
853
+ let start_group: bigint | undefined
854
+ let start_object: bigint | undefined
855
+ let end_group: bigint | undefined
856
+ if (ft === 3 || ft === 4) {
857
+ start_group = r.readVarInt()
858
+ start_object = r.readVarInt()
859
+ }
860
+ if (ft === 4) {
861
+ end_group = r.readVarInt()
862
+ }
863
+ const parameters = decodeParams(r)
864
+ const msg: Record<string, unknown> = {
865
+ type: 'publish_ok',
866
+ request_id,
867
+ forward,
868
+ subscriber_priority,
869
+ group_order,
870
+ filter_type,
871
+ parameters,
872
+ }
873
+ if (start_group !== undefined) msg.start_group = start_group
874
+ if (start_object !== undefined) msg.start_object = start_object
875
+ if (end_group !== undefined) msg.end_group = end_group
876
+ return msg as unknown as Draft13Message
877
+ }
878
+
879
+ function decodePublishErrorPayload(r: BufferReader): Draft13Message {
880
+ const request_id = r.readVarInt()
881
+ const error_code = r.readVarInt()
882
+ const reason_phrase = r.readString()
883
+ return { type: 'publish_error', request_id, error_code, reason_phrase }
884
+ }
885
+
886
+ function decodeFetchPayload(r: BufferReader): Draft13Message {
887
+ const request_id = r.readVarInt()
888
+ const subscriber_priority = r.readUint8()
889
+ const group_order = r.readUint8()
890
+ const fetch_type = r.readVarInt()
891
+ const ft = Number(fetch_type)
892
+
893
+ if (ft < 1 || ft > 3) {
894
+ throw new DecodeError('CONSTRAINT_VIOLATION', `Invalid fetch_type: ${ft}`, r.offset)
895
+ }
896
+
897
+ let standalone: StandaloneFetch | undefined
898
+ let joining: JoiningFetch | undefined
899
+
900
+ if (ft === 1) {
901
+ const track_namespace = r.readTuple()
902
+ const track_name = r.readString()
903
+ const start_group = r.readVarInt()
904
+ const start_object = r.readVarInt()
905
+ const end_group = r.readVarInt()
906
+ const end_object = r.readVarInt()
907
+ standalone = {
908
+ track_namespace,
909
+ track_name,
910
+ start_group,
911
+ start_object,
912
+ end_group,
913
+ end_object,
914
+ }
915
+ } else {
916
+ const joining_subscribe_id = r.readVarInt()
917
+ const joining_start = r.readVarInt()
918
+ joining = { joining_subscribe_id, joining_start }
919
+ }
920
+
921
+ const parameters = decodeParams(r)
922
+ return {
923
+ type: 'fetch',
924
+ request_id,
925
+ subscriber_priority,
926
+ group_order,
927
+ fetch_type,
928
+ standalone,
929
+ joining,
930
+ parameters,
931
+ } as Draft13Fetch
932
+ }
933
+
934
+ function decodeFetchOkPayload(r: BufferReader): Draft13Message {
935
+ const request_id = r.readVarInt()
936
+ const group_order = r.readUint8()
937
+ const end_of_track = r.readVarInt()
938
+ const group = r.readVarInt()
939
+ const object = r.readVarInt()
940
+ const parameters = decodeParams(r)
941
+ return {
942
+ type: 'fetch_ok',
943
+ request_id,
944
+ group_order,
945
+ end_of_track,
946
+ end_location: { group, object },
947
+ parameters,
948
+ }
949
+ }
950
+
951
+ function decodeFetchErrorPayload(r: BufferReader): Draft13Message {
952
+ const request_id = r.readVarInt()
953
+ const error_code = r.readVarInt()
954
+ const reason_phrase = r.readString()
955
+ return { type: 'fetch_error', request_id, error_code, reason_phrase }
956
+ }
957
+
958
+ function decodeFetchCancelPayload(r: BufferReader): Draft13Message {
959
+ const request_id = r.readVarInt()
960
+ return { type: 'fetch_cancel', request_id }
961
+ }
962
+
963
+ // Draft-13: track_status is SUBSCRIBE-like
964
+ function decodeTrackStatusPayload(r: BufferReader): Draft13Message {
965
+ const request_id = r.readVarInt()
966
+ const track_namespace = r.readTuple()
967
+ const track_name = r.readString()
968
+ const subscriber_priority = r.readUint8()
969
+ const group_order = r.readUint8()
970
+ const forward = r.readVarInt()
971
+ const filter_type = r.readVarInt()
972
+ const ft = Number(filter_type)
973
+ if (ft < 1 || ft > 4) {
974
+ throw new DecodeError('CONSTRAINT_VIOLATION', `Invalid filter_type: ${ft}`, r.offset)
975
+ }
976
+ let start_group: bigint | undefined
977
+ let start_object: bigint | undefined
978
+ let end_group: bigint | undefined
979
+ if (ft === 3 || ft === 4) {
980
+ start_group = r.readVarInt()
981
+ start_object = r.readVarInt()
982
+ }
983
+ if (ft === 4) {
984
+ end_group = r.readVarInt()
985
+ }
986
+ const parameters = decodeParams(r)
987
+ const msg: Record<string, unknown> = {
988
+ type: 'track_status',
989
+ request_id,
990
+ track_namespace,
991
+ track_name,
992
+ subscriber_priority,
993
+ group_order,
994
+ forward,
995
+ filter_type,
996
+ parameters,
997
+ }
998
+ if (start_group !== undefined) msg.start_group = start_group
999
+ if (start_object !== undefined) msg.start_object = start_object
1000
+ if (end_group !== undefined) msg.end_group = end_group
1001
+ return msg as unknown as Draft13Message
1002
+ }
1003
+
1004
+ // Draft-13: track_status_ok is SUBSCRIBE_OK-like
1005
+ function decodeTrackStatusOkPayload(r: BufferReader): Draft13Message {
1006
+ const request_id = r.readVarInt()
1007
+ const track_alias = r.readVarInt()
1008
+ const expires = r.readVarInt()
1009
+ const group_order = r.readUint8()
1010
+ const content_exists = r.readVarInt()
1011
+ let largest_location: LargestLocation | undefined
1012
+ if (content_exists === 1n) {
1013
+ const group = r.readVarInt()
1014
+ const object = r.readVarInt()
1015
+ largest_location = { group, object }
1016
+ }
1017
+ const parameters = decodeParams(r)
1018
+ const msg: Record<string, unknown> = {
1019
+ type: 'track_status_ok',
1020
+ request_id,
1021
+ track_alias,
1022
+ expires,
1023
+ group_order,
1024
+ content_exists,
1025
+ parameters,
1026
+ }
1027
+ if (largest_location) msg.largest_location = largest_location
1028
+ return msg as unknown as Draft13Message
1029
+ }
1030
+
1031
+ // Draft-13: track_status_error
1032
+ function decodeTrackStatusErrorPayload(r: BufferReader): Draft13Message {
1033
+ const request_id = r.readVarInt()
1034
+ const error_code = r.readVarInt()
1035
+ const reason_phrase = r.readString()
1036
+ return { type: 'track_status_error', request_id, error_code, reason_phrase }
1037
+ }
1038
+
1039
+ function decodeGoAwayPayload(r: BufferReader): Draft13Message {
1040
+ const new_session_uri = r.readString()
1041
+ return { type: 'goaway', new_session_uri }
1042
+ }
1043
+
1044
+ function decodeMaxRequestIdPayload(r: BufferReader): Draft13Message {
1045
+ const request_id = r.readVarInt()
1046
+ return { type: 'max_request_id', request_id }
1047
+ }
1048
+
1049
+ function decodeRequestsBlockedPayload(r: BufferReader): Draft13Message {
1050
+ const maximum_request_id = r.readVarInt()
1051
+ return { type: 'requests_blocked', maximum_request_id }
1052
+ }
1053
+
1054
+ // ─── Payload dispatch tables ───────────────────────────────────────────────────
1055
+
1056
+ const payloadDecoders: ReadonlyMap<bigint, (r: BufferReader) => Draft13Message> = new Map([
1057
+ [MSG_CLIENT_SETUP, decodeClientSetupPayload],
1058
+ [MSG_SERVER_SETUP, decodeServerSetupPayload],
1059
+ [MSG_SUBSCRIBE, decodeSubscribePayload],
1060
+ [MSG_SUBSCRIBE_OK, decodeSubscribeOkPayload],
1061
+ [MSG_SUBSCRIBE_ERROR, decodeSubscribeErrorPayload],
1062
+ [MSG_SUBSCRIBE_UPDATE, decodeSubscribeUpdatePayload],
1063
+ [MSG_SUBSCRIBE_DONE, decodeSubscribeDonePayload],
1064
+ [MSG_UNSUBSCRIBE, decodeUnsubscribePayload],
1065
+ [MSG_ANNOUNCE, decodeAnnouncePayload],
1066
+ [MSG_ANNOUNCE_OK, decodeAnnounceOkPayload],
1067
+ [MSG_ANNOUNCE_ERROR, decodeAnnounceErrorPayload],
1068
+ [MSG_UNANNOUNCE, decodeUnannouncePayload],
1069
+ [MSG_ANNOUNCE_CANCEL, decodeAnnounceCancelPayload],
1070
+ [MSG_SUBSCRIBE_NAMESPACE, decodeSubscribeNamespacePayload],
1071
+ [MSG_SUBSCRIBE_NAMESPACE_OK, decodeSubscribeNamespaceOkPayload],
1072
+ [MSG_SUBSCRIBE_NAMESPACE_ERROR, decodeSubscribeNamespaceErrorPayload],
1073
+ [MSG_UNSUBSCRIBE_NAMESPACE, decodeUnsubscribeNamespacePayload],
1074
+ [MSG_PUBLISH, decodePublishPayload],
1075
+ [MSG_PUBLISH_OK, decodePublishOkPayload],
1076
+ [MSG_PUBLISH_ERROR, decodePublishErrorPayload],
1077
+ [MSG_FETCH, decodeFetchPayload],
1078
+ [MSG_FETCH_OK, decodeFetchOkPayload],
1079
+ [MSG_FETCH_ERROR, decodeFetchErrorPayload],
1080
+ [MSG_FETCH_CANCEL, decodeFetchCancelPayload],
1081
+ [MSG_TRACK_STATUS, decodeTrackStatusPayload],
1082
+ [MSG_TRACK_STATUS_OK, decodeTrackStatusOkPayload],
1083
+ [MSG_TRACK_STATUS_ERROR, decodeTrackStatusErrorPayload],
1084
+ [MSG_GOAWAY, decodeGoAwayPayload],
1085
+ [MSG_MAX_REQUEST_ID, decodeMaxRequestIdPayload],
1086
+ [MSG_REQUESTS_BLOCKED, decodeRequestsBlockedPayload],
1087
+ ])
1088
+
1089
+ // ─── Public API ────────────────────────────────────────────────────────────────
1090
+
1091
+ /**
1092
+ * Encode a draft-13 control message.
1093
+ * Most messages use type(varint) + length(uint16 BE) + payload.
1094
+ * PUBLISH family uses type(varint) + length(varint) + payload.
1095
+ */
1096
+ export function encodeMessage(message: Draft13Message): Uint8Array {
1097
+ const typeId = MESSAGE_ID_MAP.get(message.type)
1098
+ if (typeId === undefined) throw new Error(`Unknown message type: ${message.type}`)
1099
+
1100
+ const payloadWriter = new BufferWriter()
1101
+ encodePayload(message, payloadWriter)
1102
+ const payload = payloadWriter.finishView()
1103
+
1104
+ const writer = new BufferWriter(payload.byteLength + 16)
1105
+ writer.writeVarInt(typeId)
1106
+
1107
+ if (payload.byteLength > 0xffff) {
1108
+ throw new Error(`Payload too large for 16-bit length: ${payload.byteLength}`)
1109
+ }
1110
+ writer.writeUint8((payload.byteLength >> 8) & 0xff)
1111
+ writer.writeUint8(payload.byteLength & 0xff)
1112
+
1113
+ writer.writeBytes(payload)
1114
+ return writer.finish()
1115
+ }
1116
+
1117
+ function encodePayload(msg: Draft13Message, w: BufferWriter): void {
1118
+ switch (msg.type) {
1119
+ case 'client_setup':
1120
+ return encodeClientSetupPayload(msg, w)
1121
+ case 'server_setup':
1122
+ return encodeServerSetupPayload(msg, w)
1123
+ case 'subscribe':
1124
+ return encodeSubscribePayload(msg, w)
1125
+ case 'subscribe_ok':
1126
+ return encodeSubscribeOkPayload(msg, w)
1127
+ case 'subscribe_error':
1128
+ return encodeSubscribeErrorPayload(msg, w)
1129
+ case 'subscribe_update':
1130
+ return encodeSubscribeUpdatePayload(msg, w)
1131
+ case 'subscribe_done':
1132
+ return encodeSubscribeDonePayload(msg, w)
1133
+ case 'unsubscribe':
1134
+ return encodeUnsubscribePayload(msg, w)
1135
+ case 'announce':
1136
+ return encodeAnnouncePayload(msg, w)
1137
+ case 'announce_ok':
1138
+ return encodeAnnounceOkPayload(msg, w)
1139
+ case 'announce_error':
1140
+ return encodeAnnounceErrorPayload(msg, w)
1141
+ case 'unannounce':
1142
+ return encodeUnannouncePayload(msg, w)
1143
+ case 'announce_cancel':
1144
+ return encodeAnnounceCancelPayload(msg, w)
1145
+ case 'subscribe_namespace':
1146
+ return encodeSubscribeNamespacePayload(msg, w)
1147
+ case 'subscribe_namespace_ok':
1148
+ return encodeSubscribeNamespaceOkPayload(msg, w)
1149
+ case 'subscribe_namespace_error':
1150
+ return encodeSubscribeNamespaceErrorPayload(msg, w)
1151
+ case 'unsubscribe_namespace':
1152
+ return encodeUnsubscribeNamespacePayload(msg, w)
1153
+ case 'publish':
1154
+ return encodePublishPayload(msg, w)
1155
+ case 'publish_ok':
1156
+ return encodePublishOkPayload(msg, w)
1157
+ case 'publish_error':
1158
+ return encodePublishErrorPayload(msg, w)
1159
+ case 'fetch':
1160
+ return encodeFetchPayload(msg, w)
1161
+ case 'fetch_ok':
1162
+ return encodeFetchOkPayload(msg, w)
1163
+ case 'fetch_error':
1164
+ return encodeFetchErrorPayload(msg, w)
1165
+ case 'fetch_cancel':
1166
+ return encodeFetchCancelPayload(msg, w)
1167
+ case 'track_status':
1168
+ return encodeTrackStatusPayload(msg, w)
1169
+ case 'track_status_ok':
1170
+ return encodeTrackStatusOkPayload(msg, w)
1171
+ case 'track_status_error':
1172
+ return encodeTrackStatusErrorPayload(msg, w)
1173
+ case 'goaway':
1174
+ return encodeGoAwayPayload(msg, w)
1175
+ case 'max_request_id':
1176
+ return encodeMaxRequestIdPayload(msg, w)
1177
+ case 'requests_blocked':
1178
+ return encodeRequestsBlockedPayload(msg, w)
1179
+ default: {
1180
+ const _exhaustive: never = msg
1181
+ throw new Error(`Unhandled message type: ${(_exhaustive as Draft13Message).type}`)
1182
+ }
1183
+ }
1184
+ }
1185
+
1186
+ /**
1187
+ * Decode a draft-13 control message from bytes.
1188
+ */
1189
+ export function decodeMessage(bytes: Uint8Array): DecodeResult<Draft13Message> {
1190
+ try {
1191
+ const reader = new BufferReader(bytes)
1192
+ const typeId = reader.readVarInt()
1193
+
1194
+ // Read 16-bit big-endian payload length
1195
+ const lenHi = reader.readUint8()
1196
+ const lenLo = reader.readUint8()
1197
+ const payloadLength = (lenHi << 8) | lenLo
1198
+
1199
+ const payloadBytes = reader.readBytes(payloadLength)
1200
+ const payloadReader = new BufferReader(payloadBytes)
1201
+
1202
+ const decoder = payloadDecoders.get(typeId)
1203
+ if (!decoder) {
1204
+ return {
1205
+ ok: false,
1206
+ error: new DecodeError(
1207
+ 'UNKNOWN_MESSAGE_TYPE',
1208
+ `Unknown message type ID: 0x${typeId.toString(16)}`,
1209
+ 0,
1210
+ ),
1211
+ }
1212
+ }
1213
+
1214
+ const message = decoder(payloadReader)
1215
+ return { ok: true, value: message, bytesRead: reader.offset }
1216
+ } catch (e) {
1217
+ if (e instanceof DecodeError) return { ok: false, error: e }
1218
+ throw e
1219
+ }
1220
+ }
1221
+
1222
+ // ─── Data Stream Encoding/Decoding ─────────────────────────────────────────────
1223
+
1224
+ export {
1225
+ decodeDatagram,
1226
+ decodeDataStream,
1227
+ decodeFetchStream,
1228
+ decodeSubgroupStream,
1229
+ encodeDatagram,
1230
+ encodeFetchStream,
1231
+ encodeSubgroupStream,
1232
+ } from './data-streams.js'
1233
+
1234
+ export function createStreamDecoder(): TransformStream<Uint8Array, Draft13Message> {
1235
+ let buffer = new Uint8Array(0)
1236
+ let offset = 0
1237
+ return new TransformStream<Uint8Array, Draft13Message>({
1238
+ transform(chunk, controller) {
1239
+ if (offset > 0) {
1240
+ buffer = buffer.subarray(offset)
1241
+ offset = 0
1242
+ }
1243
+ const newBuffer = new Uint8Array(buffer.length + chunk.length)
1244
+ newBuffer.set(buffer, 0)
1245
+ newBuffer.set(chunk, buffer.length)
1246
+ buffer = newBuffer
1247
+ while (offset < buffer.length) {
1248
+ const result = decodeMessage(buffer.subarray(offset))
1249
+ if (!result.ok) {
1250
+ if (result.error.code === 'UNEXPECTED_END') break
1251
+ controller.error(result.error)
1252
+ return
1253
+ }
1254
+ controller.enqueue(result.value)
1255
+ offset += result.bytesRead
1256
+ }
1257
+ },
1258
+ flush(controller) {
1259
+ if (offset < buffer.length)
1260
+ controller.error(new DecodeError('UNEXPECTED_END', 'Stream ended with incomplete data', 0))
1261
+ },
1262
+ })
1263
+ }
1264
+
1265
+ // ─── Codec Factory ─────────────────────────────────────────────────────────────
1266
+
1267
+ export interface Draft13Codec extends BaseCodec<Draft13Message> {
1268
+ readonly draft: '13'
1269
+ encodeSubgroupStream(stream: SubgroupStream): Uint8Array
1270
+ encodeDatagram(dg: DatagramObject): Uint8Array
1271
+ encodeFetchStream(stream: FetchStream): Uint8Array
1272
+ decodeSubgroupStream(bytes: Uint8Array): DecodeResult<SubgroupStream>
1273
+ decodeDatagram(bytes: Uint8Array): DecodeResult<DatagramObject>
1274
+ decodeFetchStream(bytes: Uint8Array): DecodeResult<FetchStream>
1275
+ decodeDataStream(
1276
+ streamType: 'subgroup' | 'datagram' | 'fetch',
1277
+ bytes: Uint8Array,
1278
+ ): DecodeResult<Draft13DataStream>
1279
+ createStreamDecoder(): TransformStream<Uint8Array, Draft13Message>
1280
+ }
1281
+
1282
+ export function createDraft13Codec(): Draft13Codec {
1283
+ return {
1284
+ draft: '13',
1285
+ encodeMessage,
1286
+ decodeMessage,
1287
+ encodeSubgroupStream,
1288
+ encodeDatagram,
1289
+ encodeFetchStream,
1290
+ decodeSubgroupStream,
1291
+ decodeDatagram,
1292
+ decodeFetchStream,
1293
+ decodeDataStream,
1294
+ createStreamDecoder,
1295
+ }
1296
+ }