@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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 moqtap
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 moqtap
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -21,82 +21,82 @@ npm install @moqtap/trace @moqtap/codec
21
21
  ### Recording a session
22
22
 
23
23
  ```typescript
24
- import { createRecorder, writeMoqtrace } from '@moqtap/trace';
25
- import { createDraft14SessionState } from '@moqtap/codec/draft14/session';
24
+ import { createRecorder, writeMoqtrace } from '@moqtap/trace'
25
+ import { createDraft17SessionState } from '@moqtap/codec/draft17/session'
26
26
 
27
27
  const recorder = createRecorder({
28
- protocol: 'moq-transport-14',
28
+ protocol: 'moq-transport-17',
29
29
  perspective: 'client',
30
30
  detail: 'control',
31
- });
31
+ })
32
32
 
33
33
  // Wrap a session to auto-capture control messages and state transitions
34
- const session = createDraft14SessionState({ codec: { draft: 'draft-ietf-moq-transport-14' }, role: 'client' });
35
- const traced = recorder.wrapSession(session);
34
+ const session = createDraft17SessionState('client')
35
+ const traced = recorder.wrapSession(session)
36
36
 
37
37
  // Use `traced` instead of `session` — all send/receive calls are recorded
38
- traced.send(clientSetupMessage);
39
- traced.receive(serverSetupMessage);
38
+ traced.send(setupMessage)
39
+ traced.receive(setupMessage)
40
40
 
41
41
  // Manually record events the session layer doesn't see
42
- recorder.recordStreamOpened(4n, 0, 0); // outgoing subgroup stream
43
- recorder.annotate('connected', { relay: 'cdn.example.com' });
42
+ recorder.recordStreamOpened(4n, 0, 0) // outgoing subgroup stream
43
+ recorder.annotate('connected', { relay: 'cdn.example.com' })
44
44
 
45
45
  // Finalize and serialize
46
- const trace = recorder.finalize();
47
- const bytes = writeMoqtrace(trace); // → Uint8Array (.moqtrace binary)
46
+ const trace = recorder.finalize()
47
+ const bytes = writeMoqtrace(trace) // → Uint8Array (.moqtrace binary)
48
48
  ```
49
49
 
50
50
  ### Reading a trace file
51
51
 
52
52
  ```typescript
53
- import { readMoqtrace, readMoqtraceHeader } from '@moqtap/trace';
53
+ import { readMoqtrace, readMoqtraceHeader } from '@moqtap/trace'
54
54
 
55
55
  // Quick metadata peek (no event parsing)
56
- const header = readMoqtraceHeader(bytes);
57
- console.log(header.protocol); // "moq-transport-14"
58
- console.log(header.perspective); // "client"
59
- console.log(header.detail); // "control"
56
+ const header = readMoqtraceHeader(bytes)
57
+ console.log(header.protocol) // "moq-transport-17"
58
+ console.log(header.perspective) // "client"
59
+ console.log(header.detail) // "control"
60
60
 
61
61
  // Full parse
62
- const trace = readMoqtrace(bytes);
62
+ const trace = readMoqtrace(bytes)
63
63
  for (const event of trace.events) {
64
- console.log(event.type, event.timestamp);
64
+ console.log(event.type, event.timestamp)
65
65
  }
66
66
  ```
67
67
 
68
68
  ### Streaming writer (for large traces)
69
69
 
70
70
  ```typescript
71
- import { createMoqtraceWriter } from '@moqtap/trace';
71
+ import { createMoqtraceWriter } from '@moqtap/trace'
72
72
 
73
- const writer = createMoqtraceWriter(header);
74
- outputStream.write(writer.preamble());
73
+ const writer = createMoqtraceWriter(header)
74
+ outputStream.write(writer.preamble())
75
75
 
76
76
  for (const event of events) {
77
- outputStream.write(writer.writeEvent(event));
77
+ outputStream.write(writer.writeEvent(event))
78
78
  }
79
79
  ```
80
80
 
81
81
  ## Detail Levels
82
82
 
83
- | Level | What's recorded |
84
- |---|---|
85
- | `control` | Control messages only (setup, subscribe, publish, goaway) |
86
- | `headers` | + data stream headers (subgroup/fetch, object metadata) |
87
- | `headers+sizes` | + payload byte lengths |
88
- | `headers+data` | + full payload bytes |
89
- | `full` | + raw wire bytes (pre-decode) |
83
+ | Level | What's recorded |
84
+ | --------------- | --------------------------------------------------------- |
85
+ | `control` | Control messages only (setup, subscribe, publish, goaway) |
86
+ | `headers` | + data stream headers (subgroup/fetch, object metadata) |
87
+ | `headers+sizes` | + payload byte lengths |
88
+ | `headers+data` | + full payload bytes |
89
+ | `full` | + raw wire bytes (pre-decode) |
90
90
 
