@blockcast/fec-worker 0.1.0-main.cde7600fad6a → 0.1.0-main.cf0df54ddcf4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -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,13 @@ 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
- | `blockUpdate` | `block: FecBlockSnapshot` | none |
130
- | `greenFill` | `sbn: number`, `data: ArrayBuffer` | `data` |
130
+ | `capabilities` | `commands: FecWorkerOptionalCommand[]` (after `configure`) | none |
131
+ | `blockUpdate` | `block: FecBlockSnapshot` (only after `subscribeBlockUpdates`) | none |
131
132
  | `cleanupComplete` | `sbns: number[]` | none |
132
133
  | `error` | `message: string` | none |
133
134
 
135
+ 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).
136
+
134
137
  `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
138
 
136
139
  ## Lifecycle
@@ -140,7 +143,7 @@ new FecWorkerClient(url)
140
143
  │ worker spawned with { type: "module" }
141
144
  ▼
142
145
  client.configure(config, wasmModule [, altaPublicKey, altaWasmModule])
143
- │ worker initSyncs WASM, instantiates MmtFecDecoder(interleaveDepth, videoFill),
146
+ │ worker initSyncs WASM, instantiates MmtFecDecoder(interleaveDepth),
144
147
  │ immediately configures exact catalog K/T (no decoder defaults),
145
148
  │ derives pendingVerifyCap = ceil(D × 1.5) and TTL = ceil(K × interleaveMs × 1.5)
146
149
  │ from catalog values, throws if any of K / D / interleaveMs ≤ 0,
@@ -154,7 +157,9 @@ client.feedSource(...) / feedRepair(...) ◄── steady state
154
157
  client.cleanup(sbn) ◄── per-block inactivity deadline
155
158
  │ coalesces queued SBNs behind one worker queue-drain barrier
156
159
  │ evicts blocks relative to max(sbns) − D × 3
157
- │ emits green-fill with the true SBN from WASM cleanup_blocks_detailed()
160
+ │ retires exactly those SBNs via WASM cleanup_blocks_detailed(); a lost
161
+ │ block is never answered with a synthesised frame (sinks hold the last
162
+ │ good frame, per @blockcast/mmt-container conceal-timeline)
158
163
  │ acknowledges every SBN in {type:"cleanupComplete", sbns}
159
164
  │ any block still in "collecting" at eviction time is counted as failed
160
165
  ▼
@@ -179,7 +184,6 @@ The `snapshot` event delivers a `FecTrackStats` every 200 ms (catalog-derived; n
179
184
  | `blocksComplete` | Block had all K source symbols without needing repair |
180
185
  | `blocksRecovered` | Block was recovered using ≥1 repair symbol |
181
186
  | `blocksFailed` | Block aged out before reaching K |
182
- | `greenFillFrames` | Frames substituted with green-fill on unrecoverable loss |
183
187
  | `recoveryRate` | `recovered / (recovered + failed)`, 0–100 |
184
188
  | `avgRecoveryMs`, `maxRecoveryMs` | Rolling decode latency (capped at 1000 samples) |
185
189
  | `liveSbn` | Highest source-block number observed |
@@ -64,10 +64,13 @@ 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
- /** Register callback for individual block state changes. */
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
- /** Register callback for green-fill frames. */
70
- onGreenFill(cb: (sbn: number, data: ArrayBuffer) => void): void;
71
74
  /** Register callback fired after the Worker drains all commands queued before cleanup. */
72
75
  onCleanupComplete(cb: (sbn: number) => void): void;
73
76
  /** Register callback for Worker errors. */
@@ -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;;IAuB3B;;;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,4DAA4D;IAC5D,aAAa,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG,IAAI;IAI1D,+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;CA4Df"}
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;;IA2B3B;;;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,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;CA6Df"}
@@ -12,7 +12,6 @@ export class FecWorkerClient {
12
12
  #onFrame = null;
13
13
  #onSnapshot = null;
14
14
  #onBlockUpdate = null;
15
- #onGreenFill = null;
16
15
  #onCleanupComplete = null;
17
16
  #onError = null;
18
17
  #cleanupInFlight = null;
@@ -29,6 +28,11 @@ export class FecWorkerClient {
29
28
  #flushQueued = false;
30
29
  // Batching starts only once the Worker announced it accepts feedSymbols.
31
30
  #batchSymbols = false;
31
+ // The Worker posts blockUpdate events only to a client that subscribed,
32
+ // which this client does once a callback is registered and the Worker
33
+ // announced the command (BLO-36599).
34
+ #blockUpdatesAnnounced = false;
35
+ #blockUpdatesSubscribed = false;
32
36
  /**
33
37
  * Create a FecWorkerClient.
34
38
  * @param workerOrUrl - Either a Worker instance (for testing) or a URL to create one
@@ -201,13 +205,24 @@ export class FecWorkerClient {
201
205
  onSnapshot(cb) {
202
206
  this.#onSnapshot = cb;
203
207
  }
204
- /** Register callback for individual block state changes. */
208
+ /**
209
+ * Register callback for individual block state changes. The Worker posts
210
+ * them only once asked, so registering subscribes (after the Worker
211
+ * announces `subscribeBlockUpdates`). An older Worker that does not
212
+ * announce it posts them unasked.
213
+ */
205
214
  onBlockUpdate(cb) {
206
215
  this.#onBlockUpdate = cb;
216
+ this.#subscribeBlockUpdates();
207
217
  }
208
- /** Register callback for green-fill frames. */
209
- onGreenFill(cb) {
210
- this.#onGreenFill = cb;
218
+ #subscribeBlockUpdates() {
219
+ if (this.#disposed ||
220
+ this.#blockUpdatesSubscribed ||
221
+ !this.#blockUpdatesAnnounced ||
222
+ !this.#onBlockUpdate)
223
+ return;
224
+ this.#blockUpdatesSubscribed = true;
225
+ this.#worker.postMessage({ type: "subscribeBlockUpdates" });
211
226
  }
212
227
  /** Register callback fired after the Worker drains all commands queued before cleanup. */
213
228
  onCleanupComplete(cb) {
@@ -247,6 +262,10 @@ export class FecWorkerClient {
247
262
  if (Array.isArray(event.commands) && event.commands.includes("feedSymbols")) {
248
263
  this.#batchSymbols = true;
249
264
  }
265
+ if (Array.isArray(event.commands) && event.commands.includes("subscribeBlockUpdates")) {
266
+ this.#blockUpdatesAnnounced = true;
267
+ this.#subscribeBlockUpdates();
268
+ }
250
269
  return;
251
270
  }
252
271
  if (event.type === "resetComplete") {
@@ -277,9 +296,6 @@ export class FecWorkerClient {
277
296
  case "blockUpdate":
278
297
  this.#onBlockUpdate?.(event.block);
279
298
  break;
280
- case "greenFill":
281
- this.#onGreenFill?.(event.sbn, event.data);
282
- break;
283
299
  case "cleanupComplete":
284
300
  for (const sbn of event.sbns) {
285
301
  this.#onCleanupComplete?.(sbn);
@@ -1 +1 @@
1
- {"version":3,"file":"fec-worker-client.js","sourceRoot":"","sources":["../src/fec-worker-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fec-worker-client.js","sourceRoot":"","sources":["../src/fec-worker-client.ts"],"names":[],"mappings":"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"}
@@ -52,7 +52,7 @@ export interface FecBlockSnapshot {
52
52
  /** Source block number */
53
53
  sbn: number;
54
54
  /** Current block state */
55
- state: "collecting" | "complete" | "recovered" | "failed" | "green-fill";
55
+ state: "collecting" | "complete" | "recovered" | "failed";
56
56
  /** Number of source symbols received */
57
57
  sourceReceived: number;
58
58
  /** Number of repair symbols received */
@@ -90,8 +90,6 @@ export interface FecTrackStats {
90
90
  blocksRecovered: number;
91
91
  /** Blocks that failed (unrecoverable) */
92
92
  blocksFailed: number;
93
- /** Frames replaced with green-fill */
94
- greenFillFrames: number;
95
93
  /** Recovery success rate 0-100 */
96
94
  recoveryRate: number;
97
95
  /** Packets with valid publisher ALTA signature */
@@ -239,6 +237,15 @@ export type FecWorkerCommand = {
239
237
  sbns: number[];
240
238
  } | {
241
239
  type: "reset";
240
+ }
241
+ /**
242
+ * Ask the Worker to post a blockUpdate on every block transition. Without
243
+ * it the Worker posts none: each one is a structured clone and a task on
244
+ * the page thread, and the player does not consume them (BLO-36599).
245
+ * Sent only to a Worker that announced it in `capabilities`.
246
+ */
247
+ | {
248
+ type: "subscribeBlockUpdates";
242
249
  } | {
243
250
  type: "dispose";
244
251
  };
@@ -259,10 +266,6 @@ export type FecWorkerEvent = {
259
266
  } | {
260
267
  type: "blockUpdate";
261
268
  block: FecBlockSnapshot;
262
- } | {
263
- type: "greenFill";
264
- sbn: number;
265
- data: ArrayBuffer;
266
269
  } | {
267
270
  type: "cleanupComplete";
268
271
  sbns: number[];
@@ -284,5 +287,5 @@ export type FecWorkerEvent = {
284
287
  code?: FecWorkerErrorCode;
285
288
  };
286
289
  /** Commands a client may send only after the Worker announced them. */
287
- export type FecWorkerOptionalCommand = "feedSymbols";
290
+ export type FecWorkerOptionalCommand = "feedSymbols" | "subscribeBlockUpdates";
288
291
  //# 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":"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"}
1
+ {"version":3,"file":"fec-worker-types.d.ts","sourceRoot":"","sources":["../src/fec-worker-types.ts"],"names":[],"mappings":"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"}
@@ -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();
@@ -26,7 +27,6 @@ var repairSymbols = 0;
26
27
  var blocksComplete = 0;
27
28
  var blocksRecovered = 0;
28
29
  var blocksFailed = 0;
29
- var greenFillFrames = 0;
30
30
  var altaVerified = 0;
31
31
  var altaFailed = 0;
32
32
  var altaFailedDetails = [];
@@ -147,7 +147,6 @@ function buildStatsSnapshot() {
147
147
  blocksComplete,
148
148
  blocksRecovered,
149
149
  blocksFailed,
150
- greenFillFrames,
151
150
  recoveryRate: blocksComplete + blocksRecovered > 0 ? Math.round(
152
151
  blocksRecovered / (blocksRecovered + blocksFailed) * 100
153
152
  ) || 0 : 0,
@@ -176,6 +175,7 @@ function emitSnapshot() {
176
175
  emit({ type: "snapshot", stats: buildStatsSnapshot() });
177
176
  }
178
177
  function emitBlockUpdate(block) {
178
+ if (!blockUpdatesSubscribed) return;
179
179
  emit({ type: "blockUpdate", block: { ...block } });
180
180
  }
181
181
  function emitRecoveredSymbols(sbn, recoveredBlock, block) {
@@ -263,10 +263,7 @@ async function handleConfigure(cmd) {
263
263
  wasmDecoder?.free();
264
264
  wasmDecoder = null;
265
265
  clearBlockTracking();
266
- wasmDecoder = new wasm.MmtFecDecoder(
267
- cmd.config.interleaveDepth,
268
- cmd.config.trackType === "video"
269
- );
266
+ wasmDecoder = new wasm.MmtFecDecoder(cmd.config.interleaveDepth);
270
267
  config = cmd.config;
271
268
  altaEnabled = !!cmd.altaPublicKey;
272
269
  pendingVerifyCap = Math.ceil(config.interleaveDepth * 1.5);
@@ -291,7 +288,7 @@ async function handleConfigure(cmd) {
291
288
  }
292
289
  }
293
290
  wasmDecoder.configure_params(config.k, config.symbolSize);
294
- emit({ type: "capabilities", commands: ["feedSymbols"] });
291
+ emit({ type: "capabilities", commands: ["feedSymbols", "subscribeBlockUpdates"] });
295
292
  startSnapshotTimer();
296
293
  emitSnapshot();
297
294
  }
@@ -435,7 +432,9 @@ function handleCleanup(cmd) {
435
432
  if (!wasmDecoder) {
436
433
  throw new Error("[FecWorker] cleanup: not configured");
437
434
  }
438
- const cleanupRecords = pendingSbns.length > 0 ? wasmDecoder.cleanup_blocks_detailed(Uint32Array.from(pendingSbns)) : new Uint8Array();
435
+ if (pendingSbns.length > 0) {
436
+ wasmDecoder.cleanup_blocks_detailed(Uint32Array.from(pendingSbns));
437
+ }
439
438
  for (const sbn of pendingSbns) {
440
439
  let currentBlock = blocks.get(sbn);
441
440
  if (currentBlock && isTerminalBlock(currentBlock)) {
@@ -452,35 +451,10 @@ function handleCleanup(cmd) {
452
451
  }
453
452
  emitBlockUpdate(currentBlock);
454
453
  }
455
- const greenFills = decodeCleanupRecords(cleanupRecords);
456
- for (const { sbn, data } of greenFills) {
457
- greenFillFrames++;
458
- emit({ type: "greenFill", sbn, data }, [data]);
459
- }
460
454
  } finally {
461
455
  emit({ type: "cleanupComplete", sbns });
462
456
  }
463
457
  }
464
- function decodeCleanupRecords(framed) {
465
- const records = [];
466
- const view = new DataView(framed.buffer, framed.byteOffset, framed.byteLength);
467
- let offset = 0;
468
- while (offset < framed.byteLength) {
469
- if (framed.byteLength - offset < 8) {
470
- throw new Error("[FecWorker] cleanup_blocks_detailed: truncated record header");
471
- }
472
- const sbn = view.getUint32(offset, false);
473
- const length = view.getUint32(offset + 4, false);
474
- offset += 8;
475
- if (length > framed.byteLength - offset) {
476
- throw new Error("[FecWorker] cleanup_blocks_detailed: truncated record payload");
477
- }
478
- const data = toOwnedArrayBuffer(framed.subarray(offset, offset + length));
479
- records.push({ sbn, data });
480
- offset += length;
481
- }
482
- return records;
483
- }
484
458
  function handleDispose() {
485
459
  if (snapshotTimer !== null) {
486
460
  clearInterval(snapshotTimer);
@@ -521,7 +495,6 @@ function handleReset() {
521
495
  blocksComplete = 0;
522
496
  blocksRecovered = 0;
523
497
  blocksFailed = 0;
524
- greenFillFrames = 0;
525
498
  altaVerified = 0;
526
499
  altaFailed = 0;
527
500
  altaFailedDetails.length = 0;
@@ -581,6 +554,9 @@ async function dispatchCommand(cmd) {
581
554
  case "reset":
582
555
  handleReset();
583
556
  break;
557
+ case "subscribeBlockUpdates":
558
+ blockUpdatesSubscribed = true;
559
+ break;
584
560
  case "dispose":
585
561
  handleDispose();
586
562
  break;
@@ -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/**\n\t * Retire the named blocks (last-chance decode + stats), driven by the\n\t * manager's exact deadlines. Nothing is synthesised for a lost block:\n\t * concealment is the sink's hold-last-good-frame.\n\t */\n\tcleanup_blocks_detailed(sbns: Uint32Array): void;\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 (interleaveDepth: number) => 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 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\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(cmd.config.interleaveDepth);\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// and only for the exact SBNs the manager's deadlines named; command\n\t\t// coalescing must not infer ownership from the largest timed-out SBN.\n\t\tif (pendingSbns.length > 0) {\n\t\t\twasmDecoder.cleanup_blocks_detailed(Uint32Array.from(pendingSbns));\n\t\t}\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.\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\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 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\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.cde7600fad6a",
3
+ "version": "0.1.0-main.cf0df54ddcf4",
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.cde7600fad6a"
38
+ "@blockcast/mmt-alta-parse": "0.1.0-main.cf0df54ddcf4"
39
39
  },
40
40
  "devDependencies": {
41
41
  "esbuild": "^0.28.0",
@@ -24,7 +24,6 @@ export class FecWorkerClient {
24
24
  #onFrame: ((data: ArrayBuffer, meta: FrameMeta) => void) | null = null;
25
25
  #onSnapshot: ((stats: FecTrackStats) => void) | null = null;
26
26
  #onBlockUpdate: ((block: FecBlockSnapshot) => void) | null = null;
27
- #onGreenFill: ((sbn: number, data: ArrayBuffer) => void) | null = null;
28
27
  #onCleanupComplete: ((sbn: number) => void) | null = null;
29
28
  #onError: ((message: string, code?: FecWorkerErrorCode) => void) | null = null;
30
29
  #cleanupInFlight: Set<number> | null = null;
@@ -41,6 +40,11 @@ export class FecWorkerClient {
41
40
  #flushQueued = false;
42
41
  // Batching starts only once the Worker announced it accepts feedSymbols.
43
42
  #batchSymbols = false;
43
+ // The Worker posts blockUpdate events only to a client that subscribed,
44
+ // which this client does once a callback is registered and the Worker
45
+ // announced the command (BLO-36599).
46
+ #blockUpdatesAnnounced = false;
47
+ #blockUpdatesSubscribed = false;
44
48
 
45
49
  /**
46
50
  * Create a FecWorkerClient.
@@ -245,14 +249,28 @@ export class FecWorkerClient {
245
249
  this.#onSnapshot = cb;
246
250
  }
247
251
 
248
- /** Register callback for individual block state changes. */
252
+ /**
253
+ * Register callback for individual block state changes. The Worker posts
254
+ * them only once asked, so registering subscribes (after the Worker
255
+ * announces `subscribeBlockUpdates`). An older Worker that does not
256
+ * announce it posts them unasked.
257
+ */
249
258
  onBlockUpdate(cb: (block: FecBlockSnapshot) => void): void {
250
259
  this.#onBlockUpdate = cb;
260
+ this.#subscribeBlockUpdates();
251
261
  }
252
262
 
253
- /** Register callback for green-fill frames. */
254
- onGreenFill(cb: (sbn: number, data: ArrayBuffer) => void): void {
255
- this.#onGreenFill = cb;
263
+ #subscribeBlockUpdates(): void {
264
+ if (
265
+ this.#disposed ||
266
+ this.#blockUpdatesSubscribed ||
267
+ !this.#blockUpdatesAnnounced ||
268
+ !this.#onBlockUpdate
269
+ ) return;
270
+ this.#blockUpdatesSubscribed = true;
271
+ this.#worker.postMessage(
272
+ { type: "subscribeBlockUpdates" } satisfies FecWorkerCommand,
273
+ );
256
274
  }
257
275
 
258
276
  /** Register callback fired after the Worker drains all commands queued before cleanup. */
@@ -296,6 +314,10 @@ export class FecWorkerClient {
296
314
  if (Array.isArray(event.commands) && event.commands.includes("feedSymbols")) {
297
315
  this.#batchSymbols = true;
298
316
  }
317
+ if (Array.isArray(event.commands) && event.commands.includes("subscribeBlockUpdates")) {
318
+ this.#blockUpdatesAnnounced = true;
319
+ this.#subscribeBlockUpdates();
320
+ }
299
321
  return;
300
322
  }
301
323
  if (event.type === "resetComplete") {
@@ -323,9 +345,6 @@ export class FecWorkerClient {
323
345
  case "blockUpdate":
324
346
  this.#onBlockUpdate?.(event.block);
325
347
  break;
326
- case "greenFill":
327
- this.#onGreenFill?.(event.sbn, event.data);
328
- break;
329
348
  case "cleanupComplete":
330
349
  for (const sbn of event.sbns) {
331
350
  this.#onCleanupComplete?.(sbn);
@@ -57,7 +57,7 @@ export interface FecBlockSnapshot {
57
57
  /** Source block number */
58
58
  sbn: number;
59
59
  /** Current block state */
60
- state: "collecting" | "complete" | "recovered" | "failed" | "green-fill";
60
+ state: "collecting" | "complete" | "recovered" | "failed";
61
61
  /** Number of source symbols received */
62
62
  sourceReceived: number;
63
63
  /** Number of repair symbols received */
@@ -99,8 +99,6 @@ export interface FecTrackStats {
99
99
  blocksRecovered: number;
100
100
  /** Blocks that failed (unrecoverable) */
101
101
  blocksFailed: number;
102
- /** Frames replaced with green-fill */
103
- greenFillFrames: number;
104
102
  /** Recovery success rate 0-100 */
105
103
  recoveryRate: number;
106
104
 
@@ -274,6 +272,13 @@ export type FecWorkerCommand =
274
272
  sbns: number[];
275
273
  }
276
274
  | { type: "reset" }
275
+ /**
276
+ * Ask the Worker to post a blockUpdate on every block transition. Without
277
+ * it the Worker posts none: each one is a structured clone and a task on
278
+ * the page thread, and the player does not consume them (BLO-36599).
279
+ * Sent only to a Worker that announced it in `capabilities`.
280
+ */
281
+ | { type: "subscribeBlockUpdates" }
277
282
  | { type: "dispose" };
278
283
 
279
284
  /** One symbol record of a `feedSymbols` batch. */
@@ -289,7 +294,6 @@ export type FecWorkerEvent =
289
294
  | { type: "frame"; data: ArrayBuffer; meta: FrameMeta }
290
295
  | { type: "snapshot"; stats: FecTrackStats }
291
296
  | { type: "blockUpdate"; block: FecBlockSnapshot }
292
- | { type: "greenFill"; sbn: number; data: ArrayBuffer }
293
297
  | { type: "cleanupComplete"; sbns: number[] }
294
298
  | { type: "resetComplete" }
295
299
  /**
@@ -302,4 +306,4 @@ export type FecWorkerEvent =
302
306
  | { type: "error"; message: string; code?: FecWorkerErrorCode };
303
307
 
304
308
  /** Commands a client may send only after the Worker announced them. */
305
- export type FecWorkerOptionalCommand = "feedSymbols";
309
+ export type FecWorkerOptionalCommand = "feedSymbols" | "subscribeBlockUpdates";
package/src/fec-worker.ts CHANGED
@@ -40,8 +40,12 @@ interface WasmFecDecoder {
40
40
  timestamp: number,
41
41
  ): Uint8Array | undefined;
42
42
  configure_params(k: number, symbolSize: number): void;
43
- /** Block-scoped counterpart driven by the manager's exact deadlines. */
44
- cleanup_blocks_detailed(sbns: Uint32Array): Uint8Array;
43
+ /**
44
+ * Retire the named blocks (last-chance decode + stats), driven by the
45
+ * manager's exact deadlines. Nothing is synthesised for a lost block:
46
+ * concealment is the sink's hold-last-good-frame.
47
+ */
48
+ cleanup_blocks_detailed(sbns: Uint32Array): void;
45
49
  /** Remove decoder blocks below its configured live-SBN interleave horizon. */
46
50
  cleanup_by_sbn(current_sbn: number): Uint32Array;
47
51
  flush(): void;
@@ -61,10 +65,7 @@ interface WasmFecDecoder {
61
65
 
62
66
  interface WasmBindings {
63
67
  initSync(module: { module: WebAssembly.Module }): unknown;
64
- MmtFecDecoder: new (
65
- interleaveDepth: number,
66
- greenFillEnabled: boolean,
67
- ) => WasmFecDecoder;
68
+ MmtFecDecoder: new (interleaveDepth: number) => WasmFecDecoder;
68
69
  }
69
70
 
70
71
  // --- Worker state (local to this Worker instance) ---
@@ -75,6 +76,8 @@ let altaEnabled = false;
75
76
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
76
77
  let altaVerifier: any = null; // JsAltaVerifier instance (loaded from ALTA WASM)
77
78
  let snapshotTimer: ReturnType<typeof setInterval> | null = null;
79
+ // blockUpdate events are posted only after a client subscribes (BLO-36599).
80
+ let blockUpdatesSubscribed = false;
78
81
 
79
82
  // Block tracking
80
83
  const blocks = new Map<number, FecBlockSnapshot>();
@@ -95,7 +98,6 @@ let repairSymbols = 0;
95
98
  let blocksComplete = 0;
96
99
  let blocksRecovered = 0;
97
100
  let blocksFailed = 0;
98
- let greenFillFrames = 0;
99
101
  let altaVerified = 0;
100
102
  let altaFailed = 0; // Crypto-confirmed tamper (MAC / sig / hash chain mismatch)
101
103
  const altaFailedDetails: Array<{
@@ -444,7 +446,6 @@ function buildStatsSnapshot(): FecTrackStats {
444
446
  blocksComplete,
445
447
  blocksRecovered,
446
448
  blocksFailed,
447
- greenFillFrames,
448
449
  recoveryRate:
449
450
  blocksComplete + blocksRecovered > 0
450
451
  ? Math.round(
@@ -478,6 +479,7 @@ function emitSnapshot(): void {
478
479
  }
479
480
 
480
481
  function emitBlockUpdate(block: FecBlockSnapshot): void {
482
+ if (!blockUpdatesSubscribed) return;
481
483
  emit({ type: "blockUpdate", block: { ...block } });
482
484
  }
483
485
 
@@ -615,10 +617,7 @@ async function handleConfigure(
615
617
 
616
618
  // Decoder policy and geometry are explicit at the epoch boundary. The WASM
617
619
  // module has no guessed K/T/interleave slow path.
618
- wasmDecoder = new wasm.MmtFecDecoder(
619
- cmd.config.interleaveDepth,
620
- cmd.config.trackType === "video",
621
- );
620
+ wasmDecoder = new wasm.MmtFecDecoder(cmd.config.interleaveDepth);
622
621
  config = cmd.config;
623
622
  altaEnabled = !!cmd.altaPublicKey;
624
623
 
@@ -668,7 +667,7 @@ async function handleConfigure(
668
667
 
669
668
  // Tell the client which optional commands this Worker accepts, so it can
670
669
  // batch symbols without risking a Worker that predates the batch command.
671
- emit({ type: "capabilities", commands: ["feedSymbols"] });
670
+ emit({ type: "capabilities", commands: ["feedSymbols", "subscribeBlockUpdates"] });
672
671
 
673
672
  // Start periodic snapshot emission (200ms floor)
674
673
  startSnapshotTimer();
@@ -882,21 +881,19 @@ function handleCleanup(
882
881
  throw new Error("[FecWorker] cleanup: not configured");
883
882
  }
884
883
 
885
- // Call WASM cleanup only after all earlier symbol commands have drained.
886
- // The detailed API carries the true owner of every fill emitted by a
887
- // single cleanup pass; command coalescing must not infer ownership from
888
- // the largest timed-out SBN.
889
- const cleanupRecords = pendingSbns.length > 0
890
- ? wasmDecoder.cleanup_blocks_detailed(Uint32Array.from(pendingSbns))
891
- : new Uint8Array();
884
+ // Call WASM cleanup only after all earlier symbol commands have drained,
885
+ // and only for the exact SBNs the manager's deadlines named; command
886
+ // coalescing must not infer ownership from the largest timed-out SBN.
887
+ if (pendingSbns.length > 0) {
888
+ wasmDecoder.cleanup_blocks_detailed(Uint32Array.from(pendingSbns));
889
+ }
892
890
  // Manager deadlines are block-scoped and may expire a newer SBN while an
893
891
  // older interleaved block is still live. Only accepted media progression
894
892
  // advances the shared JS/Rust horizon; cleanup creates exact tombstones.
895
893
 
896
894
  // Rust already finalized every SBN behind the media frontier. For each
897
895
  // remaining exact request, mirror the block-scoped WASM boundary immediately
898
- // so JS never retains state for a decoder block that is gone, even if decoding
899
- // the trusted cleanup framing were to fail.
896
+ // so JS never retains state for a decoder block that is gone.
900
897
  for (const sbn of pendingSbns) {
901
898
  let currentBlock = blocks.get(sbn);
902
899
  if (currentBlock && isTerminalBlock(currentBlock)) {
@@ -913,12 +910,6 @@ function handleCleanup(
913
910
  }
914
911
  emitBlockUpdate(currentBlock);
915
912
  }
916
-
917
- const greenFills = decodeCleanupRecords(cleanupRecords);
918
- for (const { sbn, data } of greenFills) {
919
- greenFillFrames++;
920
- emit({ type: "greenFill", sbn, data }, [data]);
921
- }
922
913
  } finally {
923
914
  // Message ordering makes this an acknowledgement that every source/repair
924
915
  // command posted before cleanup has completed. The main thread must not
@@ -927,29 +918,6 @@ function handleCleanup(
927
918
  }
928
919
  }
929
920
 
930
- function decodeCleanupRecords(
931
- framed: Uint8Array,
932
- ): Array<{ sbn: number; data: ArrayBuffer }> {
933
- const records: Array<{ sbn: number; data: ArrayBuffer }> = [];
934
- const view = new DataView(framed.buffer, framed.byteOffset, framed.byteLength);
935
- let offset = 0;
936
- while (offset < framed.byteLength) {
937
- if (framed.byteLength - offset < 8) {
938
- throw new Error("[FecWorker] cleanup_blocks_detailed: truncated record header");
939
- }
940
- const sbn = view.getUint32(offset, false);
941
- const length = view.getUint32(offset + 4, false);
942
- offset += 8;
943
- if (length > framed.byteLength - offset) {
944
- throw new Error("[FecWorker] cleanup_blocks_detailed: truncated record payload");
945
- }
946
- const data = toOwnedArrayBuffer(framed.subarray(offset, offset + length));
947
- records.push({ sbn, data });
948
- offset += length;
949
- }
950
- return records;
951
- }
952
-
953
921
  function handleDispose(): void {
954
922
  // Clear snapshot timer (MUST clear to prevent leaks)
955
923
  if (snapshotTimer !== null) {
@@ -1006,7 +974,6 @@ function handleReset(): void {
1006
974
  blocksComplete = 0;
1007
975
  blocksRecovered = 0;
1008
976
  blocksFailed = 0;
1009
- greenFillFrames = 0;
1010
977
  altaVerified = 0;
1011
978
  altaFailed = 0;
1012
979
  altaFailedDetails.length = 0;
@@ -1082,6 +1049,9 @@ async function dispatchCommand(cmd: FecWorkerCommand): Promise<void> {
1082
1049
  case "reset":
1083
1050
  handleReset();
1084
1051
  break;
1052
+ case "subscribeBlockUpdates":
1053
+ blockUpdatesSubscribed = true;
1054
+ break;
1085
1055
  case "dispose":
1086
1056
  handleDispose();
1087
1057
  break;