@moqtap/trace 0.1.0 → 0.1.2

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.
package/src/recorder.ts CHANGED
@@ -1,95 +1,110 @@
1
- import type { MoqtMessage } from '@moqtap/codec';
2
- import type { SessionState } from '@moqtap/codec/session';
3
- import type {
4
- Trace,
5
- TraceHeader,
6
- TraceEvent,
7
- RecorderOptions,
8
- DetailLevel,
9
- } from './types.js';
1
+ import type { SessionState } from '@moqtap/codec/session'
2
+ import type { DetailLevel, RecorderOptions, Trace, TraceEvent, TraceHeader } from './types.js'
10
3
 
11
4
  export interface TraceRecorder {
12
5
  /** Wrap a SessionState to auto-record control messages and state changes. */
13
- wrapSession(session: SessionState): SessionState;
6
+ wrapSession<M extends { type: string }, T extends string>(
7
+ session: SessionState<M, T>,
8
+ ): SessionState<M, T>
14
9
 
15
10
  /** Record an arbitrary event manually. */
16
- record(event: TraceEvent): void;
11
+ record(event: TraceEvent): void
17
12
 
18
13
  /** Record a stream-opened event. Ignored at 'control' detail level. */
19
- recordStreamOpened(streamId: bigint, direction: 0 | 1, streamType: 0 | 1 | 2): void;
14
+ recordStreamOpened(streamId: bigint, direction: 0 | 1, streamType: 0 | 1 | 2): void
20
15
 
21
16
  /** Record a stream-closed event. Ignored at 'control' detail level. */
22
- recordStreamClosed(streamId: bigint, errorCode?: number): void;
17
+ recordStreamClosed(streamId: bigint, errorCode?: number): void
23
18
 
24
19
  /** Record an object header event. Ignored below 'headers' detail level. */
25
20
  recordObjectHeader(
26
- streamId: bigint, groupId: bigint, objectId: bigint,
27
- publisherPriority: number, objectStatus: number,
28
- ): void;
21
+ streamId: bigint,
22
+ groupId: bigint,
23
+ objectId: bigint,
24
+ publisherPriority: number,
25
+ objectStatus: number,
26
+ ): void
29
27
 
30
28
  /** Record an object payload event. Ignored below 'headers+sizes' detail level. */
31
29
  recordObjectPayload(
32
- streamId: bigint, groupId: bigint, objectId: bigint,
33
- size: number, payload?: Uint8Array,
34
- ): void;
30
+ streamId: bigint,
31
+ groupId: bigint,
32
+ objectId: bigint,
33
+ size: number,
34
+ payload?: Uint8Array,
35
+ ): void
35
36
 
36
37
  /** Record a protocol error. */
37
- recordError(errorCode: number, reason: string): void;
38
+ recordError(errorCode: number, reason: string): void
38
39
 
39
40
  /** Record a user-defined annotation. */
40
- annotate(label: string, data?: unknown): void;
41
+ annotate(label: string, data?: unknown): void
41
42
 
42
43
  /** Finalize the trace. Stops recording and returns the trace. */
43
- finalize(): Trace;
44
+ finalize(): Trace
44
45
 
45
46
  /** Whether the recorder is still accepting events. */
46
- readonly recording: boolean;
47
+ readonly recording: boolean
47
48
  }
48
49
 
49
50
  const DETAIL_RANK: Record<DetailLevel, number> = {
50
- 'control': 0,
51
- 'headers': 1,
51
+ control: 0,
52
+ headers: 1,
52
53
  'headers+sizes': 2,
53
54
  'headers+data': 3,
54
- 'full': 4,
55
- };
55
+ full: 4,
56
+ }
56
57
 
