@moqtap/codec 0.1.0 → 0.2.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 (277) hide show
  1. package/README.md +99 -95
  2. package/dist/chunk-4RIXXEII.js +1275 -0
  3. package/dist/chunk-4XYGE53S.cjs +698 -0
  4. package/dist/chunk-4YJANAXU.cjs +1109 -0
  5. package/dist/chunk-6AEHWULA.cjs +641 -0
  6. package/dist/chunk-7DUBLRXC.js +680 -0
  7. package/dist/{chunk-WNTXF3DE.cjs → chunk-7IVGHMKJ.cjs} +164 -62
  8. package/dist/{chunk-YBSEOSSP.js → chunk-A27S7HW7.js} +5 -1
  9. package/dist/chunk-BISI45MN.cjs +680 -0
  10. package/dist/{chunk-3BSZ55L3.cjs → chunk-BPNL5YFQ.cjs} +158 -24
  11. package/dist/chunk-CXDHOMHG.js +1097 -0
  12. package/dist/chunk-DUBCL3WF.cjs +749 -0
  13. package/dist/chunk-DWK5ZQZ4.js +642 -0
  14. package/dist/chunk-E6E3NQYU.js +680 -0
  15. package/dist/chunk-EFM5T7OM.js +698 -0
  16. package/dist/chunk-ENURAVHI.cjs +680 -0
  17. package/dist/{chunk-5WFXFLL4.cjs → chunk-FUFTMAQD.cjs} +96 -63
  18. package/dist/{chunk-2NARXGVA.cjs → chunk-FWISIR26.cjs} +5 -1
  19. package/dist/{chunk-YPXLV5YK.js → chunk-FXZ2MYKJ.js} +376 -38
  20. package/dist/chunk-G26SJ6XS.cjs +1341 -0
  21. package/dist/chunk-G7GI7LJA.js +737 -0
  22. package/dist/chunk-GXEW4COZ.cjs +737 -0
  23. package/dist/chunk-HSVYF6XX.cjs +1361 -0
  24. package/dist/chunk-IBVM4DMJ.cjs +1097 -0
  25. package/dist/chunk-IV2H5CFI.cjs +1275 -0
  26. package/dist/chunk-IV2HRJVT.js +1198 -0
  27. package/dist/chunk-JSQM2MG3.js +680 -0
  28. package/dist/chunk-K4OLITS2.cjs +1055 -0
  29. package/dist/{chunk-UOBWHJA5.js → chunk-KFTCU2P6.js} +2 -3
  30. package/dist/chunk-LH4NTURO.js +1361 -0
  31. package/dist/{chunk-DC4L6ZIT.js → chunk-MFAP7R6L.js} +154 -20
  32. package/dist/chunk-NGVE2RZT.js +1097 -0
  33. package/dist/chunk-NUX5BHWO.js +1341 -0
  34. package/dist/chunk-PJRA2TQ5.js +1055 -0
  35. package/dist/chunk-RVJAGE4S.cjs +1198 -0
  36. package/dist/{chunk-QYG6KGOV.cjs → chunk-RWQ43Z4F.cjs} +2 -3
  37. package/dist/chunk-RZHAPEXO.js +749 -0
  38. package/dist/chunk-ST24APEO.js +1109 -0
  39. package/dist/chunk-SYHW3FLI.cjs +642 -0
  40. package/dist/chunk-TLYNOOQP.cjs +432 -0
  41. package/dist/{chunk-23YG7F46.js → chunk-TMNGRIPL.js} +153 -51
  42. package/dist/{chunk-IQPDRQVC.js → chunk-U2B3B42P.js} +62 -29
  43. package/dist/chunk-UNS34PTA.cjs +680 -0
  44. package/dist/chunk-UR6JKS56.js +432 -0
  45. package/dist/{chunk-GDRGWFEK.cjs → chunk-UYXTY6ZQ.cjs} +376 -38
  46. package/dist/chunk-XUUCOLWU.cjs +1097 -0
  47. package/dist/chunk-YG5KJESI.js +641 -0
  48. package/dist/chunk-ZBKE2QRQ.js +1401 -0
  49. package/dist/chunk-ZSPO2GF2.cjs +1401 -0
  50. package/dist/codec-95k8CAu5.d.cts +35 -0
  51. package/dist/codec-AFuOxfsO.d.ts +60 -0
  52. package/dist/codec-B-UJ5Iow.d.cts +75 -0
  53. package/dist/codec-BC5jfvMb.d.ts +35 -0
  54. package/dist/codec-BECYPfY8.d.ts +35 -0
  55. package/dist/codec-BsPU1vNC.d.ts +39 -0
  56. package/dist/codec-BvpuF-6u.d.cts +39 -0
  57. package/dist/codec-C8jZI5Cx.d.cts +39 -0
  58. package/dist/codec-CAevkgf5.d.cts +33 -0
  59. package/dist/codec-CSUqCrRs.d.ts +39 -0
  60. package/dist/codec-C_HMXNK_.d.ts +33 -0
  61. package/dist/{codec-CTvFtQQI.d.cts → codec-CpuvYTSV.d.cts} +5 -5
  62. package/dist/codec-D0x8-SCw.d.cts +35 -0
  63. package/dist/codec-D7ARhpG1.d.ts +75 -0
  64. package/dist/codec-DNAUGshO.d.cts +60 -0
  65. package/dist/codec-DPx_QNn0.d.ts +31 -0
  66. package/dist/{codec-qPzfmLNu.d.ts → codec-DRhCx_hw.d.ts} +5 -5
  67. package/dist/codec-Db7YPe3l.d.ts +31 -0
  68. package/dist/codec-axkJpb7D.d.cts +31 -0
  69. package/dist/codec-ujAbFaep.d.cts +31 -0
  70. package/dist/draft10-session.cjs +6 -0
  71. package/dist/draft10-session.d.cts +8 -0
  72. package/dist/draft10-session.d.ts +8 -0
  73. package/dist/draft10-session.js +6 -0
  74. package/dist/draft10.cjs +115 -0
  75. package/dist/draft10.d.cts +95 -0
  76. package/dist/draft10.d.ts +95 -0
  77. package/dist/draft10.js +115 -0
  78. package/dist/draft11-session.cjs +6 -0
  79. package/dist/draft11-session.d.cts +8 -0
  80. package/dist/draft11-session.d.ts +8 -0
  81. package/dist/draft11-session.js +6 -0
  82. package/dist/draft11.cjs +109 -0
  83. package/dist/draft11.d.cts +99 -0
  84. package/dist/draft11.d.ts +99 -0
  85. package/dist/draft11.js +109 -0
  86. package/dist/draft12-session.cjs +6 -0
  87. package/dist/draft12-session.d.cts +8 -0
  88. package/dist/draft12-session.d.ts +8 -0
  89. package/dist/draft12-session.js +6 -0
  90. package/dist/draft12.cjs +117 -0
  91. package/dist/draft12.d.cts +106 -0
  92. package/dist/draft12.d.ts +106 -0
  93. package/dist/draft12.js +117 -0
  94. package/dist/draft13-session.cjs +6 -0
  95. package/dist/draft13-session.d.cts +8 -0
  96. package/dist/draft13-session.d.ts +8 -0
  97. package/dist/draft13-session.js +6 -0
  98. package/dist/draft13.cjs +119 -0
  99. package/dist/draft13.d.cts +108 -0
  100. package/dist/draft13.d.ts +108 -0
  101. package/dist/draft13.js +119 -0
  102. package/dist/draft14-session.cjs +2 -2
  103. package/dist/draft14-session.d.cts +4 -4
  104. package/dist/draft14-session.d.ts +4 -4
  105. package/dist/draft14-session.js +1 -1
  106. package/dist/draft14.cjs +4 -4
  107. package/dist/draft14.d.cts +27 -15
  108. package/dist/draft14.d.ts +27 -15
  109. package/dist/draft14.js +3 -3
  110. package/dist/draft15-session.cjs +6 -0
  111. package/dist/draft15-session.d.cts +8 -0
  112. package/dist/draft15-session.d.ts +8 -0
  113. package/dist/draft15-session.js +6 -0
  114. package/dist/draft15.cjs +111 -0
  115. package/dist/draft15.d.cts +93 -0
  116. package/dist/draft15.d.ts +93 -0
  117. package/dist/draft15.js +111 -0
  118. package/dist/draft16-session.cjs +6 -0
  119. package/dist/draft16-session.d.cts +8 -0
  120. package/dist/draft16-session.d.ts +8 -0
  121. package/dist/draft16-session.js +6 -0
  122. package/dist/draft16.cjs +113 -0
  123. package/dist/draft16.d.cts +94 -0
  124. package/dist/draft16.d.ts +94 -0
  125. package/dist/draft16.js +113 -0
  126. package/dist/draft17-session.cjs +8 -0
  127. package/dist/draft17-session.d.cts +51 -0
  128. package/dist/draft17-session.d.ts +51 -0
  129. package/dist/draft17-session.js +8 -0
  130. package/dist/draft17.cjs +99 -0
  131. package/dist/draft17.d.cts +40 -0
  132. package/dist/draft17.d.ts +40 -0
  133. package/dist/draft17.js +99 -0
  134. package/dist/draft7-session.cjs +3 -3
  135. package/dist/draft7-session.d.cts +3 -3
  136. package/dist/draft7-session.d.ts +3 -3
  137. package/dist/draft7-session.js +2 -2
  138. package/dist/draft7.cjs +6 -6
  139. package/dist/draft7.d.cts +10 -10
  140. package/dist/draft7.d.ts +10 -10
  141. package/dist/draft7.js +3 -3
  142. package/dist/draft8-session.cjs +6 -0
  143. package/dist/draft8-session.d.cts +8 -0
  144. package/dist/draft8-session.d.ts +8 -0
  145. package/dist/draft8-session.js +6 -0
  146. package/dist/draft8.cjs +115 -0
  147. package/dist/draft8.d.cts +95 -0
  148. package/dist/draft8.d.ts +95 -0
  149. package/dist/draft8.js +115 -0
  150. package/dist/draft9-session.cjs +6 -0
  151. package/dist/draft9-session.d.cts +8 -0
  152. package/dist/draft9-session.d.ts +8 -0
  153. package/dist/draft9-session.js +6 -0
  154. package/dist/draft9.cjs +115 -0
  155. package/dist/draft9.d.cts +95 -0
  156. package/dist/draft9.d.ts +95 -0
  157. package/dist/draft9.js +115 -0
  158. package/dist/index.cjs +79 -7
  159. package/dist/index.d.cts +71 -8
  160. package/dist/index.d.ts +71 -8
  161. package/dist/index.js +77 -5
  162. package/dist/{session-types-B9NIf7_F.d.ts → session-types-CJIFbTPd.d.ts} +20 -20
  163. package/dist/{session-types-CCo-oA-d.d.cts → session-types-Cbq8IGCP.d.cts} +20 -20
  164. package/dist/session.cjs +5 -5
  165. package/dist/session.d.cts +3 -3
  166. package/dist/session.d.ts +3 -3
  167. package/dist/session.js +5 -5
  168. package/dist/types-4VxSL2Ho.d.cts +261 -0
  169. package/dist/types-4VxSL2Ho.d.ts +261 -0
  170. package/dist/types-B2afJZM-.d.cts +236 -0
  171. package/dist/types-B2afJZM-.d.ts +236 -0
  172. package/dist/{types-CIk5W10V.d.ts → types-BTFeKYCb.d.cts} +37 -37
  173. package/dist/{types-CIk5W10V.d.cts → types-BTFeKYCb.d.ts} +37 -37
  174. package/dist/types-Bg6QYNVt.d.cts +290 -0
  175. package/dist/types-Bg6QYNVt.d.ts +290 -0
  176. package/dist/types-C_1HrqBl.d.cts +306 -0
  177. package/dist/types-C_1HrqBl.d.ts +306 -0
  178. package/dist/types-Cw4WE9dh.d.cts +261 -0
  179. package/dist/types-Cw4WE9dh.d.ts +261 -0
  180. package/dist/types-D5gNQiDj.d.cts +261 -0
  181. package/dist/types-D5gNQiDj.d.ts +261 -0
  182. package/dist/types-DqCDFqgB.d.cts +230 -0
  183. package/dist/types-DqCDFqgB.d.ts +230 -0
  184. package/dist/types-ERexTpT8.d.cts +217 -0
  185. package/dist/types-ERexTpT8.d.ts +217 -0
  186. package/dist/{types-ClXELFGN.d.cts → types-QNXoxC9Y.d.cts} +36 -41
  187. package/dist/{types-ClXELFGN.d.ts → types-QNXoxC9Y.d.ts} +36 -41
  188. package/dist/types-r-CasCf1.d.cts +262 -0
  189. package/dist/types-r-CasCf1.d.ts +262 -0
  190. package/package.json +116 -8
  191. package/src/core/buffer-reader.ts +16 -9
  192. package/src/core/buffer-writer.ts +2 -2
  193. package/src/core/errors.ts +1 -1
  194. package/src/core/session-types.ts +28 -41
  195. package/src/core/types.ts +92 -75
  196. package/src/drafts/draft07/announce-fsm.ts +1 -1
  197. package/src/drafts/draft07/codec.ts +235 -118
  198. package/src/drafts/draft07/index.ts +43 -44
  199. package/src/drafts/draft07/messages.ts +1 -1
  200. package/src/drafts/draft07/parameters.ts +2 -2
  201. package/src/drafts/draft07/rules.ts +67 -38
  202. package/src/drafts/draft07/session-fsm.ts +330 -117
  203. package/src/drafts/draft07/session.ts +10 -10
  204. package/src/drafts/draft07/subscription-fsm.ts +1 -1
  205. package/src/drafts/draft07/varint.ts +4 -4
  206. package/src/drafts/draft08/codec.ts +1254 -0
  207. package/src/drafts/draft08/index.ts +125 -0
  208. package/src/drafts/draft08/messages.ts +72 -0
  209. package/src/drafts/draft08/rules.ts +91 -0
  210. package/src/drafts/draft08/session-fsm.ts +718 -0
  211. package/src/drafts/draft08/session.ts +26 -0
  212. package/src/drafts/draft08/types.ts +377 -0
  213. package/src/drafts/draft09/codec.ts +1235 -0
  214. package/src/drafts/draft09/index.ts +125 -0
  215. package/src/drafts/draft09/messages.ts +72 -0
  216. package/src/drafts/draft09/rules.ts +91 -0
  217. package/src/drafts/draft09/session-fsm.ts +718 -0
  218. package/src/drafts/draft09/session.ts +26 -0
  219. package/src/drafts/draft09/types.ts +376 -0
  220. package/src/drafts/draft10/codec.ts +1235 -0
  221. package/src/drafts/draft10/index.ts +125 -0
  222. package/src/drafts/draft10/messages.ts +72 -0
  223. package/src/drafts/draft10/rules.ts +91 -0
  224. package/src/drafts/draft10/session-fsm.ts +718 -0
  225. package/src/drafts/draft10/session.ts +26 -0
  226. package/src/drafts/draft10/types.ts +376 -0
  227. package/src/drafts/draft11/codec.ts +1198 -0
  228. package/src/drafts/draft11/index.ts +123 -0
  229. package/src/drafts/draft11/messages.ts +71 -0
  230. package/src/drafts/draft11/rules.ts +100 -0
  231. package/src/drafts/draft11/session-fsm.ts +758 -0
  232. package/src/drafts/draft11/session.ts +26 -0
  233. package/src/drafts/draft11/types.ts +375 -0
  234. package/src/drafts/draft12/codec.ts +1354 -0
  235. package/src/drafts/draft12/index.ts +130 -0
  236. package/src/drafts/draft12/messages.ts +84 -0
  237. package/src/drafts/draft12/rules.ts +106 -0
  238. package/src/drafts/draft12/session-fsm.ts +805 -0
  239. package/src/drafts/draft12/session.ts +26 -0
  240. package/src/drafts/draft12/types.ts +414 -0
  241. package/src/drafts/draft13/codec.ts +1438 -0
  242. package/src/drafts/draft13/index.ts +132 -0
  243. package/src/drafts/draft13/messages.ts +86 -0
  244. package/src/drafts/draft13/rules.ts +108 -0
  245. package/src/drafts/draft13/session-fsm.ts +819 -0
  246. package/src/drafts/draft13/session.ts +26 -0
  247. package/src/drafts/draft13/types.ts +433 -0
  248. package/src/drafts/draft14/codec.ts +339 -189
  249. package/src/drafts/draft14/index.ts +103 -108
  250. package/src/drafts/draft14/messages.ts +61 -61
  251. package/src/drafts/draft14/rules.ts +77 -34
  252. package/src/drafts/draft14/session-fsm.ts +640 -147
  253. package/src/drafts/draft14/session.ts +13 -13
  254. package/src/drafts/draft14/types.ts +68 -68
  255. package/src/drafts/draft15/codec.ts +1661 -0
  256. package/src/drafts/draft15/index.ts +121 -0
  257. package/src/drafts/draft15/messages.ts +64 -0
  258. package/src/drafts/draft15/rules.ts +95 -0
  259. package/src/drafts/draft15/session-fsm.ts +687 -0
  260. package/src/drafts/draft15/session.ts +26 -0
  261. package/src/drafts/draft15/types.ts +336 -0
  262. package/src/drafts/draft16/codec.ts +1623 -0
  263. package/src/drafts/draft16/index.ts +123 -0
  264. package/src/drafts/draft16/messages.ts +67 -0
  265. package/src/drafts/draft16/rules.ts +96 -0
  266. package/src/drafts/draft16/session-fsm.ts +682 -0
  267. package/src/drafts/draft16/session.ts +26 -0
  268. package/src/drafts/draft16/types.ts +354 -0
  269. package/src/drafts/draft17/codec.ts +1621 -0
  270. package/src/drafts/draft17/index.ts +105 -0
  271. package/src/drafts/draft17/messages.ts +53 -0
  272. package/src/drafts/draft17/rules.ts +85 -0
  273. package/src/drafts/draft17/session-fsm.ts +437 -0
  274. package/src/drafts/draft17/session.ts +15 -0
  275. package/src/drafts/draft17/types.ts +310 -0
  276. package/src/index.ts +283 -33
  277. package/src/session.ts +20 -20
