@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,19 +1,24 @@
1
- import type { Draft14Message, Draft14MessageType } from './types.js';
2
1
  import type {
3
- SessionPhase,
4
- TransitionResult,
5
- ValidationResult,
2
+ AnnounceState,
3
+ FetchState,
6
4
  ProtocolViolation,
5
+ PublishState,
6
+ SessionPhase,
7
7
  SideEffect,
8
8
  SubscriptionState,
9
- AnnounceState,
10
- PublishState,
11
- FetchState,
12
- } from '../../core/session-types.js';
13
- import { getLegalOutgoing, getLegalIncoming, CLIENT_ONLY_MESSAGES, SERVER_ONLY_MESSAGES } from './rules.js';
9
+ TransitionResult,
10
+ ValidationResult,
11
+ } from "../../core/session-types.js";
12
+ import {
13
+ CLIENT_ONLY_MESSAGES,
14
+ getLegalIncoming,
15
+ getLegalOutgoing,
16
+ SERVER_ONLY_MESSAGES,
17
+ } from "./rules.js";
18
+ import type { Draft14Message, Draft14MessageType } from "./types.js";
14
19
 
15
20
  function violation(
16
- code: ProtocolViolation<Draft14MessageType>['code'],
21
+ code: ProtocolViolation<Draft14MessageType>["code"],
17
22
  message: string,
18
23
  currentPhase: SessionPhase,
19
24
  offendingMessage: Draft14MessageType,
@@ -22,23 +27,35 @@ function violation(
22
27
  }
23
28
 
24
29
  export class Draft14SessionFSM {
25
- private _phase: SessionPhase = 'idle';
26
- private _role: 'client' | 'server';
30
+ private _phase: SessionPhase = "idle";
31
+ private _role: "client" | "server";
27
32
  private _subscriptions = new Map<bigint, SubscriptionState>();
28
33
  private _publishes = new Map<bigint, PublishState>();
29
34
  private _fetches = new Map<bigint, FetchState>();
30
35
  private _requestIds = new Set<bigint>();
31
36
 
32
- constructor(role: 'client' | 'server') {
37
+ constructor(role: "client" | "server") {
33
38
  this._role = role;
34
39
  }
35
40
 
36
- get phase(): SessionPhase { return this._phase; }
37
- get role(): 'client' | 'server' { return this._role; }
38
- get subscriptions(): ReadonlyMap<bigint, SubscriptionState> { return this._subscriptions; }
39
- get announces(): ReadonlyMap<string, AnnounceState> { return new Map(); }
40
- get publishes(): ReadonlyMap<bigint, PublishState> { return this._publishes; }
41
- get fetches(): ReadonlyMap<bigint, FetchState> { return this._fetches; }
41
+ get phase(): SessionPhase {
42
+ return this._phase;
43
+ }
44
+ get role(): "client" | "server" {
45
+ return this._role;
46
+ }
47
+ get subscriptions(): ReadonlyMap<bigint, SubscriptionState> {
48
+ return this._subscriptions;
49
+ }
50
+ get announces(): ReadonlyMap<string, AnnounceState> {
51
+ return new Map();
52
+ }
53
+ get publishes(): ReadonlyMap<bigint, PublishState> {
54
+ return this._publishes;
55
+ }
56
+ get fetches(): ReadonlyMap<bigint, FetchState> {
57
+ return this._fetches;
58
+ }
42
59
 
43
60
  get legalOutgoing(): ReadonlySet<Draft14MessageType> {
44
61
  return getLegalOutgoing(this._phase, this._role);
@@ -49,41 +66,73 @@ export class Draft14SessionFSM {
49
66
  }
50
67
 
51
68
  // Validate role constraints
52
- private checkRole(message: Draft14Message, direction: 'inbound' | 'outbound'): ProtocolViolation<Draft14MessageType> | null {
53
- const senderRole = direction === 'outbound' ? this._role : (this._role === 'client' ? 'server' : 'client');
54
-
55
- if (CLIENT_ONLY_MESSAGES.has(message.type) && senderRole !== 'client') {
56
- return violation('ROLE_VIOLATION', `${message.type} can only be sent by client`, this._phase, message.type);
69
+ private checkRole(
70
+ message: Draft14Message,
71
+ direction: "inbound" | "outbound",
72
+ ): ProtocolViolation<Draft14MessageType> | null {
73
+ const senderRole =
74
+ direction === "outbound" ? this._role : this._role === "client" ? "server" : "client";
75
+
76
+ if (CLIENT_ONLY_MESSAGES.has(message.type) && senderRole !== "client") {
77
+ return violation(
78
+ "ROLE_VIOLATION",
79
+ `${message.type} can only be sent by client`,
80
+ this._phase,
81
+ message.type,
82
+ );
57
83
  }
58
- if (SERVER_ONLY_MESSAGES.has(message.type) && senderRole !== 'server') {
59
- return violation('ROLE_VIOLATION', `${message.type} can only be sent by server`, this._phase, message.type);
84
+ if (SERVER_ONLY_MESSAGES.has(message.type) && senderRole !== "server") {
85
+ return violation(
86
+ "ROLE_VIOLATION",
87
+ `${message.type} can only be sent by server`,
88
+ this._phase,
89
+ message.type,
90
+ );
60
91
  }
61
92
  return null;
62
93
  }
63
94
 
64
- private checkDuplicateRequestId(requestId: bigint, msgType: Draft14MessageType): ProtocolViolation<Draft14MessageType> | null {
95
+ private checkDuplicateRequestId(
96
+ requestId: bigint,
97
+ msgType: Draft14MessageType,
98
+ ): ProtocolViolation<Draft14MessageType> | null {
65
99
  if (this._requestIds.has(requestId)) {
66
- return violation('DUPLICATE_REQUEST_ID', `Request ID ${requestId} already in use`, this._phase, msgType);
100
+ return violation(
101
+ "DUPLICATE_REQUEST_ID",
102
+ `Request ID ${requestId} already in use`,
103
+ this._phase,
104
+ msgType,
105
+ );
67
106
  }
68
107
  return null;
69
108
  }
70
109
 
71
- private checkKnownRequestId(requestId: bigint, msgType: Draft14MessageType): ProtocolViolation<Draft14MessageType> | null {
110
+ private checkKnownRequestId(
111
+ requestId: bigint,
112
+ msgType: Draft14MessageType,
113
+ ): ProtocolViolation<Draft14MessageType> | null {
72
114
  if (!this._requestIds.has(requestId)) {
73
- return violation('UNKNOWN_REQUEST_ID', `No request with ID ${requestId}`, this._phase, msgType);
115
+ return violation(
116
+ "UNKNOWN_REQUEST_ID",
117
+ `No request with ID ${requestId}`,
118
+ this._phase,
119
+ msgType,
120
+ );
74
121
  }
75
122
  return null;
76
123
  }
77
124
 
78
125
  validateOutgoing(message: Draft14Message): ValidationResult<Draft14MessageType> {
79
- const roleViolation = this.checkRole(message, 'outbound');
126
+ const roleViolation = this.checkRole(message, "outbound");
80
127
  if (roleViolation) return { ok: false, violation: roleViolation };
81
128
 
82
129
  if (!this.legalOutgoing.has(message.type)) {
83
130
  return {
84
131
  ok: false,
85
132
  violation: violation(
86
- this._phase === 'idle' || this._phase === 'setup' ? 'MESSAGE_BEFORE_SETUP' : 'UNEXPECTED_MESSAGE',
133
+ this._phase === "idle" || this._phase === "setup"
134
+ ? "MESSAGE_BEFORE_SETUP"
135
+ : "UNEXPECTED_MESSAGE",
87
136
  `Cannot send ${message.type} in phase ${this._phase}`,
88
137
  this._phase,
89
138
  message.type,
@@ -94,108 +143,193 @@ export class Draft14SessionFSM {
94
143
  }
95
144
 
96
145
  receive(message: Draft14Message): TransitionResult<Draft14MessageType> {
97
- const roleViolation = this.checkRole(message, 'inbound');
146
+ const roleViolation = this.checkRole(message, "inbound");
98
147
  if (roleViolation) return { ok: false, violation: roleViolation };
99
148
 
100
- return this.applyTransition(message, 'inbound');
149
+ return this.applyTransition(message, "inbound");
101
150
  }
102
151
 
103
152
  send(message: Draft14Message): TransitionResult<Draft14MessageType> {
104
- const roleViolation = this.checkRole(message, 'outbound');
153
+ const roleViolation = this.checkRole(message, "outbound");
105
154
  if (roleViolation) return { ok: false, violation: roleViolation };
106
155
 
107
- return this.applyTransition(message, 'outbound');
156
+ return this.applyTransition(message, "outbound");
108
157
  }
109
158
 
110
- private applyTransition(message: Draft14Message, direction: 'inbound' | 'outbound'): TransitionResult<Draft14MessageType> {
159
+ private applyTransition(
160
+ message: Draft14Message,
161
+ direction: "inbound" | "outbound",
162
+ ): TransitionResult<Draft14MessageType> {
111
163
  const sideEffects: SideEffect[] = [];
112
164
 
113
165
  switch (message.type) {
114
- case 'client_setup':
166
+ case "client_setup":
115
167
  return this.handleClientSetup(message, direction);
116
- case 'server_setup':
168
+ case "server_setup":
117
169
  return this.handleServerSetup(message, direction);
118
- case 'goaway':
170
+ case "goaway":
119
171
  return this.handleGoAway(message, direction, sideEffects);
120
172
 
121
173
  // Subscribe lifecycle
122
- case 'subscribe':
174
+ case "subscribe":
123
175
  return this.handleSubscribe(message, direction, sideEffects);
124
- case 'subscribe_ok':
176
+ case "subscribe_ok":
125
177
  return this.handleSubscribeOk(message, direction, sideEffects);
126
- case 'subscribe_error':
178
+ case "subscribe_error":
127
179
  return this.handleSubscribeError(message, direction, sideEffects);
128
- case 'subscribe_update':
180
+ case "subscribe_update":
129
181
  return this.handleSubscribeUpdate(message, direction, sideEffects);
130
- case 'unsubscribe':
182
+ case "unsubscribe":
131
183
  return this.handleUnsubscribe(message, direction, sideEffects);
132
184
 
133
185
  // Publish lifecycle
134
- case 'publish':
186
+ case "publish":
135
187
  return this.handlePublish(message, direction, sideEffects);
136
- case 'publish_ok':
188
+ case "publish_ok":
137
189
  return this.handlePublishOk(message, direction, sideEffects);
138
- case 'publish_error':
190
+ case "publish_error":
139
191
  return this.handlePublishError(message, direction, sideEffects);
140
- case 'publish_done':
192
+ case "publish_done":
141
193
  return this.handlePublishDone(message, direction, sideEffects);
142
194
 
143
195
  // Fetch lifecycle
144
- case 'fetch':
196
+ case "fetch":
145
197
  return this.handleFetch(message, direction, sideEffects);
146
- case 'fetch_ok':
198
+ case "fetch_ok":
147
199
  return this.handleFetchOk(message, direction, sideEffects);
148
- case 'fetch_error':
200
+ case "fetch_error":
149
201
  return this.handleFetchError(message, direction, sideEffects);
150
- case 'fetch_cancel':
202
+ case "fetch_cancel":
151
203
  return this.handleFetchCancel(message, direction, sideEffects);
152
204
 
153
- // Publish namespace, subscribe namespace, track status, and other ready-phase messages
205
+ // Publish namespace lifecycle
206
+ case "publish_namespace":
207
+ return this.handlePublishNamespace(message, sideEffects);
208
+ case "publish_namespace_ok":
209
+ return this.handlePublishNamespaceOk(message, sideEffects);
210
+ case "publish_namespace_error":
211
+ return this.handlePublishNamespaceError(message, sideEffects);
212
+ case "publish_namespace_done":
213
+ return this.handlePublishNamespaceDone(message, sideEffects);
214
+ case "publish_namespace_cancel":
215
+ return this.handlePublishNamespaceCancel(message, sideEffects);
216
+
217
+ // Subscribe namespace lifecycle
218
+ case "subscribe_namespace":
219
+ return this.handleSubscribeNamespace(message, sideEffects);
220
+ case "subscribe_namespace_ok":
221
+ return this.handleSubscribeNamespaceOk(message, sideEffects);
222
+ case "subscribe_namespace_error":
223
+ return this.handleSubscribeNamespaceError(message, sideEffects);
224
+ case "unsubscribe_namespace":
225
+ return this.handleUnsubscribeNamespace(message, sideEffects);
226
+
227
+ // Track status lifecycle
228
+ case "track_status":
229
+ return this.handleTrackStatus(message, sideEffects);
230
+ case "track_status_ok":
231
+ return this.handleTrackStatusOk(message, sideEffects);
232
+ case "track_status_error":
233
+ return this.handleTrackStatusError(message, sideEffects);
234
+
235
+ // Other ready-phase messages
154
236
  default:
155
237
  return this.handleReadyPhaseMessage(message);
156
238
  }
157
239
  }
158
240
 
159
- private handleClientSetup(_message: Draft14Message, direction: 'inbound' | 'outbound'): TransitionResult<Draft14MessageType> {
160
- if (this._phase !== 'idle') {
161
- return { ok: false, violation: violation('SETUP_VIOLATION', 'CLIENT_SETUP already sent/received', this._phase, 'client_setup') };
241
+ private handleClientSetup(
242
+ _message: Draft14Message,
243
+ direction: "inbound" | "outbound",
244
+ ): TransitionResult<Draft14MessageType> {
245
+ if (this._phase !== "idle") {
246
+ return {
247
+ ok: false,
248
+ violation: violation(
249
+ "SETUP_VIOLATION",
250
+ "CLIENT_SETUP already sent/received",
251
+ this._phase,
252
+ "client_setup",
253
+ ),
254
+ };
162
255
  }
163
256
 
164
- if (direction === 'outbound' && this._role !== 'client') {
165
- return { ok: false, violation: violation('ROLE_VIOLATION', 'Only client can send CLIENT_SETUP', this._phase, 'client_setup') };
257
+ if (direction === "outbound" && this._role !== "client") {
258
+ return {
259
+ ok: false,
260
+ violation: violation(
261
+ "ROLE_VIOLATION",
262
+ "Only client can send CLIENT_SETUP",
263
+ this._phase,
264
+ "client_setup",
265
+ ),
266
+ };
166
267
  }
167
268
 
168
- this._phase = 'setup';
269
+ this._phase = "setup";
169
270
  return { ok: true, phase: this._phase, sideEffects: [] };
170
271
  }
171
272
 
172
- private handleServerSetup(_message: Draft14Message, direction: 'inbound' | 'outbound'): TransitionResult<Draft14MessageType> {
173
- if (this._phase !== 'setup') {
174
- return { ok: false, violation: violation('SETUP_VIOLATION', 'SERVER_SETUP before CLIENT_SETUP', this._phase, 'server_setup') };
273
+ private handleServerSetup(
274
+ _message: Draft14Message,
275
+ direction: "inbound" | "outbound",
276
+ ): TransitionResult<Draft14MessageType> {
277
+ if (this._phase !== "setup") {
278
+ return {
279
+ ok: false,
280
+ violation: violation(
281
+ "SETUP_VIOLATION",
282
+ "SERVER_SETUP before CLIENT_SETUP",
283
+ this._phase,
284
+ "server_setup",
285
+ ),
286
+ };
175
287
  }
176
288
 
177
- if (direction === 'outbound' && this._role !== 'server') {
178
- return { ok: false, violation: violation('ROLE_VIOLATION', 'Only server can send SERVER_SETUP', this._phase, 'server_setup') };
289
+ if (direction === "outbound" && this._role !== "server") {
290
+ return {
291
+ ok: false,
292
+ violation: violation(
293
+ "ROLE_VIOLATION",
294
+ "Only server can send SERVER_SETUP",
295
+ this._phase,
296
+ "server_setup",
297
+ ),
298
+ };
179
299
  }
180
300
 
181
- this._phase = 'ready';
182
- return { ok: true, phase: this._phase, sideEffects: [{ type: 'session-ready' }] };
301
+ this._phase = "ready";
302
+ return { ok: true, phase: this._phase, sideEffects: [{ type: "session-ready" }] };
183
303
  }
184
304
 
185
- private handleGoAway(message: Draft14Message, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult<Draft14MessageType> {
186
- if (this._phase !== 'ready' && this._phase !== 'draining') {
187
- return { ok: false, violation: violation('UNEXPECTED_MESSAGE', `GOAWAY not valid in phase ${this._phase}`, this._phase, 'goaway') };
305
+ private handleGoAway(
306
+ message: Draft14Message,
307
+ _direction: "inbound" | "outbound",
308
+ sideEffects: SideEffect[],
309
+ ): TransitionResult<Draft14MessageType> {
310
+ if (this._phase !== "ready" && this._phase !== "draining") {
311
+ return {
312
+ ok: false,
313
+ violation: violation(
314
+ "UNEXPECTED_MESSAGE",
315
+ `GOAWAY not valid in phase ${this._phase}`,
316
+ this._phase,
317
+ "goaway",
318
+ ),
319
+ };
188
320
  }
189
- this._phase = 'draining';
190
- const goaway = message as import('./types.js').Draft14GoAway;
191
- sideEffects.push({ type: 'session-draining', goAwayUri: goaway.new_session_uri });
321
+ this._phase = "draining";
322
+ const goaway = message as import("./types.js").Draft14GoAway;
323
+ sideEffects.push({ type: "session-draining", goAwayUri: goaway.new_session_uri });
192
324
  return { ok: true, phase: this._phase, sideEffects };
193
325
  }
194
326
 
195
327
  private requireReady(msgType: Draft14MessageType): ProtocolViolation<Draft14MessageType> | null {
196
- if (this._phase !== 'ready' && this._phase !== 'draining') {
328
+ if (this._phase !== "ready" && this._phase !== "draining") {
197
329
  return violation(
198
- this._phase === 'idle' || this._phase === 'setup' ? 'MESSAGE_BEFORE_SETUP' : 'UNEXPECTED_MESSAGE',
330
+ this._phase === "idle" || this._phase === "setup"
331
+ ? "MESSAGE_BEFORE_SETUP"
332
+ : "UNEXPECTED_MESSAGE",
199
333
  `${msgType} requires ready phase, current: ${this._phase}`,
200
334
  this._phase,
201
335
  msgType,
@@ -206,18 +340,22 @@ export class Draft14SessionFSM {
206
340
 
207
341
  // ─── Subscribe lifecycle ──────────────────────────────────────────────────────
208
342
 
209
- private handleSubscribe(message: Draft14Message, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult<Draft14MessageType> {
343
+ private handleSubscribe(
344
+ message: Draft14Message,
345
+ _direction: "inbound" | "outbound",
346
+ sideEffects: SideEffect[],
347
+ ): TransitionResult<Draft14MessageType> {
210
348
  const err = this.requireReady(message.type);
211
349
  if (err) return { ok: false, violation: err };
212
350
 
213
- const sub = message as import('./types.js').Draft14Subscribe;
351
+ const sub = message as import("./types.js").Draft14Subscribe;
214
352
  const dupErr = this.checkDuplicateRequestId(sub.request_id, message.type);
215
353
  if (dupErr) return { ok: false, violation: dupErr };
216
354
 
217
355
  this._requestIds.add(sub.request_id);
218
356
  this._subscriptions.set(sub.request_id, {
219
357
  subscribeId: sub.request_id,
220
- phase: 'pending',
358
+ phase: "pending",
221
359
  trackNamespace: sub.track_namespace,
222
360
  trackName: sub.track_name,
223
361
  });
@@ -225,240 +363,595 @@ export class Draft14SessionFSM {
225
363
  return { ok: true, phase: this._phase, sideEffects };
226
364
  }
227
365
 
228
- private handleSubscribeOk(message: Draft14Message, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult<Draft14MessageType> {
366
+ private handleSubscribeOk(
367
+ message: Draft14Message,
368
+ _direction: "inbound" | "outbound",
369
+ sideEffects: SideEffect[],
370
+ ): TransitionResult<Draft14MessageType> {
229
371
  const err = this.requireReady(message.type);
230
372
  if (err) return { ok: false, violation: err };
231
373
 
232
- const ok = message as import('./types.js').Draft14SubscribeOk;
374
+ const ok = message as import("./types.js").Draft14SubscribeOk;
233
375
  const idErr = this.checkKnownRequestId(ok.request_id, message.type);
234
376
  if (idErr) return { ok: false, violation: idErr };
235
377
 
236
378
  const existing = this._subscriptions.get(ok.request_id);
237
379
  if (!existing) {
238
- return { ok: false, violation: violation('UNKNOWN_REQUEST_ID', `No subscription with request ID ${ok.request_id}`, this._phase, message.type) };
380
+ return {
381
+ ok: false,
382
+ violation: violation(
383
+ "UNKNOWN_REQUEST_ID",
384
+ `No subscription with request ID ${ok.request_id}`,
385
+ this._phase,
386
+ message.type,
387
+ ),
388
+ };
239
389
  }
240
- if (existing.phase !== 'pending') {
241
- return { ok: false, violation: violation('STATE_VIOLATION', `Subscription ${ok.request_id} is ${existing.phase}, not pending`, this._phase, message.type) };
390
+ if (existing.phase !== "pending") {
391
+ return {
392
+ ok: false,
393
+ violation: violation(
394
+ "STATE_VIOLATION",
395
+ `Subscription ${ok.request_id} is ${existing.phase}, not pending`,
396
+ this._phase,
397
+ message.type,
398
+ ),
399
+ };
242
400
  }
243
401
 
244
- this._subscriptions.set(ok.request_id, { ...existing, phase: 'active' });
245
- sideEffects.push({ type: 'subscription-activated', subscribeId: ok.request_id });
402
+ this._subscriptions.set(ok.request_id, { ...existing, phase: "active" });
403
+ sideEffects.push({ type: "subscription-activated", subscribeId: ok.request_id });
246
404
  return { ok: true, phase: this._phase, sideEffects };
247
405
  }
248
406
 
249
- private handleSubscribeError(message: Draft14Message, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult<Draft14MessageType> {
407
+ private handleSubscribeError(
408
+ message: Draft14Message,
409
+ _direction: "inbound" | "outbound",
410
+ sideEffects: SideEffect[],
411
+ ): TransitionResult<Draft14MessageType> {
250
412
  const err = this.requireReady(message.type);
251
413
  if (err) return { ok: false, violation: err };
252
414
 
253
- const subErr = message as import('./types.js').Draft14SubscribeError;
415
+ const subErr = message as import("./types.js").Draft14SubscribeError;
254
416
  const idErr = this.checkKnownRequestId(subErr.request_id, message.type);
255
417
  if (idErr) return { ok: false, violation: idErr };
256
418
 
257
419
  const existing = this._subscriptions.get(subErr.request_id);
258
420
  if (!existing) {
259
- return { ok: false, violation: violation('UNKNOWN_REQUEST_ID', `No subscription with request ID ${subErr.request_id}`, this._phase, message.type) };
421
+ return {
422
+ ok: false,
423
+ violation: violation(
424
+ "UNKNOWN_REQUEST_ID",
425
+ `No subscription with request ID ${subErr.request_id}`,
426
+ this._phase,
427
+ message.type,
428
+ ),
429
+ };
260
430
  }
261
- if (existing.phase !== 'pending') {
262
- return { ok: false, violation: violation('STATE_VIOLATION', `Subscription ${subErr.request_id} is ${existing.phase}, not pending`, this._phase, message.type) };
431
+ if (existing.phase !== "pending") {
432
+ return {
433
+ ok: false,
434
+ violation: violation(
435
+ "STATE_VIOLATION",
436
+ `Subscription ${subErr.request_id} is ${existing.phase}, not pending`,
437
+ this._phase,
438
+ message.type,
439
+ ),
440
+ };
263
441
  }
264
442
 
265
- this._subscriptions.set(subErr.request_id, { ...existing, phase: 'error' });
266
- sideEffects.push({ type: 'subscription-ended', subscribeId: subErr.request_id, reason: subErr.reason_phrase });
443
+ this._subscriptions.set(subErr.request_id, { ...existing, phase: "error" });
444
+ sideEffects.push({
445
+ type: "subscription-ended",
446
+ subscribeId: subErr.request_id,
447
+ reason: subErr.reason_phrase,
448
+ });
267
449
  return { ok: true, phase: this._phase, sideEffects };
268
450
  }
269
451
 
270
- private handleSubscribeUpdate(message: Draft14Message, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult<Draft14MessageType> {
452
+ private handleSubscribeUpdate(
453
+ message: Draft14Message,
454
+ _direction: "inbound" | "outbound",
455
+ sideEffects: SideEffect[],
456
+ ): TransitionResult<Draft14MessageType> {
271
457
  const err = this.requireReady(message.type);
272
458
  if (err) return { ok: false, violation: err };
273
459
 
274
- const update = message as import('./types.js').Draft14SubscribeUpdate;
460
+ const update = message as import("./types.js").Draft14SubscribeUpdate;
275
461
  const idErr = this.checkKnownRequestId(update.request_id, message.type);
276
462
  if (idErr) return { ok: false, violation: idErr };
277
463
 
278
464
  const existing = this._subscriptions.get(update.request_id);
279
465
  if (!existing) {
280
- return { ok: false, violation: violation('UNKNOWN_REQUEST_ID', `No subscription with request ID ${update.request_id}`, this._phase, message.type) };
466
+ return {
467
+ ok: false,
468
+ violation: violation(
469
+ "UNKNOWN_REQUEST_ID",
470
+ `No subscription with request ID ${update.request_id}`,
471
+ this._phase,
472
+ message.type,
473
+ ),
474
+ };
281
475
  }
282
- if (existing.phase !== 'active') {
283
- return { ok: false, violation: violation('STATE_VIOLATION', `Subscription ${update.request_id} is ${existing.phase}, not active`, this._phase, message.type) };
476
+ if (existing.phase !== "active") {
477
+ return {
478
+ ok: false,
479
+ violation: violation(
480
+ "STATE_VIOLATION",
481
+ `Subscription ${update.request_id} is ${existing.phase}, not active`,
482
+ this._phase,
483
+ message.type,
484
+ ),
485
+ };
284
486
  }
285
487
 
286
488
  return { ok: true, phase: this._phase, sideEffects };
287
489
  }
288
490
 
289
- private handleUnsubscribe(message: Draft14Message, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult<Draft14MessageType> {
491
+ private handleUnsubscribe(
492
+ message: Draft14Message,
493
+ _direction: "inbound" | "outbound",
494
+ sideEffects: SideEffect[],
495
+ ): TransitionResult<Draft14MessageType> {
290
496
  const err = this.requireReady(message.type);
291
497
  if (err) return { ok: false, violation: err };
292
498
 
293
- const unsub = message as import('./types.js').Draft14Unsubscribe;
499
+ const unsub = message as import("./types.js").Draft14Unsubscribe;
294
500
  const idErr = this.checkKnownRequestId(unsub.request_id, message.type);
295
501
  if (idErr) return { ok: false, violation: idErr };
296
502
 
297
503
  const existing = this._subscriptions.get(unsub.request_id);
298
504
  if (!existing) {
299
- return { ok: false, violation: violation('UNKNOWN_REQUEST_ID', `No subscription with request ID ${unsub.request_id}`, this._phase, message.type) };
505
+ return {
506
+ ok: false,
507
+ violation: violation(
508
+ "UNKNOWN_REQUEST_ID",
509
+ `No subscription with request ID ${unsub.request_id}`,
510
+ this._phase,
511
+ message.type,
512
+ ),
513
+ };
300
514
  }
301
515
 
302
- this._subscriptions.set(unsub.request_id, { ...existing, phase: 'done' });
303
- sideEffects.push({ type: 'subscription-ended', subscribeId: unsub.request_id, reason: 'unsubscribed' });
516
+ this._subscriptions.set(unsub.request_id, { ...existing, phase: "done" });
517
+ sideEffects.push({
518
+ type: "subscription-ended",
519
+ subscribeId: unsub.request_id,
520
+ reason: "unsubscribed",
521
+ });
304
522
  return { ok: true, phase: this._phase, sideEffects };
305
523
  }
306
524
 
307
525
  // ─── Publish lifecycle ────────────────────────────────────────────────────────
308
526
 
309
- private handlePublish(message: Draft14Message, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult<Draft14MessageType> {
527
+ private handlePublish(
528
+ message: Draft14Message,
529
+ _direction: "inbound" | "outbound",
530
+ sideEffects: SideEffect[],
531
+ ): TransitionResult<Draft14MessageType> {
310
532
  const err = this.requireReady(message.type);
311
533
  if (err) return { ok: false, violation: err };
312
534
 
313
- const pub = message as import('./types.js').Draft14Publish;
535
+ const pub = message as import("./types.js").Draft14Publish;
314
536
  const dupErr = this.checkDuplicateRequestId(pub.request_id, message.type);
315
537
  if (dupErr) return { ok: false, violation: dupErr };
316
538
 
317
539
  this._requestIds.add(pub.request_id);
318
540
  this._publishes.set(pub.request_id, {
319
541
  requestId: pub.request_id,
320
- phase: 'pending',
542
+ phase: "pending",
321
543
  });
322
544
 
323
545
  return { ok: true, phase: this._phase, sideEffects };
324
546
  }
325
547
 
326
- private handlePublishOk(message: Draft14Message, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult<Draft14MessageType> {
548
+ private handlePublishOk(
549
+ message: Draft14Message,
550
+ _direction: "inbound" | "outbound",
551
+ sideEffects: SideEffect[],
552
+ ): TransitionResult<Draft14MessageType> {
327
553
  const err = this.requireReady(message.type);
328
554
  if (err) return { ok: false, violation: err };
329
555
 
330
- const ok = message as import('./types.js').Draft14PublishOk;
556
+ const ok = message as import("./types.js").Draft14PublishOk;
331
557
  const idErr = this.checkKnownRequestId(ok.request_id, message.type);
332
558
  if (idErr) return { ok: false, violation: idErr };
333
559
 
334
560
  const existing = this._publishes.get(ok.request_id);
335
561
  if (!existing) {
336
- return { ok: false, violation: violation('UNKNOWN_REQUEST_ID', `No publish with request ID ${ok.request_id}`, this._phase, message.type) };
562
+ return {
563
+ ok: false,
564
+ violation: violation(
565
+ "UNKNOWN_REQUEST_ID",
566
+ `No publish with request ID ${ok.request_id}`,
567
+ this._phase,
568
+ message.type,
569
+ ),
570
+ };
337
571
  }
338
- if (existing.phase !== 'pending') {
339
- return { ok: false, violation: violation('STATE_VIOLATION', `Publish ${ok.request_id} is ${existing.phase}, not pending`, this._phase, message.type) };
572
+ if (existing.phase !== "pending") {
573
+ return {
574
+ ok: false,
575
+ violation: violation(
576
+ "STATE_VIOLATION",
577
+ `Publish ${ok.request_id} is ${existing.phase}, not pending`,
578
+ this._phase,
579
+ message.type,
580
+ ),
581
+ };
340
582
  }
341
583
 
342
- this._publishes.set(ok.request_id, { ...existing, phase: 'active' });
343
- sideEffects.push({ type: 'publish-activated', requestId: ok.request_id });
584
+ this._publishes.set(ok.request_id, { ...existing, phase: "active" });
585
+ sideEffects.push({ type: "publish-activated", requestId: ok.request_id });
344
586
  return { ok: true, phase: this._phase, sideEffects };
345
587
  }
346
588
 
347
- private handlePublishError(message: Draft14Message, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult<Draft14MessageType> {
589
+ private handlePublishError(
590
+ message: Draft14Message,
591
+ _direction: "inbound" | "outbound",
592
+ sideEffects: SideEffect[],
593
+ ): TransitionResult<Draft14MessageType> {
348
594
  const err = this.requireReady(message.type);
349
595
  if (err) return { ok: false, violation: err };
350
596
 
351
- const pubErr = message as import('./types.js').Draft14PublishError;
597
+ const pubErr = message as import("./types.js").Draft14PublishError;
352
598
  const idErr = this.checkKnownRequestId(pubErr.request_id, message.type);
353
599
  if (idErr) return { ok: false, violation: idErr };
354
600
 
355
601
  const existing = this._publishes.get(pubErr.request_id);
356
602
  if (!existing) {
357
- return { ok: false, violation: violation('UNKNOWN_REQUEST_ID', `No publish with request ID ${pubErr.request_id}`, this._phase, message.type) };
603
+ return {
604
+ ok: false,
605
+ violation: violation(
606
+ "UNKNOWN_REQUEST_ID",
607
+ `No publish with request ID ${pubErr.request_id}`,
608
+ this._phase,
609
+ message.type,
610
+ ),
611
+ };
358
612
  }
359
- if (existing.phase !== 'pending') {
360
- return { ok: false, violation: violation('STATE_VIOLATION', `Publish ${pubErr.request_id} is ${existing.phase}, not pending`, this._phase, message.type) };
613
+ if (existing.phase !== "pending") {
614
+ return {
615
+ ok: false,
616
+ violation: violation(
617
+ "STATE_VIOLATION",
618
+ `Publish ${pubErr.request_id} is ${existing.phase}, not pending`,
619
+ this._phase,
620
+ message.type,
621
+ ),
622
+ };
361
623
  }
362
624
 
363
- this._publishes.set(pubErr.request_id, { ...existing, phase: 'error' });
364
- sideEffects.push({ type: 'publish-ended', requestId: pubErr.request_id, reason: pubErr.reason_phrase });
625
+ this._publishes.set(pubErr.request_id, { ...existing, phase: "error" });
626
+ sideEffects.push({
627
+ type: "publish-ended",
628
+ requestId: pubErr.request_id,
629
+ reason: pubErr.reason_phrase,
630
+ });
365
631
  return { ok: true, phase: this._phase, sideEffects };
366
632
  }
367
633
 
368
- private handlePublishDone(message: Draft14Message, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult<Draft14MessageType> {
634
+ private handlePublishDone(
635
+ message: Draft14Message,
636
+ _direction: "inbound" | "outbound",
637
+ sideEffects: SideEffect[],
638
+ ): TransitionResult<Draft14MessageType> {
369
639
  const err = this.requireReady(message.type);
370
640
  if (err) return { ok: false, violation: err };
371
641
 
372
- const done = message as import('./types.js').Draft14PublishDone;
642
+ const done = message as import("./types.js").Draft14PublishDone;
373
643
  const idErr = this.checkKnownRequestId(done.request_id, message.type);
374
644
  if (idErr) return { ok: false, violation: idErr };
375
645
 
376
646
  const existing = this._publishes.get(done.request_id);
377
647
  if (!existing) {
378
- return { ok: false, violation: violation('UNKNOWN_REQUEST_ID', `No publish with request ID ${done.request_id}`, this._phase, message.type) };
648
+ return {
649
+ ok: false,
650
+ violation: violation(
651
+ "UNKNOWN_REQUEST_ID",
652
+ `No publish with request ID ${done.request_id}`,
653
+ this._phase,
654
+ message.type,
655
+ ),
656
+ };
379
657
  }
380
658
 
381
- this._publishes.set(done.request_id, { ...existing, phase: 'done' });
382
- sideEffects.push({ type: 'publish-ended', requestId: done.request_id, reason: done.reason_phrase });
659
+ this._publishes.set(done.request_id, { ...existing, phase: "done" });
660
+ sideEffects.push({
661
+ type: "publish-ended",
662
+ requestId: done.request_id,
663
+ reason: done.reason_phrase,
664
+ });
383
665
  return { ok: true, phase: this._phase, sideEffects };
384
666
  }
385
667
 
386
668
  // ─── Fetch lifecycle ──────────────────────────────────────────────────────────
387
669
 
388
- private handleFetch(message: Draft14Message, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult<Draft14MessageType> {
670
+ private handleFetch(
671
+ message: Draft14Message,
672
+ _direction: "inbound" | "outbound",
673
+ sideEffects: SideEffect[],
674
+ ): TransitionResult<Draft14MessageType> {
389
675
  const err = this.requireReady(message.type);
390
676
  if (err) return { ok: false, violation: err };
391
677
 
392
- const fetch = message as import('./types.js').Draft14Fetch;
678
+ const fetch = message as import("./types.js").Draft14Fetch;
393
679
  const dupErr = this.checkDuplicateRequestId(fetch.request_id, message.type);
394
680
  if (dupErr) return { ok: false, violation: dupErr };
395
681
 
396
682
  this._requestIds.add(fetch.request_id);
397
683
  this._fetches.set(fetch.request_id, {
398
684
  requestId: fetch.request_id,
399
- phase: 'pending',
685
+ phase: "pending",
400
686
  });
401
687
 
402
688
  return { ok: true, phase: this._phase, sideEffects };
403
689
  }
404
690
 
405
- private handleFetchOk(message: Draft14Message, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult<Draft14MessageType> {
691
+ private handleFetchOk(
692
+ message: Draft14Message,
693
+ _direction: "inbound" | "outbound",
694
+ sideEffects: SideEffect[],
695
+ ): TransitionResult<Draft14MessageType> {
406
696
  const err = this.requireReady(message.type);
407
697
  if (err) return { ok: false, violation: err };
408
698
 
409
- const ok = message as import('./types.js').Draft14FetchOk;
699
+ const ok = message as import("./types.js").Draft14FetchOk;
410
700
  const idErr = this.checkKnownRequestId(ok.request_id, message.type);
411
701
  if (idErr) return { ok: false, violation: idErr };
412
702
 
413
703
  const existing = this._fetches.get(ok.request_id);
414
704
  if (!existing) {
415
- return { ok: false, violation: violation('UNKNOWN_REQUEST_ID', `No fetch with request ID ${ok.request_id}`, this._phase, message.type) };
705
+ return {
706
+ ok: false,
707
+ violation: violation(
708
+ "UNKNOWN_REQUEST_ID",
709
+ `No fetch with request ID ${ok.request_id}`,
710
+ this._phase,
711
+ message.type,
712
+ ),
713
+ };
416
714
  }
417
- if (existing.phase !== 'pending') {
418
- return { ok: false, violation: violation('STATE_VIOLATION', `Fetch ${ok.request_id} is ${existing.phase}, not pending`, this._phase, message.type) };
715
+ if (existing.phase !== "pending") {
716
+ return {
717
+ ok: false,
718
+ violation: violation(
719
+ "STATE_VIOLATION",
720
+ `Fetch ${ok.request_id} is ${existing.phase}, not pending`,
721
+ this._phase,
722
+ message.type,
723
+ ),
724
+ };
419
725
  }
420
726
 
421
- this._fetches.set(ok.request_id, { ...existing, phase: 'active' });
422
- sideEffects.push({ type: 'fetch-activated', requestId: ok.request_id });
727
+ this._fetches.set(ok.request_id, { ...existing, phase: "active" });
728
+ sideEffects.push({ type: "fetch-activated", requestId: ok.request_id });
423
729
  return { ok: true, phase: this._phase, sideEffects };
424
730
  }
425
731
 
426
- private handleFetchError(message: Draft14Message, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult<Draft14MessageType> {
732
+ private handleFetchError(
733
+ message: Draft14Message,
734
+ _direction: "inbound" | "outbound",
735
+ sideEffects: SideEffect[],
736
+ ): TransitionResult<Draft14MessageType> {
427
737
  const err = this.requireReady(message.type);
428
738
  if (err) return { ok: false, violation: err };
429
739
 
430
- const fetchErr = message as import('./types.js').Draft14FetchError;
740
+ const fetchErr = message as import("./types.js").Draft14FetchError;
431
741
  const idErr = this.checkKnownRequestId(fetchErr.request_id, message.type);
432
742
  if (idErr) return { ok: false, violation: idErr };
433
743
 
434
744
  const existing = this._fetches.get(fetchErr.request_id);
435
745
  if (!existing) {
436
- return { ok: false, violation: violation('UNKNOWN_REQUEST_ID', `No fetch with request ID ${fetchErr.request_id}`, this._phase, message.type) };
746
+ return {
747
+ ok: false,
748
+ violation: violation(
749
+ "UNKNOWN_REQUEST_ID",
750
+ `No fetch with request ID ${fetchErr.request_id}`,
751
+ this._phase,
752
+ message.type,
753
+ ),
754
+ };
437
755
  }
438
- if (existing.phase !== 'pending') {
439
- return { ok: false, violation: violation('STATE_VIOLATION', `Fetch ${fetchErr.request_id} is ${existing.phase}, not pending`, this._phase, message.type) };
756
+ if (existing.phase !== "pending") {
757
+ return {
758
+ ok: false,
759
+ violation: violation(
760
+ "STATE_VIOLATION",
761
+ `Fetch ${fetchErr.request_id} is ${existing.phase}, not pending`,
762
+ this._phase,
763
+ message.type,
764
+ ),
765
+ };
440
766
  }
441
767
 
442
- this._fetches.set(fetchErr.request_id, { ...existing, phase: 'error' });
443
- sideEffects.push({ type: 'fetch-ended', requestId: fetchErr.request_id, reason: fetchErr.reason_phrase });
768
+ this._fetches.set(fetchErr.request_id, { ...existing, phase: "error" });
769
+ sideEffects.push({
770
+ type: "fetch-ended",
771
+ requestId: fetchErr.request_id,
772
+ reason: fetchErr.reason_phrase,
773
+ });
444
774
  return { ok: true, phase: this._phase, sideEffects };
445
775
  }
446
776
 
447
- private handleFetchCancel(message: Draft14Message, _direction: 'inbound' | 'outbound', sideEffects: SideEffect[]): TransitionResult<Draft14MessageType> {
777
+ private handleFetchCancel(
778
+ message: Draft14Message,
779
+ _direction: "inbound" | "outbound",
780
+ sideEffects: SideEffect[],
781
+ ): TransitionResult<Draft14MessageType> {
448
782
  const err = this.requireReady(message.type);
449
783
  if (err) return { ok: false, violation: err };
450
784
 
451
- const cancel = message as import('./types.js').Draft14FetchCancel;
785
+ const cancel = message as import("./types.js").Draft14FetchCancel;
452
786
  const idErr = this.checkKnownRequestId(cancel.request_id, message.type);
453
787
  if (idErr) return { ok: false, violation: idErr };
454
788
 
455
789
  const existing = this._fetches.get(cancel.request_id);
456
790
  if (!existing) {
457
- return { ok: false, violation: violation('UNKNOWN_REQUEST_ID', `No fetch with request ID ${cancel.request_id}`, this._phase, message.type) };
791
+ return {
792
+ ok: false,
793
+ violation: violation(
794
+ "UNKNOWN_REQUEST_ID",
795
+ `No fetch with request ID ${cancel.request_id}`,
796
+ this._phase,
797
+ message.type,
798
+ ),
799
+ };
458
800
  }
459
801
 
460
- this._fetches.set(cancel.request_id, { ...existing, phase: 'cancelled' });
461
- sideEffects.push({ type: 'fetch-ended', requestId: cancel.request_id, reason: 'cancelled' });
802
+ this._fetches.set(cancel.request_id, { ...existing, phase: "cancelled" });
803
+ sideEffects.push({ type: "fetch-ended", requestId: cancel.request_id, reason: "cancelled" });
804
+ return { ok: true, phase: this._phase, sideEffects };
805
+ }
806
+
807
+ // ─── Publish namespace lifecycle ──────────────────────────────────────────────
808
+
809
+ private handlePublishNamespace(
810
+ message: Draft14Message,
811
+ sideEffects: SideEffect[],
812
+ ): TransitionResult<Draft14MessageType> {
813
+ const err = this.requireReady(message.type);
814
+ if (err) return { ok: false, violation: err };
815
+ const pn = message as import("./types.js").Draft14PublishNamespace;
816
+ const dupErr = this.checkDuplicateRequestId(pn.request_id, message.type);
817
+ if (dupErr) return { ok: false, violation: dupErr };
818
+ this._requestIds.add(pn.request_id);
819
+ return { ok: true, phase: this._phase, sideEffects };
820
+ }
821
+
822
+ private handlePublishNamespaceOk(
823
+ message: Draft14Message,
824
+ sideEffects: SideEffect[],
825
+ ): TransitionResult<Draft14MessageType> {
826
+ const err = this.requireReady(message.type);
827
+ if (err) return { ok: false, violation: err };
828
+ const ok = message as import("./types.js").Draft14PublishNamespaceOk;
829
+ const idErr = this.checkKnownRequestId(ok.request_id, message.type);
830
+ if (idErr) return { ok: false, violation: idErr };
831
+ return { ok: true, phase: this._phase, sideEffects };
832
+ }
833
+
834
+ private handlePublishNamespaceError(
835
+ message: Draft14Message,
836
+ sideEffects: SideEffect[],
837
+ ): TransitionResult<Draft14MessageType> {
838
+ const err = this.requireReady(message.type);
839
+ if (err) return { ok: false, violation: err };
840
+ const pnErr = message as import("./types.js").Draft14PublishNamespaceError;
841
+ const idErr = this.checkKnownRequestId(pnErr.request_id, message.type);
842
+ if (idErr) return { ok: false, violation: idErr };
843
+ return { ok: true, phase: this._phase, sideEffects };
844
+ }
845
+
846
+ private handlePublishNamespaceDone(
847
+ message: Draft14Message,
848
+ sideEffects: SideEffect[],
849
+ ): TransitionResult<Draft14MessageType> {
850
+ const err = this.requireReady(message.type);
851
+ if (err) return { ok: false, violation: err };
852
+ const done = message as import("./types.js").Draft14PublishNamespaceDone;
853
+ const idErr = this.checkKnownRequestId(done.request_id, message.type);
854
+ if (idErr) return { ok: false, violation: idErr };
855
+ return { ok: true, phase: this._phase, sideEffects };
856
+ }
857
+
858
+ private handlePublishNamespaceCancel(
859
+ message: Draft14Message,
860
+ sideEffects: SideEffect[],
861
+ ): TransitionResult<Draft14MessageType> {
862
+ const err = this.requireReady(message.type);
863
+ if (err) return { ok: false, violation: err };
864
+ const cancel = message as import("./types.js").Draft14PublishNamespaceCancel;
865
+ const idErr = this.checkKnownRequestId(cancel.request_id, message.type);
866
+ if (idErr) return { ok: false, violation: idErr };
867
+ return { ok: true, phase: this._phase, sideEffects };
868
+ }
869
+
870
+ // ─── Subscribe namespace lifecycle ──────────────────────────────────────────────
871
+
872
+ private handleSubscribeNamespace(
873
+ message: Draft14Message,
874
+ sideEffects: SideEffect[],
875
+ ): TransitionResult<Draft14MessageType> {
876
+ const err = this.requireReady(message.type);
877
+ if (err) return { ok: false, violation: err };
878
+ const sn = message as import("./types.js").Draft14SubscribeNamespace;
879
+ const dupErr = this.checkDuplicateRequestId(sn.request_id, message.type);
880
+ if (dupErr) return { ok: false, violation: dupErr };
881
+ this._requestIds.add(sn.request_id);
882
+ return { ok: true, phase: this._phase, sideEffects };
883
+ }
884
+
885
+ private handleSubscribeNamespaceOk(
886
+ message: Draft14Message,
887
+ sideEffects: SideEffect[],
888
+ ): TransitionResult<Draft14MessageType> {
889
+ const err = this.requireReady(message.type);
890
+ if (err) return { ok: false, violation: err };
891
+ const ok = message as import("./types.js").Draft14SubscribeNamespaceOk;
892
+ const idErr = this.checkKnownRequestId(ok.request_id, message.type);
893
+ if (idErr) return { ok: false, violation: idErr };
894
+ return { ok: true, phase: this._phase, sideEffects };
895
+ }
896
+
897
+ private handleSubscribeNamespaceError(
898
+ message: Draft14Message,
899
+ sideEffects: SideEffect[],
900
+ ): TransitionResult<Draft14MessageType> {
901
+ const err = this.requireReady(message.type);
902
+ if (err) return { ok: false, violation: err };
903
+ const snErr = message as import("./types.js").Draft14SubscribeNamespaceError;
904
+ const idErr = this.checkKnownRequestId(snErr.request_id, message.type);
905
+ if (idErr) return { ok: false, violation: idErr };
906
+ return { ok: true, phase: this._phase, sideEffects };
907
+ }
908
+
909
+ private handleUnsubscribeNamespace(
910
+ message: Draft14Message,
911
+ sideEffects: SideEffect[],
912
+ ): TransitionResult<Draft14MessageType> {
913
+ const err = this.requireReady(message.type);
914
+ if (err) return { ok: false, violation: err };
915
+ // unsubscribe_namespace has no request_id — namespace-based
916
+ return { ok: true, phase: this._phase, sideEffects };
917
+ }
918
+
919
+ // ─── Track status lifecycle ──────────────────────────────────────────────────
920
+
921
+ private handleTrackStatus(
922
+ message: Draft14Message,
923
+ sideEffects: SideEffect[],
924
+ ): TransitionResult<Draft14MessageType> {
925
+ const err = this.requireReady(message.type);
926
+ if (err) return { ok: false, violation: err };
927
+ const ts = message as import("./types.js").Draft14TrackStatus;
928
+ const dupErr = this.checkDuplicateRequestId(ts.request_id, message.type);
929
+ if (dupErr) return { ok: false, violation: dupErr };
930
+ this._requestIds.add(ts.request_id);
931
+ return { ok: true, phase: this._phase, sideEffects };
932
+ }
933
+
934
+ private handleTrackStatusOk(
935
+ message: Draft14Message,
936
+ sideEffects: SideEffect[],
937
+ ): TransitionResult<Draft14MessageType> {
938
+ const err = this.requireReady(message.type);
939
+ if (err) return { ok: false, violation: err };
940
+ const ok = message as import("./types.js").Draft14TrackStatusOk;
941
+ const idErr = this.checkKnownRequestId(ok.request_id, message.type);
942
+ if (idErr) return { ok: false, violation: idErr };
943
+ return { ok: true, phase: this._phase, sideEffects };
944
+ }
945
+
946
+ private handleTrackStatusError(
947
+ message: Draft14Message,
948
+ sideEffects: SideEffect[],
949
+ ): TransitionResult<Draft14MessageType> {
950
+ const err = this.requireReady(message.type);
951
+ if (err) return { ok: false, violation: err };
952
+ const tsErr = message as import("./types.js").Draft14TrackStatusError;
953
+ const idErr = this.checkKnownRequestId(tsErr.request_id, message.type);
954
+ if (idErr) return { ok: false, violation: idErr };
462
955
  return { ok: true, phase: this._phase, sideEffects };
463
956
  }
464
957
 
@@ -471,7 +964,7 @@ export class Draft14SessionFSM {
471
964
  }
472
965
 
473
966
  reset(): void {
474
- this._phase = 'idle';
967
+ this._phase = "idle";
475
968
  this._subscriptions.clear();
476
969
  this._publishes.clear();
477
970
  this._fetches.clear();