57
58
  export function createRecorder(options: RecorderOptions): TraceRecorder {
58
- const detail = options.detail;
59
- const detailRank = DETAIL_RANK[detail];
60
- const maxEvents = options.maxEvents ?? 100_000;
61
- const clock = options.clock ?? (() => Math.round(performance.now() * 1000));
62
- const messageTypeId = options.messageTypeId ?? (() => 0);
59
+ const detail = options.detail
60
+ const detailRank = DETAIL_RANK[detail]
61
+ const maxEvents = options.maxEvents ?? 100_000
62
+ const clock = options.clock ?? (() => Math.round(performance.now() * 1000))
63
+ const messageTypeId = options.messageTypeId ?? (() => 0)
63
64
 
64
- const events: TraceEvent[] = [];
65
- let _recording = true;
66
- let _seq = 0;
67
- const startTime = Date.now();
65
+ const events: TraceEvent[] = []
66
+ let _recording = true
67
+ let _seq = 0
68
+ const startTime = Date.now()
68
69
 
69
70
  function addEvent(event: TraceEvent): void {
70
- if (!_recording) return;
71
+ if (!_recording) return
71
72
  if (events.length >= maxEvents) {
72
- events.shift();
73
+ events.shift()
73
74
  }
74
- events.push(event);
75
+ events.push(event)
75
76
  }
76
77
 
77
78
  function nextSeq(): number {
78
- return _seq++;
79
+ return _seq++
79
80
  }
80
81
 
81
- function wrapSession(session: SessionState): SessionState {
82
+ function wrapSession<M extends { type: string }, T extends string>(
83
+ session: SessionState<M, T>,
84
+ ): SessionState<M, T> {
82
85
  return {
83
- get phase() { return session.phase; },
84
- get role() { return session.role; },
85
- get subscriptions() { return session.subscriptions; },
86
- get announces() { return session.announces; },
87
- get legalOutgoing() { return session.legalOutgoing; },
88
- get legalIncoming() { return session.legalIncoming; },
86
+ get phase() {
87
+ return session.phase
88
+ },
89
+ get role() {
90
+ return session.role
91
+ },
92
+ get subscriptions() {
93
+ return session.subscriptions
94
+ },
95
+ get announces() {
96
+ return session.announces
97
+ },
98
+ get legalOutgoing() {
99
+ return session.legalOutgoing
100
+ },
101
+ get legalIncoming() {
102
+ return session.legalIncoming
103
+ },
89
104
 
90
- receive(message: MoqtMessage) {
91
- const prevPhase = session.phase;
92
- const result = session.receive(message);
105
+ receive(message: M) {
106
+ const prevPhase = session.phase
107
+ const result = session.receive(message)
93
108
 
94
109
  addEvent({
95
110
  type: 'control',
@@ -98,7 +113,7 @@ export function createRecorder(options: RecorderOptions): TraceRecorder {
98
113
  direction: 1, // rx
99
114
  messageType: messageTypeId(message.type),
100
115
  message: message as unknown as Record<string, unknown>,
101
- });
116
+ })
102
117
 
103
118
  if (result.ok && result.phase !== prevPhase) {
104
119
  addEvent({
@@ -107,19 +122,19 @@ export function createRecorder(options: RecorderOptions): TraceRecorder {
107
122
  timestamp: clock(),
108
123
  from: prevPhase,
109
124
  to: result.phase,
110
- });
125
+ })
111
126
  }
112
127
 
113
- return result;
128
+ return result
114
129
  },
115
130
 
116
- validateOutgoing(message: MoqtMessage) {
117
- return session.validateOutgoing(message);
131
+ validateOutgoing(message: M) {
132
+ return session.validateOutgoing(message)
118
133
  },
119
134
 
120
- send(message: MoqtMessage) {
121
- const prevPhase = session.phase;
122
- const result = session.send(message);
135
+ send(message: M) {
136
+ const prevPhase = session.phase
137
+ const result = session.send(message)
123
138
 
124
139
  addEvent({
125
140
  type: 'control',
@@ -128,7 +143,7 @@ export function createRecorder(options: RecorderOptions): TraceRecorder {
128
143
  direction: 0, // tx
129
144
  messageType: messageTypeId(message.type),
130
145
  message: message as unknown as Record<string, unknown>,
131
- });
146
+ })
132
147
 
133
148
  if (result.ok && result.phase !== prevPhase) {
134
149
  addEvent({
@@ -137,16 +152,16 @@ export function createRecorder(options: RecorderOptions): TraceRecorder {
137
152
  timestamp: clock(),
138
153
  from: prevPhase,
139
154
  to: result.phase,
140
- });
155
+ })
141
156
  }
142
157
 
143
- return result;
158
+ return result
144
159
  },
145
160
 
146
161
  reset() {
147
- session.reset();
162
+ session.reset()
148
163
  },
149
- };
164
+ }
150
165
  }
151
166
 
152
167
  return {
@@ -155,7 +170,7 @@ export function createRecorder(options: RecorderOptions): TraceRecorder {
155
170
  record: addEvent,
156
171
 
157
172
  recordStreamOpened(streamId, direction, streamType) {
158
- if (detailRank < DETAIL_RANK['headers']) return;
173
+ if (detailRank < DETAIL_RANK.headers) return
159
174
  addEvent({
160
175
  type: 'stream-opened',
161
176
  seq: nextSeq(),
@@ -163,22 +178,22 @@ export function createRecorder(options: RecorderOptions): TraceRecorder {
163
178
  streamId,
164
179
  direction,
165
180
  streamType,
166
- });
181
+ })
167
182
  },
168
183
 
169
184
  recordStreamClosed(streamId, errorCode = 0) {
170
- if (detailRank < DETAIL_RANK['headers']) return;
185
+ if (detailRank < DETAIL_RANK.headers) return
171
186
  addEvent({
172
187
  type: 'stream-closed',
173
188
  seq: nextSeq(),
174
189
  timestamp: clock(),
175
190
  streamId,
176
191
  errorCode,
177
- });
192
+ })
178
193
  },
