@moqtap/trace
Read, write, and record MoQT session traces.
What it does
Section titled “What it does”@moqtap/trace implements the .moqtrace binary format for JavaScript/TypeScript. It provides:
- One-shot and streaming read/write of
.moqtracefiles - A recorder that wraps
@moqtap/codecsessions and auto-captures events - Detail-level filtering (control only through full wire bytes)
- JSON serialization for inspection and debugging
Peer dependency on @moqtap/codec. Uses cbor-x for CBOR encoding.
Installation
Section titled “Installation”npm install @moqtap/trace @moqtap/codecReading traces
Section titled “Reading traces”import { readMoqtrace, readMoqtraceHeader } from '@moqtap/trace';
// Full read — header + all eventsconst trace = readMoqtrace(bytes);console.log(trace.header.protocol); // 'moq-transport-14'console.log(trace.events.length); // number of events
// Header-only read — fast metadata extractionconst header = readMoqtraceHeader(bytes);console.log(header.perspective); // 'client'console.log(header.detail); // 'control'Writing traces
Section titled “Writing traces”import { writeMoqtrace, createMoqtraceWriter } from '@moqtap/trace';
// One-shot — serialize a complete trace to bytesconst bytes = writeMoqtrace({ header, events });
// Streaming — write incrementally as events arriveconst writer = createMoqtraceWriter(header);const preamble = writer.preamble(); // magic + version + headerconst chunk1 = writer.writeEvent(event1);const chunk2 = writer.writeEvent(event2);// Concatenate preamble + chunks to get the complete fileRecording sessions
Section titled “Recording sessions”The recorder wraps a SessionState from @moqtap/codec and auto-captures control messages, state transitions, and data events filtered by detail level.
import { createRecorder } from '@moqtap/trace';import { createSessionState } from '@moqtap/codec/session';import { MESSAGE_ID_MAP } from '@moqtap/codec/draft14';
const recorder = createRecorder({ detail: 'headers', protocol: 'moq-transport-14', perspective: 'client', source: 'my-app/1.0.0', endpoint: 'https://relay.example.com/moq', messageTypeId: (name) => Number(MESSAGE_ID_MAP.get(name) ?? 0),});
// messageTypeId resolves message names to wire type IDs for control events.// The recorder is draft-agnostic — you bring your own mapping.// Defaults to () => 0 if omitted.
// Wrap a session — receive/send calls are auto-recordedconst session = createSessionState({ draft: 'moq-transport-14', role: 'client' });const traced = recorder.wrapSession(session);
// Use the wrapped session normallytraced.send(clientSetup);traced.receive(serverSetup);// State changes and control messages are captured automatically
// Record data events manuallyrecorder.recordStreamOpened(0n, 1, 0); // incoming subgroup streamrecorder.recordObjectHeader(0n, 1n, 0n, 128, 0);recorder.recordObjectPayload(0n, 1n, 0n, 256);recorder.recordStreamClosed(0n, 0);
// Add custom annotationsrecorder.annotate('user-action', { clicked: 'subscribe' });
// Finalize and exportconst trace = recorder.finalize();const bytes = writeMoqtrace(trace);Detail-level filtering
Section titled “Detail-level filtering”The recorder automatically filters events based on the configured detail level:
| Detail level | Control messages | Stream/object headers | Payload sizes | Payload bytes | Raw wire bytes |
|---|---|---|---|---|---|
control | Yes | — | — | — | — |
headers | Yes | Yes | — | — | — |
headers+sizes | Yes | Yes | Yes | — | — |
headers+data | Yes | Yes | Yes | Yes | — |
full | Yes | Yes | Yes | Yes | Yes |
Errors and annotations are always recorded regardless of detail level.
Circular buffer
Section titled “Circular buffer”The recorder defaults to a 100,000 event buffer. When the limit is reached, the oldest events are evicted. Sequence numbers remain monotonically increasing (they are not reset on eviction).
const recorder = createRecorder({ // ... maxEvents: 50_000, // custom buffer size});JSON export
Section titled “JSON export”For inspection and debugging, traces can be converted to JSON. Note that this is lossy — bigint values become hex strings and Uint8Array values become hex strings.
import { traceToJSON } from '@moqtap/trace';
const json = traceToJSON(trace); // pretty-printed JSON stringAPI reference
Section titled “API reference”Functions
Section titled “Functions”| Function | Description |
|---|---|
readMoqtrace(bytes) | Parse a complete .moqtrace file into a Trace |
readMoqtraceHeader(bytes) | Parse only the header (skip events) |
writeMoqtrace(trace) | Serialize a Trace to .moqtrace bytes |
createMoqtraceWriter(header) | Create a streaming writer |
createRecorder(options) | Create a session recorder |
traceToJSON(trace) | Serialize to JSON (lossy) |
Event types
Section titled “Event types”Events use a discriminated union on the type field:
| Type | Description |
|---|---|
control | Control message sent or received |
stream-opened | QUIC stream opened |
stream-closed | QUIC stream closed |
object-header | Object metadata parsed from data stream |
object-payload | Object payload bytes |
state-change | Session FSM phase transition |
error | Protocol or transport error |
annotation | User-defined event |
See .moqtrace File Format for the complete binary specification.