91
91
  ## JSON Export
92
92
 
93
93
  For human-readable debugging (not lossless — bigint/Uint8Array become strings):
94
94
 
95
95
  ```typescript
96
- import { traceToJSON } from '@moqtap/trace';
96
+ import { traceToJSON } from '@moqtap/trace'
97
97
 
98
- const json = traceToJSON(trace);
99
- console.log(json);
98
+ const json = traceToJSON(trace)
99
+ console.log(json)
100
100
  ```
101
101
 
102
102
  ## File Format
package/dist/index.cjs CHANGED
@@ -4,14 +4,14 @@ var MAGIC = new Uint8Array([77, 79, 81, 84, 82, 65, 67, 69]);
4
4
  var FORMAT_VERSION = 1;
5
5
  var PREAMBLE_SIZE = 16;
6
6
  var EVENT_TYPE_TO_INT = {
7
- "control": 0,
7
+ control: 0,
8
8
  "stream-opened": 1,
9
9
  "stream-closed": 2,
10
10
  "object-header": 3,
11
11
  "object-payload": 4,
12
12
  "state-change": 5,
13
- "error": 6,
14
- "annotation": 7
13
+ error: 6,
14
+ annotation: 7
15
15
  };
16
16
  var INT_TO_EVENT_TYPE = {
17
17
  0: "control",
@@ -279,13 +279,30 @@ function createMoqtraceWriter(header) {
279
279
  };
280
280
  }
281
281
 
282
+ // src/json.ts
283
+ function traceToJSON(trace) {
284
+ return JSON.stringify(
285
+ trace,
286
+ (_key, value) => {
287
+ if (typeof value === "bigint") {
288
+ return `0x${value.toString(16)}`;
289
+ }
290
+ if (value instanceof Uint8Array) {
291
+ return Array.from(value).map((b) => b.toString(16).padStart(2, "0")).join("");
292
+ }
293
+ return value;
294
+ },
295
+ 2
296
+ );
297
+ }
298
+
282
299
  // src/recorder.ts
283
300
  var DETAIL_RANK = {
284
- "control": 0,
285
- "headers": 1,
301
+ control: 0,
302
+ headers: 1,
286
303
  "headers+sizes": 2,
287
304
  "headers+data": 3,
288
- "full": 4
305
+ full: 4
289
306
  };