179
194
 
180
195
  recordObjectHeader(streamId, groupId, objectId, publisherPriority, objectStatus) {
181
- if (detailRank < DETAIL_RANK['headers']) return;
196
+ if (detailRank < DETAIL_RANK.headers) return
182
197
  addEvent({
183
198
  type: 'object-header',
184
199
  seq: nextSeq(),
@@ -188,11 +203,11 @@ export function createRecorder(options: RecorderOptions): TraceRecorder {
188
203
  objectId,
189
204
  publisherPriority,
190
205
  objectStatus,
191
- });
206
+ })
192
207
  },
193
208
 
194
209
  recordObjectPayload(streamId, groupId, objectId, size, payload) {
195
- if (detailRank < DETAIL_RANK['headers+sizes']) return;
210
+ if (detailRank < DETAIL_RANK['headers+sizes']) return
196
211
  const event: TraceEvent = {
197
212
  type: 'object-payload',
198
213
  seq: nextSeq(),
@@ -202,8 +217,8 @@ export function createRecorder(options: RecorderOptions): TraceRecorder {
202
217
  objectId,
203
218
  size,
204
219
  ...(detailRank >= DETAIL_RANK['headers+data'] && payload != null ? { payload } : {}),
205
- };
206
- addEvent(event);
220
+ }
221
+ addEvent(event)
207
222
  },
208
223
 
209
224
  recordError(errorCode, reason) {
@@ -213,7 +228,7 @@ export function createRecorder(options: RecorderOptions): TraceRecorder {
213
228
  timestamp: clock(),
214
229
  errorCode,
215
230
  reason,
216
- });
231
+ })
217
232
  },
218
233
 
219
234
  annotate(label, data) {
@@ -223,11 +238,11 @@ export function createRecorder(options: RecorderOptions): TraceRecorder {
223
238
  timestamp: clock(),
224
239
  label,
225
240
  data,
226
- });
241
+ })
227
242
  },
228
243
 
229
244
  finalize(): Trace {
230
- _recording = false;
245
+ _recording = false
231
246
  const header: TraceHeader = {
232
247
  protocol: options.protocol,
233
248
  perspective: options.perspective,
@@ -238,10 +253,12 @@ export function createRecorder(options: RecorderOptions): TraceRecorder {
238
253
  ...(options.source != null ? { source: options.source } : {}),
239
254
  ...(options.endpoint != null ? { endpoint: options.endpoint } : {}),
240
255
  ...(options.sessionId != null ? { sessionId: options.sessionId } : {}),
241
- };
242
- return { header, events: [...events] };
256
+ }
257
+ return { header, events: [...events] }
243
258
  },
244
259
 
245
- get recording() { return _recording; },
246
- };
260
+ get recording() {
261
+ return _recording
262
+ },
263
+ }
247
264
  }
package/src/types.ts CHANGED
@@ -1,79 +1,79 @@
1
- export type DetailLevel = 'control' | 'headers' | 'headers+sizes' | 'headers+data' | 'full';
2
- export type Perspective = 'client' | 'server' | 'observer';
1
+ export type DetailLevel = 'control' | 'headers' | 'headers+sizes' | 'headers+data' | 'full'
2
+ export type Perspective = 'client' | 'server' | 'observer'
3
3
 
4
4
  export interface TraceHeader {
5
- readonly protocol: string;
6
- readonly perspective: Perspective;
7
- readonly detail: DetailLevel;
8
- readonly startTime: number;
9
- readonly endTime?: number;
10
- readonly transport?: string;
11
- readonly source?: string;
12
- readonly endpoint?: string;
13
- readonly sessionId?: string;
14
- readonly custom?: Record<string, unknown>;
5
+ readonly protocol: string
6
+ readonly perspective: Perspective
7
+ readonly detail: DetailLevel
8
+ readonly startTime: number
9
+ readonly endTime?: number
10
+ readonly transport?: string
11
+ readonly source?: string
12
+ readonly endpoint?: string
13
+ readonly sessionId?: string
14
+ readonly custom?: Record<string, unknown>
15
15
  }
16
16
 
17
17
  interface BaseEvent {
18
- readonly seq: number;
19
- readonly timestamp: number;
18
+ readonly seq: number
19
+ readonly timestamp: number
20
20
  }
21
21
 
