@moqtap/codec 0.1.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 (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +95 -0
  3. package/dist/chunk-23YG7F46.js +764 -0
  4. package/dist/chunk-2NARXGVA.cjs +194 -0
  5. package/dist/chunk-3BSZ55L3.cjs +307 -0
  6. package/dist/chunk-5WFXFLL4.cjs +1185 -0
  7. package/dist/chunk-DC4L6ZIT.js +307 -0
  8. package/dist/chunk-GDRGWFEK.cjs +498 -0
  9. package/dist/chunk-IQPDRQVC.js +1185 -0
  10. package/dist/chunk-QYG6KGOV.cjs +101 -0
  11. package/dist/chunk-UOBWHJA5.js +101 -0
  12. package/dist/chunk-WNTXF3DE.cjs +764 -0
  13. package/dist/chunk-YBSEOSSP.js +194 -0
  14. package/dist/chunk-YPXLV5YK.js +498 -0
  15. package/dist/codec-CTvFtQQI.d.cts +86 -0
  16. package/dist/codec-qPzfmLNu.d.ts +86 -0
  17. package/dist/draft14-session.cjs +6 -0
  18. package/dist/draft14-session.d.cts +8 -0
  19. package/dist/draft14-session.d.ts +8 -0
  20. package/dist/draft14-session.js +6 -0
  21. package/dist/draft14.cjs +121 -0
  22. package/dist/draft14.d.cts +96 -0
  23. package/dist/draft14.d.ts +96 -0
  24. package/dist/draft14.js +121 -0
  25. package/dist/draft7-session.cjs +7 -0
  26. package/dist/draft7-session.d.cts +7 -0
  27. package/dist/draft7-session.d.ts +7 -0
  28. package/dist/draft7-session.js +7 -0
  29. package/dist/draft7.cjs +60 -0
  30. package/dist/draft7.d.cts +72 -0
  31. package/dist/draft7.d.ts +72 -0
  32. package/dist/draft7.js +60 -0
  33. package/dist/index.cjs +40 -0
  34. package/dist/index.d.cts +40 -0
  35. package/dist/index.d.ts +40 -0
  36. package/dist/index.js +40 -0
  37. package/dist/session-types-B9NIf7_F.d.ts +101 -0
  38. package/dist/session-types-CCo-oA-d.d.cts +101 -0
  39. package/dist/session.cjs +27 -0
  40. package/dist/session.d.cts +24 -0
  41. package/dist/session.d.ts +24 -0
  42. package/dist/session.js +27 -0
  43. package/dist/types-CIk5W10V.d.cts +249 -0
  44. package/dist/types-CIk5W10V.d.ts +249 -0
  45. package/dist/types-ClXELFGN.d.cts +241 -0
  46. package/dist/types-ClXELFGN.d.ts +241 -0
  47. package/package.json +84 -0
  48. package/src/core/buffer-reader.ts +107 -0
  49. package/src/core/buffer-writer.ts +91 -0
  50. package/src/core/errors.ts +1 -0
  51. package/src/core/session-types.ts +103 -0
  52. package/src/core/types.ts +363 -0
  53. package/src/drafts/draft07/announce-fsm.ts +2 -0
  54. package/src/drafts/draft07/codec.ts +874 -0
  55. package/src/drafts/draft07/index.ts +70 -0
  56. package/src/drafts/draft07/messages.ts +44 -0
  57. package/src/drafts/draft07/parameters.ts +12 -0
  58. package/src/drafts/draft07/rules.ts +75 -0
  59. package/src/drafts/draft07/session-fsm.ts +353 -0
  60. package/src/drafts/draft07/session.ts +21 -0
  61. package/src/drafts/draft07/subscription-fsm.ts +3 -0
  62. package/src/drafts/draft07/varint.ts +23 -0
  63. package/src/drafts/draft14/codec.ts +1330 -0
  64. package/src/drafts/draft14/index.ts +132 -0
  65. package/src/drafts/draft14/messages.ts +76 -0
  66. package/src/drafts/draft14/rules.ts +70 -0
  67. package/src/drafts/draft14/session-fsm.ts +480 -0
  68. package/src/drafts/draft14/session.ts +26 -0
  69. package/src/drafts/draft14/types.ts +365 -0
  70. package/src/index.ts +85 -0
  71. package/src/session.ts +58 -0
@@ -0,0 +1,91 @@
1
+ export class BufferWriter {
2
+ private buffer: Uint8Array;
3
+ private view: DataView;
4
+ private pos: number;
5
+
6
+ constructor(initialSize = 256) {
7
+ this.buffer = new Uint8Array(initialSize);
8
+ this.view = new DataView(this.buffer.buffer);
9
+ this.pos = 0;
10
+ }
11
+
12
+ get offset(): number {
13
+ return this.pos;
14
+ }
15
+
16
+ private ensureCapacity(needed: number): void {
17
+ const required = this.pos + needed;
18
+ if (required <= this.buffer.byteLength) return;
19
+
20
+ let newSize = this.buffer.byteLength * 2;
21
+ while (newSize < required) newSize *= 2;
22
+
23
+ const newBuffer = new Uint8Array(newSize);
24
+ newBuffer.set(this.buffer);
25
+ this.buffer = newBuffer;
26
+ this.view = new DataView(this.buffer.buffer);
27
+ }
28
+
29
+ writeUint8(value: number): void {
30
+ this.ensureCapacity(1);
31
+ this.view.setUint8(this.pos, value);
32
+ this.pos += 1;
33
+ }
34
+
35
+ writeBytes(bytes: Uint8Array): void {
36
+ this.ensureCapacity(bytes.byteLength);
37
+ this.buffer.set(bytes, this.pos);
38
+ this.pos += bytes.byteLength;
39
+ }
40
+
41
+ writeVarInt(value: number | bigint): void {
42
+ const v = BigInt(value);
43
+ if (v < 0n) throw new Error('VarInt value must be non-negative');
44
+
45
+ if (v < 0x40n) {
46
+ this.ensureCapacity(1);
47
+ this.view.setUint8(this.pos, Number(v));
48
+ this.pos += 1;
49
+ } else if (v < 0x4000n) {
50
+ this.ensureCapacity(2);
51
+ this.view.setUint16(this.pos, Number(v) | 0x4000);
52
+ this.pos += 2;
53
+ } else if (v < 0x40000000n) {
54
+ this.ensureCapacity(4);
55
+ this.view.setUint32(this.pos, Number(v) | 0x80000000);
56
+ this.pos += 4;
57
+ } else if (v < 0x4000000000000000n) {
58
+ this.ensureCapacity(8);
59
+ this.view.setBigUint64(this.pos, v | 0xc000000000000000n);
60
+ this.pos += 8;
61
+ } else {
62
+ throw new Error('VarInt value exceeds 62-bit range');
63
+ }
64
+ }
65
+
66
+ writeString(str: string): void {
67
+ const encoded = new TextEncoder().encode(str);
68
+ this.writeVarInt(encoded.byteLength);
69
+ this.writeBytes(encoded);
70
+ }
71
+
72
+ writeTuple(values: string[]): void {
73
+ this.writeVarInt(values.length);
74
+ for (const v of values) {
75
+ this.writeString(v);
76
+ }
77
+ }
78
+
79
+ writeParameters(params: Map<bigint, Uint8Array>): void {
80
+ this.writeVarInt(params.size);
81
+ for (const [key, value] of params) {
82
+ this.writeVarInt(key);
83
+ this.writeVarInt(value.byteLength);
84
+ this.writeBytes(value);
85
+ }
86
+ }
87
+
88
+ finish(): Uint8Array {
89
+ return this.buffer.slice(0, this.pos);
90
+ }
91
+ }
@@ -0,0 +1 @@
1
+ export { DecodeError, type DecodeErrorCode } from './types.js';
@@ -0,0 +1,103 @@
1
+ import type { Codec, MoqtMessage, MoqtMessageType } from './types.js';
2
+
3
+ export type SessionPhase =
4
+ | 'idle'
5
+ | 'setup'
6
+ | 'ready'
7
+ | 'draining'
8
+ | 'closed'
9
+ | 'error';
10
+
11
+ export type SubscriptionPhase =
12
+ | 'pending'
13
+ | 'active'
14
+ | 'error'
15
+ | 'done';
16
+
17
+ export type AnnouncePhase =
18
+ | 'pending'
19
+ | 'active'
20
+ | 'error';
21
+
22
+ export type PublishPhase = 'pending' | 'active' | 'error' | 'done';
23
+
24
+ export type FetchPhase = 'pending' | 'active' | 'error' | 'cancelled';
25
+
26
+ export interface SubscriptionState {
27
+ readonly subscribeId: bigint;
28
+ readonly phase: SubscriptionPhase;
29
+ readonly trackNamespace: string[];
30
+ readonly trackName: string;
31
+ }
32
+
33
+ export interface AnnounceState {
34
+ readonly namespace: string[];
35
+ readonly phase: AnnouncePhase;
36
+ }
37
+
38
+ export interface PublishState {
39
+ readonly requestId: bigint;
40
+ readonly phase: PublishPhase;
41
+ }
42
+
43
+ export interface FetchState {
44
+ readonly requestId: bigint;
45
+ readonly phase: FetchPhase;
46
+ }
47
+
48
+ export interface SessionStateOptions {
49
+ codec: { draft: string };
50
+ role: 'client' | 'server';
51
+ }
52
+
53
+ export interface SessionState<M = MoqtMessage, T extends string = MoqtMessageType> {
54
+ readonly phase: SessionPhase;
55
+ readonly role: 'client' | 'server';
56
+ receive(message: M): TransitionResult<T>;
57
+ validateOutgoing(message: M): ValidationResult<T>;
58
+ send(message: M): TransitionResult<T>;
59
+ readonly subscriptions: ReadonlyMap<bigint, SubscriptionState>;
60
+ readonly announces: ReadonlyMap<string, AnnounceState>;
61
+ readonly legalOutgoing: ReadonlySet<T>;
62
+ readonly legalIncoming: ReadonlySet<T>;
63
+ reset(): void;
64
+ }
65
+
66
+ export type TransitionResult<T extends string = MoqtMessageType> =
67
+ | { ok: true; phase: SessionPhase; sideEffects: SideEffect[] }
68
+ | { ok: false; violation: ProtocolViolation<T> };
69
+
70
+ export type ValidationResult<T extends string = MoqtMessageType> =
71
+ | { ok: true }
72
+ | { ok: false; violation: ProtocolViolation<T> };
73
+
74
+ export interface ProtocolViolation<T extends string = MoqtMessageType> {
75
+ readonly code: ProtocolViolationCode;
76
+ readonly message: string;
77
+ readonly currentPhase: SessionPhase;
78
+ readonly offendingMessage: T;
79
+ }
80
+
81
+ export type ProtocolViolationCode =
82
+ | 'MESSAGE_BEFORE_SETUP'
83
+ | 'UNEXPECTED_MESSAGE'
84
+ | 'DUPLICATE_SUBSCRIBE_ID'
85
+ | 'UNKNOWN_SUBSCRIBE_ID'
86
+ | 'DUPLICATE_REQUEST_ID'
87
+ | 'UNKNOWN_REQUEST_ID'
88
+ | 'ROLE_VIOLATION'
89
+ | 'STATE_VIOLATION'
90
+ | 'SETUP_VIOLATION';
91
+
92
+ export type SideEffect =
93
+ | { type: 'subscription-activated'; subscribeId: bigint }
94
+ | { type: 'subscription-ended'; subscribeId: bigint; reason: string }
95
+ | { type: 'announce-activated'; namespace: string[] }
96
+ | { type: 'announce-ended'; namespace: string[] }
97
+ | { type: 'publish-activated'; requestId: bigint }
98
+ | { type: 'publish-ended'; requestId: bigint; reason: string }
99
+ | { type: 'fetch-activated'; requestId: bigint }
100
+ | { type: 'fetch-ended'; requestId: bigint; reason: string }
101
+ | { type: 'session-ready' }
102
+ | { type: 'session-draining'; goAwayUri: string }
103
+ | { type: 'session-closed' };
@@ -0,0 +1,363 @@
1
+ // Draft identifiers
2
+ export type Draft = 'draft-ietf-moq-transport-07' | 'draft-ietf-moq-transport-14';
3
+ export type DraftShorthand = '07' | '14';
4
+
5
+ // All MoQT message type tags
6
+ export type MoqtMessageType =
7
+ | 'client_setup'
8
+ | 'server_setup'
9
+ | 'subscribe'
10
+ | 'subscribe_ok'
11
+ | 'subscribe_error'
12
+ | 'subscribe_done'
13
+ | 'subscribe_update'
14
+ | 'unsubscribe'
15
+ | 'announce'
16
+ | 'announce_ok'
17
+ | 'announce_error'
18
+ | 'announce_cancel'
19
+ | 'unannounce'
20
+ | 'track_status_request'
21
+ | 'track_status'
22
+ | 'object_stream'
23
+ | 'object_datagram'
24
+ | 'stream_header_track'
25
+ | 'stream_header_group'
26
+ | 'stream_header_subgroup'
27
+ | 'goaway'
28
+ | 'subscribe_announces'
29
+ | 'subscribe_announces_ok'
30
+ | 'subscribe_announces_error'
31
+ | 'unsubscribe_announces'
32
+ | 'max_subscribe_id'
33
+ | 'fetch'
34
+ | 'fetch_ok'
35
+ | 'fetch_error'
36
+ | 'fetch_cancel';
37
+
38
+ // Base message interface
39
+ export interface BaseMessage {
40
+ readonly type: MoqtMessageType;
41
+ }
42
+
43
+ // Setup messages
44
+ export interface ClientSetup extends BaseMessage {
45
+ readonly type: 'client_setup';
46
+ readonly supportedVersions: bigint[];
47
+ readonly parameters: Map<bigint, Uint8Array>;
48
+ }
49
+
50
+ export interface ServerSetup extends BaseMessage {
51
+ readonly type: 'server_setup';
52
+ readonly selectedVersion: bigint;
53
+ readonly parameters: Map<bigint, Uint8Array>;
54
+ }
55
+
56
+ // Subscribe messages
57
+ export interface Subscribe extends BaseMessage {
58
+ readonly type: 'subscribe';
59
+ readonly subscribeId: bigint;
60
+ readonly trackAlias: bigint;
61
+ readonly trackNamespace: string[];
62
+ readonly trackName: string;
63
+ readonly subscriberPriority: number;
64
+ readonly groupOrder: GroupOrderValue;
65
+ readonly filterType: FilterType;
66
+ readonly startGroup?: bigint;
67
+ readonly startObject?: bigint;
68
+ readonly endGroup?: bigint;
69
+ readonly endObject?: bigint;
70
+ readonly parameters: Map<bigint, Uint8Array>;
71
+ }
72
+
73
+ export type FilterType = 'latest_group' | 'latest_object' | 'absolute_start' | 'absolute_range';
74
+
75
+ export interface SubscribeOk extends BaseMessage {
76
+ readonly type: 'subscribe_ok';
77
+ readonly subscribeId: bigint;
78
+ readonly expires: bigint;
79
+ readonly groupOrder: GroupOrderValue;
80
+ readonly contentExists: boolean;
81
+ readonly largestGroupId?: bigint;
82
+ readonly largestObjectId?: bigint;
83
+ readonly parameters: Map<bigint, Uint8Array>;
84
+ }
85
+
86
+ export type GroupOrderValue = 'ascending' | 'descending' | 'original';
87
+
88
+ export interface SubscribeError extends BaseMessage {
89
+ readonly type: 'subscribe_error';
90
+ readonly subscribeId: bigint;
91
+ readonly errorCode: bigint;
92
+ readonly reasonPhrase: string;
93
+ readonly trackAlias: bigint;
94
+ }
95
+
96
+ export interface SubscribeDone extends BaseMessage {
97
+ readonly type: 'subscribe_done';
98
+ readonly subscribeId: bigint;
99
+ readonly statusCode: bigint;
100
+ readonly reasonPhrase: string;
101
+ readonly contentExists: boolean;
102
+ readonly finalGroupId?: bigint;
103
+ readonly finalObjectId?: bigint;
104
+ }
105
+
106
+ export interface SubscribeUpdate extends BaseMessage {
107
+ readonly type: 'subscribe_update';
108
+ readonly subscribeId: bigint;
109
+ readonly startGroup: bigint;
110
+ readonly startObject: bigint;
111
+ readonly endGroup: bigint;
112
+ readonly endObject: bigint;
113
+ readonly subscriberPriority: number;
114
+ readonly parameters: Map<bigint, Uint8Array>;
115
+ }
116
+
117
+ export interface Unsubscribe extends BaseMessage {
118
+ readonly type: 'unsubscribe';
119
+ readonly subscribeId: bigint;
120
+ }
121
+
122
+ // Announce messages
123
+ export interface Announce extends BaseMessage {
124
+ readonly type: 'announce';
125
+ readonly trackNamespace: string[];
126
+ readonly parameters: Map<bigint, Uint8Array>;
127
+ }
128
+
129
+ export interface AnnounceOk extends BaseMessage {
130
+ readonly type: 'announce_ok';
131
+ readonly trackNamespace: string[];
132
+ }
133
+
134
+ export interface AnnounceError extends BaseMessage {
135
+ readonly type: 'announce_error';
136
+ readonly trackNamespace: string[];
137
+ readonly errorCode: bigint;
138
+ readonly reasonPhrase: string;
139
+ }
140
+
141
+ export interface AnnounceCancel extends BaseMessage {
142
+ readonly type: 'announce_cancel';
143
+ readonly trackNamespace: string[];
144
+ readonly errorCode: bigint;
145
+ readonly reasonPhrase: string;
146
+ }
147
+
148
+ export interface Unannounce extends BaseMessage {
149
+ readonly type: 'unannounce';
150
+ readonly trackNamespace: string[];
151
+ }
152
+
153
+ // Track status
154
+ export interface TrackStatusRequest extends BaseMessage {
155
+ readonly type: 'track_status_request';
156
+ readonly trackNamespace: string[];
157
+ readonly trackName: string;
158
+ }
159
+
160
+ export interface TrackStatus extends BaseMessage {
161
+ readonly type: 'track_status';
162
+ readonly trackNamespace: string[];
163
+ readonly trackName: string;
164
+ readonly statusCode: bigint;
165
+ readonly lastGroupId: bigint;
166
+ readonly lastObjectId: bigint;
167
+ }
168
+
169
+ // Object/stream messages
170
+ export interface ObjectStream extends BaseMessage {
171
+ readonly type: 'object_stream';
172
+ readonly subscribeId: bigint;
173
+ readonly trackAlias: bigint;
174
+ readonly groupId: bigint;
175
+ readonly objectId: bigint;
176
+ readonly publisherPriority: number;
177
+ readonly objectStatus?: number;
178
+ readonly payload: Uint8Array;
179
+ }
180
+
181
+ export interface ObjectDatagram extends BaseMessage {
182
+ readonly type: 'object_datagram';
183
+ readonly subscribeId: bigint;
184
+ readonly trackAlias: bigint;
185
+ readonly groupId: bigint;
186
+ readonly objectId: bigint;
187
+ readonly publisherPriority: number;
188
+ readonly objectStatus?: number;
189
+ readonly payload: Uint8Array;
190
+ }
191
+
192
+ export interface StreamHeaderTrack extends BaseMessage {
193
+ readonly type: 'stream_header_track';
194
+ readonly subscribeId: bigint;
195
+ readonly trackAlias: bigint;
196
+ readonly publisherPriority: number;
197
+ }
198
+
199
+ export interface StreamHeaderGroup extends BaseMessage {
200
+ readonly type: 'stream_header_group';
201
+ readonly subscribeId: bigint;
202
+ readonly trackAlias: bigint;
203
+ readonly groupId: bigint;
204
+ readonly publisherPriority: number;
205
+ }
206
+
207
+ export interface StreamHeaderSubgroup extends BaseMessage {
208
+ readonly type: 'stream_header_subgroup';
209
+ readonly subscribeId: bigint;
210
+ readonly trackAlias: bigint;
211
+ readonly groupId: bigint;
212
+ readonly subgroupId: bigint;
213
+ readonly publisherPriority: number;
214
+ }
215
+
216
+ // GoAway
217
+ export interface GoAway extends BaseMessage {
218
+ readonly type: 'goaway';
219
+ readonly newSessionUri: string;
220
+ }
221
+
222
+ // Subscribe Announces
223
+ export interface SubscribeAnnounces extends BaseMessage {
224
+ readonly type: 'subscribe_announces';
225
+ readonly trackNamespace: string[];
226
+ readonly parameters: Map<bigint, Uint8Array>;
227
+ }
228
+
229
+ export interface SubscribeAnnouncesOk extends BaseMessage {
230
+ readonly type: 'subscribe_announces_ok';
231
+ readonly trackNamespace: string[];
232
+ }
233
+
234
+ export interface SubscribeAnnouncesError extends BaseMessage {
235
+ readonly type: 'subscribe_announces_error';
236
+ readonly trackNamespace: string[];
237
+ readonly errorCode: bigint;
238
+ readonly reasonPhrase: string;
239
+ }
240
+
241
+ // Unsubscribe Announces
242
+ export interface UnsubscribeAnnounces extends BaseMessage {
243
+ readonly type: 'unsubscribe_announces';
244
+ readonly trackNamespace: string[];
245
+ }
246
+
247
+ // Max Subscribe ID
248
+ export interface MaxSubscribeId extends BaseMessage {
249
+ readonly type: 'max_subscribe_id';
250
+ readonly subscribeId: bigint;
251
+ }
252
+
253
+ // Fetch
254
+ export interface Fetch extends BaseMessage {
255
+ readonly type: 'fetch';
256
+ readonly subscribeId: bigint;
257
+ readonly trackNamespace: string[];
258
+ readonly trackName: string;
259
+ readonly subscriberPriority: number;
260
+ readonly groupOrder: GroupOrderValue;
261
+ readonly startGroup: bigint;
262
+ readonly startObject: bigint;
263
+ readonly endGroup: bigint;
264
+ readonly endObject: bigint;
265
+ readonly parameters: Map<bigint, Uint8Array>;
266
+ }
267
+
268
+ export interface FetchOk extends BaseMessage {
269
+ readonly type: 'fetch_ok';
270
+ readonly subscribeId: bigint;
271
+ readonly groupOrder: GroupOrderValue;
272
+ readonly endOfTrack: boolean;
273
+ readonly largestGroupId: bigint;
274
+ readonly largestObjectId: bigint;
275
+ readonly parameters: Map<bigint, Uint8Array>;
276
+ }
277
+
278
+ export interface FetchError extends BaseMessage {
279
+ readonly type: 'fetch_error';
280
+ readonly subscribeId: bigint;
281
+ readonly errorCode: bigint;
282
+ readonly reasonPhrase: string;
283
+ }
284
+
285
+ export interface FetchCancel extends BaseMessage {
286
+ readonly type: 'fetch_cancel';
287
+ readonly subscribeId: bigint;
288
+ }
289
+
290
+ // Union type of all messages
291
+ export type MoqtMessage =
292
+ | ClientSetup
293
+ | ServerSetup
294
+ | Subscribe
295
+ | SubscribeOk
296
+ | SubscribeError
297
+ | SubscribeDone
298
+ | SubscribeUpdate
299
+ | Unsubscribe
300
+ | Announce
301
+ | AnnounceOk
302
+ | AnnounceError
303
+ | AnnounceCancel
304
+ | Unannounce
305
+ | TrackStatusRequest
306
+ | TrackStatus
307
+ | ObjectStream
308
+ | ObjectDatagram
309
+ | StreamHeaderTrack
310
+ | StreamHeaderGroup
311
+ | StreamHeaderSubgroup
312
+ | GoAway
313
+ | SubscribeAnnounces
314
+ | SubscribeAnnouncesOk
315
+ | SubscribeAnnouncesError
316
+ | UnsubscribeAnnounces
317
+ | MaxSubscribeId
318
+ | Fetch
319
+ | FetchOk
320
+ | FetchError
321
+ | FetchCancel;
322
+
323
+ // Base codec interface — draft-specific codecs extend this
324
+ export interface BaseCodec<M = MoqtMessage> {
325
+ readonly draft: Draft;
326
+ encodeMessage(message: M): Uint8Array;
327
+ decodeMessage(bytes: Uint8Array): DecodeResult<M>;
328
+ }
329
+
330
+ // Full codec interface for drafts that support varint + stream decode
331
+ export interface Codec extends BaseCodec<MoqtMessage> {
332
+ encodeVarInt(value: number | bigint): Uint8Array;
333
+ decodeVarInt(bytes: Uint8Array, offset?: number): DecodeResult<bigint>;
334
+ createStreamDecoder(): TransformStream<Uint8Array, MoqtMessage>;
335
+ }
336
+
337
+ export interface CodecOptions {
338
+ draft: Draft | DraftShorthand;
339
+ }
340
+
341
+ // Result types
342
+ export type DecodeResult<T> =
343
+ | { ok: true; value: T; bytesRead: number }
344
+ | { ok: false; error: DecodeError };
345
+
346
+ export type DecodeErrorCode =
347
+ | 'UNEXPECTED_END'
348
+ | 'INVALID_VARINT'
349
+ | 'UNKNOWN_MESSAGE_TYPE'
350
+ | 'INVALID_PARAMETER'
351
+ | 'CONSTRAINT_VIOLATION';
352
+
353
+ export class DecodeError extends Error {
354
+ readonly code: DecodeErrorCode;
355
+ readonly offset: number;
356
+
357
+ constructor(code: DecodeErrorCode, message: string, offset: number) {
358
+ super(message);
359
+ this.name = 'DecodeError';
360
+ this.code = code;
361
+ this.offset = offset;
362
+ }
363
+ }
@@ -0,0 +1,2 @@
1
+ // Announce state tracking is integrated into SessionFSM.
2
+ export type { AnnounceState, AnnouncePhase } from '../../core/session-types.js';