@moqtap/trace 0.2.0 → 0.3.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.
package/README.md CHANGED
@@ -3,11 +3,14 @@
3
3
  Record, serialize, and analyze MoQT (Media over QUIC Transport) sessions using the `.moqtrace` binary format.
4
4
 
5
5
  - Binary `.moqtrace` format with CBOR encoding (compact, streamable, cross-language)
6
+ - Segmented traces for capture rotation, live carriage, and recovery from a damaged region
6
7
  - Configurable detail levels from control-only to full wire captures
7
8
  - Session recorder that wraps `@moqtap/codec` session state machines
8
9
  - Human-readable JSON export for debugging
9
10
  - Zero-copy streaming writer for large traces
10
11
 
12
+ Writes format version 2 and reads versions 1 and 2.
13
+
11
14
  ## Install
12
15
 
13
16
  ```bash
@@ -78,6 +81,66 @@ for (const event of events) {
78
81
  }
79
82
  ```
80
83
 
84
+ ### Segmented traces
85
+
86
+ A segment is a complete `.moqtrace` blob — magic, version, header, events —
87
+ appended after the one before it, so a reader can start at any segment without
88
+ the ones preceding it. That buys capture rotation without finalizing a file, a
89
+ natural cut point for live carriage, and recovery from a damaged region.
90
+
91
+ ```typescript
92
+ import { readMoqtraceSegments, writeMoqtraceSegments } from '@moqtap/trace'
93
+
94
+ // Each segment header must carry `segment` metadata: it is what tells a reader
95
+ // that `seq` and `timestamp` restart at zero in every segment.
96
+ const bytes = writeMoqtraceSegments([
97
+ { header: { ...header, segment: { sequence: 0 } }, events: firstBatch },
98
+ { header: { ...header, segment: { sequence: 1, continues: true } }, events: secondBatch },
99
+ ])
100
+
101
+ // Read the boundaries — you need them to order events across segments.
102
+ for (const segment of readMoqtraceSegments(bytes)) {
103
+ console.log(segment.header.segment?.sequence, segment.events.length)
104
+ }
105
+
106
+ // Or ignore them: readMoqtrace flattens every segment's events in order.
107
+ ```
108
+
109
+ ### Damaged and truncated files
110
+
111
+ A file that stops part-way through an event throws `TruncatedTraceError`, which
112
+ carries everything that decoded before the cut — a truncated trace is still
113
+ evidence. On a segmented trace, `{ recover: true }` skips a damaged region and
114
+ resumes at the next segment instead of throwing.
115
+
116
+ ```typescript
117
+ import { readMoqtrace, readMoqtraceSegments, TruncatedTraceError } from '@moqtap/trace'
118
+
119
+ try {
120
+ const trace = readMoqtrace(bytes)
121
+ } catch (error) {
122
+ if (error instanceof TruncatedTraceError) {
123
+ console.warn(`truncated at byte ${error.offset}`)
124
+ console.log(error.trace?.events.length, 'events survived')
125
+ }
126
+ }
127
+
128
+ const whatSurvived = readMoqtraceSegments(bytes, { recover: true })
129
+ ```
130
+
131
+ ## Reading a trace you did not write
132
+
133
+ Nothing here rejects a file for carrying something newer than this version
134
+ knows. An unrecognised event type arrives as an `UnknownEvent` with its fields
135
+ intact, so it survives a read-modify-write round trip rather than being dropped
136
+ or relabelled; an unrecognised perspective, detail level, role, side, drop
137
+ policy or derivation kind is kept verbatim.
138
+
139
+ The one place that refuses is `createRecorder`, which cannot honour a detail
140
+ level it does not implement: it would either capture less than you asked for
141
+ while writing a header claiming otherwise, or — for the payload-bearing levels
142
+ — capture more.
143
+
81
144
  ## Detail Levels
82
145
 
83
146
  | Level | What's recorded |
@@ -88,6 +151,10 @@ for (const event of events) {
88
151
  | `headers+data` | + full payload bytes |
89
152
  | `full` | + raw wire bytes (pre-decode) |
90
153
 
154
+ `headers+data` and `full`, and the `raw` field on any event, carry user
155
+ content. Capturing them is optional for a conformant recorder and should be an
156
+ explicit choice, not a default.
157
+
91
158
  ## JSON Export
92
159
 
93
160
  For human-readable debugging (not lossless — bigint/Uint8Array become strings):
@@ -99,6 +166,34 @@ const json = traceToJSON(trace)
99
166
  console.log(json)
100
167
  ```
101
168
 
169
+ ## Upgrading from 0.2.0
170
+
171
+ The package now writes format version 2. A 0.2.0 reader rejects those files at
172
+ byte 8, which is the point of the bump: it turns what would have been silent
173
+ desynchronization on a segmented trace into a clear refusal. Reading is
174
+ unaffected in the other direction — 0.3.0 reads everything 0.2.0 wrote.
175
+
176
+ What changes for code already using it:
177
+
178
+ - `TraceEvent` gained four members (`peer-connected`, `peer-disconnected`,
179
+ `subscription-derivation`, `unknown`). A `switch` that handles only the
180
+ cases it cares about is unaffected; one that asserts exhaustiveness is not.
181
+ - `Perspective`, `DetailLevel` and the new string unions accept values they do
182
+ not name, so indexing a `Record<DetailLevel, T>` with one now yields
183
+ `T | undefined`.
184
+ - An unknown event type used to arrive as an annotation labelled
185
+ `unknown-event-<n>`. It now arrives as an `UnknownEvent` carrying its real
186
+ type and fields, so code matching that label should match `type === 'unknown'`
187
+ instead. The old shape was lossy in a way that mattered: writing the trace
188
+ back rewrote the event's type to 7.
189
+ - `createRecorder` throws on a detail level it does not implement, which the
190
+ types previously made unexpressible.
191
+ - The bytes changed in two ways that make files readable by the Rust
192
+ implementation for the first time: an integral value past 32 bits is now
193
+ written as a CBOR integer rather than a float64, and a byte string as major
194
+ type 2 rather than wrapped in the typed-array tag 64. Both readers accept
195
+ either form, so nothing already written becomes unreadable.
196
+
102
197
  ## File Format
103
198
 
104
199
  See [SPEC.md](./SPEC.md) for the complete `.moqtrace` binary format specification.