22
22
  export interface ControlMessageEvent extends BaseEvent {
23
- readonly type: 'control';
24
- readonly direction: 0 | 1;
25
- readonly messageType: number;
26
- readonly message: Record<string, unknown>;
27
- readonly raw?: Uint8Array;
23
+ readonly type: 'control'
24
+ readonly direction: 0 | 1
25
+ readonly messageType: number
26
+ readonly message: Record<string, unknown>
27
+ readonly raw?: Uint8Array
28
28
  }
29
29
 
30
30
  export interface StreamOpenedEvent extends BaseEvent {
31
- readonly type: 'stream-opened';
32
- readonly streamId: bigint;
33
- readonly direction: 0 | 1;
34
- readonly streamType: 0 | 1 | 2;
31
+ readonly type: 'stream-opened'
32
+ readonly streamId: bigint
33
+ readonly direction: 0 | 1
34
+ readonly streamType: 0 | 1 | 2
35
35
  }
36
36
 
37
37
  export interface StreamClosedEvent extends BaseEvent {
38
- readonly type: 'stream-closed';
39
- readonly streamId: bigint;
40
- readonly errorCode: number;
38
+ readonly type: 'stream-closed'
39
+ readonly streamId: bigint
40
+ readonly errorCode: number
41
41
  }
42
42
 
43
43
  export interface ObjectHeaderEvent extends BaseEvent {
44
- readonly type: 'object-header';
45
- readonly streamId: bigint;
46
- readonly groupId: bigint;
47
- readonly objectId: bigint;
48
- readonly publisherPriority: number;
49
- readonly objectStatus: number;
44
+ readonly type: 'object-header'
45
+ readonly streamId: bigint
46
+ readonly groupId: bigint
47
+ readonly objectId: bigint
48
+ readonly publisherPriority: number
49
+ readonly objectStatus: number
50
50
  }
51
51
 
52
52
  export interface ObjectPayloadEvent extends BaseEvent {
53
- readonly type: 'object-payload';
54
- readonly streamId: bigint;
55
- readonly groupId: bigint;
56
- readonly objectId: bigint;
57
- readonly size: number;
58
- readonly payload?: Uint8Array;
53
+ readonly type: 'object-payload'
54
+ readonly streamId: bigint
55
+ readonly groupId: bigint
56
+ readonly objectId: bigint
57
+ readonly size: number
58
+ readonly payload?: Uint8Array
59
59
  }
60
60
 
61
61
  export interface StateChangeEvent extends BaseEvent {
62
- readonly type: 'state-change';
63
- readonly from: string;
64
- readonly to: string;
62
+ readonly type: 'state-change'
63
+ readonly from: string
64
+ readonly to: string
65
65
  }
66
66
 
67
67
  export interface TraceErrorEvent extends BaseEvent {
68
- readonly type: 'error';
69
- readonly errorCode: number;
70
- readonly reason: string;
68
+ readonly type: 'error'
69
+ readonly errorCode: number
70
+ readonly reason: string
71
71
  }
72
72
 
73
73
  export interface AnnotationEvent extends BaseEvent {
74
- readonly type: 'annotation';
75
- readonly label: string;
76
- readonly data: unknown;
74
+ readonly type: 'annotation'
75
+ readonly label: string
76
+ readonly data: unknown
77
77
  }
78
78
 
79
79
  export type TraceEvent =
@@ -84,23 +84,23 @@ export type TraceEvent =
84
84
  | ObjectPayloadEvent
85
85
  | StateChangeEvent
86
86
  | TraceErrorEvent
87
- | AnnotationEvent;
87
+ | AnnotationEvent
88
88
 
89
89
  export interface Trace {
90
- readonly header: TraceHeader;
91
- readonly events: TraceEvent[];
90
+ readonly header: TraceHeader
91
+ readonly events: TraceEvent[]
92
92
  }
93
93
 
94
94
  export interface RecorderOptions {
95
- readonly detail: DetailLevel;
96
- readonly protocol: string;
97
- readonly perspective: Perspective;
98
- readonly transport?: string;
99
- readonly source?: string;
100
- readonly endpoint?: string;
101
- readonly sessionId?: string;
102
- readonly maxEvents?: number;
103
- readonly clock?: () => number;
95
+ readonly detail: DetailLevel
96
+ readonly protocol: string
97
+ readonly perspective: Perspective
98
+ readonly transport?: string
99
+ readonly source?: string
100
+ readonly endpoint?: string
101
+ readonly sessionId?: string
102
+ readonly maxEvents?: number
103
+ readonly clock?: () => number
104
104
  /** Map message type name (e.g. 'subscribe') to wire ID (e.g. 0x03). Required for session-layer recording. */
105
- readonly messageTypeId?: (name: string) => number;
105
+ readonly messageTypeId?: (name: string) => number
106
106
  }