@@ -1,33 +1,51 @@
1
- import type { MoqtMessage, MoqtMessageType } from '../../core/types.js';
2
1
  import type {
3
- SessionPhase,
4
- TransitionResult,
5
- ValidationResult,
2
+ AnnounceState,
6
3
  ProtocolViolation,
4
+ SessionPhase,
7
5
  SideEffect,
8
6
  SubscriptionState,
9
- AnnounceState,
10
- } from '../../core/session-types.js';
11
- import { getLegalOutgoing, getLegalIncoming, CLIENT_ONLY_MESSAGES, SERVER_ONLY_MESSAGES } from './rules.js';
12
-
13
- function violation(code: ProtocolViolation['code'], message: string, currentPhase: SessionPhase, offendingMessage: MoqtMessageType): ProtocolViolation {
7
+ TransitionResult,
8
+ ValidationResult,
9
+ } from "../../core/session-types.js";
10
+ import type { MoqtMessage, MoqtMessageType } from "../../core/types.js";
11
+ import {
12
+ CLIENT_ONLY_MESSAGES,
13
+ getLegalIncoming,
14
+ getLegalOutgoing,
15
+ SERVER_ONLY_MESSAGES,
16
+ } from "./rules.js";
17
+
18
+ function violation(
19
+ code: ProtocolViolation["code"],
20
+ message: string,
21
+ currentPhase: SessionPhase,
22
+ offendingMessage: MoqtMessageType,
23
+ ): ProtocolViolation {
14
24
  return { code, message, currentPhase, offendingMessage };
15
25
  }