290
307
  function createRecorder(options) {
291
308
  const detail = options.detail;
@@ -385,7 +402,7 @@ function createRecorder(options) {
385
402
  wrapSession,
386
403
  record: addEvent,
387
404
  recordStreamOpened(streamId, direction, streamType) {
388
- if (detailRank < DETAIL_RANK["headers"]) return;
405
+ if (detailRank < DETAIL_RANK.headers) return;
389
406
  addEvent({
390
407
  type: "stream-opened",
391
408
  seq: nextSeq(),
@@ -396,7 +413,7 @@ function createRecorder(options) {
396
413
  });
397
414
  },
398
415
  recordStreamClosed(streamId, errorCode = 0) {
399
- if (detailRank < DETAIL_RANK["headers"]) return;
416
+ if (detailRank < DETAIL_RANK.headers) return;
400
417
  addEvent({
401
418
  type: "stream-closed",
402
419
  seq: nextSeq(),
@@ -406,7 +423,7 @@ function createRecorder(options) {
406
423
  });
407
424
  },
408
425
  recordObjectHeader(streamId, groupId, objectId, publisherPriority, objectStatus) {
409
- if (detailRank < DETAIL_RANK["headers"]) return;
426
+ if (detailRank < DETAIL_RANK.headers) return;
410
427
  addEvent({
411
428
  type: "object-header",
412
429
  seq: nextSeq(),
@@ -471,19 +488,6 @@ function createRecorder(options) {
471
488
  };
472
489
  }
473
490
 
474
- // src/json.ts
475
- function traceToJSON(trace) {
476
- return JSON.stringify(trace, (_key, value) => {
477
- if (typeof value === "bigint") {
478
- return `0x${value.toString(16)}`;
479
- }
480
- if (value instanceof Uint8Array) {
481
- return Array.from(value).map((b) => b.toString(16).padStart(2, "0")).join("");
482
- }
483
- return value;
484
- }, 2);
485
- }
486
-
487
491
 
488
492
 
489
493
 
package/dist/index.d.cts CHANGED
@@ -109,9 +109,21 @@ interface MoqtraceWriter {
109
109
  }
110
110
  declare function createMoqtraceWriter(header: TraceHeader): MoqtraceWriter;
111
111
 
112
+ /**
113
+ * Serialize a trace to human-readable JSON.
114
+ *
115
+ * Handles bigint → hex string and Uint8Array → hex string.
116
+ * This is a one-way export for human inspection and debugging.
117
+ * For lossless round-tripping, use the binary .moqtrace format
118
+ * (`writeMoqtrace` / `readMoqtrace`).
119
+ */
120
+ declare function traceToJSON(trace: Trace): string;
121
+
112
122
  interface TraceRecorder {
113
123
  /** Wrap a SessionState to auto-record control messages and state changes. */
114
- wrapSession(session: SessionState): SessionState;
124
+ wrapSession<M extends {
125
+ type: string;
126
+ }, T extends string>(session: SessionState<M, T>): SessionState<M, T>;
115
127
  /** Record an arbitrary event manually. */
116
128
  record(event: TraceEvent): void;
117
129
  /** Record a stream-opened event. Ignored at 'control' detail level. */
@@ -133,14 +145,4 @@ interface TraceRecorder {
133
145
  }
134
146
  declare function createRecorder(options: RecorderOptions): TraceRecorder;
135
147
 
136
- /**
137
- * Serialize a trace to human-readable JSON.
138
- *
139
- * Handles bigint → hex string and Uint8Array → hex string.
140
- * This is a one-way export for human inspection and debugging.
141
- * For lossless round-tripping, use the binary .moqtrace format
142
- * (`writeMoqtrace` / `readMoqtrace`).
143
- */
144
- declare function traceToJSON(trace: Trace): string;
145
-
146
148
  export { type AnnotationEvent, type ControlMessageEvent, type DetailLevel, type MoqtraceWriter, type ObjectHeaderEvent, type ObjectPayloadEvent, type Perspective, type RecorderOptions, type StateChangeEvent, type StreamClosedEvent, type StreamOpenedEvent, type Trace, type TraceErrorEvent, type TraceEvent, type TraceHeader, type TraceRecorder, createMoqtraceWriter, createRecorder, readMoqtrace, readMoqtraceHeader, traceToJSON, writeMoqtrace };
package/dist/index.d.ts CHANGED
@@ -109,9 +109,21 @@ interface MoqtraceWriter {
109
109
  }
110
110
  declare function createMoqtraceWriter(header: TraceHeader): MoqtraceWriter;
111
111
 
112
+ /**
113
+ * Serialize a trace to human-readable JSON.
114
+ *
115
+ * Handles bigint → hex string and Uint8Array → hex string.
116
+ * This is a one-way export for human inspection and debugging.
117
+ * For lossless round-tripping, use the binary .moqtrace format
118
+ * (`writeMoqtrace` / `readMoqtrace`).
119
+ */
120
+ declare function traceToJSON(trace: Trace): string;
121
+
112
122
  interface TraceRecorder {
113
123
  /** Wrap a SessionState to auto-record control messages and state changes. */
114
- wrapSession(session: SessionState): SessionState;
124
+ wrapSession<M extends {
125
+ type: string;
126
+ }, T extends string>(session: SessionState<M, T>): SessionState<M, T>;
115
127
  /** Record an arbitrary event manually. */
116
128
  record(event: TraceEvent): void;
117
129
  /** Record a stream-opened event. Ignored at 'control' detail level. */
@@ -133,14 +145,4 @@ interface TraceRecorder {
133
145
  }
134
146
  declare function createRecorder(options: RecorderOptions): TraceRecorder;
135
147
 
136
- /**
137
- * Serialize a trace to human-readable JSON.
138
- *
139
- * Handles bigint → hex string and Uint8Array → hex string.
140
- * This is a one-way export for human inspection and debugging.
141
- * For lossless round-tripping, use the binary .moqtrace format
142
- * (`writeMoqtrace` / `readMoqtrace`).
143
- */
144
- declare function traceToJSON(trace: Trace): string;
145
-
146
148
  export { type AnnotationEvent, type ControlMessageEvent, type DetailLevel, type MoqtraceWriter, type ObjectHeaderEvent, type ObjectPayloadEvent, type Perspective, type RecorderOptions, type StateChangeEvent, type StreamClosedEvent, type StreamOpenedEvent, type Trace, type TraceErrorEvent, type TraceEvent, type TraceHeader, type TraceRecorder, createMoqtraceWriter, createRecorder, readMoqtrace, readMoqtraceHeader, traceToJSON, writeMoqtrace };
package/dist/index.js CHANGED
@@ -1,17 +1,17 @@
1
1
  // src/binary.ts
2
- import { encode, decode, decodeMultiple } from "cbor-x";
2
+ import { decode, decodeMultiple, encode } from "cbor-x";
3
3
  var MAGIC = new Uint8Array([77, 79, 81, 84, 82, 65, 67, 69]);
4
4
  var FORMAT_VERSION = 1;
5
5
  var PREAMBLE_SIZE = 16;
6
6
  var EVENT_TYPE_TO_INT = {
7
- "control": 0,
7
+ control: 0,
8
8
  "stream-opened": 1,
9
9
  "stream-closed": 2,
10
10
  "object-header": 3,
11
11
  "object-payload": 4,
12
12
  "state-change": 5,
13
- "error": 6,
14
- "annotation": 7
13
+ error: 6,
14
+ annotation: 7
15
15
  };
16
16
  var INT_TO_EVENT_TYPE = {
17
17
  0: "control",
@@ -279,13 +279,30 @@ function createMoqtraceWriter(header) {
279
279
  };
280
280
  }
281
281
 
282
+ // src/json.ts
283
+ function traceToJSON(trace) {
284
+ return JSON.stringify(
285
+ trace,
286
+ (_key, value) => {
287
+ if (typeof value === "bigint") {
288
+ return `0x${value.toString(16)}`;
289
+ }
290
+ if (value instanceof Uint8Array) {
291
+ return Array.from(value).map((b) => b.toString(16).padStart(2, "0")).join("");
292
+ }
293
+ return value;
294
+ },
295
+ 2
296
+ );
297
+ }
298
+
282
299
  // src/recorder.ts
283
300
  var DETAIL_RANK = {
284
- "control": 0,
285
- "headers": 1,
301
+ control: 0,
302
+ headers: 1,
286
303
  "headers+sizes": 2,
287
304
  "headers+data": 3,
288
- "full": 4
305
+ full: 4
289
306
  };
290
307
  function createRecorder(options) {
291
308
  const detail = options.detail;
@@ -385,7 +402,7 @@ function createRecorder(options) {
385
402
  wrapSession,
386
403
  record: addEvent,
387
404
  recordStreamOpened(streamId, direction, streamType) {
388
- if (detailRank < DETAIL_RANK["headers"]) return;
405
+ if (detailRank < DETAIL_RANK.headers) return;
389
406
  addEvent({
390
407
  type: "stream-opened",
391
408
  seq: nextSeq(),
@@ -396,7 +413,7 @@ function createRecorder(options) {
396
413
  });
397
414
  },
398
415
  recordStreamClosed(streamId, errorCode = 0) {
399
- if (detailRank < DETAIL_RANK["headers"]) return;
416
+ if (detailRank < DETAIL_RANK.headers) return;
400
417
  addEvent({
401
418
  type: "stream-closed",
402
419
  seq: nextSeq(),
@@ -406,7 +423,7 @@ function createRecorder(options) {
406
423
  });
407
424
  },
408
425
  recordObjectHeader(streamId, groupId, objectId, publisherPriority, objectStatus) {
409
- if (detailRank < DETAIL_RANK["headers"]) return;
426
+ if (detailRank < DETAIL_RANK.headers) return;
410
427
  addEvent({
411
428
  type: "object-header",
412
429
  seq: nextSeq(),
@@ -470,19 +487,6 @@ function createRecorder(options) {
470
487
  }
471
488
  };
472
489
  }
473
-
474
- // src/json.ts
475
- function traceToJSON(trace) {
476
- return JSON.stringify(trace, (_key, value) => {
477
- if (typeof value === "bigint") {
478
- return `0x${value.toString(16)}`;
479
- }
480
- if (value instanceof Uint8Array) {
481
- return Array.from(value).map((b) => b.toString(16).padStart(2, "0")).join("");
482
- }
483
- return value;
484
- }, 2);
485
- }
486
490
  export {
487
491
  createMoqtraceWriter,
488
492
  createRecorder,
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@moqtap/trace",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "MoQT session trace format — record, serialize, and analyze .moqtrace files",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "moqtap",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "https://github.com/moqtap/moqtap-js.git",
10
+ "url": "git+https://github.com/moqtap/moqtap-js.git",
11
11
  "directory": "packages/trace"
12
12
  },
13
13
  "homepage": "https://github.com/moqtap/moqtap-js/tree/master/packages/trace",
@@ -32,7 +32,7 @@
32
32
  ".": {
33
33
  "bun": "./src/index.ts",
34
34
  "types": "./dist/index.d.ts",
35
- "import": "./dist/index.mjs",
35
+ "import": "./dist/index.js",
36
36
  "require": "./dist/index.cjs"
37
37
  }
38
38
  },