@blockcast/fec-worker 0.1.0-main.cde7600fad6a → 0.1.0-main.ce3c8aeb7f68
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 +5 -1
- package/dist/fec-worker-client.d.ts +6 -1
- package/dist/fec-worker-client.d.ts.map +1 -1
- package/dist/fec-worker-client.js +25 -1
- package/dist/fec-worker-client.js.map +1 -1
- package/dist/fec-worker-types.d.ts +10 -1
- package/dist/fec-worker-types.d.ts.map +1 -1
- package/dist/fec-worker.js +6 -1
- package/dist/fec-worker.js.map +2 -2
- package/package.json +2 -2
- package/src/fec-worker-client.ts +29 -1
- package/src/fec-worker-types.ts +8 -1
- package/src/fec-worker.ts +7 -1
package/README.md
CHANGED
|
@@ -114,6 +114,7 @@ All messages are typed as discriminated unions in `fec-worker-types.ts`. Both th
|
|
|
114
114
|
| `feedRepair` | `ssStart: number`, `ssbLength: number`, `rsId: number`, `data: ArrayBuffer`, `ts: number` | `data` |
|
|
115
115
|
| `relayBlockSig` | `sbn: number`, `sig: ArrayBuffer`, `hash: ArrayBuffer` | `sig`, `hash` |
|
|
116
116
|
| `cleanup` | `sbns: number[]` (all timed-out blocks covered by this coalesced barrier) | none |
|
|
117
|
+
| `subscribeBlockUpdates` | — (send only after `capabilities` lists it) | none |
|
|
117
118
|
| `dispose` | — | none |
|
|
118
119
|
|
|
119
120
|
`feedSource` carries the **flat 32-bit Source Symbol ID** from the MMTP Source FEC Payload ID (ISO 23008-1 §C.5.2). The WASM decoder derives `SBN = floor(ssId / K)` and `ESI = ssId % K` internally — do not pre-compute these.
|
|
@@ -126,11 +127,14 @@ All messages are typed as discriminated unions in `fec-worker-types.ts`. Both th
|
|
|
126
127
|
|--------|---------|------------------|
|
|
127
128
|
| `frame` | `data: ArrayBuffer`, `meta: FrameMeta` | `data` |
|
|
128
129
|
| `snapshot` | `stats: FecTrackStats` | none (structured clone) |
|
|
129
|
-
| `
|
|
130
|
+
| `capabilities` | `commands: FecWorkerOptionalCommand[]` (after `configure`) | none |
|
|
131
|
+
| `blockUpdate` | `block: FecBlockSnapshot` (only after `subscribeBlockUpdates`) | none |
|
|
130
132
|
| `greenFill` | `sbn: number`, `data: ArrayBuffer` | `data` |
|
|
131
133
|
| `cleanupComplete` | `sbns: number[]` | none |
|
|
132
134
|
| `error` | `message: string` | none |
|
|
133
135
|
|
|
136
|
+
The Worker posts `blockUpdate` only to a client that sent `subscribeBlockUpdates`: each one is a structured clone and a page-thread task, and the snapshot already counts every transition. `FecWorkerClient` subscribes when `onBlockUpdate` is registered and the Worker has announced the command. A Worker that predates the command posts `blockUpdate` unasked and is never sent it (BLO-36599).
|
|
137
|
+
|
|
134
138
|
`FrameMeta` carries `recovered: boolean` (this frame came out of the FEC decode, not the direct path), `sbn: number`, and `altaVerified: boolean | null` (signature outcome, or `null` when ALTA is not configured / verification is pending).
|
|
135
139
|
|
|
136
140
|
## Lifecycle
|
|
@@ -64,7 +64,12 @@ export declare class FecWorkerClient {
|
|
|
64
64
|
onFrame(cb: (data: ArrayBuffer, meta: FrameMeta) => void): void;
|
|
65
65
|
/** Register callback for periodic stats snapshots (FEC-04). */
|
|
66
66
|
onSnapshot(cb: (stats: FecTrackStats) => void): void;
|
|
67
|
-
/**
|
|
67
|
+
/**
|
|
68
|
+
* Register callback for individual block state changes. The Worker posts
|
|
69
|
+
* them only once asked, so registering subscribes (after the Worker
|
|
70
|
+
* announces `subscribeBlockUpdates`). An older Worker that does not
|
|
71
|
+
* announce it posts them unasked.
|
|
72
|
+
*/
|
|
68
73
|
onBlockUpdate(cb: (block: FecBlockSnapshot) => void): void;
|
|
69
74
|
/** Register callback for green-fill frames. */
|
|
70
75
|
onGreenFill(cb: (sbn: number, data: ArrayBuffer) => void): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fec-worker-client.d.ts","sourceRoot":"","sources":["../src/fec-worker-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAIX,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,kBAAkB,EAClB,MAAM,uBAAuB,CAAC;AAE/B,qBAAa,eAAe;;
|
|
1
|
+
{"version":3,"file":"fec-worker-client.d.ts","sourceRoot":"","sources":["../src/fec-worker-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAIX,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,kBAAkB,EAClB,MAAM,uBAAuB,CAAC;AAE/B,qBAAa,eAAe;;IA4B3B;;;OAGG;gBACS,WAAW,EAAE,MAAM,GAAG,GAAG;IAgBrC;;;;;;OAMG;IACH,SAAS,CACR,MAAM,EAAE,cAAc,EACtB,UAAU,EAAE,WAAW,CAAC,MAAM,EAC9B,aAAa,CAAC,EAAE,WAAW,EAC3B,cAAc,CAAC,EAAE,WAAW,CAAC,MAAM,GACjC,IAAI;IAmBP;;;;;;;;;OASG;IACH,UAAU,CACT,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,WAAW,EACjB,EAAE,EAAE,MAAM,EACV,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAC9C,IAAI;IAoBP;;;;;;;;OAQG;IACH,UAAU,CACT,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,WAAW,EACjB,EAAE,EAAE,MAAM,GACR,IAAI;IAqCP;;OAEG;IACH,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI;IASrE;;;;;OAKG;IACH,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAgBjC,8EAA8E;IAC9E,KAAK,IAAI,IAAI;IAoBb,+DAA+D;IAC/D,OAAO,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,KAAK,IAAI,GAAG,IAAI;IAI/D,+DAA+D;IAC/D,UAAU,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAAG,IAAI;IAIpD;;;;;OAKG;IACH,aAAa,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG,IAAI;IAkB1D,+CAA+C;IAC/C,WAAW,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,KAAK,IAAI,GAAG,IAAI;IAI/D,0FAA0F;IAC1F,iBAAiB,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAIlD,2CAA2C;IAC3C,OAAO,CAAC,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,kBAAkB,KAAK,IAAI,GAAG,IAAI;IAIvE;;;;;OAKG;IACH,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED;;;OAGG;IACH,OAAO,IAAI,IAAI;CAgEf"}
|
|
@@ -29,6 +29,11 @@ export class FecWorkerClient {
|
|
|
29
29
|
#flushQueued = false;
|
|
30
30
|
// Batching starts only once the Worker announced it accepts feedSymbols.
|
|
31
31
|
#batchSymbols = false;
|
|
32
|
+
// The Worker posts blockUpdate events only to a client that subscribed,
|
|
33
|
+
// which this client does once a callback is registered and the Worker
|
|
34
|
+
// announced the command (BLO-36599).
|
|
35
|
+
#blockUpdatesAnnounced = false;
|
|
36
|
+
#blockUpdatesSubscribed = false;
|
|
32
37
|
/**
|
|
33
38
|
* Create a FecWorkerClient.
|
|
34
39
|
* @param workerOrUrl - Either a Worker instance (for testing) or a URL to create one
|
|
@@ -201,9 +206,24 @@ export class FecWorkerClient {
|
|
|
201
206
|
onSnapshot(cb) {
|
|
202
207
|
this.#onSnapshot = cb;
|
|
203
208
|
}
|
|
204
|
-
/**
|
|
209
|
+
/**
|
|
210
|
+
* Register callback for individual block state changes. The Worker posts
|
|
211
|
+
* them only once asked, so registering subscribes (after the Worker
|
|
212
|
+
* announces `subscribeBlockUpdates`). An older Worker that does not
|
|
213
|
+
* announce it posts them unasked.
|
|
214
|
+
*/
|
|
205
215
|
onBlockUpdate(cb) {
|
|
206
216
|
this.#onBlockUpdate = cb;
|
|
217
|
+
this.#subscribeBlockUpdates();
|
|
218
|
+
}
|
|
219
|
+
#subscribeBlockUpdates() {
|
|
220
|
+
if (this.#disposed ||
|
|
221
|
+
this.#blockUpdatesSubscribed ||
|
|
222
|
+
!this.#blockUpdatesAnnounced ||
|
|
223
|
+
!this.#onBlockUpdate)
|
|
224
|
+
return;
|
|
225
|
+
this.#blockUpdatesSubscribed = true;
|
|
226
|
+
this.#worker.postMessage({ type: "subscribeBlockUpdates" });
|
|
207
227
|
}
|
|
208
228
|
/** Register callback for green-fill frames. */
|
|
209
229
|
onGreenFill(cb) {
|
|
@@ -247,6 +267,10 @@ export class FecWorkerClient {
|
|
|
247
267
|
if (Array.isArray(event.commands) && event.commands.includes("feedSymbols")) {
|
|
248
268
|
this.#batchSymbols = true;
|
|
249
269
|
}
|
|
270
|
+
if (Array.isArray(event.commands) && event.commands.includes("subscribeBlockUpdates")) {
|
|
271
|
+
this.#blockUpdatesAnnounced = true;
|
|
272
|
+
this.#subscribeBlockUpdates();
|
|
273
|
+
}
|
|
250
274
|
return;
|
|
251
275
|
}
|
|
252
276
|
if (event.type === "resetComplete") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fec-worker-client.js","sourceRoot":"","sources":["../src/fec-worker-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,MAAM,OAAO,eAAe;IAClB,OAAO,CAAS;IACzB,QAAQ,GAA0D,IAAI,CAAC;IACvE,WAAW,GAA4C,IAAI,CAAC;IAC5D,cAAc,GAA+C,IAAI,CAAC;IAClE,YAAY,GAAsD,IAAI,CAAC;IACvE,kBAAkB,GAAmC,IAAI,CAAC;IAC1D,QAAQ,GAAkE,IAAI,CAAC;IAC/E,gBAAgB,GAAuB,IAAI,CAAC;IAC5C,kBAAkB,GAAG,IAAI,GAAG,EAAU,CAAC;IACvC,cAAc,GAAG,CAAC,CAAC;IACnB,YAAY,GAAG,KAAK,CAAC;IACrB,SAAS,GAAG,KAAK,CAAC;IAClB,uEAAuE;IACvE,4EAA4E;IAC5E,0EAA0E;IAC1E,2EAA2E;IAC3E,eAAe,GAAuB,EAAE,CAAC;IACzC,gBAAgB,GAAmB,EAAE,CAAC;IACtC,YAAY,GAAG,KAAK,CAAC;IACrB,yEAAyE;IACzE,aAAa,GAAG,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"fec-worker-client.js","sourceRoot":"","sources":["../src/fec-worker-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -239,6 +239,15 @@ export type FecWorkerCommand = {
|
|
|
239
239
|
sbns: number[];
|
|
240
240
|
} | {
|
|
241
241
|
type: "reset";
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Ask the Worker to post a blockUpdate on every block transition. Without
|
|
245
|
+
* it the Worker posts none: each one is a structured clone and a task on
|
|
246
|
+
* the page thread, and the player does not consume them (BLO-36599).
|
|
247
|
+
* Sent only to a Worker that announced it in `capabilities`.
|
|
248
|
+
*/
|
|
249
|
+
| {
|
|
250
|
+
type: "subscribeBlockUpdates";
|
|
242
251
|
} | {
|
|
243
252
|
type: "dispose";
|
|
244
253
|
};
|
|
@@ -284,5 +293,5 @@ export type FecWorkerEvent = {
|
|
|
284
293
|
code?: FecWorkerErrorCode;
|
|
285
294
|
};
|
|
286
295
|
/** Commands a client may send only after the Worker announced them. */
|
|
287
|
-
export type FecWorkerOptionalCommand = "feedSymbols";
|
|
296
|
+
export type FecWorkerOptionalCommand = "feedSymbols" | "subscribeBlockUpdates";
|
|
288
297
|
//# sourceMappingURL=fec-worker-types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fec-worker-types.d.ts","sourceRoot":"","sources":["../src/fec-worker-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,mFAAmF;AACnF,MAAM,WAAW,cAAc;IAC9B,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,uBAAuB;IACvB,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC;IAC7B,2CAA2C;IAC3C,UAAU,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,CAAC,EAAE,MAAM,CAAC;IACV,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAC;IACpB,uBAAuB;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,eAAe,EAAE,MAAM,CAAC;IACxB,6DAA6D;IAC7D,YAAY,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,gBAAgB,EAAE,MAAM,CAAC;IACzB,wBAAwB;IACxB,OAAO,EAAE,UAAU,GAAG,QAAQ,CAAC;IAC/B,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,uDAAuD;IACvD,WAAW,EAAE,OAAO,CAAC;IACrB,sEAAsE;IACtE,oBAAoB,EAAE,OAAO,CAAC;CAC9B;AAID,iEAAiE;AACjE,MAAM,WAAW,gBAAgB;IAChC,0BAA0B;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,0BAA0B;IAC1B,KAAK,EAAE,YAAY,GAAG,UAAU,GAAG,WAAW,GAAG,QAAQ,GAAG,YAAY,CAAC;IACzE,wCAAwC;IACxC,cAAc,EAAE,MAAM,CAAC;IACvB,wCAAwC;IACxC,cAAc,EAAE,MAAM,CAAC;IACvB,2CAA2C;IAC3C,CAAC,EAAE,MAAM,CAAC;IACV,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,6CAA6C;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,YAAY,EAAE,MAAM,CAAC;IACrB,wGAAwG;IACxG,kBAAkB,EAAE,OAAO,GAAG,IAAI,CAAC;CACnC;AAID,kFAAkF;AAClF,MAAM,WAAW,aAAa;IAC7B,0BAA0B;IAC1B,MAAM,EAAE,cAAc,CAAC;IAEvB,oCAAoC;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,oCAAoC;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,cAAc,EAAE,MAAM,CAAC;IACvB,+BAA+B;IAC/B,eAAe,EAAE,MAAM,CAAC;IACxB,yCAAyC;IACzC,YAAY,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,eAAe,EAAE,MAAM,CAAC;IACxB,kCAAkC;IAClC,YAAY,EAAE,MAAM,CAAC;IAErB,kDAAkD;IAClD,YAAY,EAAE,MAAM,CAAC;IACrB,4EAA4E;IAC5E,UAAU,EAAE,MAAM,CAAC;IACnB;mFAC+E;IAC/E,WAAW,EAAE,MAAM,CAAC;IACpB;oFACgF;IAChF,qBAAqB,EAAE,MAAM,CAAC;IAC9B,oEAAoE;IACpE,qBAAqB,EAAE,MAAM,CAAC;IAC9B;;wDAEoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB;;kFAE8E;IAC9E,cAAc,EAAE,OAAO,CAAC;IACxB;;;;mDAI+C;IAC/C,iBAAiB,CAAC,EAAE,KAAK,CAAC;QACzB,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;KACb,CAAC,CAAC;IACH,wCAAwC;IACxC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,0CAA0C;IAC1C,iBAAiB,EAAE,MAAM,CAAC;IAE1B,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,6BAA6B;IAC7B,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,6CAA6C;IAC7C,sBAAsB,EAAE,MAAM,CAAC;IAE/B,oCAAoC;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,oCAAoC;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,qFAAqF;IACrF,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,6FAA6F;IAC7F,OAAO,CAAC,EAAE;QACT,aAAa,EAAE,MAAM,CAAC;QACtB,aAAa,EAAE,MAAM,CAAC;QACtB,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;QACvB,aAAa,EAAE,MAAM,CAAC;KACtB,CAAC;CACF;AAED,qFAAqF;AACrF,MAAM,MAAM,kBAAkB,GAC3B,oCAAoC,GACpC,2BAA2B,GAC3B,qCAAqC,GACrC,6BAA6B,GAC7B,4BAA4B,GAC5B,cAAc,GACd,iBAAiB,GACjB,gBAAgB,GAChB,gBAAgB,GAChB,gBAAgB,CAAC;AAIpB,gEAAgE;AAChE,MAAM,WAAW,SAAS;IACzB,gGAAgG;IAChG,YAAY,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,uEAAuE;IACvE,SAAS,EAAE,OAAO,CAAC;IACnB,gDAAgD;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,6EAA6E;IAC7E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAID,2EAA2E;AAC3E,MAAM,MAAM,gBAAgB,GACzB;IACA,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,EAAE,cAAc,CAAC;IACvB,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC;IAC/B;;;;OAIG;IACH,aAAa,CAAC,EAAE,WAAW,CAAC;IAC5B,4GAA4G;IAC5G,cAAc,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC;CACnC,GACD;IACA,IAAI,EAAE,YAAY,CAAC;IACnB;;8DAE0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,WAAW,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX;;;;mCAI+B;IAC/B,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB;;;;+CAI2C;IAC3C,cAAc,CAAC,EAAE,MAAM,CAAC;CACvB,GACD;IACA,IAAI,EAAE,YAAY,CAAC;IACnB;;gCAE4B;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB;6CACyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,WAAW,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;CACV,GACD;IACA;;;;;;OAMG;IACH,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,gBAAgB,EAAE,CAAC;CAC3B,GACD;IACA,IAAI,EAAE,eAAe,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,WAAW,CAAC;IACjB,IAAI,EAAE,WAAW,CAAC;CACjB,GACD;IACA,IAAI,EAAE,SAAS,CAAC;IAChB,2EAA2E;IAC3E,IAAI,EAAE,MAAM,EAAE,CAAC;CACd,GACD;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"fec-worker-types.d.ts","sourceRoot":"","sources":["../src/fec-worker-types.ts"],"names":[],"mappings":"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"}
|
package/dist/fec-worker.js
CHANGED
|
@@ -13,6 +13,7 @@ var config = null;
|
|
|
13
13
|
var altaEnabled = false;
|
|
14
14
|
var altaVerifier = null;
|
|
15
15
|
var snapshotTimer = null;
|
|
16
|
+
var blockUpdatesSubscribed = false;
|
|
16
17
|
var blocks = /* @__PURE__ */ new Map();
|
|
17
18
|
var acceptedSourceEsis = /* @__PURE__ */ new Map();
|
|
18
19
|
var acceptedRepairEsis = /* @__PURE__ */ new Map();
|
|
@@ -176,6 +177,7 @@ function emitSnapshot() {
|
|
|
176
177
|
emit({ type: "snapshot", stats: buildStatsSnapshot() });
|
|
177
178
|
}
|
|
178
179
|
function emitBlockUpdate(block) {
|
|
180
|
+
if (!blockUpdatesSubscribed) return;
|
|
179
181
|
emit({ type: "blockUpdate", block: { ...block } });
|
|
180
182
|
}
|
|
181
183
|
function emitRecoveredSymbols(sbn, recoveredBlock, block) {
|
|
@@ -291,7 +293,7 @@ async function handleConfigure(cmd) {
|
|
|
291
293
|
}
|
|
292
294
|
}
|
|
293
295
|
wasmDecoder.configure_params(config.k, config.symbolSize);
|
|
294
|
-
emit({ type: "capabilities", commands: ["feedSymbols"] });
|
|
296
|
+
emit({ type: "capabilities", commands: ["feedSymbols", "subscribeBlockUpdates"] });
|
|
295
297
|
startSnapshotTimer();
|
|
296
298
|
emitSnapshot();
|
|
297
299
|
}
|
|
@@ -581,6 +583,9 @@ async function dispatchCommand(cmd) {
|
|
|
581
583
|
case "reset":
|
|
582
584
|
handleReset();
|
|
583
585
|
break;
|
|
586
|
+
case "subscribeBlockUpdates":
|
|
587
|
+
blockUpdatesSubscribed = true;
|
|
588
|
+
break;
|
|
584
589
|
case "dispose":
|
|
585
590
|
handleDispose();
|
|
586
591
|
break;
|
package/dist/fec-worker.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/transfer.ts", "../src/fec-worker.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Return a fresh ArrayBuffer containing exactly the bytes in `view`.\n *\n * Worker outputs must not transfer buffers that might be owned by a WASM\n * binding or by a larger shared packet. A fresh JS-owned buffer gives\n * postMessage() exclusive ownership without detaching unrelated memory.\n */\nexport function toOwnedArrayBuffer(view: Uint8Array): ArrayBuffer {\n\tconst copy = new Uint8Array(view.byteLength);\n\tcopy.set(view);\n\treturn copy.buffer;\n}\n", "/**\n * @blockcast/fec-worker \u2014 Worker Entry Point\n *\n * Runs inside DedicatedWorkerGlobalScope. Receives FecWorkerCommand messages,\n * drives the MmtFecDecoder WASM module, emits FecWorkerEvent messages back.\n *\n * WASM bindings are resolved via:\n * 1. (self as any).__MMT_WASM_BINDINGS__ \u2014 set by consumer's loader/importmap\n * 2. Falls back to error if not available (no defaults)\n *\n * This file must NOT import DOM globals incompatible with Worker scope.\n */\n\nimport type {\n\tFecWorkerCommand,\n\tFecWorkerEvent,\n\tFecTrackConfig,\n\tFecTrackStats,\n\tFecBlockSnapshot,\n\tFrameMeta,\n} from \"./fec-worker-types.js\";\nimport { toOwnedArrayBuffer } from \"./transfer.js\";\n\n// --- WASM binding types (from mmt-wasm) ---\n// Signatures MUST match a freshly generated external mmt_wasm.d.ts exactly.\n// The decoder derives SBN/ESI internally \u2014 do not pre-compute.\ninterface WasmFecDecoder {\n\t/** ISO 23008-1 \u00A7C.5.2: ss_id is the flat 32-bit Source Symbol ID. */\n\tadd_source(\n\t\tss_id: number,\n\t\tdata: Uint8Array,\n\t\ttimestamp: number,\n\t): Uint8Array | undefined;\n\t/** ISO 23008-1 \u00A7C.5.3: repair ESI = configured K + rs_id, SBN = ss_start / configured K. */\n\tadd_repair(\n\t\tss_start: number,\n\t\tssb_length: number,\n\t\trs_id: number,\n\t\tdata: Uint8Array,\n\t\ttimestamp: number,\n\t): Uint8Array | undefined;\n\tconfigure_params(k: number, symbolSize: number): void;\n\t/** Block-scoped counterpart driven by the manager's exact deadlines. */\n\tcleanup_blocks_detailed(sbns: Uint32Array): Uint8Array;\n\t/** Remove decoder blocks below its configured live-SBN interleave horizon. */\n\tcleanup_by_sbn(current_sbn: number): Uint32Array;\n\tflush(): void;\n\tpending_blocks(): number;\n\treset_stats(): void;\n\tget_stats(): {\n\t\tsource_packets: bigint;\n\t\trepair_packets: bigint;\n\t\tblocks_complete: bigint;\n\t\tblocks_recovered: bigint;\n\t\tblocks_failed: bigint;\n\t\tbytes_recovered: bigint;\n\t\trecovery_rate(): number;\n\t};\n\tfree(): void;\n}\n\ninterface WasmBindings {\n\tinitSync(module: { module: WebAssembly.Module }): unknown;\n\tMmtFecDecoder: new (\n\t\tinterleaveDepth: number,\n\t\tgreenFillEnabled: boolean,\n\t) => WasmFecDecoder;\n}\n\n// --- Worker state (local to this Worker instance) ---\n\nlet wasmDecoder: WasmFecDecoder | null = null;\nlet config: FecTrackConfig | null = null;\nlet altaEnabled = false;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet altaVerifier: any = null; // JsAltaVerifier instance (loaded from ALTA WASM)\nlet snapshotTimer: ReturnType<typeof setInterval> | null = null;\n\n// Block tracking\nconst blocks = new Map<number, FecBlockSnapshot>();\nconst acceptedSourceEsis = new Map<number, Set<number>>();\nconst acceptedRepairEsis = new Map<number, Set<number>>();\nconst UINT32_MAX = 0xffff_ffff;\nconst UINT24_MAX = 0xff_ffff;\n// ISO/IEC 23008-1 AL-FEC signaling carries interleave_depth in one byte.\nconst MAX_INTERLEAVE_DEPTH = 0xff;\n// Rust stores T as u16 and this decoder profile fixes RFC 6330 Al=8.\nconst MAX_SYMBOL_SIZE = 0xfff8;\n// RFC 6330 systematic table limit, enforced by the vendored raptorq decoder.\nconst MAX_RAPTORQ_SOURCE_SYMBOLS_PER_BLOCK = 56_403;\n\n// Aggregate counters\nlet sourceSymbols = 0;\nlet repairSymbols = 0;\nlet blocksComplete = 0;\nlet blocksRecovered = 0;\nlet blocksFailed = 0;\nlet greenFillFrames = 0;\nlet altaVerified = 0;\nlet altaFailed = 0; // Crypto-confirmed tamper (MAC / sig / hash chain mismatch)\nconst altaFailedDetails: Array<{\n\tseq: number;\n\terror: string;\n\tauthLen: number;\n\tpayloadLen: number;\n\tpath: string; // \"direct\" | \"recovered\" | \"pending-retry\"\n}> = [];\nlet altaPending = 0; // Unverifiable but not tamper (no trailer, verifier not ready, anchor missing)\nlet altaPendingEvictedCap = 0; // Incoming \"pending\" packets rejected because the queue was at cap after stale-sweep (flood indicator)\nlet altaPendingEvictedTtl = 0; // Deferred-queue entries dropped by TTL (retry window expired)\nlet altaError = 0; // verifyDetached threw \u2014 infrastructure flake, not tamper\nlet altaInitFailed = false; // ALTA configured but WASM init failed; altaEnabled was forced off\nlet relayBlocksVerified = 0;\nlet relayBlocksFailed = 0;\nlet liveSbn = 0;\n// Highest SBN that has fallen strictly below the decoder's retained\n// interleave window. Numeric ordering is per decoder epoch; reset/configure\n// starts a new epoch before the 32-bit source-symbol counter can wrap.\nlet retiredSbnFrontier = -1;\nlet repairSymbolSizeMismatch = 0;\n\n// Recovery timing\nconst recoveryTimes: number[] = [];\n\n// \u2500\u2500 Deferred ALTA verification queue (08-07 Path 3) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// When an ALTA packet arrives whose MAC references point at sequences not yet\n// in the verifier's ring buffer, we can't anchor trust \u2014 verify returns \"No\n// trust anchor\". Typical cause: a predecessor packet was lost and hasn't been\n// FEC-recovered yet. Rather than drop these, hold them in a bounded FIFO and\n// retry verification after every successful verify (direct or FEC-recovered)\n// that seeds the ring buffer with new raw bytes.\n//\n// Zero-copy (1B-B): the queue entry stores a Uint8Array view into the\n// original `data` ArrayBuffer. JS GC keeps the buffer alive via the view\n// reference; no bytes are copied on queue.\n//\n// Timeout entries (TTL exceeded) and cap-rejections count as `altaPending`\n// (2C-B): they're not tamper indicators, they're just packets that couldn't\n// be authenticated within the receive window \u2014 either loss or, in the cap\n// case, a flood of unanchorable arrivals (real or attacker-driven) that\n// exceeded the queue budget. See enqueuePendingVerify for the rate-limit\n// protocol.\ninterface PendingVerify {\n\tseq: number;\n\tauth: Uint8Array;\n\tpayload: Uint8Array;\n\tarrivalTs: number;\n}\nconst pendingVerify = new Map<number, PendingVerify>();\nlet pendingVerifyCap = 0;\nlet pendingVerifyTtlMs = 0;\n\n// --- Helpers ---\n\nfunction emit(event: FecWorkerEvent, transfer?: Transferable[]): void {\n\tself.postMessage(event, transfer ?? []);\n}\n\nfunction isUint32(value: number): boolean {\n\treturn Number.isInteger(value) && value >= 0 && value <= UINT32_MAX;\n}\n\nfunction isTerminalBlock(block: FecBlockSnapshot): boolean {\n\treturn block.state !== \"collecting\";\n}\n\nfunction retireAcceptedIds(sbn: number): void {\n\tacceptedSourceEsis.delete(sbn);\n\tacceptedRepairEsis.delete(sbn);\n}\n\nfunction clearBlockTracking(): void {\n\tblocks.clear();\n\tacceptedSourceEsis.clear();\n\tacceptedRepairEsis.clear();\n\tpendingVerify.clear();\n\trecoveryTimes.length = 0;\n\tliveSbn = 0;\n\tretiredSbnFrontier = -1;\n}\n\n/**\n * Retire terminal diagnostics only after the decoder's monotonic interleave\n * horizon proves that the SBN can no longer be valid out-of-order media.\n * Admission keeps the compact frontier as the durable tombstone, so deletion\n * never reopens a finalized block.\n */\nfunction trimBlockRetention(): void {\n\tfor (const [sbn, block] of blocks) {\n\t\tif (!isTerminalBlock(block) || sbn > retiredSbnFrontier) continue;\n\t\tblocks.delete(sbn);\n\t\tretireAcceptedIds(sbn);\n\t}\n}\n\nfunction advanceMediaHorizon(sbn: number): void {\n\tif (!config || sbn <= liveSbn) return;\n\tliveSbn = sbn;\n\tconst firstRetainedSbn = Math.max(0, liveSbn - config.interleaveDepth);\n\tretiredSbnFrontier = Math.max(retiredSbnFrontier, firstRetainedSbn - 1);\n\ttrimBlockRetention();\n}\n\nfunction markBlockTerminal(\n\tblock: FecBlockSnapshot,\n\tstate: \"complete\" | \"recovered\" | \"failed\",\n): void {\n\tblock.state = state;\n\tblock.completedTs = performance.now();\n\tretireAcceptedIds(block.sbn);\n\ttrimBlockRetention();\n}\n\nfunction synchronizeDecoderHorizon(): void {\n\tif (!wasmDecoder) return;\n\tconst expired = wasmDecoder.cleanup_by_sbn(liveSbn);\n\tfor (const sbn of expired) {\n\t\tconst block = blocks.get(sbn);\n\t\tif (!block || isTerminalBlock(block)) {\n\t\t\tretireAcceptedIds(sbn);\n\t\t\tcontinue;\n\t\t}\n\t\tmarkBlockTerminal(block, \"failed\");\n\t\tblocksFailed++;\n\t\temitBlockUpdate(block);\n\t}\n\ttrimBlockRetention();\n}\n\n/**\n * Run detached ALTA verify against the current verifier. Four-state result:\n * - \"valid\": crypto passed, packet authentic. Caller increments altaVerified.\n * - \"failed\": crypto confirmed tamper (MAC/sig mismatch, hash chain mismatch).\n * Caller increments altaFailed. Not retryable.\n * - \"pending\": anchor missing (MAC refs not in buffer, no signature, not\n * early-warmup). Caller may enqueue for retry after the next\n * successful verify seeds the ring buffer.\n * - \"error\": verifier threw \u2014 WASM trap, unmarshal panic, OOM. Caller\n * increments altaError (infrastructure flake, not tamper).\n * The distinction matters for ops alerting: a spike in\n * altaError indicates a runtime problem, not an attack.\n */\nlet _lastVerifyError = \"\";\nfunction runDetachedVerify(\n\tauth: Uint8Array,\n\tpayload: Uint8Array,\n): \"valid\" | \"failed\" | \"pending\" | \"error\" {\n\tif (!altaVerifier) return \"pending\";\n\ttry {\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\tconst r: any = altaVerifier.verifyDetached(auth, payload);\n\t\tif (r.valid) {\n\t\t\t_lastVerifyError = \"\";\n\t\t\treturn \"valid\";\n\t\t}\n\t\tconst err = (r.error as string | undefined) ?? \"\";\n\t\t_lastVerifyError = err;\n\t\t// The bootstrap-friendly verifier returns this specific error string when\n\t\t// the only obstacle to verification is a missing anchor (see alta-rs\n\t\t// verifier.rs verify()). Distinguishing this from tamper cases keeps us\n\t\t// from queuing packets that are definitively invalid.\n\t\tif (err.startsWith(\"No trust anchor\")) return \"pending\";\n\t\treturn \"failed\";\n\t} catch (e) {\n\t\t// Ratelimit: first ~N occurrences get logged. A sustained exception\n\t\t// stream indicates a real problem \u2014 the altaError counter is the\n\t\t// durable signal.\n\t\tif (altaError < 5) {\n\t\t\tconsole.warn(\"[FecWorker] verifyDetached threw:\", e);\n\t\t}\n\t\treturn \"error\";\n\t}\n}\n\n/** Extract u32 BE sequence number from the ALTA authenticator header (offset 0..3). */\nfunction seqFromAuth(auth: Uint8Array): number {\n\treturn (\n\t\t((auth[0]! << 24) | (auth[1]! << 16) | (auth[2]! << 8) | auth[3]!) >>> 0\n\t);\n}\n\n/**\n * Enqueue a packet whose ALTA verification returned \"pending\" (anchor\n * missing). Dedupes by sequence number.\n *\n * Flood resistance (08-08 review): when the queue is at cap we do NOT\n * evict the oldest entry. The oldest entry is typically the most\n * load-bearing one \u2014 it's the packet whose anchor everything else in the\n * queue is waiting on. Evict-oldest-on-insert let an attacker emitting a\n * stream of \"No trust anchor\" packets flood out legitimate entries and\n * prevent them from ever anchoring. Instead:\n * 1. First try to free stale TTL'd entries (they'd be evicted on next\n * drain anyway \u2014 pulling that forward is free).\n * 2. If still full, reject the NEW entry. altaPendingEvictedCap counts\n * these rejections (rate-limit trigger).\n *\n * Stores Uint8Array views into the original data + auth buffers \u2014 no copy.\n * The views keep their underlying ArrayBuffers alive via GC references.\n */\nfunction enqueuePendingVerify(\n\tauth: Uint8Array,\n\tpayload: Uint8Array,\n): void {\n\tconst seq = seqFromAuth(auth);\n\tif (pendingVerify.has(seq)) return;\n\n\tif (pendingVerify.size >= pendingVerifyCap) {\n\t\t// Step 1: sweep stale entries so legitimate traffic isn't penalized\n\t\t// by TTL'd junk that happens to still occupy slots.\n\t\tconst now = performance.now();\n\t\tfor (const [staleSeq, entry] of pendingVerify) {\n\t\t\tif (now - entry.arrivalTs > pendingVerifyTtlMs) {\n\t\t\t\tpendingVerify.delete(staleSeq);\n\t\t\t\taltaPendingEvictedTtl++;\n\t\t\t}\n\t\t}\n\t\t// Step 2: if the queue is still at cap it's a genuine flood \u2014\n\t\t// reject the new entry rather than evict an existing one. The\n\t\t// rejected packet counts as altaPendingEvictedCap (which is\n\t\t// surfaced up through stats for rate-limit alerting).\n\t\tif (pendingVerify.size >= pendingVerifyCap) {\n\t\t\taltaPendingEvictedCap++;\n\t\t\treturn;\n\t\t}\n\t}\n\n\tpendingVerify.set(seq, {\n\t\tseq,\n\t\tauth,\n\t\tpayload,\n\t\tarrivalTs: performance.now(),\n\t});\n}\n\n/**\n * Retry every queued entry. Called after every successful verify (direct\n * source or FEC-recovered) \u2014 new raw bytes in the verifier's ring buffer may\n * unblock previously-pending MAC references.\n *\n * Three outcomes per entry:\n * - \"valid\" \u2192 remove from queue, altaVerified++\n * - \"failed\" \u2192 remove from queue, altaFailed++ (tamper on retry)\n * - \"pending\" \u2192 keep in queue, unless TTL expired (then evict as altaPending)\n *\n * Iteration order: Map preserves insertion order, so we drain oldest-first.\n * No-op when the queue is empty or the verifier isn't ready.\n */\nfunction drainPendingVerify(): void {\n\tif (pendingVerify.size === 0 || !altaVerifier) return;\n\tconst now = performance.now();\n\t// Collect keys to avoid mutating Map during iteration\n\tconst keys = Array.from(pendingVerify.keys());\n\tfor (const seq of keys) {\n\t\tconst entry = pendingVerify.get(seq);\n\t\tif (!entry) continue;\n\t\tif (now - entry.arrivalTs > pendingVerifyTtlMs) {\n\t\t\tpendingVerify.delete(seq);\n\t\t\taltaPendingEvictedTtl++;\n\t\t\tcontinue;\n\t\t}\n\t\tconst result = runDetachedVerify(entry.auth, entry.payload);\n\t\tif (result === \"valid\") {\n\t\t\tpendingVerify.delete(seq);\n\t\t\taltaVerified++;\n\t\t} else if (result === \"failed\") {\n\t\t\tpendingVerify.delete(seq);\n\t\t\taltaFailed++;\n\t\t\tif (altaFailedDetails.length < 16) {\n\t\t\t\taltaFailedDetails.push({\n\t\t\t\t\tseq: entry.seq,\n\t\t\t\t\terror: _lastVerifyError,\n\t\t\t\t\tauthLen: entry.auth.length,\n\t\t\t\t\tpayloadLen: entry.payload.length,\n\t\t\t\t\tpath: \"pending-retry\",\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (result === \"error\") {\n\t\t\t// Infrastructure failure \u2014 remove from queue, count as altaError.\n\t\t\t// Keeping it around would just re-throw on every subsequent drain.\n\t\t\tpendingVerify.delete(seq);\n\t\t\taltaError++;\n\t\t}\n\t\t// \"pending\" \u2192 leave in queue for next drain\n\t}\n}\n\nfunction getOrCreateBlock(sbn: number): FecBlockSnapshot {\n\tlet block = blocks.get(sbn);\n\tif (!block) {\n\t\tif (!config) throw new Error(\"[FecWorker] not configured\");\n\t\tconst now = performance.now();\n\t\tblock = {\n\t\t\tsbn,\n\t\t\tstate: \"collecting\",\n\t\t\tsourceReceived: 0,\n\t\t\trepairReceived: 0,\n\t\t\tk: config.k,\n\t\t\trepairCount: config.repairCount,\n\t\t\tfirstSymbolTs: now,\n\t\t\tlastSymbolTs: now,\n\t\t\tdeadlineTs: now + config.deliveryWindowMs,\n\t\t\taltaVerified: 0,\n\t\t\trelayBlockSigValid: null,\n\t\t};\n\t\tblocks.set(sbn, block);\n\t\ttrimBlockRetention();\n\t}\n\treturn block;\n}\n\nfunction buildStatsSnapshot(): FecTrackStats {\n\tif (!config) throw new Error(\"[FecWorker] not configured\");\n\tconst decoderStats = wasmDecoder?.get_stats();\n\tconst decoder = decoderStats\n\t\t? {\n\t\t\tsourcePackets: Number(decoderStats.source_packets),\n\t\t\trepairPackets: Number(decoderStats.repair_packets),\n\t\t\tblocksComplete: Number(decoderStats.blocks_complete),\n\t\t\tblocksRecovered: Number(decoderStats.blocks_recovered),\n\t\t\tblocksFailed: Number(decoderStats.blocks_failed),\n\t\t\tbytesRecovered: Number(decoderStats.bytes_recovered),\n\t\t\tpendingBlocks: wasmDecoder?.pending_blocks() ?? 0,\n\t\t}\n\t\t: undefined;\n\n\tconst avgRecoveryMs =\n\t\trecoveryTimes.length > 0\n\t\t\t? recoveryTimes.reduce((a, b) => a + b, 0) / recoveryTimes.length\n\t\t\t: 0;\n\tconst maxRecoveryMs =\n\t\trecoveryTimes.length > 0 ? Math.max(...recoveryTimes) : 0;\n\n\t// Cap block array to interleave window\n\tconst maxBlocks = Math.max(config.interleaveDepth * 3, 24);\n\tconst blockArray = Array.from(blocks.values())\n\t\t.sort((a, b) => b.sbn - a.sbn)\n\t\t.slice(0, maxBlocks);\n\n\treturn {\n\t\tconfig,\n\t\tsourceSymbols,\n\t\trepairSymbols,\n\t\tblocksComplete,\n\t\tblocksRecovered,\n\t\tblocksFailed,\n\t\tgreenFillFrames,\n\t\trecoveryRate:\n\t\t\tblocksComplete + blocksRecovered > 0\n\t\t\t\t? Math.round(\n\t\t\t\t\t\t(blocksRecovered / (blocksRecovered + blocksFailed)) * 100,\n\t\t\t\t\t) || 0\n\t\t\t\t: 0,\n\t\taltaVerified,\n\t\taltaFailed,\n\t\taltaPending,\n\t\taltaPendingEvictedCap,\n\t\taltaPendingEvictedTtl,\n\t\taltaError,\n\t\taltaInitFailed,\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\taltaFailedDetails: altaFailedDetails.slice() as any,\n\t\trelayBlocksVerified,\n\t\trelayBlocksFailed,\n\t\tliveSbn,\n\t\tblocks: blockArray,\n\t\tinterleaveWindowBlocks: config.interleaveDepth,\n\t\tavgRecoveryMs: Math.round(avgRecoveryMs * 100) / 100,\n\t\tmaxRecoveryMs: Math.round(maxRecoveryMs * 100) / 100,\n\t\trepairSymbolSizeMismatch,\n\t\tdecoder,\n\t};\n}\n\nfunction emitSnapshot(): void {\n\tif (!config) return;\n\temit({ type: \"snapshot\", stats: buildStatsSnapshot() });\n}\n\nfunction emitBlockUpdate(block: FecBlockSnapshot): void {\n\temit({ type: \"blockUpdate\", block: { ...block } });\n}\n\nfunction emitRecoveredSymbols(\n\tsbn: number,\n\trecoveredBlock: Uint8Array,\n\tblock: FecBlockSnapshot,\n): void {\n\tif (!config) return;\n\tconst symbolSize = config.symbolSize;\n\tif (symbolSize <= 0) {\n\t\temit({\n\t\t\ttype: \"error\",\n\t\t\tcode: \"recovered-symbol-size-unconfigured\",\n\t\t\tmessage: \"[FecWorker] recovered block has no configured symbolSize\",\n\t\t});\n\t\treturn;\n\t}\n\t// The decoder returns only the block's authored source symbols\n\t// (SSB_length x T); the encoder's zero padding is not media.\n\tconst ssbLength = block.ssbLength ?? config.k;\n\tif (recoveredBlock.byteLength !== symbolSize * ssbLength) {\n\t\temit({\n\t\t\ttype: \"error\",\n\t\t\tcode: \"recovered-block-too-short\",\n\t\t\tmessage: `[FecWorker] recovered block has the wrong length: got ${recoveredBlock.byteLength}B, expected ${symbolSize * ssbLength}B (SSB_length=${ssbLength})`,\n\t\t});\n\t\treturn;\n\t}\n\n\tfor (let i = 0; i < ssbLength; i++) {\n\t\tconst symbol = recoveredBlock.subarray(i * symbolSize, (i + 1) * symbolSize);\n\n\t\t// Source authentication now runs once at router admission in\n\t\t// @blockcast/mmt-manifest-verify. The worker only records that this\n\t\t// legacy ALTA-enabled symbol bypassed hot-path verification.\n\t\tconst recoveredAltaVerified: boolean | null = null;\n\t\tif (altaEnabled) altaPending++;\n\n\t\tconst buf = toOwnedArrayBuffer(symbol);\n\t\tconst meta: FrameMeta = {\n\t\t\taltaVerified: recoveredAltaVerified,\n\t\t\trecovered: true,\n\t\t\tsbn,\n\t\t\tesi: i,\n\t\t\tssbLength,\n\t\t};\n\t\temit({ type: \"frame\", data: buf, meta }, [buf]);\n\t}\n}\n\nfunction startSnapshotTimer(): void {\n\tif (snapshotTimer !== null) clearInterval(snapshotTimer);\n\tsnapshotTimer = setInterval(() => emitSnapshot(), 200);\n}\n\nfunction resolveWasmBindings(): WasmBindings {\n\tconst bindings = (self as unknown as Record<string, unknown>)\n\t\t.__MMT_WASM_BINDINGS__ as WasmBindings | undefined;\n\tif (!bindings) {\n\t\tthrow new Error(\n\t\t\t\"[FecWorker] WASM bindings not available. Set self.__MMT_WASM_BINDINGS__ = { initSync, MmtFecDecoder } before configure.\",\n\t\t);\n\t}\n\treturn bindings;\n}\n\n// --- Command handlers ---\n\nasync function handleConfigure(\n\tcmd: Extract<FecWorkerCommand, { type: \"configure\" }>,\n): Promise<void> {\n\tif (!cmd.wasmModule) {\n\t\tthrow new Error(\"[FecWorker] configure: wasmModule required\");\n\t}\n\tif (!cmd.config) {\n\t\tthrow new Error(\"[FecWorker] configure: config required\");\n\t}\n\tif (\n\t\t!Number.isFinite(cmd.config.interleaveDepth) ||\n\t\t!Number.isInteger(cmd.config.interleaveDepth) ||\n\t\tcmd.config.interleaveDepth <= 0 ||\n\t\tcmd.config.interleaveDepth > MAX_INTERLEAVE_DEPTH\n\t) {\n\t\tthrow new Error(\n\t\t\t`[FecWorker] configure: interleaveDepth must be an integer in 1..${MAX_INTERLEAVE_DEPTH} (got ${cmd.config.interleaveDepth})`,\n\t\t);\n\t}\n\tif (\n\t\t!Number.isFinite(cmd.config.interleaveMs) ||\n\t\tcmd.config.interleaveMs <= 0\n\t) {\n\t\tthrow new Error(\n\t\t\t`[FecWorker] configure: interleaveMs must be finite and > 0 (got ${cmd.config.interleaveMs})`,\n\t\t);\n\t}\n\tif (\n\t\t!Number.isFinite(cmd.config.deliveryWindowMs) ||\n\t\tcmd.config.deliveryWindowMs <= 0\n\t) {\n\t\tthrow new Error(\n\t\t\t`[FecWorker] configure: deliveryWindowMs must be > 0 (got ${cmd.config.deliveryWindowMs})`,\n\t\t);\n\t}\n\tif (\n\t\t!Number.isInteger(cmd.config.k) ||\n\t\tcmd.config.k <= 0 ||\n\t\tcmd.config.k > MAX_RAPTORQ_SOURCE_SYMBOLS_PER_BLOCK\n\t) {\n\t\tthrow new Error(\n\t\t\t`[FecWorker] configure: k must be an integer in 1..${MAX_RAPTORQ_SOURCE_SYMBOLS_PER_BLOCK} (got ${cmd.config.k})`,\n\t\t);\n\t}\n\tif (\n\t\t!Number.isInteger(cmd.config.symbolSize) ||\n\t\tcmd.config.symbolSize <= 0 ||\n\t\tcmd.config.symbolSize > MAX_SYMBOL_SIZE ||\n\t\tcmd.config.symbolSize % 8 !== 0\n\t) {\n\t\tthrow new Error(\n\t\t\t`[FecWorker] configure: symbolSize must be an 8-byte-aligned integer in 8..${MAX_SYMBOL_SIZE} (got ${cmd.config.symbolSize})`,\n\t\t);\n\t}\n\n\tconst wasm = resolveWasmBindings();\n\n\t// Initialize WASM from pre-compiled Module (no fetch, no re-compile)\n\twasm.initSync({ module: cmd.wasmModule });\n\n\t// A configure command starts a new decoder epoch. No terminal tombstone,\n\t// accepted-symbol ID, or active block from the previous decoder may affect it.\n\twasmDecoder?.free();\n\twasmDecoder = null;\n\tclearBlockTracking();\n\n\t// Decoder policy and geometry are explicit at the epoch boundary. The WASM\n\t// module has no guessed K/T/interleave slow path.\n\twasmDecoder = new wasm.MmtFecDecoder(\n\t\tcmd.config.interleaveDepth,\n\t\tcmd.config.trackType === \"video\",\n\t);\n\tconfig = cmd.config;\n\taltaEnabled = !!cmd.altaPublicKey;\n\n\t// Size the deferred-verify queue strictly from catalog-derived FEC\n\t// parameters. CLAUDE.md bans Math.max(x, floor) magic \u2014 if catalog\n\t// values are zero/missing, throw at init rather than silently applying\n\t// a hardcoded floor. Consumers of an undersized queue cap would see\n\t// altaPendingEvictedCap spike with no actionable signal.\n\t// Cap: 1.5\u00D7 interleave depth \u2014 wide enough to hold one full FEC recovery\n\t// window plus jitter, narrow enough to bound memory.\n\tpendingVerifyCap = Math.ceil(config.interleaveDepth * 1.5);\n\t// TTL: 1.5\u00D7 block duration (K symbols at one per interleave tick) \u2014\n\t// after this many ms, any unresolved entry has missed its FEC window.\n\tpendingVerifyTtlMs = Math.ceil(config.interleaveMs * config.k * 1.5);\n\n\t// Initialize ALTA WASM verifier if public key and WASM module provided (D-05, D-09)\n\taltaVerifier = null;\n\taltaInitFailed = false;\n\tif (cmd.altaPublicKey && cmd.altaWasmModule) {\n\t\ttry {\n\t\t\t// Load ALTA JS glue (wasm-bindgen output from wasm-pack build).\n\t\t\t// Mirrors the RaptorQ WASM pattern: initSync({ module }) then construct.\n\t\t\t// Dynamic import path matches the IWA static asset layout.\n\t\t\t// Cast via string to prevent TypeScript from attempting static resolution\n\t\t\t// of a runtime-only URL (same pattern as fec-worker-entry.js loading).\n\t\t\tconst altaGlueUrl = '/wasm/alta/alta_rs.js' as string;\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t\tconst altaGlue: any = await import(/* webpackIgnore: true */ altaGlueUrl);\n\t\t\taltaGlue.initSync({ module: cmd.altaWasmModule });\n\t\t\tconst keyBytes = new Uint8Array(cmd.altaPublicKey);\n\t\t\taltaVerifier = new altaGlue.JsAltaVerifier(keyBytes);\n\t\t} catch (e) {\n\t\t\t// T-08-15: WASM init failure must NOT silently look like \"not-ready-yet\".\n\t\t\t// Force altaEnabled = false so handleFeedSource takes the disabled\n\t\t\t// branch instead of continuously bumping altaPending under the\n\t\t\t// misleading \"verifier not yet ready\" comment. altaInitFailed\n\t\t\t// flag in the snapshot tells consumers this is an error state, not\n\t\t\t// an opt-out.\n\t\t\tconsole.warn('[FecWorker] ALTA WASM init failed, verification disabled:', e);\n\t\t\taltaVerifier = null;\n\t\t\taltaEnabled = false;\n\t\t\taltaInitFailed = true;\n\t\t}\n\t}\n\n\twasmDecoder.configure_params(config.k, config.symbolSize);\n\n\t// Tell the client which optional commands this Worker accepts, so it can\n\t// batch symbols without risking a Worker that predates the batch command.\n\temit({ type: \"capabilities\", commands: [\"feedSymbols\"] });\n\n\t// Start periodic snapshot emission (200ms floor)\n\tstartSnapshotTimer();\n\n\t// Emit initial snapshot\n\temitSnapshot();\n}\n\nfunction handleFeedSource(\n\tcmd: Extract<FecWorkerCommand, { type: \"feedSource\" }>,\n): void {\n\tif (!wasmDecoder) {\n\t\tthrow new Error(\"[FecWorker] feedSource: not configured\");\n\t}\n\tif (!config) {\n\t\tthrow new Error(\"[FecWorker] feedSource: config missing\");\n\t}\n\tif (\n\t\t!isUint32(cmd.ssId) ||\n\t\t!(cmd.data instanceof ArrayBuffer)\n\t) {\n\t\tthrow new Error(\"[FecWorker] feedSource: invalid source symbol\");\n\t}\n\n\tconst view = new Uint8Array(cmd.data);\n\n\t// Derive SBN locally for block-level accounting. WASM derives the same\n\t// value internally (ISO 23008-1 \u00A7C.5.2: SBN = floor(SS_ID / K)).\n\tconst sbn = config.k > 0 ? Math.floor(cmd.ssId / config.k) : cmd.ssId;\n\tconst esi = cmd.ssId % config.k;\n\tif (sbn <= retiredSbnFrontier) return;\n\tconst existingBlock = blocks.get(sbn);\n\tif (existingBlock && isTerminalBlock(existingBlock)) return;\n\tif (acceptedSourceEsis.get(sbn)?.has(esi)) return;\n\n\tconst result = wasmDecoder.add_source(cmd.ssId, view, cmd.ts);\n\tadvanceMediaHorizon(sbn);\n\tlet sourceEsis = acceptedSourceEsis.get(sbn);\n\tif (!sourceEsis) {\n\t\tsourceEsis = new Set<number>();\n\t\tacceptedSourceEsis.set(sbn, sourceEsis);\n\t}\n\tsourceEsis.add(esi);\n\n\t// Update block state\n\tconst block = getOrCreateBlock(sbn);\n\tblock.sourceReceived++;\n\tblock.lastSymbolTs = performance.now();\n\tsourceSymbols++;\n\n\t// The FEC worker must remain decode-only on the per-symbol hot path. ALTA\n\t// WASM is retained temporarily for low-rate signaling users, but media is\n\t// authenticated by the canonical manifest verifier at router admission.\n\tif (altaEnabled) altaPending++;\n\n\t// Check if block is complete (all authored source symbols received:\n\t// SSB_length once a repair symbol declared it, otherwise K)\n\tconst authoredLength = block.ssbLength ?? block.k;\n\tif (\n\t\tblock.sourceReceived >= authoredLength &&\n\t\tblock.state === \"collecting\"\n\t) {\n\t\tmarkBlockTerminal(block, \"complete\");\n\t\tblocksComplete++;\n\t\temitBlockUpdate(block);\n\t}\n\n\t\t// A source-triggered return can be either clean block completion or recovery\n\t\t// of earlier missing symbols after repair arrived. Clean completion is\n\t\t// already routed by the source path, so only emit when the block did not\n\t\t// reach K source arrivals.\n\t\tif (result && result.byteLength > 0 && block.sourceReceived < authoredLength) {\n\t\t\tconst recovered = new Uint8Array(toOwnedArrayBuffer(result));\n\t\t\tconst recoveryMs = performance.now() - block.firstSymbolTs;\n\t\t\trecoveryTimes.push(recoveryMs);\n\t\t\tif (recoveryTimes.length > 1000) recoveryTimes.shift();\n\n\t\t\tif (block.state === \"collecting\") {\n\t\t\t\tmarkBlockTerminal(block, \"recovered\");\n\t\t\t\tblocksRecovered++;\n\t\t\t\temitBlockUpdate(block);\n\t\t\t}\n\t\t\temitRecoveredSymbols(sbn, recovered, block);\n\t\t}\n\t\tsynchronizeDecoderHorizon();\n\t}\n\nfunction handleFeedRepair(\n\tcmd: Extract<FecWorkerCommand, { type: \"feedRepair\" }>,\n): void {\n\tif (!wasmDecoder) {\n\t\tthrow new Error(\"[FecWorker] feedRepair: not configured\");\n\t}\n\tif (!config) {\n\t\tthrow new Error(\"[FecWorker] feedRepair: config missing\");\n\t}\n\tif (\n\t\t!isUint32(cmd.ssStart) ||\n\t\t!isUint32(cmd.ssbLength) ||\n\t\tcmd.ssbLength <= 0 ||\n\t\tcmd.ssbLength > config.k ||\n\t\tcmd.ssStart % config.k !== 0 ||\n\t\t!isUint32(cmd.rsId) ||\n\t\tconfig.k + cmd.rsId > UINT24_MAX ||\n\t\t!(cmd.data instanceof ArrayBuffer)\n\t) {\n\t\tthrow new Error(\"[FecWorker] feedRepair: invalid repair symbol\");\n\t}\n\n\tconst view = new Uint8Array(cmd.data);\n\tif (config.symbolSize > 0 && view.byteLength !== config.symbolSize) {\n\t\trepairSymbolSizeMismatch++;\n\t\tif (repairSymbolSizeMismatch <= 5 || repairSymbolSizeMismatch % 100 === 0) {\n\t\t\temit({\n\t\t\t\ttype: \"error\",\n\t\t\t\tcode: \"repair-symbol-size-mismatch\",\n\t\t\t\tmessage: `[FecWorker] Dropping repair symbol with invalid size: got ${view.byteLength}B, expected ${config.symbolSize}B (ssStart=${cmd.ssStart}, ssbLength=${cmd.ssbLength}, rsId=${cmd.rsId})`,\n\t\t\t});\n\t\t}\n\t\treturn;\n\t}\n\tconst sbn = Math.floor(cmd.ssStart / config.k);\n\tconst repairEsi = config.k + cmd.rsId;\n\tif (sbn <= retiredSbnFrontier) return;\n\tconst existingBlock = blocks.get(sbn);\n\tif (existingBlock && isTerminalBlock(existingBlock)) return;\n\tif (acceptedRepairEsis.get(sbn)?.has(repairEsi)) return;\n\t// One block has one authored SSB_length; a repair symbol naming another\n\t// is dropped before it reaches the decoder (which rejects it too).\n\tif (existingBlock?.ssbLength !== undefined && existingBlock.ssbLength !== cmd.ssbLength) {\n\t\temit({\n\t\t\ttype: \"error\",\n\t\t\tcode: \"repair-ssb-length-conflict\",\n\t\t\tmessage: `[FecWorker] Dropping repair symbol whose SSB_length ${cmd.ssbLength} conflicts with ${existingBlock.ssbLength} (sbn=${sbn})`,\n\t\t});\n\t\treturn;\n\t}\n\n\tconst result = wasmDecoder.add_repair(\n\t\tcmd.ssStart,\n\t\tcmd.ssbLength,\n\t\tcmd.rsId,\n\t\tview,\n\t\tcmd.ts,\n\t);\n\tadvanceMediaHorizon(sbn);\n\tlet repairEsis = acceptedRepairEsis.get(sbn);\n\tif (!repairEsis) {\n\t\trepairEsis = new Set<number>();\n\t\tacceptedRepairEsis.set(sbn, repairEsis);\n\t}\n\trepairEsis.add(repairEsi);\n\tconst block = getOrCreateBlock(sbn);\n\tblock.ssbLength = cmd.ssbLength;\n\tblock.repairReceived++;\n\tblock.lastSymbolTs = performance.now();\n\trepairSymbols++;\n\t\t// Every authored source symbol already arrived: knowing SSB_length makes\n\t\t// the block clean. The decoder's return is the source already routed.\n\t\t// Otherwise, returned data is the recovered source block: its SSB_length\n\t\t// fixed-width authored symbols concatenated. Split it before emitting so\n\t\t// MmtpRouter gets the same one-symbol shape as a direct source packet.\n\t\tif (block.sourceReceived >= cmd.ssbLength && block.state === \"collecting\") {\n\t\t\tmarkBlockTerminal(block, \"complete\");\n\t\t\tblocksComplete++;\n\t\t\temitBlockUpdate(block);\n\t\t} else if (result && result.byteLength > 0) {\n\t\t\tconst recovered = new Uint8Array(toOwnedArrayBuffer(result));\n\t\t\tconst recoveryMs = performance.now() - block.firstSymbolTs;\n\t\t\trecoveryTimes.push(recoveryMs);\n\t\t// Cap recovery times array at 1000 entries\n\t\tif (recoveryTimes.length > 1000) recoveryTimes.shift();\n\n\t\tif (block.state === \"collecting\") {\n\t\t\tmarkBlockTerminal(block, \"recovered\");\n\t\t\tblocksRecovered++;\n\t\t\t\temitBlockUpdate(block);\n\t\t\t}\n\t\t\temitRecoveredSymbols(sbn, recovered, block);\n\t\t}\n\t\tsynchronizeDecoderHorizon();\n\t}\n\nfunction handleRelayBlockSig(\n\tcmd: Extract<FecWorkerCommand, { type: \"relayBlockSig\" }>,\n): void {\n\t// No-op AUTH stub: store sig on block, mark as not-yet-verified\n\tconst block = blocks.get(cmd.sbn);\n\tif (block) {\n\t\t// Real verification deferred to ALTA milestone\n\t\t// For now, mark as null (not verified) \u2014 will be wired when ALTA is implemented\n\t\tblock.relayBlockSigValid = null;\n\t\temitBlockUpdate(block);\n\t}\n}\n\nfunction handleCleanup(\n\tcmd: Extract<FecWorkerCommand, { type: \"cleanup\" }>,\n): void {\n\tif (\n\t\t!Array.isArray(cmd.sbns) ||\n\t\tcmd.sbns.length === 0 ||\n\t\t!cmd.sbns.every(isUint32)\n\t) {\n\t\tthrow new Error(\"[FecWorker] cleanup: invalid SBNs\");\n\t}\n\tconst sbns = [...new Set(cmd.sbns)].sort((a, b) => a - b);\n\tconst pendingSbns = sbns.filter((sbn) => sbn > retiredSbnFrontier);\n\ttry {\n\t\tif (!wasmDecoder) {\n\t\t\tthrow new Error(\"[FecWorker] cleanup: not configured\");\n\t\t}\n\n\t\t// Call WASM cleanup only after all earlier symbol commands have drained.\n\t\t// The detailed API carries the true owner of every fill emitted by a\n\t\t// single cleanup pass; command coalescing must not infer ownership from\n\t\t// the largest timed-out SBN.\n\t\tconst cleanupRecords = pendingSbns.length > 0\n\t\t\t? wasmDecoder.cleanup_blocks_detailed(Uint32Array.from(pendingSbns))\n\t\t\t: new Uint8Array();\n\t\t// Manager deadlines are block-scoped and may expire a newer SBN while an\n\t\t// older interleaved block is still live. Only accepted media progression\n\t\t// advances the shared JS/Rust horizon; cleanup creates exact tombstones.\n\n\t\t// Rust already finalized every SBN behind the media frontier. For each\n\t\t// remaining exact request, mirror the block-scoped WASM boundary immediately\n\t\t// so JS never retains state for a decoder block that is gone, even if decoding\n\t\t// the trusted cleanup framing were to fail.\n\t\tfor (const sbn of pendingSbns) {\n\t\t\tlet currentBlock = blocks.get(sbn);\n\t\t\tif (currentBlock && isTerminalBlock(currentBlock)) {\n\t\t\t\tretireAcceptedIds(sbn);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tcurrentBlock ??= getOrCreateBlock(sbn);\n\t\t\tif (currentBlock.sourceReceived >= currentBlock.k) {\n\t\t\t\tmarkBlockTerminal(currentBlock, \"complete\");\n\t\t\t\tblocksComplete++;\n\t\t\t} else {\n\t\t\t\tmarkBlockTerminal(currentBlock, \"failed\");\n\t\t\t\tblocksFailed++;\n\t\t\t}\n\t\t\temitBlockUpdate(currentBlock);\n\t\t}\n\n\t\tconst greenFills = decodeCleanupRecords(cleanupRecords);\n\t\tfor (const { sbn, data } of greenFills) {\n\t\t\tgreenFillFrames++;\n\t\t\temit({ type: \"greenFill\", sbn, data }, [data]);\n\t\t}\n\t} finally {\n\t\t// Message ordering makes this an acknowledgement that every source/repair\n\t\t// command posted before cleanup has completed. The main thread must not\n\t\t// commit failure accounting until it observes this event.\n\t\temit({ type: \"cleanupComplete\", sbns });\n\t}\n}\n\nfunction decodeCleanupRecords(\n\tframed: Uint8Array,\n): Array<{ sbn: number; data: ArrayBuffer }> {\n\tconst records: Array<{ sbn: number; data: ArrayBuffer }> = [];\n\tconst view = new DataView(framed.buffer, framed.byteOffset, framed.byteLength);\n\tlet offset = 0;\n\twhile (offset < framed.byteLength) {\n\t\tif (framed.byteLength - offset < 8) {\n\t\t\tthrow new Error(\"[FecWorker] cleanup_blocks_detailed: truncated record header\");\n\t\t}\n\t\tconst sbn = view.getUint32(offset, false);\n\t\tconst length = view.getUint32(offset + 4, false);\n\t\toffset += 8;\n\t\tif (length > framed.byteLength - offset) {\n\t\t\tthrow new Error(\"[FecWorker] cleanup_blocks_detailed: truncated record payload\");\n\t\t}\n\t\tconst data = toOwnedArrayBuffer(framed.subarray(offset, offset + length));\n\t\trecords.push({ sbn, data });\n\t\toffset += length;\n\t}\n\treturn records;\n}\n\nfunction handleDispose(): void {\n\t// Clear snapshot timer (MUST clear to prevent leaks)\n\tif (snapshotTimer !== null) {\n\t\tclearInterval(snapshotTimer);\n\t\tsnapshotTimer = null;\n\t}\n\n\t// Free ALTA verifier WASM object (MUST call to release WASM memory \u2014\n\t// Worker.terminate() fires immediately after dispose, so the FinalizationRegistry\n\t// callback has no chance to run and this memory would leak without explicit free)\n\tif (altaVerifier) {\n\t\ttry {\n\t\t\taltaVerifier.free();\n\t\t} catch (_e) {\n\t\t\t// Best-effort \u2014 verifier may already be freed\n\t\t}\n\t\taltaVerifier = null;\n\t}\n\n\t// Free WASM decoder (MUST call to release WASM memory)\n\tif (wasmDecoder) {\n\t\twasmDecoder.free();\n\t\twasmDecoder = null;\n\t}\n\n\t// Clear deferred-verify queue so pinned ArrayBuffers drop.\n\tpendingVerify.clear();\n\tpendingVerifyCap = 0;\n\tpendingVerifyTtlMs = 0;\n\n\tconfig = null;\n\tclearBlockTracking();\n}\n\nfunction handleReset(): void {\n\ttry {\n\t\twasmDecoder?.flush();\n\t\twasmDecoder?.reset_stats();\n\t} catch (err) {\n\t\temit({\n\t\t\ttype: \"error\",\n\t\t\tcode: \"reset-failed\",\n\t\t\tmessage:\n\t\t\t\terr instanceof Error\n\t\t\t\t\t? `[FecWorker] reset failed: ${err.message}`\n\t\t\t\t\t: `[FecWorker] reset failed: ${String(err)}`,\n\t\t});\n\t\treturn;\n\t}\n\n\tclearBlockTracking();\n\tsourceSymbols = 0;\n\trepairSymbols = 0;\n\tblocksComplete = 0;\n\tblocksRecovered = 0;\n\tblocksFailed = 0;\n\tgreenFillFrames = 0;\n\taltaVerified = 0;\n\taltaFailed = 0;\n\taltaFailedDetails.length = 0;\n\taltaPending = 0;\n\taltaPendingEvictedCap = 0;\n\taltaPendingEvictedTtl = 0;\n\taltaError = 0;\n\trelayBlocksVerified = 0;\n\trelayBlocksFailed = 0;\n\trepairSymbolSizeMismatch = 0;\n\temit({ type: \"resetComplete\" });\n\tif (config) emitSnapshot();\n}\n\n/**\n * A batch of symbols fed in one page task (BLO-36599). Each record is\n * handled exactly as its single-symbol command would be, in order, and a\n * failing record reports the same `command-failed` error that command would\n * without stopping the records after it.\n */\nfunction handleFeedSymbols(\n\tcmd: Extract<FecWorkerCommand, { type: \"feedSymbols\" }>,\n): void {\n\tif (!Array.isArray(cmd.symbols)) {\n\t\tthrow new Error(\"[FecWorker] feedSymbols: symbols must be an array\");\n\t}\n\tfor (const symbol of cmd.symbols) {\n\t\ttry {\n\t\t\tif (symbol?.type === \"feedSource\") handleFeedSource(symbol);\n\t\t\telse if (symbol?.type === \"feedRepair\") handleFeedRepair(symbol);\n\t\t\telse {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`[FecWorker] feedSymbols: unknown symbol type ${String((symbol as { type?: unknown } | null)?.type)}`,\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\temit({\n\t\t\t\ttype: \"error\",\n\t\t\t\tcode: \"command-failed\",\n\t\t\t\tmessage:\n\t\t\t\t\terr instanceof Error ? err.message : `[FecWorker] ${String(err)}`,\n\t\t\t});\n\t\t}\n\t}\n}\n\n// --- Main message handler ---\n\nasync function dispatchCommand(cmd: FecWorkerCommand): Promise<void> {\n\ttry {\n\t\tswitch (cmd.type) {\n\t\t\tcase \"configure\":\n\t\t\t\t// handleConfigure is async (ALTA WASM init uses dynamic import).\n\t\t\t\t// Await it so feed/cleanup messages cannot run against a partially\n\t\t\t\t// configured decoder.\n\t\t\t\tawait handleConfigure(cmd);\n\t\t\t\tbreak;\n\t\t\tcase \"feedSource\":\n\t\t\t\thandleFeedSource(cmd);\n\t\t\t\tbreak;\n\t\t\tcase \"feedRepair\":\n\t\t\t\thandleFeedRepair(cmd);\n\t\t\t\tbreak;\n\t\t\tcase \"feedSymbols\":\n\t\t\t\thandleFeedSymbols(cmd);\n\t\t\t\tbreak;\n\t\t\tcase \"relayBlockSig\":\n\t\t\t\thandleRelayBlockSig(cmd);\n\t\t\t\tbreak;\n\t\t\tcase \"cleanup\":\n\t\t\t\thandleCleanup(cmd);\n\t\t\t\tbreak;\n\t\t\tcase \"reset\":\n\t\t\t\thandleReset();\n\t\t\t\tbreak;\n\t\t\tcase \"dispose\":\n\t\t\t\thandleDispose();\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\temit({\n\t\t\t\t\ttype: \"error\",\n\t\t\t\t\tcode: \"unknown-command\",\n\t\t\t\t\tmessage: `Unknown command type: ${(cmd as Record<string, unknown>).type}`,\n\t\t\t\t});\n\t\t}\n\t} catch (err) {\n\t\temit({\n\t\t\ttype: \"error\",\n\t\t\tcode: \"command-failed\",\n\t\t\tmessage:\n\t\t\t\terr instanceof Error ? err.message : `[FecWorker] ${String(err)}`,\n\t\t});\n\t}\n}\n\nlet commandQueue: Promise<void> = Promise.resolve();\n\nself.onmessage = (e: MessageEvent<FecWorkerCommand>) => {\n\tconst cmd = e.data;\n\tcommandQueue = commandQueue.then(() => dispatchCommand(cmd));\n};\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * Return a fresh ArrayBuffer containing exactly the bytes in `view`.\n *\n * Worker outputs must not transfer buffers that might be owned by a WASM\n * binding or by a larger shared packet. A fresh JS-owned buffer gives\n * postMessage() exclusive ownership without detaching unrelated memory.\n */\nexport function toOwnedArrayBuffer(view: Uint8Array): ArrayBuffer {\n\tconst copy = new Uint8Array(view.byteLength);\n\tcopy.set(view);\n\treturn copy.buffer;\n}\n", "/**\n * @blockcast/fec-worker \u2014 Worker Entry Point\n *\n * Runs inside DedicatedWorkerGlobalScope. Receives FecWorkerCommand messages,\n * drives the MmtFecDecoder WASM module, emits FecWorkerEvent messages back.\n *\n * WASM bindings are resolved via:\n * 1. (self as any).__MMT_WASM_BINDINGS__ \u2014 set by consumer's loader/importmap\n * 2. Falls back to error if not available (no defaults)\n *\n * This file must NOT import DOM globals incompatible with Worker scope.\n */\n\nimport type {\n\tFecWorkerCommand,\n\tFecWorkerEvent,\n\tFecTrackConfig,\n\tFecTrackStats,\n\tFecBlockSnapshot,\n\tFrameMeta,\n} from \"./fec-worker-types.js\";\nimport { toOwnedArrayBuffer } from \"./transfer.js\";\n\n// --- WASM binding types (from mmt-wasm) ---\n// Signatures MUST match a freshly generated external mmt_wasm.d.ts exactly.\n// The decoder derives SBN/ESI internally \u2014 do not pre-compute.\ninterface WasmFecDecoder {\n\t/** ISO 23008-1 \u00A7C.5.2: ss_id is the flat 32-bit Source Symbol ID. */\n\tadd_source(\n\t\tss_id: number,\n\t\tdata: Uint8Array,\n\t\ttimestamp: number,\n\t): Uint8Array | undefined;\n\t/** ISO 23008-1 \u00A7C.5.3: repair ESI = configured K + rs_id, SBN = ss_start / configured K. */\n\tadd_repair(\n\t\tss_start: number,\n\t\tssb_length: number,\n\t\trs_id: number,\n\t\tdata: Uint8Array,\n\t\ttimestamp: number,\n\t): Uint8Array | undefined;\n\tconfigure_params(k: number, symbolSize: number): void;\n\t/** Block-scoped counterpart driven by the manager's exact deadlines. */\n\tcleanup_blocks_detailed(sbns: Uint32Array): Uint8Array;\n\t/** Remove decoder blocks below its configured live-SBN interleave horizon. */\n\tcleanup_by_sbn(current_sbn: number): Uint32Array;\n\tflush(): void;\n\tpending_blocks(): number;\n\treset_stats(): void;\n\tget_stats(): {\n\t\tsource_packets: bigint;\n\t\trepair_packets: bigint;\n\t\tblocks_complete: bigint;\n\t\tblocks_recovered: bigint;\n\t\tblocks_failed: bigint;\n\t\tbytes_recovered: bigint;\n\t\trecovery_rate(): number;\n\t};\n\tfree(): void;\n}\n\ninterface WasmBindings {\n\tinitSync(module: { module: WebAssembly.Module }): unknown;\n\tMmtFecDecoder: new (\n\t\tinterleaveDepth: number,\n\t\tgreenFillEnabled: boolean,\n\t) => WasmFecDecoder;\n}\n\n// --- Worker state (local to this Worker instance) ---\n\nlet wasmDecoder: WasmFecDecoder | null = null;\nlet config: FecTrackConfig | null = null;\nlet altaEnabled = false;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet altaVerifier: any = null; // JsAltaVerifier instance (loaded from ALTA WASM)\nlet snapshotTimer: ReturnType<typeof setInterval> | null = null;\n// blockUpdate events are posted only after a client subscribes (BLO-36599).\nlet blockUpdatesSubscribed = false;\n\n// Block tracking\nconst blocks = new Map<number, FecBlockSnapshot>();\nconst acceptedSourceEsis = new Map<number, Set<number>>();\nconst acceptedRepairEsis = new Map<number, Set<number>>();\nconst UINT32_MAX = 0xffff_ffff;\nconst UINT24_MAX = 0xff_ffff;\n// ISO/IEC 23008-1 AL-FEC signaling carries interleave_depth in one byte.\nconst MAX_INTERLEAVE_DEPTH = 0xff;\n// Rust stores T as u16 and this decoder profile fixes RFC 6330 Al=8.\nconst MAX_SYMBOL_SIZE = 0xfff8;\n// RFC 6330 systematic table limit, enforced by the vendored raptorq decoder.\nconst MAX_RAPTORQ_SOURCE_SYMBOLS_PER_BLOCK = 56_403;\n\n// Aggregate counters\nlet sourceSymbols = 0;\nlet repairSymbols = 0;\nlet blocksComplete = 0;\nlet blocksRecovered = 0;\nlet blocksFailed = 0;\nlet greenFillFrames = 0;\nlet altaVerified = 0;\nlet altaFailed = 0; // Crypto-confirmed tamper (MAC / sig / hash chain mismatch)\nconst altaFailedDetails: Array<{\n\tseq: number;\n\terror: string;\n\tauthLen: number;\n\tpayloadLen: number;\n\tpath: string; // \"direct\" | \"recovered\" | \"pending-retry\"\n}> = [];\nlet altaPending = 0; // Unverifiable but not tamper (no trailer, verifier not ready, anchor missing)\nlet altaPendingEvictedCap = 0; // Incoming \"pending\" packets rejected because the queue was at cap after stale-sweep (flood indicator)\nlet altaPendingEvictedTtl = 0; // Deferred-queue entries dropped by TTL (retry window expired)\nlet altaError = 0; // verifyDetached threw \u2014 infrastructure flake, not tamper\nlet altaInitFailed = false; // ALTA configured but WASM init failed; altaEnabled was forced off\nlet relayBlocksVerified = 0;\nlet relayBlocksFailed = 0;\nlet liveSbn = 0;\n// Highest SBN that has fallen strictly below the decoder's retained\n// interleave window. Numeric ordering is per decoder epoch; reset/configure\n// starts a new epoch before the 32-bit source-symbol counter can wrap.\nlet retiredSbnFrontier = -1;\nlet repairSymbolSizeMismatch = 0;\n\n// Recovery timing\nconst recoveryTimes: number[] = [];\n\n// \u2500\u2500 Deferred ALTA verification queue (08-07 Path 3) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// When an ALTA packet arrives whose MAC references point at sequences not yet\n// in the verifier's ring buffer, we can't anchor trust \u2014 verify returns \"No\n// trust anchor\". Typical cause: a predecessor packet was lost and hasn't been\n// FEC-recovered yet. Rather than drop these, hold them in a bounded FIFO and\n// retry verification after every successful verify (direct or FEC-recovered)\n// that seeds the ring buffer with new raw bytes.\n//\n// Zero-copy (1B-B): the queue entry stores a Uint8Array view into the\n// original `data` ArrayBuffer. JS GC keeps the buffer alive via the view\n// reference; no bytes are copied on queue.\n//\n// Timeout entries (TTL exceeded) and cap-rejections count as `altaPending`\n// (2C-B): they're not tamper indicators, they're just packets that couldn't\n// be authenticated within the receive window \u2014 either loss or, in the cap\n// case, a flood of unanchorable arrivals (real or attacker-driven) that\n// exceeded the queue budget. See enqueuePendingVerify for the rate-limit\n// protocol.\ninterface PendingVerify {\n\tseq: number;\n\tauth: Uint8Array;\n\tpayload: Uint8Array;\n\tarrivalTs: number;\n}\nconst pendingVerify = new Map<number, PendingVerify>();\nlet pendingVerifyCap = 0;\nlet pendingVerifyTtlMs = 0;\n\n// --- Helpers ---\n\nfunction emit(event: FecWorkerEvent, transfer?: Transferable[]): void {\n\tself.postMessage(event, transfer ?? []);\n}\n\nfunction isUint32(value: number): boolean {\n\treturn Number.isInteger(value) && value >= 0 && value <= UINT32_MAX;\n}\n\nfunction isTerminalBlock(block: FecBlockSnapshot): boolean {\n\treturn block.state !== \"collecting\";\n}\n\nfunction retireAcceptedIds(sbn: number): void {\n\tacceptedSourceEsis.delete(sbn);\n\tacceptedRepairEsis.delete(sbn);\n}\n\nfunction clearBlockTracking(): void {\n\tblocks.clear();\n\tacceptedSourceEsis.clear();\n\tacceptedRepairEsis.clear();\n\tpendingVerify.clear();\n\trecoveryTimes.length = 0;\n\tliveSbn = 0;\n\tretiredSbnFrontier = -1;\n}\n\n/**\n * Retire terminal diagnostics only after the decoder's monotonic interleave\n * horizon proves that the SBN can no longer be valid out-of-order media.\n * Admission keeps the compact frontier as the durable tombstone, so deletion\n * never reopens a finalized block.\n */\nfunction trimBlockRetention(): void {\n\tfor (const [sbn, block] of blocks) {\n\t\tif (!isTerminalBlock(block) || sbn > retiredSbnFrontier) continue;\n\t\tblocks.delete(sbn);\n\t\tretireAcceptedIds(sbn);\n\t}\n}\n\nfunction advanceMediaHorizon(sbn: number): void {\n\tif (!config || sbn <= liveSbn) return;\n\tliveSbn = sbn;\n\tconst firstRetainedSbn = Math.max(0, liveSbn - config.interleaveDepth);\n\tretiredSbnFrontier = Math.max(retiredSbnFrontier, firstRetainedSbn - 1);\n\ttrimBlockRetention();\n}\n\nfunction markBlockTerminal(\n\tblock: FecBlockSnapshot,\n\tstate: \"complete\" | \"recovered\" | \"failed\",\n): void {\n\tblock.state = state;\n\tblock.completedTs = performance.now();\n\tretireAcceptedIds(block.sbn);\n\ttrimBlockRetention();\n}\n\nfunction synchronizeDecoderHorizon(): void {\n\tif (!wasmDecoder) return;\n\tconst expired = wasmDecoder.cleanup_by_sbn(liveSbn);\n\tfor (const sbn of expired) {\n\t\tconst block = blocks.get(sbn);\n\t\tif (!block || isTerminalBlock(block)) {\n\t\t\tretireAcceptedIds(sbn);\n\t\t\tcontinue;\n\t\t}\n\t\tmarkBlockTerminal(block, \"failed\");\n\t\tblocksFailed++;\n\t\temitBlockUpdate(block);\n\t}\n\ttrimBlockRetention();\n}\n\n/**\n * Run detached ALTA verify against the current verifier. Four-state result:\n * - \"valid\": crypto passed, packet authentic. Caller increments altaVerified.\n * - \"failed\": crypto confirmed tamper (MAC/sig mismatch, hash chain mismatch).\n * Caller increments altaFailed. Not retryable.\n * - \"pending\": anchor missing (MAC refs not in buffer, no signature, not\n * early-warmup). Caller may enqueue for retry after the next\n * successful verify seeds the ring buffer.\n * - \"error\": verifier threw \u2014 WASM trap, unmarshal panic, OOM. Caller\n * increments altaError (infrastructure flake, not tamper).\n * The distinction matters for ops alerting: a spike in\n * altaError indicates a runtime problem, not an attack.\n */\nlet _lastVerifyError = \"\";\nfunction runDetachedVerify(\n\tauth: Uint8Array,\n\tpayload: Uint8Array,\n): \"valid\" | \"failed\" | \"pending\" | \"error\" {\n\tif (!altaVerifier) return \"pending\";\n\ttry {\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\tconst r: any = altaVerifier.verifyDetached(auth, payload);\n\t\tif (r.valid) {\n\t\t\t_lastVerifyError = \"\";\n\t\t\treturn \"valid\";\n\t\t}\n\t\tconst err = (r.error as string | undefined) ?? \"\";\n\t\t_lastVerifyError = err;\n\t\t// The bootstrap-friendly verifier returns this specific error string when\n\t\t// the only obstacle to verification is a missing anchor (see alta-rs\n\t\t// verifier.rs verify()). Distinguishing this from tamper cases keeps us\n\t\t// from queuing packets that are definitively invalid.\n\t\tif (err.startsWith(\"No trust anchor\")) return \"pending\";\n\t\treturn \"failed\";\n\t} catch (e) {\n\t\t// Ratelimit: first ~N occurrences get logged. A sustained exception\n\t\t// stream indicates a real problem \u2014 the altaError counter is the\n\t\t// durable signal.\n\t\tif (altaError < 5) {\n\t\t\tconsole.warn(\"[FecWorker] verifyDetached threw:\", e);\n\t\t}\n\t\treturn \"error\";\n\t}\n}\n\n/** Extract u32 BE sequence number from the ALTA authenticator header (offset 0..3). */\nfunction seqFromAuth(auth: Uint8Array): number {\n\treturn (\n\t\t((auth[0]! << 24) | (auth[1]! << 16) | (auth[2]! << 8) | auth[3]!) >>> 0\n\t);\n}\n\n/**\n * Enqueue a packet whose ALTA verification returned \"pending\" (anchor\n * missing). Dedupes by sequence number.\n *\n * Flood resistance (08-08 review): when the queue is at cap we do NOT\n * evict the oldest entry. The oldest entry is typically the most\n * load-bearing one \u2014 it's the packet whose anchor everything else in the\n * queue is waiting on. Evict-oldest-on-insert let an attacker emitting a\n * stream of \"No trust anchor\" packets flood out legitimate entries and\n * prevent them from ever anchoring. Instead:\n * 1. First try to free stale TTL'd entries (they'd be evicted on next\n * drain anyway \u2014 pulling that forward is free).\n * 2. If still full, reject the NEW entry. altaPendingEvictedCap counts\n * these rejections (rate-limit trigger).\n *\n * Stores Uint8Array views into the original data + auth buffers \u2014 no copy.\n * The views keep their underlying ArrayBuffers alive via GC references.\n */\nfunction enqueuePendingVerify(\n\tauth: Uint8Array,\n\tpayload: Uint8Array,\n): void {\n\tconst seq = seqFromAuth(auth);\n\tif (pendingVerify.has(seq)) return;\n\n\tif (pendingVerify.size >= pendingVerifyCap) {\n\t\t// Step 1: sweep stale entries so legitimate traffic isn't penalized\n\t\t// by TTL'd junk that happens to still occupy slots.\n\t\tconst now = performance.now();\n\t\tfor (const [staleSeq, entry] of pendingVerify) {\n\t\t\tif (now - entry.arrivalTs > pendingVerifyTtlMs) {\n\t\t\t\tpendingVerify.delete(staleSeq);\n\t\t\t\taltaPendingEvictedTtl++;\n\t\t\t}\n\t\t}\n\t\t// Step 2: if the queue is still at cap it's a genuine flood \u2014\n\t\t// reject the new entry rather than evict an existing one. The\n\t\t// rejected packet counts as altaPendingEvictedCap (which is\n\t\t// surfaced up through stats for rate-limit alerting).\n\t\tif (pendingVerify.size >= pendingVerifyCap) {\n\t\t\taltaPendingEvictedCap++;\n\t\t\treturn;\n\t\t}\n\t}\n\n\tpendingVerify.set(seq, {\n\t\tseq,\n\t\tauth,\n\t\tpayload,\n\t\tarrivalTs: performance.now(),\n\t});\n}\n\n/**\n * Retry every queued entry. Called after every successful verify (direct\n * source or FEC-recovered) \u2014 new raw bytes in the verifier's ring buffer may\n * unblock previously-pending MAC references.\n *\n * Three outcomes per entry:\n * - \"valid\" \u2192 remove from queue, altaVerified++\n * - \"failed\" \u2192 remove from queue, altaFailed++ (tamper on retry)\n * - \"pending\" \u2192 keep in queue, unless TTL expired (then evict as altaPending)\n *\n * Iteration order: Map preserves insertion order, so we drain oldest-first.\n * No-op when the queue is empty or the verifier isn't ready.\n */\nfunction drainPendingVerify(): void {\n\tif (pendingVerify.size === 0 || !altaVerifier) return;\n\tconst now = performance.now();\n\t// Collect keys to avoid mutating Map during iteration\n\tconst keys = Array.from(pendingVerify.keys());\n\tfor (const seq of keys) {\n\t\tconst entry = pendingVerify.get(seq);\n\t\tif (!entry) continue;\n\t\tif (now - entry.arrivalTs > pendingVerifyTtlMs) {\n\t\t\tpendingVerify.delete(seq);\n\t\t\taltaPendingEvictedTtl++;\n\t\t\tcontinue;\n\t\t}\n\t\tconst result = runDetachedVerify(entry.auth, entry.payload);\n\t\tif (result === \"valid\") {\n\t\t\tpendingVerify.delete(seq);\n\t\t\taltaVerified++;\n\t\t} else if (result === \"failed\") {\n\t\t\tpendingVerify.delete(seq);\n\t\t\taltaFailed++;\n\t\t\tif (altaFailedDetails.length < 16) {\n\t\t\t\taltaFailedDetails.push({\n\t\t\t\t\tseq: entry.seq,\n\t\t\t\t\terror: _lastVerifyError,\n\t\t\t\t\tauthLen: entry.auth.length,\n\t\t\t\t\tpayloadLen: entry.payload.length,\n\t\t\t\t\tpath: \"pending-retry\",\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (result === \"error\") {\n\t\t\t// Infrastructure failure \u2014 remove from queue, count as altaError.\n\t\t\t// Keeping it around would just re-throw on every subsequent drain.\n\t\t\tpendingVerify.delete(seq);\n\t\t\taltaError++;\n\t\t}\n\t\t// \"pending\" \u2192 leave in queue for next drain\n\t}\n}\n\nfunction getOrCreateBlock(sbn: number): FecBlockSnapshot {\n\tlet block = blocks.get(sbn);\n\tif (!block) {\n\t\tif (!config) throw new Error(\"[FecWorker] not configured\");\n\t\tconst now = performance.now();\n\t\tblock = {\n\t\t\tsbn,\n\t\t\tstate: \"collecting\",\n\t\t\tsourceReceived: 0,\n\t\t\trepairReceived: 0,\n\t\t\tk: config.k,\n\t\t\trepairCount: config.repairCount,\n\t\t\tfirstSymbolTs: now,\n\t\t\tlastSymbolTs: now,\n\t\t\tdeadlineTs: now + config.deliveryWindowMs,\n\t\t\taltaVerified: 0,\n\t\t\trelayBlockSigValid: null,\n\t\t};\n\t\tblocks.set(sbn, block);\n\t\ttrimBlockRetention();\n\t}\n\treturn block;\n}\n\nfunction buildStatsSnapshot(): FecTrackStats {\n\tif (!config) throw new Error(\"[FecWorker] not configured\");\n\tconst decoderStats = wasmDecoder?.get_stats();\n\tconst decoder = decoderStats\n\t\t? {\n\t\t\tsourcePackets: Number(decoderStats.source_packets),\n\t\t\trepairPackets: Number(decoderStats.repair_packets),\n\t\t\tblocksComplete: Number(decoderStats.blocks_complete),\n\t\t\tblocksRecovered: Number(decoderStats.blocks_recovered),\n\t\t\tblocksFailed: Number(decoderStats.blocks_failed),\n\t\t\tbytesRecovered: Number(decoderStats.bytes_recovered),\n\t\t\tpendingBlocks: wasmDecoder?.pending_blocks() ?? 0,\n\t\t}\n\t\t: undefined;\n\n\tconst avgRecoveryMs =\n\t\trecoveryTimes.length > 0\n\t\t\t? recoveryTimes.reduce((a, b) => a + b, 0) / recoveryTimes.length\n\t\t\t: 0;\n\tconst maxRecoveryMs =\n\t\trecoveryTimes.length > 0 ? Math.max(...recoveryTimes) : 0;\n\n\t// Cap block array to interleave window\n\tconst maxBlocks = Math.max(config.interleaveDepth * 3, 24);\n\tconst blockArray = Array.from(blocks.values())\n\t\t.sort((a, b) => b.sbn - a.sbn)\n\t\t.slice(0, maxBlocks);\n\n\treturn {\n\t\tconfig,\n\t\tsourceSymbols,\n\t\trepairSymbols,\n\t\tblocksComplete,\n\t\tblocksRecovered,\n\t\tblocksFailed,\n\t\tgreenFillFrames,\n\t\trecoveryRate:\n\t\t\tblocksComplete + blocksRecovered > 0\n\t\t\t\t? Math.round(\n\t\t\t\t\t\t(blocksRecovered / (blocksRecovered + blocksFailed)) * 100,\n\t\t\t\t\t) || 0\n\t\t\t\t: 0,\n\t\taltaVerified,\n\t\taltaFailed,\n\t\taltaPending,\n\t\taltaPendingEvictedCap,\n\t\taltaPendingEvictedTtl,\n\t\taltaError,\n\t\taltaInitFailed,\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\taltaFailedDetails: altaFailedDetails.slice() as any,\n\t\trelayBlocksVerified,\n\t\trelayBlocksFailed,\n\t\tliveSbn,\n\t\tblocks: blockArray,\n\t\tinterleaveWindowBlocks: config.interleaveDepth,\n\t\tavgRecoveryMs: Math.round(avgRecoveryMs * 100) / 100,\n\t\tmaxRecoveryMs: Math.round(maxRecoveryMs * 100) / 100,\n\t\trepairSymbolSizeMismatch,\n\t\tdecoder,\n\t};\n}\n\nfunction emitSnapshot(): void {\n\tif (!config) return;\n\temit({ type: \"snapshot\", stats: buildStatsSnapshot() });\n}\n\nfunction emitBlockUpdate(block: FecBlockSnapshot): void {\n\tif (!blockUpdatesSubscribed) return;\n\temit({ type: \"blockUpdate\", block: { ...block } });\n}\n\nfunction emitRecoveredSymbols(\n\tsbn: number,\n\trecoveredBlock: Uint8Array,\n\tblock: FecBlockSnapshot,\n): void {\n\tif (!config) return;\n\tconst symbolSize = config.symbolSize;\n\tif (symbolSize <= 0) {\n\t\temit({\n\t\t\ttype: \"error\",\n\t\t\tcode: \"recovered-symbol-size-unconfigured\",\n\t\t\tmessage: \"[FecWorker] recovered block has no configured symbolSize\",\n\t\t});\n\t\treturn;\n\t}\n\t// The decoder returns only the block's authored source symbols\n\t// (SSB_length x T); the encoder's zero padding is not media.\n\tconst ssbLength = block.ssbLength ?? config.k;\n\tif (recoveredBlock.byteLength !== symbolSize * ssbLength) {\n\t\temit({\n\t\t\ttype: \"error\",\n\t\t\tcode: \"recovered-block-too-short\",\n\t\t\tmessage: `[FecWorker] recovered block has the wrong length: got ${recoveredBlock.byteLength}B, expected ${symbolSize * ssbLength}B (SSB_length=${ssbLength})`,\n\t\t});\n\t\treturn;\n\t}\n\n\tfor (let i = 0; i < ssbLength; i++) {\n\t\tconst symbol = recoveredBlock.subarray(i * symbolSize, (i + 1) * symbolSize);\n\n\t\t// Source authentication now runs once at router admission in\n\t\t// @blockcast/mmt-manifest-verify. The worker only records that this\n\t\t// legacy ALTA-enabled symbol bypassed hot-path verification.\n\t\tconst recoveredAltaVerified: boolean | null = null;\n\t\tif (altaEnabled) altaPending++;\n\n\t\tconst buf = toOwnedArrayBuffer(symbol);\n\t\tconst meta: FrameMeta = {\n\t\t\taltaVerified: recoveredAltaVerified,\n\t\t\trecovered: true,\n\t\t\tsbn,\n\t\t\tesi: i,\n\t\t\tssbLength,\n\t\t};\n\t\temit({ type: \"frame\", data: buf, meta }, [buf]);\n\t}\n}\n\nfunction startSnapshotTimer(): void {\n\tif (snapshotTimer !== null) clearInterval(snapshotTimer);\n\tsnapshotTimer = setInterval(() => emitSnapshot(), 200);\n}\n\nfunction resolveWasmBindings(): WasmBindings {\n\tconst bindings = (self as unknown as Record<string, unknown>)\n\t\t.__MMT_WASM_BINDINGS__ as WasmBindings | undefined;\n\tif (!bindings) {\n\t\tthrow new Error(\n\t\t\t\"[FecWorker] WASM bindings not available. Set self.__MMT_WASM_BINDINGS__ = { initSync, MmtFecDecoder } before configure.\",\n\t\t);\n\t}\n\treturn bindings;\n}\n\n// --- Command handlers ---\n\nasync function handleConfigure(\n\tcmd: Extract<FecWorkerCommand, { type: \"configure\" }>,\n): Promise<void> {\n\tif (!cmd.wasmModule) {\n\t\tthrow new Error(\"[FecWorker] configure: wasmModule required\");\n\t}\n\tif (!cmd.config) {\n\t\tthrow new Error(\"[FecWorker] configure: config required\");\n\t}\n\tif (\n\t\t!Number.isFinite(cmd.config.interleaveDepth) ||\n\t\t!Number.isInteger(cmd.config.interleaveDepth) ||\n\t\tcmd.config.interleaveDepth <= 0 ||\n\t\tcmd.config.interleaveDepth > MAX_INTERLEAVE_DEPTH\n\t) {\n\t\tthrow new Error(\n\t\t\t`[FecWorker] configure: interleaveDepth must be an integer in 1..${MAX_INTERLEAVE_DEPTH} (got ${cmd.config.interleaveDepth})`,\n\t\t);\n\t}\n\tif (\n\t\t!Number.isFinite(cmd.config.interleaveMs) ||\n\t\tcmd.config.interleaveMs <= 0\n\t) {\n\t\tthrow new Error(\n\t\t\t`[FecWorker] configure: interleaveMs must be finite and > 0 (got ${cmd.config.interleaveMs})`,\n\t\t);\n\t}\n\tif (\n\t\t!Number.isFinite(cmd.config.deliveryWindowMs) ||\n\t\tcmd.config.deliveryWindowMs <= 0\n\t) {\n\t\tthrow new Error(\n\t\t\t`[FecWorker] configure: deliveryWindowMs must be > 0 (got ${cmd.config.deliveryWindowMs})`,\n\t\t);\n\t}\n\tif (\n\t\t!Number.isInteger(cmd.config.k) ||\n\t\tcmd.config.k <= 0 ||\n\t\tcmd.config.k > MAX_RAPTORQ_SOURCE_SYMBOLS_PER_BLOCK\n\t) {\n\t\tthrow new Error(\n\t\t\t`[FecWorker] configure: k must be an integer in 1..${MAX_RAPTORQ_SOURCE_SYMBOLS_PER_BLOCK} (got ${cmd.config.k})`,\n\t\t);\n\t}\n\tif (\n\t\t!Number.isInteger(cmd.config.symbolSize) ||\n\t\tcmd.config.symbolSize <= 0 ||\n\t\tcmd.config.symbolSize > MAX_SYMBOL_SIZE ||\n\t\tcmd.config.symbolSize % 8 !== 0\n\t) {\n\t\tthrow new Error(\n\t\t\t`[FecWorker] configure: symbolSize must be an 8-byte-aligned integer in 8..${MAX_SYMBOL_SIZE} (got ${cmd.config.symbolSize})`,\n\t\t);\n\t}\n\n\tconst wasm = resolveWasmBindings();\n\n\t// Initialize WASM from pre-compiled Module (no fetch, no re-compile)\n\twasm.initSync({ module: cmd.wasmModule });\n\n\t// A configure command starts a new decoder epoch. No terminal tombstone,\n\t// accepted-symbol ID, or active block from the previous decoder may affect it.\n\twasmDecoder?.free();\n\twasmDecoder = null;\n\tclearBlockTracking();\n\n\t// Decoder policy and geometry are explicit at the epoch boundary. The WASM\n\t// module has no guessed K/T/interleave slow path.\n\twasmDecoder = new wasm.MmtFecDecoder(\n\t\tcmd.config.interleaveDepth,\n\t\tcmd.config.trackType === \"video\",\n\t);\n\tconfig = cmd.config;\n\taltaEnabled = !!cmd.altaPublicKey;\n\n\t// Size the deferred-verify queue strictly from catalog-derived FEC\n\t// parameters. CLAUDE.md bans Math.max(x, floor) magic \u2014 if catalog\n\t// values are zero/missing, throw at init rather than silently applying\n\t// a hardcoded floor. Consumers of an undersized queue cap would see\n\t// altaPendingEvictedCap spike with no actionable signal.\n\t// Cap: 1.5\u00D7 interleave depth \u2014 wide enough to hold one full FEC recovery\n\t// window plus jitter, narrow enough to bound memory.\n\tpendingVerifyCap = Math.ceil(config.interleaveDepth * 1.5);\n\t// TTL: 1.5\u00D7 block duration (K symbols at one per interleave tick) \u2014\n\t// after this many ms, any unresolved entry has missed its FEC window.\n\tpendingVerifyTtlMs = Math.ceil(config.interleaveMs * config.k * 1.5);\n\n\t// Initialize ALTA WASM verifier if public key and WASM module provided (D-05, D-09)\n\taltaVerifier = null;\n\taltaInitFailed = false;\n\tif (cmd.altaPublicKey && cmd.altaWasmModule) {\n\t\ttry {\n\t\t\t// Load ALTA JS glue (wasm-bindgen output from wasm-pack build).\n\t\t\t// Mirrors the RaptorQ WASM pattern: initSync({ module }) then construct.\n\t\t\t// Dynamic import path matches the IWA static asset layout.\n\t\t\t// Cast via string to prevent TypeScript from attempting static resolution\n\t\t\t// of a runtime-only URL (same pattern as fec-worker-entry.js loading).\n\t\t\tconst altaGlueUrl = '/wasm/alta/alta_rs.js' as string;\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t\tconst altaGlue: any = await import(/* webpackIgnore: true */ altaGlueUrl);\n\t\t\taltaGlue.initSync({ module: cmd.altaWasmModule });\n\t\t\tconst keyBytes = new Uint8Array(cmd.altaPublicKey);\n\t\t\taltaVerifier = new altaGlue.JsAltaVerifier(keyBytes);\n\t\t} catch (e) {\n\t\t\t// T-08-15: WASM init failure must NOT silently look like \"not-ready-yet\".\n\t\t\t// Force altaEnabled = false so handleFeedSource takes the disabled\n\t\t\t// branch instead of continuously bumping altaPending under the\n\t\t\t// misleading \"verifier not yet ready\" comment. altaInitFailed\n\t\t\t// flag in the snapshot tells consumers this is an error state, not\n\t\t\t// an opt-out.\n\t\t\tconsole.warn('[FecWorker] ALTA WASM init failed, verification disabled:', e);\n\t\t\taltaVerifier = null;\n\t\t\taltaEnabled = false;\n\t\t\taltaInitFailed = true;\n\t\t}\n\t}\n\n\twasmDecoder.configure_params(config.k, config.symbolSize);\n\n\t// Tell the client which optional commands this Worker accepts, so it can\n\t// batch symbols without risking a Worker that predates the batch command.\n\temit({ type: \"capabilities\", commands: [\"feedSymbols\", \"subscribeBlockUpdates\"] });\n\n\t// Start periodic snapshot emission (200ms floor)\n\tstartSnapshotTimer();\n\n\t// Emit initial snapshot\n\temitSnapshot();\n}\n\nfunction handleFeedSource(\n\tcmd: Extract<FecWorkerCommand, { type: \"feedSource\" }>,\n): void {\n\tif (!wasmDecoder) {\n\t\tthrow new Error(\"[FecWorker] feedSource: not configured\");\n\t}\n\tif (!config) {\n\t\tthrow new Error(\"[FecWorker] feedSource: config missing\");\n\t}\n\tif (\n\t\t!isUint32(cmd.ssId) ||\n\t\t!(cmd.data instanceof ArrayBuffer)\n\t) {\n\t\tthrow new Error(\"[FecWorker] feedSource: invalid source symbol\");\n\t}\n\n\tconst view = new Uint8Array(cmd.data);\n\n\t// Derive SBN locally for block-level accounting. WASM derives the same\n\t// value internally (ISO 23008-1 \u00A7C.5.2: SBN = floor(SS_ID / K)).\n\tconst sbn = config.k > 0 ? Math.floor(cmd.ssId / config.k) : cmd.ssId;\n\tconst esi = cmd.ssId % config.k;\n\tif (sbn <= retiredSbnFrontier) return;\n\tconst existingBlock = blocks.get(sbn);\n\tif (existingBlock && isTerminalBlock(existingBlock)) return;\n\tif (acceptedSourceEsis.get(sbn)?.has(esi)) return;\n\n\tconst result = wasmDecoder.add_source(cmd.ssId, view, cmd.ts);\n\tadvanceMediaHorizon(sbn);\n\tlet sourceEsis = acceptedSourceEsis.get(sbn);\n\tif (!sourceEsis) {\n\t\tsourceEsis = new Set<number>();\n\t\tacceptedSourceEsis.set(sbn, sourceEsis);\n\t}\n\tsourceEsis.add(esi);\n\n\t// Update block state\n\tconst block = getOrCreateBlock(sbn);\n\tblock.sourceReceived++;\n\tblock.lastSymbolTs = performance.now();\n\tsourceSymbols++;\n\n\t// The FEC worker must remain decode-only on the per-symbol hot path. ALTA\n\t// WASM is retained temporarily for low-rate signaling users, but media is\n\t// authenticated by the canonical manifest verifier at router admission.\n\tif (altaEnabled) altaPending++;\n\n\t// Check if block is complete (all authored source symbols received:\n\t// SSB_length once a repair symbol declared it, otherwise K)\n\tconst authoredLength = block.ssbLength ?? block.k;\n\tif (\n\t\tblock.sourceReceived >= authoredLength &&\n\t\tblock.state === \"collecting\"\n\t) {\n\t\tmarkBlockTerminal(block, \"complete\");\n\t\tblocksComplete++;\n\t\temitBlockUpdate(block);\n\t}\n\n\t\t// A source-triggered return can be either clean block completion or recovery\n\t\t// of earlier missing symbols after repair arrived. Clean completion is\n\t\t// already routed by the source path, so only emit when the block did not\n\t\t// reach K source arrivals.\n\t\tif (result && result.byteLength > 0 && block.sourceReceived < authoredLength) {\n\t\t\tconst recovered = new Uint8Array(toOwnedArrayBuffer(result));\n\t\t\tconst recoveryMs = performance.now() - block.firstSymbolTs;\n\t\t\trecoveryTimes.push(recoveryMs);\n\t\t\tif (recoveryTimes.length > 1000) recoveryTimes.shift();\n\n\t\t\tif (block.state === \"collecting\") {\n\t\t\t\tmarkBlockTerminal(block, \"recovered\");\n\t\t\t\tblocksRecovered++;\n\t\t\t\temitBlockUpdate(block);\n\t\t\t}\n\t\t\temitRecoveredSymbols(sbn, recovered, block);\n\t\t}\n\t\tsynchronizeDecoderHorizon();\n\t}\n\nfunction handleFeedRepair(\n\tcmd: Extract<FecWorkerCommand, { type: \"feedRepair\" }>,\n): void {\n\tif (!wasmDecoder) {\n\t\tthrow new Error(\"[FecWorker] feedRepair: not configured\");\n\t}\n\tif (!config) {\n\t\tthrow new Error(\"[FecWorker] feedRepair: config missing\");\n\t}\n\tif (\n\t\t!isUint32(cmd.ssStart) ||\n\t\t!isUint32(cmd.ssbLength) ||\n\t\tcmd.ssbLength <= 0 ||\n\t\tcmd.ssbLength > config.k ||\n\t\tcmd.ssStart % config.k !== 0 ||\n\t\t!isUint32(cmd.rsId) ||\n\t\tconfig.k + cmd.rsId > UINT24_MAX ||\n\t\t!(cmd.data instanceof ArrayBuffer)\n\t) {\n\t\tthrow new Error(\"[FecWorker] feedRepair: invalid repair symbol\");\n\t}\n\n\tconst view = new Uint8Array(cmd.data);\n\tif (config.symbolSize > 0 && view.byteLength !== config.symbolSize) {\n\t\trepairSymbolSizeMismatch++;\n\t\tif (repairSymbolSizeMismatch <= 5 || repairSymbolSizeMismatch % 100 === 0) {\n\t\t\temit({\n\t\t\t\ttype: \"error\",\n\t\t\t\tcode: \"repair-symbol-size-mismatch\",\n\t\t\t\tmessage: `[FecWorker] Dropping repair symbol with invalid size: got ${view.byteLength}B, expected ${config.symbolSize}B (ssStart=${cmd.ssStart}, ssbLength=${cmd.ssbLength}, rsId=${cmd.rsId})`,\n\t\t\t});\n\t\t}\n\t\treturn;\n\t}\n\tconst sbn = Math.floor(cmd.ssStart / config.k);\n\tconst repairEsi = config.k + cmd.rsId;\n\tif (sbn <= retiredSbnFrontier) return;\n\tconst existingBlock = blocks.get(sbn);\n\tif (existingBlock && isTerminalBlock(existingBlock)) return;\n\tif (acceptedRepairEsis.get(sbn)?.has(repairEsi)) return;\n\t// One block has one authored SSB_length; a repair symbol naming another\n\t// is dropped before it reaches the decoder (which rejects it too).\n\tif (existingBlock?.ssbLength !== undefined && existingBlock.ssbLength !== cmd.ssbLength) {\n\t\temit({\n\t\t\ttype: \"error\",\n\t\t\tcode: \"repair-ssb-length-conflict\",\n\t\t\tmessage: `[FecWorker] Dropping repair symbol whose SSB_length ${cmd.ssbLength} conflicts with ${existingBlock.ssbLength} (sbn=${sbn})`,\n\t\t});\n\t\treturn;\n\t}\n\n\tconst result = wasmDecoder.add_repair(\n\t\tcmd.ssStart,\n\t\tcmd.ssbLength,\n\t\tcmd.rsId,\n\t\tview,\n\t\tcmd.ts,\n\t);\n\tadvanceMediaHorizon(sbn);\n\tlet repairEsis = acceptedRepairEsis.get(sbn);\n\tif (!repairEsis) {\n\t\trepairEsis = new Set<number>();\n\t\tacceptedRepairEsis.set(sbn, repairEsis);\n\t}\n\trepairEsis.add(repairEsi);\n\tconst block = getOrCreateBlock(sbn);\n\tblock.ssbLength = cmd.ssbLength;\n\tblock.repairReceived++;\n\tblock.lastSymbolTs = performance.now();\n\trepairSymbols++;\n\t\t// Every authored source symbol already arrived: knowing SSB_length makes\n\t\t// the block clean. The decoder's return is the source already routed.\n\t\t// Otherwise, returned data is the recovered source block: its SSB_length\n\t\t// fixed-width authored symbols concatenated. Split it before emitting so\n\t\t// MmtpRouter gets the same one-symbol shape as a direct source packet.\n\t\tif (block.sourceReceived >= cmd.ssbLength && block.state === \"collecting\") {\n\t\t\tmarkBlockTerminal(block, \"complete\");\n\t\t\tblocksComplete++;\n\t\t\temitBlockUpdate(block);\n\t\t} else if (result && result.byteLength > 0) {\n\t\t\tconst recovered = new Uint8Array(toOwnedArrayBuffer(result));\n\t\t\tconst recoveryMs = performance.now() - block.firstSymbolTs;\n\t\t\trecoveryTimes.push(recoveryMs);\n\t\t// Cap recovery times array at 1000 entries\n\t\tif (recoveryTimes.length > 1000) recoveryTimes.shift();\n\n\t\tif (block.state === \"collecting\") {\n\t\t\tmarkBlockTerminal(block, \"recovered\");\n\t\t\tblocksRecovered++;\n\t\t\t\temitBlockUpdate(block);\n\t\t\t}\n\t\t\temitRecoveredSymbols(sbn, recovered, block);\n\t\t}\n\t\tsynchronizeDecoderHorizon();\n\t}\n\nfunction handleRelayBlockSig(\n\tcmd: Extract<FecWorkerCommand, { type: \"relayBlockSig\" }>,\n): void {\n\t// No-op AUTH stub: store sig on block, mark as not-yet-verified\n\tconst block = blocks.get(cmd.sbn);\n\tif (block) {\n\t\t// Real verification deferred to ALTA milestone\n\t\t// For now, mark as null (not verified) \u2014 will be wired when ALTA is implemented\n\t\tblock.relayBlockSigValid = null;\n\t\temitBlockUpdate(block);\n\t}\n}\n\nfunction handleCleanup(\n\tcmd: Extract<FecWorkerCommand, { type: \"cleanup\" }>,\n): void {\n\tif (\n\t\t!Array.isArray(cmd.sbns) ||\n\t\tcmd.sbns.length === 0 ||\n\t\t!cmd.sbns.every(isUint32)\n\t) {\n\t\tthrow new Error(\"[FecWorker] cleanup: invalid SBNs\");\n\t}\n\tconst sbns = [...new Set(cmd.sbns)].sort((a, b) => a - b);\n\tconst pendingSbns = sbns.filter((sbn) => sbn > retiredSbnFrontier);\n\ttry {\n\t\tif (!wasmDecoder) {\n\t\t\tthrow new Error(\"[FecWorker] cleanup: not configured\");\n\t\t}\n\n\t\t// Call WASM cleanup only after all earlier symbol commands have drained.\n\t\t// The detailed API carries the true owner of every fill emitted by a\n\t\t// single cleanup pass; command coalescing must not infer ownership from\n\t\t// the largest timed-out SBN.\n\t\tconst cleanupRecords = pendingSbns.length > 0\n\t\t\t? wasmDecoder.cleanup_blocks_detailed(Uint32Array.from(pendingSbns))\n\t\t\t: new Uint8Array();\n\t\t// Manager deadlines are block-scoped and may expire a newer SBN while an\n\t\t// older interleaved block is still live. Only accepted media progression\n\t\t// advances the shared JS/Rust horizon; cleanup creates exact tombstones.\n\n\t\t// Rust already finalized every SBN behind the media frontier. For each\n\t\t// remaining exact request, mirror the block-scoped WASM boundary immediately\n\t\t// so JS never retains state for a decoder block that is gone, even if decoding\n\t\t// the trusted cleanup framing were to fail.\n\t\tfor (const sbn of pendingSbns) {\n\t\t\tlet currentBlock = blocks.get(sbn);\n\t\t\tif (currentBlock && isTerminalBlock(currentBlock)) {\n\t\t\t\tretireAcceptedIds(sbn);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tcurrentBlock ??= getOrCreateBlock(sbn);\n\t\t\tif (currentBlock.sourceReceived >= currentBlock.k) {\n\t\t\t\tmarkBlockTerminal(currentBlock, \"complete\");\n\t\t\t\tblocksComplete++;\n\t\t\t} else {\n\t\t\t\tmarkBlockTerminal(currentBlock, \"failed\");\n\t\t\t\tblocksFailed++;\n\t\t\t}\n\t\t\temitBlockUpdate(currentBlock);\n\t\t}\n\n\t\tconst greenFills = decodeCleanupRecords(cleanupRecords);\n\t\tfor (const { sbn, data } of greenFills) {\n\t\t\tgreenFillFrames++;\n\t\t\temit({ type: \"greenFill\", sbn, data }, [data]);\n\t\t}\n\t} finally {\n\t\t// Message ordering makes this an acknowledgement that every source/repair\n\t\t// command posted before cleanup has completed. The main thread must not\n\t\t// commit failure accounting until it observes this event.\n\t\temit({ type: \"cleanupComplete\", sbns });\n\t}\n}\n\nfunction decodeCleanupRecords(\n\tframed: Uint8Array,\n): Array<{ sbn: number; data: ArrayBuffer }> {\n\tconst records: Array<{ sbn: number; data: ArrayBuffer }> = [];\n\tconst view = new DataView(framed.buffer, framed.byteOffset, framed.byteLength);\n\tlet offset = 0;\n\twhile (offset < framed.byteLength) {\n\t\tif (framed.byteLength - offset < 8) {\n\t\t\tthrow new Error(\"[FecWorker] cleanup_blocks_detailed: truncated record header\");\n\t\t}\n\t\tconst sbn = view.getUint32(offset, false);\n\t\tconst length = view.getUint32(offset + 4, false);\n\t\toffset += 8;\n\t\tif (length > framed.byteLength - offset) {\n\t\t\tthrow new Error(\"[FecWorker] cleanup_blocks_detailed: truncated record payload\");\n\t\t}\n\t\tconst data = toOwnedArrayBuffer(framed.subarray(offset, offset + length));\n\t\trecords.push({ sbn, data });\n\t\toffset += length;\n\t}\n\treturn records;\n}\n\nfunction handleDispose(): void {\n\t// Clear snapshot timer (MUST clear to prevent leaks)\n\tif (snapshotTimer !== null) {\n\t\tclearInterval(snapshotTimer);\n\t\tsnapshotTimer = null;\n\t}\n\n\t// Free ALTA verifier WASM object (MUST call to release WASM memory \u2014\n\t// Worker.terminate() fires immediately after dispose, so the FinalizationRegistry\n\t// callback has no chance to run and this memory would leak without explicit free)\n\tif (altaVerifier) {\n\t\ttry {\n\t\t\taltaVerifier.free();\n\t\t} catch (_e) {\n\t\t\t// Best-effort \u2014 verifier may already be freed\n\t\t}\n\t\taltaVerifier = null;\n\t}\n\n\t// Free WASM decoder (MUST call to release WASM memory)\n\tif (wasmDecoder) {\n\t\twasmDecoder.free();\n\t\twasmDecoder = null;\n\t}\n\n\t// Clear deferred-verify queue so pinned ArrayBuffers drop.\n\tpendingVerify.clear();\n\tpendingVerifyCap = 0;\n\tpendingVerifyTtlMs = 0;\n\n\tconfig = null;\n\tclearBlockTracking();\n}\n\nfunction handleReset(): void {\n\ttry {\n\t\twasmDecoder?.flush();\n\t\twasmDecoder?.reset_stats();\n\t} catch (err) {\n\t\temit({\n\t\t\ttype: \"error\",\n\t\t\tcode: \"reset-failed\",\n\t\t\tmessage:\n\t\t\t\terr instanceof Error\n\t\t\t\t\t? `[FecWorker] reset failed: ${err.message}`\n\t\t\t\t\t: `[FecWorker] reset failed: ${String(err)}`,\n\t\t});\n\t\treturn;\n\t}\n\n\tclearBlockTracking();\n\tsourceSymbols = 0;\n\trepairSymbols = 0;\n\tblocksComplete = 0;\n\tblocksRecovered = 0;\n\tblocksFailed = 0;\n\tgreenFillFrames = 0;\n\taltaVerified = 0;\n\taltaFailed = 0;\n\taltaFailedDetails.length = 0;\n\taltaPending = 0;\n\taltaPendingEvictedCap = 0;\n\taltaPendingEvictedTtl = 0;\n\taltaError = 0;\n\trelayBlocksVerified = 0;\n\trelayBlocksFailed = 0;\n\trepairSymbolSizeMismatch = 0;\n\temit({ type: \"resetComplete\" });\n\tif (config) emitSnapshot();\n}\n\n/**\n * A batch of symbols fed in one page task (BLO-36599). Each record is\n * handled exactly as its single-symbol command would be, in order, and a\n * failing record reports the same `command-failed` error that command would\n * without stopping the records after it.\n */\nfunction handleFeedSymbols(\n\tcmd: Extract<FecWorkerCommand, { type: \"feedSymbols\" }>,\n): void {\n\tif (!Array.isArray(cmd.symbols)) {\n\t\tthrow new Error(\"[FecWorker] feedSymbols: symbols must be an array\");\n\t}\n\tfor (const symbol of cmd.symbols) {\n\t\ttry {\n\t\t\tif (symbol?.type === \"feedSource\") handleFeedSource(symbol);\n\t\t\telse if (symbol?.type === \"feedRepair\") handleFeedRepair(symbol);\n\t\t\telse {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`[FecWorker] feedSymbols: unknown symbol type ${String((symbol as { type?: unknown } | null)?.type)}`,\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\temit({\n\t\t\t\ttype: \"error\",\n\t\t\t\tcode: \"command-failed\",\n\t\t\t\tmessage:\n\t\t\t\t\terr instanceof Error ? err.message : `[FecWorker] ${String(err)}`,\n\t\t\t});\n\t\t}\n\t}\n}\n\n// --- Main message handler ---\n\nasync function dispatchCommand(cmd: FecWorkerCommand): Promise<void> {\n\ttry {\n\t\tswitch (cmd.type) {\n\t\t\tcase \"configure\":\n\t\t\t\t// handleConfigure is async (ALTA WASM init uses dynamic import).\n\t\t\t\t// Await it so feed/cleanup messages cannot run against a partially\n\t\t\t\t// configured decoder.\n\t\t\t\tawait handleConfigure(cmd);\n\t\t\t\tbreak;\n\t\t\tcase \"feedSource\":\n\t\t\t\thandleFeedSource(cmd);\n\t\t\t\tbreak;\n\t\t\tcase \"feedRepair\":\n\t\t\t\thandleFeedRepair(cmd);\n\t\t\t\tbreak;\n\t\t\tcase \"feedSymbols\":\n\t\t\t\thandleFeedSymbols(cmd);\n\t\t\t\tbreak;\n\t\t\tcase \"relayBlockSig\":\n\t\t\t\thandleRelayBlockSig(cmd);\n\t\t\t\tbreak;\n\t\t\tcase \"cleanup\":\n\t\t\t\thandleCleanup(cmd);\n\t\t\t\tbreak;\n\t\t\tcase \"reset\":\n\t\t\t\thandleReset();\n\t\t\t\tbreak;\n\t\t\tcase \"subscribeBlockUpdates\":\n\t\t\t\tblockUpdatesSubscribed = true;\n\t\t\t\tbreak;\n\t\t\tcase \"dispose\":\n\t\t\t\thandleDispose();\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\temit({\n\t\t\t\t\ttype: \"error\",\n\t\t\t\t\tcode: \"unknown-command\",\n\t\t\t\t\tmessage: `Unknown command type: ${(cmd as Record<string, unknown>).type}`,\n\t\t\t\t});\n\t\t}\n\t} catch (err) {\n\t\temit({\n\t\t\ttype: \"error\",\n\t\t\tcode: \"command-failed\",\n\t\t\tmessage:\n\t\t\t\terr instanceof Error ? err.message : `[FecWorker] ${String(err)}`,\n\t\t});\n\t}\n}\n\nlet commandQueue: Promise<void> = Promise.resolve();\n\nself.onmessage = (e: MessageEvent<FecWorkerCommand>) => {\n\tconst cmd = e.data;\n\tcommandQueue = commandQueue.then(() => dispatchCommand(cmd));\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blockcast/fec-worker",
|
|
3
|
-
"version": "0.1.0-main.
|
|
3
|
+
"version": "0.1.0-main.ce3c8aeb7f68",
|
|
4
4
|
"description": "Worker-per-track FEC decode with zero-copy ArrayBuffer transfer and ALTA auth plumbing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"test:watch": "vitest"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@blockcast/mmt-alta-parse": "0.1.0-main.
|
|
38
|
+
"@blockcast/mmt-alta-parse": "0.1.0-main.ce3c8aeb7f68"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"esbuild": "^0.28.0",
|
package/src/fec-worker-client.ts
CHANGED
|
@@ -41,6 +41,11 @@ export class FecWorkerClient {
|
|
|
41
41
|
#flushQueued = false;
|
|
42
42
|
// Batching starts only once the Worker announced it accepts feedSymbols.
|
|
43
43
|
#batchSymbols = false;
|
|
44
|
+
// The Worker posts blockUpdate events only to a client that subscribed,
|
|
45
|
+
// which this client does once a callback is registered and the Worker
|
|
46
|
+
// announced the command (BLO-36599).
|
|
47
|
+
#blockUpdatesAnnounced = false;
|
|
48
|
+
#blockUpdatesSubscribed = false;
|
|
44
49
|
|
|
45
50
|
/**
|
|
46
51
|
* Create a FecWorkerClient.
|
|
@@ -245,9 +250,28 @@ export class FecWorkerClient {
|
|
|
245
250
|
this.#onSnapshot = cb;
|
|
246
251
|
}
|
|
247
252
|
|
|
248
|
-
/**
|
|
253
|
+
/**
|
|
254
|
+
* Register callback for individual block state changes. The Worker posts
|
|
255
|
+
* them only once asked, so registering subscribes (after the Worker
|
|
256
|
+
* announces `subscribeBlockUpdates`). An older Worker that does not
|
|
257
|
+
* announce it posts them unasked.
|
|
258
|
+
*/
|
|
249
259
|
onBlockUpdate(cb: (block: FecBlockSnapshot) => void): void {
|
|
250
260
|
this.#onBlockUpdate = cb;
|
|
261
|
+
this.#subscribeBlockUpdates();
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
#subscribeBlockUpdates(): void {
|
|
265
|
+
if (
|
|
266
|
+
this.#disposed ||
|
|
267
|
+
this.#blockUpdatesSubscribed ||
|
|
268
|
+
!this.#blockUpdatesAnnounced ||
|
|
269
|
+
!this.#onBlockUpdate
|
|
270
|
+
) return;
|
|
271
|
+
this.#blockUpdatesSubscribed = true;
|
|
272
|
+
this.#worker.postMessage(
|
|
273
|
+
{ type: "subscribeBlockUpdates" } satisfies FecWorkerCommand,
|
|
274
|
+
);
|
|
251
275
|
}
|
|
252
276
|
|
|
253
277
|
/** Register callback for green-fill frames. */
|
|
@@ -296,6 +320,10 @@ export class FecWorkerClient {
|
|
|
296
320
|
if (Array.isArray(event.commands) && event.commands.includes("feedSymbols")) {
|
|
297
321
|
this.#batchSymbols = true;
|
|
298
322
|
}
|
|
323
|
+
if (Array.isArray(event.commands) && event.commands.includes("subscribeBlockUpdates")) {
|
|
324
|
+
this.#blockUpdatesAnnounced = true;
|
|
325
|
+
this.#subscribeBlockUpdates();
|
|
326
|
+
}
|
|
299
327
|
return;
|
|
300
328
|
}
|
|
301
329
|
if (event.type === "resetComplete") {
|
package/src/fec-worker-types.ts
CHANGED
|
@@ -274,6 +274,13 @@ export type FecWorkerCommand =
|
|
|
274
274
|
sbns: number[];
|
|
275
275
|
}
|
|
276
276
|
| { type: "reset" }
|
|
277
|
+
/**
|
|
278
|
+
* Ask the Worker to post a blockUpdate on every block transition. Without
|
|
279
|
+
* it the Worker posts none: each one is a structured clone and a task on
|
|
280
|
+
* the page thread, and the player does not consume them (BLO-36599).
|
|
281
|
+
* Sent only to a Worker that announced it in `capabilities`.
|
|
282
|
+
*/
|
|
283
|
+
| { type: "subscribeBlockUpdates" }
|
|
277
284
|
| { type: "dispose" };
|
|
278
285
|
|
|
279
286
|
/** One symbol record of a `feedSymbols` batch. */
|
|
@@ -302,4 +309,4 @@ export type FecWorkerEvent =
|
|
|
302
309
|
| { type: "error"; message: string; code?: FecWorkerErrorCode };
|
|
303
310
|
|
|
304
311
|
/** Commands a client may send only after the Worker announced them. */
|
|
305
|
-
export type FecWorkerOptionalCommand = "feedSymbols";
|
|
312
|
+
export type FecWorkerOptionalCommand = "feedSymbols" | "subscribeBlockUpdates";
|
package/src/fec-worker.ts
CHANGED
|
@@ -75,6 +75,8 @@ let altaEnabled = false;
|
|
|
75
75
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
76
76
|
let altaVerifier: any = null; // JsAltaVerifier instance (loaded from ALTA WASM)
|
|
77
77
|
let snapshotTimer: ReturnType<typeof setInterval> | null = null;
|
|
78
|
+
// blockUpdate events are posted only after a client subscribes (BLO-36599).
|
|
79
|
+
let blockUpdatesSubscribed = false;
|
|
78
80
|
|
|
79
81
|
// Block tracking
|
|
80
82
|
const blocks = new Map<number, FecBlockSnapshot>();
|
|
@@ -478,6 +480,7 @@ function emitSnapshot(): void {
|
|
|
478
480
|
}
|
|
479
481
|
|
|
480
482
|
function emitBlockUpdate(block: FecBlockSnapshot): void {
|
|
483
|
+
if (!blockUpdatesSubscribed) return;
|
|
481
484
|
emit({ type: "blockUpdate", block: { ...block } });
|
|
482
485
|
}
|
|
483
486
|
|
|
@@ -668,7 +671,7 @@ async function handleConfigure(
|
|
|
668
671
|
|
|
669
672
|
// Tell the client which optional commands this Worker accepts, so it can
|
|
670
673
|
// batch symbols without risking a Worker that predates the batch command.
|
|
671
|
-
emit({ type: "capabilities", commands: ["feedSymbols"] });
|
|
674
|
+
emit({ type: "capabilities", commands: ["feedSymbols", "subscribeBlockUpdates"] });
|
|
672
675
|
|
|
673
676
|
// Start periodic snapshot emission (200ms floor)
|
|
674
677
|
startSnapshotTimer();
|
|
@@ -1082,6 +1085,9 @@ async function dispatchCommand(cmd: FecWorkerCommand): Promise<void> {
|
|
|
1082
1085
|
case "reset":
|
|
1083
1086
|
handleReset();
|
|
1084
1087
|
break;
|
|
1088
|
+
case "subscribeBlockUpdates":
|
|
1089
|
+
blockUpdatesSubscribed = true;
|
|
1090
|
+
break;
|
|
1085
1091
|
case "dispose":
|
|
1086
1092
|
handleDispose();
|
|
1087
1093
|
break;
|