16
26
 
17
27
  export class SessionFSM {
18
- private _phase: SessionPhase = 'idle';
19
- private _role: 'client' | 'server';
28
+ private _phase: SessionPhase = "idle";
29
+ private _role: "client" | "server";
20
30
  private _subscriptions = new Map<bigint, SubscriptionState>();
21
31
  private _announces = new Map<string, AnnounceState>();
22
32
 
23
- constructor(role: 'client' | 'server') {
33
+ constructor(role: "client" | "server") {
24
34
  this._role = role;
25
35
  }
26
36
 
27
- get phase(): SessionPhase { return this._phase; }
28
- get role(): 'client' | 'server' { return this._role; }
29
- get subscriptions(): ReadonlyMap<bigint, SubscriptionState> { return this._subscriptions; }
30
- get announces(): ReadonlyMap<string, AnnounceState> { return this._announces; }
37
+ get phase(): SessionPhase {
38
+ return this._phase;
39
+ }
40
+ get role(): "client" | "server" {
41
+ return this._role;
42
+ }
43
+ get subscriptions(): ReadonlyMap<bigint, SubscriptionState> {
44
+ return this._subscriptions;
45
+ }
46
+ get announces(): ReadonlyMap<string, AnnounceState> {
47
+ return this._announces;
48
+ }
31
49
 
32
50
  get legalOutgoing(): ReadonlySet<MoqtMessageType> {
33
51
  return getLegalOutgoing(this._phase, this._role);
@@ -38,27 +56,43 @@ export class SessionFSM {
38
56
  }
39
57
 
40
58
  // Validate role constraints
41
- private checkRole(message: MoqtMessage, direction: 'inbound' | 'outbound'): ProtocolViolation | null {
42
- const senderRole = direction === 'outbound' ? this._role : (this._role === 'client' ? 'server' : 'client');
43
-
44
- if (CLIENT_ONLY_MESSAGES.has(message.type) && senderRole !== 'client') {
45
- return violation('ROLE_VIOLATION', `${message.type} can only be sent by client`, this._phase, message.type);
59
+ private checkRole(
60
+ message: MoqtMessage,
61
+ direction: "inbound" | "outbound",
62
+ ): ProtocolViolation | null {
63
+ const senderRole =
64
+ direction === "outbound" ? this._role : this._role === "client" ? "server" : "client";
65
+
66
+ if (CLIENT_ONLY_MESSAGES.has(message.type) && senderRole !== "client") {
67
+ return violation(
68
+ "ROLE_VIOLATION",
69
+ `${message.type} can only be sent by client`,
70
+ this._phase,
71
+ message.type,
72
+ );
46
73
  }
47
- if (SERVER_ONLY_MESSAGES.has(message.type) && senderRole !== 'server') {
48
- return violation('ROLE_VIOLATION', `${message.type} can only be sent by server`, this._phase, message.type);
74
+ if (SERVER_ONLY_MESSAGES.has(message.type) && senderRole !== "server") {
75
+ return violation(
76
+ "ROLE_VIOLATION",
77
+ `${message.type} can only be sent by server`,
78
+ this._phase,
79
+ message.type,
80
+ );
49
81
  }
50
82
  return null;
51
83
  }
52
84
 
53
85
  validateOutgoing(message: MoqtMessage): ValidationResult {
54
- const roleViolation = this.checkRole(message, 'outbound');
86
+ const roleViolation = this.checkRole(message, "outbound");
55
87
  if (roleViolation) return { ok: false, violation: roleViolation };
56
88
 
57
89
  if (!this.legalOutgoing.has(message.type)) {
58
90
  return {
59
91
  ok: false,
60
92
  violation: violation(
61
- this._phase === 'idle' || this._phase === 'setup' ? 'MESSAGE_BEFORE_SETUP' : 'UNEXPECTED_MESSAGE',
93
+ this._phase === "idle" || this._phase === "setup"
94
+ ? "MESSAGE_BEFORE_SETUP"
95
+ : "UNEXPECTED_MESSAGE",
62
96
  `Cannot send ${message.type} in phase ${this._phase}`,
63
97
  this._phase,
64
98
  message.type,
@@ -69,59 +103,62 @@ export class SessionFSM {
69
103
  }
70
104
 
71
105
  receive(message: MoqtMessage): TransitionResult {
72
- const roleViolation = this.checkRole(message, 'inbound');
106
+ const roleViolation = this.checkRole(message, "inbound");
73
107
  if (roleViolation) return { ok: false, violation: roleViolation };
74
108
 
75
- return this.applyTransition(message, 'inbound');
109
+ return this.applyTransition(message, "inbound");
76
110
  }
77
111
 
78
112
  send(message: MoqtMessage): TransitionResult {
79
- const roleViolation = this.checkRole(message, 'outbound');
113
+ const roleViolation = this.checkRole(message, "outbound");
80
114
  if (roleViolation) return { ok: false, violation: roleViolation };
81
115
 
82
- return this.applyTransition(message, 'outbound');
116
+ return this.applyTransition(message, "outbound");
83
117
  }
84
118
 
85
- private applyTransition(message: MoqtMessage, direction: 'inbound' | 'outbound'): TransitionResult {
119
+ private applyTransition(
120
+ message: MoqtMessage,
121
+ direction: "inbound" | "outbound",
122
+ ): TransitionResult {
86
123
  const sideEffects: SideEffect[] = [];
87
124
 
88
125
  switch (message.type) {
89
- case 'client_setup':
126
+ case "client_setup":
90
127
  return this.handleClientSetup(message, direction);
91
- case 'server_setup':
128
+ case "server_setup":
92
129
  return this.handleServerSetup(message, direction);
93
- case 'goaway':
130
+ case "goaway":
94
131
  return this.handleGoAway(message, direction, sideEffects);
95
132
 
96
133
  // Subscription lifecycle
97
- case 'subscribe':
134
+ case "subscribe":
98
135
  return this.handleSubscribe(message, direction, sideEffects);
99
- case 'subscribe_ok':
136
+ case "subscribe_ok":
100
137
  return this.handleSubscribeOk(message, direction, sideEffects);
101
- case 'subscribe_error':
138
+ case "subscribe_error":
102
139
  return this.handleSubscribeError(message, direction, sideEffects);
103
- case 'subscribe_done':
140
+ case "subscribe_done":
104
141
  return this.handleSubscribeDone(message, direction, sideEffects);
105
- case 'unsubscribe':
142
+ case "unsubscribe":
106
143
  return this.handleUnsubscribe(message, direction, sideEffects);
107
144
 
108
145
  // Announce lifecycle
109
- case 'announce':
146
+ case "announce":
110
147
  return this.handleAnnounce(message, direction, sideEffects);
111
- case 'announce_ok':
148
+ case "announce_ok":
112
149
  return this.handleAnnounceOk(message, direction, sideEffects);
113
- case 'announce_error':
150
+ case "announce_error":
114
151
  return this.handleAnnounceError(message, direction, sideEffects);
115
- case 'announce_cancel':
152
+ case "announce_cancel":
116
153
  return this.handleAnnounceCancel(message, direction, sideEffects);
117
- case 'unannounce':
154
+ case "unannounce":
118
155
  return this.handleUnannounce(message, direction, sideEffects);
119
156
 
120
157
  // Fetch lifecycle
121
- case 'fetch':
122
- case 'fetch_ok':
123
- case 'fetch_error':
124
- case 'fetch_cancel':
158
+ case "fetch":
159
+ case "fetch_ok":
160
+ case "fetch_error":
161
+ case "fetch_cancel":
125
162
  return this.handleReadyPhaseMessage(message);
126
163
 
127
164
  // Other ready-phase messages
@@ -130,46 +167,98 @@ export class SessionFSM {
130
167
  }
131
168
  }
132
169
 
133
- private handleClientSetup(_message: MoqtMessage, direction: 'inbound' | 'outbound'): TransitionResult {
134
- if (this._phase !== 'idle') {
135
- return { ok: false, violation: violation('SETUP_VIOLATION', 'CLIENT_SETUP already sent/received', this._phase, 'client_setup') };
170
+ private handleClientSetup(
171
+ _message: MoqtMessage,
172
+ direction: "inbound" | "outbound",
173
+ ): TransitionResult {
174
+ if (this._phase !== "idle") {
175
+ return {
176
+ ok: false,
177
+ violation: violation(
178
+ "SETUP_VIOLATION",
179
+ "CLIENT_SETUP already sent/received",
180
+ this._phase,
181
+ "client_setup",
182
+ ),
183
+ };
136
184
  }
137
185
 
138
- if (direction === 'outbound' && this._role !== 'client') {
139
- return { ok: false, violation: violation('ROLE_VIOLATION', 'Only client can send CLIENT_SETUP', this._phase, 'client_setup') };
186
+ if (direction === "outbound" && this._role !== "client") {
187
+ return {
188
+ ok: false,
189
+ violation: violation(
190
+ "ROLE_VIOLATION",
191
+ "Only client can send CLIENT_SETUP",
192
+ this._phase,
193
+ "client_setup",
194
+ ),
195
+ };
140
196
  }
141
197
 
142
- this._phase = 'setup';
198
+ this._phase = "setup";
143
199
  return { ok: true, phase: this._phase, sideEffects: [] };
144
200
  }
145
201
 
146
- private handleServerSetup(_message: MoqtMessage, direction: 'inbound' | 'outbound'): TransitionResult {
147
- if (this._phase !== 'setup') {
148
- return { ok: false, violation: violation('SETUP_VIOLATION', 'SERVER_SETUP before CLIENT_SETUP', this._phase, 'server_setup') };
202
+ private handleServerSetup(
203
+ _message: MoqtMessage,
204
+ direction: "inbound" | "outbound",
205
+ ): TransitionResult {
206
+ if (this._phase !== "setup") {
207
+ return {
208
+ ok: false,
209
+ violation: violation(
210
+ "SETUP_VIOLATION",
211
+ "SERVER_SETUP before CLIENT_SETUP",
212
+ this._phase,
213
+ "server_setup",
214
+ ),
215
+ };
149
216
  }
150
217
 
151
- if (direction === 'outbound' && this._role !== 'server') {
152
- return { ok: false, violation: violation('ROLE_VIOLATION', 'Only server can send SERVER_SETUP', this._phase, 'server_setup') };
218
+ if (direction === "outbound" && this._role !== "server") {
219
+ return {
220
+ ok: false,
221
+ violation: violation(
222
+ "ROLE_VIOLATION",
223
+ "Only server can send SERVER_SETUP",
224
+ this._phase,
225
+ "server_setup",
226
+ ),
227
+ };
153
228
  }
154
229
 
155
- this._phase = 'ready';
156
- return { ok: true, phase: this._phase, sideEffects: [{ type: 'session-ready' }] };
230
+ this._phase = "ready";
231
+ return { ok: true, phase: this._phase, sideEffects: [{ type: "session-ready" }] };
157
232
  }
158
233
 
159
- private handleGoAway(message: MoqtMessage, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult {
160
- if (this._phase !== 'ready' && this._phase !== 'draining') {
161
- return { ok: false, violation: violation('UNEXPECTED_MESSAGE', `GOAWAY not valid in phase ${this._phase}`, this._phase, 'goaway') };
234
+ private handleGoAway(
235
+ message: MoqtMessage,
236
+ _direction: "inbound" | "outbound",
237
+ sideEffects: SideEffect[],
238
+ ): TransitionResult {
239
+ if (this._phase !== "ready" && this._phase !== "draining") {
240
+ return {
241
+ ok: false,
242
+ violation: violation(
243
+ "UNEXPECTED_MESSAGE",
244
+ `GOAWAY not valid in phase ${this._phase}`,
245
+ this._phase,
246
+ "goaway",
247
+ ),
248
+ };
162
249
  }
163
- this._phase = 'draining';
164
- const goaway = message as import('../../core/types.js').GoAway;
165
- sideEffects.push({ type: 'session-draining', goAwayUri: goaway.newSessionUri });
250
+ this._phase = "draining";
251
+ const goaway = message as import("../../core/types.js").GoAway;
252
+ sideEffects.push({ type: "session-draining", goAwayUri: goaway.newSessionUri });
166
253
  return { ok: true, phase: this._phase, sideEffects };
167
254
  }
168
255
 
169
256
  private requireReady(msgType: MoqtMessageType): ProtocolViolation | null {
170
- if (this._phase !== 'ready' && this._phase !== 'draining') {
257
+ if (this._phase !== "ready" && this._phase !== "draining") {
171
258
  return violation(
172
- this._phase === 'idle' || this._phase === 'setup' ? 'MESSAGE_BEFORE_SETUP' : 'UNEXPECTED_MESSAGE',
259
+ this._phase === "idle" || this._phase === "setup"
260
+ ? "MESSAGE_BEFORE_SETUP"
261
+ : "UNEXPECTED_MESSAGE",
173
262
  `${msgType} requires ready phase, current: ${this._phase}`,
174
263
  this._phase,
175
264
  msgType,
@@ -178,18 +267,30 @@ export class SessionFSM {
178
267
  return null;
179
268
  }
180
269
 
181
- private handleSubscribe(message: MoqtMessage, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult {
270
+ private handleSubscribe(
271
+ message: MoqtMessage,
272
+ _direction: "inbound" | "outbound",
273
+ sideEffects: SideEffect[],
274
+ ): TransitionResult {
182
275
  const err = this.requireReady(message.type);
183
276
  if (err) return { ok: false, violation: err };
184
277
 
185
- const sub = message as import('../../core/types.js').Subscribe;
278
+ const sub = message as import("../../core/types.js").Subscribe;
186
279
  if (this._subscriptions.has(sub.subscribeId)) {
187
- return { ok: false, violation: violation('DUPLICATE_SUBSCRIBE_ID', `Subscribe ID ${sub.subscribeId} already exists`, this._phase, message.type) };
280
+ return {
281
+ ok: false,
282
+ violation: violation(
283
+ "DUPLICATE_SUBSCRIBE_ID",
284
+ `Subscribe ID ${sub.subscribeId} already exists`,
285
+ this._phase,
286
+ message.type,
287
+ ),
288
+ };
188
289
  }
189
290
 
190
291
  this._subscriptions.set(sub.subscribeId, {
191
292
  subscribeId: sub.subscribeId,
192
- phase: 'pending',
293
+ phase: "pending",
193
294
  trackNamespace: sub.trackNamespace,
194
295
  trackName: sub.trackName,
195
296
  });
@@ -197,144 +298,256 @@ export class SessionFSM {
197
298
  return { ok: true, phase: this._phase, sideEffects };
198
299
  }
199
300
 
200
- private handleSubscribeOk(message: MoqtMessage, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult {
301
+ private handleSubscribeOk(
302
+ message: MoqtMessage,
303
+ _direction: "inbound" | "outbound",
304
+ sideEffects: SideEffect[],
305
+ ): TransitionResult {
201
306
  const err = this.requireReady(message.type);
202
307
  if (err) return { ok: false, violation: err };
203
308
 
204
- const ok = message as import('../../core/types.js').SubscribeOk;
309
+ const ok = message as import("../../core/types.js").SubscribeOk;
205
310
  const existing = this._subscriptions.get(ok.subscribeId);
206
311
  if (!existing) {
207
- return { ok: false, violation: violation('UNKNOWN_SUBSCRIBE_ID', `No subscription with ID ${ok.subscribeId}`, this._phase, message.type) };
312
+ return {
313
+ ok: false,
314
+ violation: violation(
315
+ "UNKNOWN_SUBSCRIBE_ID",
316
+ `No subscription with ID ${ok.subscribeId}`,
317
+ this._phase,
318
+ message.type,
319
+ ),
320
+ };
208
321
  }
209
- if (existing.phase !== 'pending') {
210
- return { ok: false, violation: violation('STATE_VIOLATION', `Subscription ${ok.subscribeId} is ${existing.phase}, not pending`, this._phase, message.type) };
322
+ if (existing.phase !== "pending") {
323
+ return {
324
+ ok: false,
325
+ violation: violation(
326
+ "STATE_VIOLATION",
327
+ `Subscription ${ok.subscribeId} is ${existing.phase}, not pending`,
328
+ this._phase,
329
+ message.type,
330
+ ),
331
+ };
211
332
  }
212
333
 
213
- this._subscriptions.set(ok.subscribeId, { ...existing, phase: 'active' });
214
- sideEffects.push({ type: 'subscription-activated', subscribeId: ok.subscribeId });
334
+ this._subscriptions.set(ok.subscribeId, { ...existing, phase: "active" });
335
+ sideEffects.push({ type: "subscription-activated", subscribeId: ok.subscribeId });
215
336
  return { ok: true, phase: this._phase, sideEffects };
216
337
  }
217
338
 
218
- private handleSubscribeError(message: MoqtMessage, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult {
339
+ private handleSubscribeError(
340
+ message: MoqtMessage,
341
+ _direction: "inbound" | "outbound",
342
+ sideEffects: SideEffect[],
343
+ ): TransitionResult {
219
344
  const err = this.requireReady(message.type);
220
345
  if (err) return { ok: false, violation: err };
221
346
 
222
- const subErr = message as import('../../core/types.js').SubscribeError;
347
+ const subErr = message as import("../../core/types.js").SubscribeError;
223
348
  const existing = this._subscriptions.get(subErr.subscribeId);
224
349
  if (!existing) {
225
- return { ok: false, violation: violation('UNKNOWN_SUBSCRIBE_ID', `No subscription with ID ${subErr.subscribeId}`, this._phase, message.type) };
350
+ return {
351
+ ok: false,
352
+ violation: violation(
353
+ "UNKNOWN_SUBSCRIBE_ID",
354
+ `No subscription with ID ${subErr.subscribeId}`,
355
+ this._phase,
356
+ message.type,
357
+ ),
358
+ };
226
359
  }
227
- if (existing.phase !== 'pending') {
228
- return { ok: false, violation: violation('STATE_VIOLATION', `Subscription ${subErr.subscribeId} is ${existing.phase}, not pending`, this._phase, message.type) };
360
+ if (existing.phase !== "pending") {
361
+ return {
362
+ ok: false,
363
+ violation: violation(
364
+ "STATE_VIOLATION",
365
+ `Subscription ${subErr.subscribeId} is ${existing.phase}, not pending`,
366
+ this._phase,
367
+ message.type,
368
+ ),
369
+ };
229
370
  }
230
371
 
231
- this._subscriptions.set(subErr.subscribeId, { ...existing, phase: 'error' });
232
- sideEffects.push({ type: 'subscription-ended', subscribeId: subErr.subscribeId, reason: subErr.reasonPhrase });
372
+ this._subscriptions.set(subErr.subscribeId, { ...existing, phase: "error" });
373
+ sideEffects.push({
374
+ type: "subscription-ended",
375
+ subscribeId: subErr.subscribeId,
376
+ reason: subErr.reasonPhrase,
377
+ });
233
378
  return { ok: true, phase: this._phase, sideEffects };
234
379
  }
235
380
 
236
- private handleSubscribeDone(message: MoqtMessage, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult {
381
+ private handleSubscribeDone(
382
+ message: MoqtMessage,
383
+ _direction: "inbound" | "outbound",
384
+ sideEffects: SideEffect[],
385
+ ): TransitionResult {
237
386
  const err = this.requireReady(message.type);
238
387
  if (err) return { ok: false, violation: err };
239
388
 
240
- const done = message as import('../../core/types.js').SubscribeDone;
389
+ const done = message as import("../../core/types.js").SubscribeDone;
241
390
  const existing = this._subscriptions.get(done.subscribeId);
242
391
  if (!existing) {
243
- return { ok: false, violation: violation('UNKNOWN_SUBSCRIBE_ID', `No subscription with ID ${done.subscribeId}`, this._phase, message.type) };
392
+ return {
393
+ ok: false,
394
+ violation: violation(
395
+ "UNKNOWN_SUBSCRIBE_ID",
396
+ `No subscription with ID ${done.subscribeId}`,
397
+ this._phase,
398
+ message.type,
399
+ ),
400
+ };
244
401
  }
245
402
 
246
- this._subscriptions.set(done.subscribeId, { ...existing, phase: 'done' });
247
- sideEffects.push({ type: 'subscription-ended', subscribeId: done.subscribeId, reason: done.reasonPhrase });
403
+ this._subscriptions.set(done.subscribeId, { ...existing, phase: "done" });
404
+ sideEffects.push({
405
+ type: "subscription-ended",
406
+ subscribeId: done.subscribeId,
407
+ reason: done.reasonPhrase,
408
+ });
248
409
  return { ok: true, phase: this._phase, sideEffects };
249
410
  }
250
411
 
251
- private handleUnsubscribe(message: MoqtMessage, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult {
412
+ private handleUnsubscribe(
413
+ message: MoqtMessage,
414
+ _direction: "inbound" | "outbound",
415
+ sideEffects: SideEffect[],
416
+ ): TransitionResult {
252
417
  const err = this.requireReady(message.type);
253
418
  if (err) return { ok: false, violation: err };
254
419
 
255
- const unsub = message as import('../../core/types.js').Unsubscribe;
420
+ const unsub = message as import("../../core/types.js").Unsubscribe;
256
421
  const existing = this._subscriptions.get(unsub.subscribeId);
257
422
  if (!existing) {
258
- return { ok: false, violation: violation('UNKNOWN_SUBSCRIBE_ID', `No subscription with ID ${unsub.subscribeId}`, this._phase, message.type) };
423
+ return {
424
+ ok: false,
425
+ violation: violation(
426
+ "UNKNOWN_SUBSCRIBE_ID",
427
+ `No subscription with ID ${unsub.subscribeId}`,
428
+ this._phase,
429
+ message.type,
430
+ ),
431
+ };
259
432
  }
260
433
 
261
- this._subscriptions.set(unsub.subscribeId, { ...existing, phase: 'done' });
262
- sideEffects.push({ type: 'subscription-ended', subscribeId: unsub.subscribeId, reason: 'unsubscribed' });
434
+ this._subscriptions.set(unsub.subscribeId, { ...existing, phase: "done" });
435
+ sideEffects.push({
436
+ type: "subscription-ended",
437
+ subscribeId: unsub.subscribeId,
438
+ reason: "unsubscribed",
439
+ });
263
440
  return { ok: true, phase: this._phase, sideEffects };
264
441
  }
265
442
 
266
443
  // Announce handlers
267
444
  private namespaceKey(ns: string[]): string {
268
- return ns.join('/');
445
+ return ns.join("/");
269
446
  }
270
447
 
271
- private handleAnnounce(message: MoqtMessage, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult {
448
+ private handleAnnounce(
449
+ message: MoqtMessage,
450
+ _direction: "inbound" | "outbound",
451
+ sideEffects: SideEffect[],
452
+ ): TransitionResult {
272
453
  const err = this.requireReady(message.type);
273
454
  if (err) return { ok: false, violation: err };
274
455
 
275
- const ann = message as import('../../core/types.js').Announce;
456
+ const ann = message as import("../../core/types.js").Announce;
276
457
  const key = this.namespaceKey(ann.trackNamespace);
277
458
 
278
- this._announces.set(key, { namespace: ann.trackNamespace, phase: 'pending' });
459
+ this._announces.set(key, { namespace: ann.trackNamespace, phase: "pending" });
279
460
  return { ok: true, phase: this._phase, sideEffects };
280
461
  }
281
462
 
282
- private handleAnnounceOk(message: MoqtMessage, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult {
463
+ private handleAnnounceOk(
464
+ message: MoqtMessage,
465
+ _direction: "inbound" | "outbound",
466
+ sideEffects: SideEffect[],
467
+ ): TransitionResult {
283
468
  const err = this.requireReady(message.type);
284
469
  if (err) return { ok: false, violation: err };
285
470
 
286
- const ok = message as import('../../core/types.js').AnnounceOk;
471
+ const ok = message as import("../../core/types.js").AnnounceOk;
287
472
  const key = this.namespaceKey(ok.trackNamespace);
288
473
  const existing = this._announces.get(key);
289
474
  if (!existing) {
290
- return { ok: false, violation: violation('UNEXPECTED_MESSAGE', `No announce for namespace ${key}`, this._phase, message.type) };
475
+ return {
476
+ ok: false,
477
+ violation: violation(
478
+ "UNEXPECTED_MESSAGE",
479
+ `No announce for namespace ${key}`,
480
+ this._phase,
481
+ message.type,
482
+ ),
483
+ };
291
484
  }
292
485
 
293
- this._announces.set(key, { ...existing, phase: 'active' });
294
- sideEffects.push({ type: 'announce-activated', namespace: ok.trackNamespace });
486
+ this._announces.set(key, { ...existing, phase: "active" });
487
+ sideEffects.push({ type: "announce-activated", namespace: ok.trackNamespace });
295
488
  return { ok: true, phase: this._phase, sideEffects };
296
489
  }
297
490
 
298
- private handleAnnounceError(message: MoqtMessage, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult {
491
+ private handleAnnounceError(
492
+ message: MoqtMessage,
493
+ _direction: "inbound" | "outbound",
494
+ sideEffects: SideEffect[],
495
+ ): TransitionResult {
299
496
  const err = this.requireReady(message.type);
300
497
  if (err) return { ok: false, violation: err };
301
498
 
302
- const annErr = message as import('../../core/types.js').AnnounceError;
499
+ const annErr = message as import("../../core/types.js").AnnounceError;
303
500
  const key = this.namespaceKey(annErr.trackNamespace);
304
501
  const existing = this._announces.get(key);
305
502
  if (!existing) {
306
- return { ok: false, violation: violation('UNEXPECTED_MESSAGE', `No announce for namespace ${key}`, this._phase, message.type) };
503
+ return {
504
+ ok: false,
505
+ violation: violation(
506
+ "UNEXPECTED_MESSAGE",
507
+ `No announce for namespace ${key}`,
508
+ this._phase,
509
+ message.type,
510
+ ),
511
+ };
307
512
  }
308
513
 
309
- this._announces.set(key, { ...existing, phase: 'error' });
310
- sideEffects.push({ type: 'announce-ended', namespace: annErr.trackNamespace });
514
+ this._announces.set(key, { ...existing, phase: "error" });
515
+ sideEffects.push({ type: "announce-ended", namespace: annErr.trackNamespace });
311
516
  return { ok: true, phase: this._phase, sideEffects };
312
517
  }
313
518
 
314
- private handleAnnounceCancel(message: MoqtMessage, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult {
519
+ private handleAnnounceCancel(
520
+ message: MoqtMessage,
521
+ _direction: "inbound" | "outbound",
522
+ sideEffects: SideEffect[],
523
+ ): TransitionResult {
315
524
  const err = this.requireReady(message.type);
316
525
  if (err) return { ok: false, violation: err };
317
526
 
318
- const cancel = message as import('../../core/types.js').AnnounceCancel;
527
+ const cancel = message as import("../../core/types.js").AnnounceCancel;
319
528
  const key = this.namespaceKey(cancel.trackNamespace);
320
529
  const existing = this._announces.get(key);
321
530
  if (existing) {
322
531
  this._announces.delete(key);
323
- sideEffects.push({ type: 'announce-ended', namespace: cancel.trackNamespace });
532
+ sideEffects.push({ type: "announce-ended", namespace: cancel.trackNamespace });
324
533
  }
325
534
  return { ok: true, phase: this._phase, sideEffects };
326
535
  }
327
536
 
328
- private handleUnannounce(message: MoqtMessage, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult {
537
+ private handleUnannounce(
538
+ message: MoqtMessage,
539
+ _direction: "inbound" | "outbound",
540
+ sideEffects: SideEffect[],
541
+ ): TransitionResult {
329
542
  const err = this.requireReady(message.type);
330
543
  if (err) return { ok: false, violation: err };
331
544
 
332
- const unann = message as import('../../core/types.js').Unannounce;
545
+ const unann = message as import("../../core/types.js").Unannounce;
333
546
  const key = this.namespaceKey(unann.trackNamespace);
334
547
  const existing = this._announces.get(key);
335
548
  if (existing) {
336
549
  this._announces.delete(key);
337
- sideEffects.push({ type: 'announce-ended', namespace: unann.trackNamespace });
550
+ sideEffects.push({ type: "announce-ended", namespace: unann.trackNamespace });
338
551
  }
339
552
  return { ok: true, phase: this._phase, sideEffects };
340
553
  }
@@ -346,7 +559,7 @@ export class SessionFSM {
346
559
  }
347
560
 
348
561
  reset(): void {
349
- this._phase = 'idle';
562
+ this._phase = "idle";
350
563
  this._subscriptions.clear();
351
564
  this._announces.clear();
352
565
  }