@blockcast/mmt-container 0.1.0 → 0.3.0-main.d9354616fdf2

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.
Files changed (799) hide show
  1. package/README.md +524 -0
  2. package/dist/.build-stamp +0 -0
  3. package/dist/__tests__/test-utils.d.ts +71 -0
  4. package/dist/__tests__/test-utils.d.ts.map +1 -0
  5. package/dist/align.d.ts +152 -0
  6. package/dist/align.d.ts.map +1 -0
  7. package/dist/align.js +88 -0
  8. package/dist/align.js.map +7 -0
  9. package/dist/audio-init-parser.d.ts +66 -0
  10. package/dist/audio-init-parser.d.ts.map +1 -0
  11. package/dist/audio-init-parser.js +118 -0
  12. package/dist/audio-init-parser.js.map +7 -0
  13. package/dist/audio-ring-buffer.d.ts +50 -0
  14. package/dist/audio-ring-buffer.d.ts.map +1 -0
  15. package/dist/audio-ring-buffer.js +80 -0
  16. package/dist/audio-ring-buffer.js.map +7 -0
  17. package/dist/authored-timing.d.ts +32 -0
  18. package/dist/authored-timing.d.ts.map +1 -0
  19. package/dist/authored-timing.js +32 -0
  20. package/dist/authored-timing.js.map +7 -0
  21. package/dist/block-policy.d.ts +314 -0
  22. package/dist/block-policy.d.ts.map +1 -0
  23. package/dist/block-policy.js +466 -0
  24. package/dist/block-policy.js.map +7 -0
  25. package/dist/browser-runtime.js +26549 -0
  26. package/dist/browser-runtime.js.map +7 -0
  27. package/dist/carp-catalog.d.ts +429 -0
  28. package/dist/carp-catalog.d.ts.map +1 -0
  29. package/dist/carp-catalog.js +985 -0
  30. package/dist/carp-catalog.js.map +7 -0
  31. package/dist/catalog-fec-params.d.ts +275 -0
  32. package/dist/catalog-fec-params.d.ts.map +1 -0
  33. package/dist/catalog-fec-params.js +702 -0
  34. package/dist/catalog-fec-params.js.map +7 -0
  35. package/dist/catalog-network-source.d.ts +42 -0
  36. package/dist/catalog-network-source.d.ts.map +1 -0
  37. package/dist/catalog-network-source.js +47 -0
  38. package/dist/catalog-network-source.js.map +7 -0
  39. package/dist/catalog-validator.d.ts +18 -0
  40. package/dist/catalog-validator.d.ts.map +1 -0
  41. package/dist/catalog-validator.js +7747 -0
  42. package/dist/catalog-validator.js.map +7 -0
  43. package/dist/cmaf-block-assembler.d.ts +285 -0
  44. package/dist/cmaf-block-assembler.d.ts.map +1 -0
  45. package/dist/cmaf-block-assembler.js +1966 -0
  46. package/dist/cmaf-block-assembler.js.map +7 -0
  47. package/dist/cmaf-group-batcher.d.ts +38 -0
  48. package/dist/cmaf-group-batcher.d.ts.map +1 -0
  49. package/dist/cmaf-group-batcher.js +176 -0
  50. package/dist/cmaf-group-batcher.js.map +7 -0
  51. package/dist/cmcd-producer.d.ts +104 -0
  52. package/dist/cmcd-producer.d.ts.map +1 -0
  53. package/dist/cmcd-producer.js +183 -0
  54. package/dist/cmcd-producer.js.map +7 -0
  55. package/dist/codec-utils.d.ts +384 -0
  56. package/dist/codec-utils.d.ts.map +1 -0
  57. package/dist/codec-utils.js +1546 -0
  58. package/dist/codec-utils.js.map +7 -0
  59. package/dist/conceal-timeline.d.ts +146 -0
  60. package/dist/conceal-timeline.d.ts.map +1 -0
  61. package/dist/conceal-timeline.js +137 -0
  62. package/dist/conceal-timeline.js.map +7 -0
  63. package/dist/delivery-gap.d.ts +75 -0
  64. package/dist/delivery-gap.d.ts.map +1 -0
  65. package/dist/delivery-gap.js +88 -0
  66. package/dist/delivery-gap.js.map +7 -0
  67. package/dist/duplicate-ledger.d.ts +197 -0
  68. package/dist/duplicate-ledger.d.ts.map +1 -0
  69. package/dist/duplicate-ledger.js +226 -0
  70. package/dist/duplicate-ledger.js.map +7 -0
  71. package/dist/fec-codec.d.ts +32 -0
  72. package/dist/fec-codec.d.ts.map +1 -0
  73. package/dist/fec-codec.js +47 -0
  74. package/dist/fec-codec.js.map +7 -0
  75. package/dist/fec-manager.d.ts +583 -0
  76. package/dist/fec-manager.d.ts.map +1 -0
  77. package/dist/fec-manager.js +3899 -0
  78. package/dist/fec-manager.js.map +7 -0
  79. package/dist/fec-starvation-detector.d.ts +59 -0
  80. package/dist/fec-starvation-detector.d.ts.map +1 -0
  81. package/dist/fec-starvation-detector.js +60 -0
  82. package/dist/fec-starvation-detector.js.map +7 -0
  83. package/dist/fec-state.d.ts +131 -0
  84. package/dist/fec-state.d.ts.map +1 -0
  85. package/dist/fec-state.js +87 -0
  86. package/dist/fec-state.js.map +7 -0
  87. package/dist/fec-worker-setup.d.ts +94 -0
  88. package/dist/fec-worker-setup.d.ts.map +1 -0
  89. package/dist/fec-worker-setup.js +673 -0
  90. package/dist/fec-worker-setup.js.map +7 -0
  91. package/dist/fmp4-completion.d.ts +30 -0
  92. package/dist/fmp4-completion.d.ts.map +1 -0
  93. package/dist/fmp4-completion.js +21 -0
  94. package/dist/fmp4-completion.js.map +7 -0
  95. package/dist/fmp4-reader.d.ts +37 -0
  96. package/dist/fmp4-reader.d.ts.map +1 -0
  97. package/dist/fmp4-reader.js +100 -0
  98. package/dist/fmp4-reader.js.map +7 -0
  99. package/dist/index.d.ts +44 -3
  100. package/dist/index.d.ts.map +1 -1
  101. package/dist/index.js +460 -14
  102. package/dist/index.js.map +7 -1
  103. package/dist/iso-block-locator.d.ts +118 -0
  104. package/dist/iso-block-locator.d.ts.map +1 -0
  105. package/dist/iso-block-locator.js +289 -0
  106. package/dist/iso-block-locator.js.map +7 -0
  107. package/dist/iso-bmff-timing.d.ts +78 -0
  108. package/dist/iso-bmff-timing.d.ts.map +1 -0
  109. package/dist/iso-bmff-timing.js +593 -0
  110. package/dist/iso-bmff-timing.js.map +7 -0
  111. package/dist/loc.d.ts +70 -75
  112. package/dist/loc.d.ts.map +1 -1
  113. package/dist/loc.js +106 -192
  114. package/dist/loc.js.map +7 -1
  115. package/dist/manifest-auth-plan.d.ts +30 -0
  116. package/dist/manifest-auth-plan.d.ts.map +1 -0
  117. package/dist/manifest-auth-plan.js +1266 -0
  118. package/dist/manifest-auth-plan.js.map +7 -0
  119. package/dist/media-router.d.ts +262 -0
  120. package/dist/media-router.d.ts.map +1 -0
  121. package/dist/media-router.js +584 -0
  122. package/dist/media-router.js.map +7 -0
  123. package/dist/mfu-reassembler.d.ts +313 -9
  124. package/dist/mfu-reassembler.d.ts.map +1 -1
  125. package/dist/mfu-reassembler.js +3051 -285
  126. package/dist/mfu-reassembler.js.map +7 -1
  127. package/dist/mfu-reassembly-policy.d.ts +24 -0
  128. package/dist/mfu-reassembly-policy.d.ts.map +1 -0
  129. package/dist/mfu-reassembly-policy.js +99 -0
  130. package/dist/mfu-reassembly-policy.js.map +7 -0
  131. package/dist/mmtp-container-parser.d.ts +568 -0
  132. package/dist/mmtp-container-parser.d.ts.map +1 -0
  133. package/dist/mmtp-container-parser.js +6580 -0
  134. package/dist/mmtp-container-parser.js.map +7 -0
  135. package/dist/mmtp-container-parser.no-modules.js +9616 -0
  136. package/dist/mmtp-container-parser.no-modules.js.map +7 -0
  137. package/dist/mmtp-decode-pipeline.d.ts +138 -0
  138. package/dist/mmtp-decode-pipeline.d.ts.map +1 -0
  139. package/dist/mmtp-decode-pipeline.js +6814 -0
  140. package/dist/mmtp-decode-pipeline.js.map +7 -0
  141. package/dist/mmtp-router.d.ts +679 -0
  142. package/dist/mmtp-router.d.ts.map +1 -0
  143. package/dist/mmtp-router.js +11278 -0
  144. package/dist/mmtp-router.js.map +7 -0
  145. package/dist/mmtp-to-cmaf.d.ts +187 -0
  146. package/dist/mmtp-to-cmaf.d.ts.map +1 -0
  147. package/dist/mmtp-to-cmaf.js +716 -0
  148. package/dist/mmtp-to-cmaf.js.map +7 -0
  149. package/dist/multicast-auth-catalog.d.ts +115 -0
  150. package/dist/multicast-auth-catalog.d.ts.map +1 -0
  151. package/dist/multicast-auth-catalog.js +202 -0
  152. package/dist/multicast-auth-catalog.js.map +7 -0
  153. package/dist/multicast-input.d.ts +141 -0
  154. package/dist/multicast-input.d.ts.map +1 -0
  155. package/dist/multicast-input.js +390 -0
  156. package/dist/multicast-input.js.map +7 -0
  157. package/dist/multicast-manager.d.ts +669 -0
  158. package/dist/multicast-manager.d.ts.map +1 -0
  159. package/dist/multicast-manager.js +19101 -0
  160. package/dist/multicast-manager.js.map +7 -0
  161. package/dist/multicast-transport.d.ts +271 -0
  162. package/dist/multicast-transport.d.ts.map +1 -0
  163. package/dist/multicast-transport.js +2264 -0
  164. package/dist/multicast-transport.js.map +7 -0
  165. package/dist/multicast-unwrap.d.ts +57 -0
  166. package/dist/multicast-unwrap.d.ts.map +1 -0
  167. package/dist/multicast-unwrap.js +418 -0
  168. package/dist/multicast-unwrap.js.map +7 -0
  169. package/dist/no-modules.d.ts +10 -0
  170. package/dist/no-modules.d.ts.map +1 -0
  171. package/dist/no-modules.js +9491 -0
  172. package/dist/no-modules.js.map +7 -0
  173. package/dist/phase14-catalog-override.d.ts +126 -0
  174. package/dist/phase14-catalog-override.d.ts.map +1 -0
  175. package/dist/phase14-catalog-override.js +155 -0
  176. package/dist/phase14-catalog-override.js.map +7 -0
  177. package/dist/repair-fetch.d.ts +270 -0
  178. package/dist/repair-fetch.d.ts.map +1 -0
  179. package/dist/repair-fetch.js +381 -0
  180. package/dist/repair-fetch.js.map +7 -0
  181. package/dist/repair-layers.d.ts +158 -0
  182. package/dist/repair-layers.d.ts.map +1 -0
  183. package/dist/repair-layers.js +344 -0
  184. package/dist/repair-layers.js.map +7 -0
  185. package/dist/sample-combiner.d.ts +212 -0
  186. package/dist/sample-combiner.d.ts.map +1 -0
  187. package/dist/sample-combiner.js +595 -0
  188. package/dist/sample-combiner.js.map +7 -0
  189. package/dist/signaling.d.ts +29 -0
  190. package/dist/signaling.d.ts.map +1 -0
  191. package/dist/signaling.js +572 -0
  192. package/dist/signaling.js.map +7 -0
  193. package/dist/source-auth.d.ts +2 -0
  194. package/dist/source-auth.d.ts.map +1 -0
  195. package/dist/source-auth.js +1209 -0
  196. package/dist/source-auth.js.map +7 -0
  197. package/dist/switching-set-alignment.d.ts +61 -0
  198. package/dist/switching-set-alignment.d.ts.map +1 -0
  199. package/dist/switching-set-alignment.js +70 -0
  200. package/dist/switching-set-alignment.js.map +7 -0
  201. package/dist/timestamp-codec.d.ts +34 -0
  202. package/dist/timestamp-codec.d.ts.map +1 -0
  203. package/dist/timestamp-codec.js +473 -0
  204. package/dist/timestamp-codec.js.map +7 -0
  205. package/dist/track-pipeline-setup.d.ts +83 -0
  206. package/dist/track-pipeline-setup.d.ts.map +1 -0
  207. package/dist/track-pipeline-setup.js +291 -0
  208. package/dist/track-pipeline-setup.js.map +7 -0
  209. package/dist/track-pipeline.d.ts +121 -0
  210. package/dist/track-pipeline.d.ts.map +1 -0
  211. package/dist/track-pipeline.js +209 -0
  212. package/dist/track-pipeline.js.map +7 -0
  213. package/dist/track-session.d.ts +414 -0
  214. package/dist/track-session.d.ts.map +1 -0
  215. package/dist/track-session.js +754 -0
  216. package/dist/track-session.js.map +7 -0
  217. package/dist/transport-pool.d.ts +69 -0
  218. package/dist/transport-pool.d.ts.map +1 -0
  219. package/dist/transport-pool.js +74 -0
  220. package/dist/transport-pool.js.map +7 -0
  221. package/dist/wasm-mfu-reassembler.d.ts +97 -0
  222. package/dist/wasm-mfu-reassembler.d.ts.map +1 -0
  223. package/dist/wasm-mfu-reassembler.js +2133 -0
  224. package/dist/wasm-mfu-reassembler.js.map +7 -0
  225. package/dist/wasm-mmtp-container-parser.d.ts +10 -0
  226. package/dist/wasm-mmtp-container-parser.d.ts.map +1 -0
  227. package/dist/wasm-mmtp-container-parser.js +6834 -0
  228. package/dist/wasm-mmtp-container-parser.js.map +7 -0
  229. package/node_modules/@blockcast/fec-worker/README.md +223 -0
  230. package/node_modules/@blockcast/fec-worker/dist/.build-stamp +0 -0
  231. package/node_modules/@blockcast/fec-worker/dist/block-order.d.ts +38 -0
  232. package/node_modules/@blockcast/fec-worker/dist/block-order.d.ts.map +1 -0
  233. package/node_modules/@blockcast/fec-worker/dist/block-order.js +78 -0
  234. package/node_modules/@blockcast/fec-worker/dist/block-order.js.map +1 -0
  235. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts +93 -0
  236. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts.map +1 -0
  237. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.js +346 -0
  238. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.js.map +1 -0
  239. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts +380 -0
  240. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts.map +1 -0
  241. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.js +11 -0
  242. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.js.map +1 -0
  243. package/node_modules/@blockcast/fec-worker/dist/fec-worker.d.ts +14 -0
  244. package/node_modules/@blockcast/fec-worker/dist/fec-worker.d.ts.map +1 -0
  245. package/node_modules/@blockcast/fec-worker/dist/fec-worker.js +859 -0
  246. package/node_modules/@blockcast/fec-worker/dist/fec-worker.js.map +7 -0
  247. package/node_modules/@blockcast/fec-worker/dist/index.d.ts +10 -0
  248. package/node_modules/@blockcast/fec-worker/dist/index.d.ts.map +1 -0
  249. package/node_modules/@blockcast/fec-worker/dist/index.js +8 -0
  250. package/node_modules/@blockcast/fec-worker/dist/index.js.map +1 -0
  251. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.d.ts +20 -0
  252. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.d.ts.map +1 -0
  253. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.js +370 -0
  254. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.js.map +7 -0
  255. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.d.ts +104 -0
  256. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.d.ts.map +1 -0
  257. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.js +181 -0
  258. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.js.map +1 -0
  259. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.d.ts +194 -0
  260. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.d.ts.map +1 -0
  261. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.js +62 -0
  262. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.js.map +1 -0
  263. package/node_modules/@blockcast/fec-worker/dist/transfer.d.ts +9 -0
  264. package/node_modules/@blockcast/fec-worker/dist/transfer.d.ts.map +1 -0
  265. package/node_modules/@blockcast/fec-worker/dist/transfer.js +13 -0
  266. package/node_modules/@blockcast/fec-worker/dist/transfer.js.map +1 -0
  267. package/node_modules/@blockcast/fec-worker/package.json +59 -0
  268. package/node_modules/@blockcast/fec-worker/src/block-order.ts +93 -0
  269. package/node_modules/@blockcast/fec-worker/src/fec-worker-client.ts +392 -0
  270. package/node_modules/@blockcast/fec-worker/src/fec-worker-types.ts +399 -0
  271. package/node_modules/@blockcast/fec-worker/src/fec-worker.ts +1502 -0
  272. package/node_modules/@blockcast/fec-worker/src/index.ts +35 -0
  273. package/node_modules/@blockcast/fec-worker/src/shred-fec-worker.ts +589 -0
  274. package/node_modules/@blockcast/fec-worker/src/shred-worker-client.ts +222 -0
  275. package/node_modules/@blockcast/fec-worker/src/shred-worker-types.ts +209 -0
  276. package/node_modules/@blockcast/fec-worker/src/transfer.ts +12 -0
  277. package/node_modules/@blockcast/mmt-alta-parse/README.md +181 -0
  278. package/node_modules/@blockcast/mmt-alta-parse/dist/.build-stamp +0 -0
  279. package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts +57 -0
  280. package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts.map +1 -0
  281. package/node_modules/@blockcast/mmt-alta-parse/dist/index.js +115 -0
  282. package/node_modules/@blockcast/mmt-alta-parse/dist/index.js.map +1 -0
  283. package/node_modules/@blockcast/mmt-alta-parse/package.json +50 -0
  284. package/node_modules/@blockcast/mmt-alta-parse/src/index.ts +136 -0
  285. package/node_modules/@blockcast/mmt-base/README.md +134 -0
  286. package/node_modules/@blockcast/mmt-base/dist/.build-stamp +0 -0
  287. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts +55 -0
  288. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts.map +1 -0
  289. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js +128 -0
  290. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js.map +1 -0
  291. package/node_modules/@blockcast/mmt-base/dist/index.d.ts +7 -0
  292. package/node_modules/@blockcast/mmt-base/dist/index.d.ts.map +1 -0
  293. package/node_modules/@blockcast/mmt-base/dist/index.js +7 -0
  294. package/node_modules/@blockcast/mmt-base/dist/index.js.map +1 -0
  295. package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +292 -0
  296. package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +1 -0
  297. package/node_modules/@blockcast/mmt-base/dist/mmtp.js +612 -0
  298. package/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +1 -0
  299. package/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts +18 -0
  300. package/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts.map +1 -0
  301. package/node_modules/@blockcast/mmt-base/dist/net-constants.js +18 -0
  302. package/node_modules/@blockcast/mmt-base/dist/net-constants.js.map +1 -0
  303. package/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts +47 -0
  304. package/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts.map +1 -0
  305. package/node_modules/@blockcast/mmt-base/dist/ntp-short.js +54 -0
  306. package/node_modules/@blockcast/mmt-base/dist/ntp-short.js.map +1 -0
  307. package/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts +57 -0
  308. package/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts.map +1 -0
  309. package/node_modules/@blockcast/mmt-base/dist/serial-number.js +98 -0
  310. package/node_modules/@blockcast/mmt-base/dist/serial-number.js.map +1 -0
  311. package/node_modules/@blockcast/mmt-base/dist/signalling.d.ts +312 -0
  312. package/node_modules/@blockcast/mmt-base/dist/signalling.d.ts.map +1 -0
  313. package/node_modules/@blockcast/mmt-base/dist/signalling.js +637 -0
  314. package/node_modules/@blockcast/mmt-base/dist/signalling.js.map +1 -0
  315. package/node_modules/@blockcast/mmt-base/package.json +49 -0
  316. package/node_modules/@blockcast/mmt-base/src/fec-block-profile.ts +145 -0
  317. package/node_modules/@blockcast/mmt-base/src/index.ts +6 -0
  318. package/node_modules/@blockcast/mmt-base/src/mmtp.ts +892 -0
  319. package/node_modules/@blockcast/mmt-base/src/net-constants.ts +17 -0
  320. package/node_modules/@blockcast/mmt-base/src/ntp-short.ts +67 -0
  321. package/node_modules/@blockcast/mmt-base/src/serial-number.ts +107 -0
  322. package/node_modules/@blockcast/mmt-base/src/signalling.ts +862 -0
  323. package/node_modules/@blockcast/mmt-fec/README.md +271 -0
  324. package/node_modules/@blockcast/mmt-fec/dist/.build-stamp +0 -0
  325. package/node_modules/@blockcast/mmt-fec/dist/block-manager.d.ts +82 -0
  326. package/node_modules/@blockcast/mmt-fec/dist/block-manager.d.ts.map +1 -0
  327. package/node_modules/@blockcast/mmt-fec/dist/block-manager.js +296 -0
  328. package/node_modules/@blockcast/mmt-fec/dist/block-manager.js.map +1 -0
  329. package/node_modules/@blockcast/mmt-fec/dist/block-processor.d.ts +106 -0
  330. package/node_modules/@blockcast/mmt-fec/dist/block-processor.d.ts.map +1 -0
  331. package/node_modules/@blockcast/mmt-fec/dist/block-processor.js +288 -0
  332. package/node_modules/@blockcast/mmt-fec/dist/block-processor.js.map +1 -0
  333. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.d.ts +46 -0
  334. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.d.ts.map +1 -0
  335. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.js +96 -0
  336. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.js.map +1 -0
  337. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.d.ts +33 -0
  338. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.d.ts.map +1 -0
  339. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.js +57 -0
  340. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.js.map +1 -0
  341. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.d.ts +205 -0
  342. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.d.ts.map +1 -0
  343. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.js +240 -0
  344. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.js.map +1 -0
  345. package/node_modules/@blockcast/mmt-fec/dist/index.d.ts +28 -0
  346. package/node_modules/@blockcast/mmt-fec/dist/index.d.ts.map +1 -0
  347. package/node_modules/@blockcast/mmt-fec/dist/index.js +28 -0
  348. package/node_modules/@blockcast/mmt-fec/dist/index.js.map +1 -0
  349. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.d.ts +20 -0
  350. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.d.ts.map +1 -0
  351. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.js +28 -0
  352. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.js.map +1 -0
  353. package/node_modules/@blockcast/mmt-fec/dist/oti.d.ts +258 -0
  354. package/node_modules/@blockcast/mmt-fec/dist/oti.d.ts.map +1 -0
  355. package/node_modules/@blockcast/mmt-fec/dist/oti.js +382 -0
  356. package/node_modules/@blockcast/mmt-fec/dist/oti.js.map +1 -0
  357. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.d.ts +41 -0
  358. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.d.ts.map +1 -0
  359. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.js +81 -0
  360. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.js.map +1 -0
  361. package/node_modules/@blockcast/mmt-fec/dist/repair-group.d.ts +82 -0
  362. package/node_modules/@blockcast/mmt-fec/dist/repair-group.d.ts.map +1 -0
  363. package/node_modules/@blockcast/mmt-fec/dist/repair-group.js +170 -0
  364. package/node_modules/@blockcast/mmt-fec/dist/repair-group.js.map +1 -0
  365. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/README.md +181 -0
  366. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/.build-stamp +0 -0
  367. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts +57 -0
  368. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts.map +1 -0
  369. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.js +115 -0
  370. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.js.map +1 -0
  371. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/package.json +50 -0
  372. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/src/index.ts +136 -0
  373. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/README.md +134 -0
  374. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/.build-stamp +0 -0
  375. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts +55 -0
  376. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts.map +1 -0
  377. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js +128 -0
  378. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js.map +1 -0
  379. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.d.ts +7 -0
  380. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.d.ts.map +1 -0
  381. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.js +7 -0
  382. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.js.map +1 -0
  383. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +292 -0
  384. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +1 -0
  385. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js +612 -0
  386. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +1 -0
  387. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts +18 -0
  388. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts.map +1 -0
  389. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.js +18 -0
  390. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.js.map +1 -0
  391. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts +47 -0
  392. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts.map +1 -0
  393. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.js +54 -0
  394. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.js.map +1 -0
  395. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts +57 -0
  396. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts.map +1 -0
  397. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.js +98 -0
  398. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.js.map +1 -0
  399. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.d.ts +312 -0
  400. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.d.ts.map +1 -0
  401. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.js +637 -0
  402. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.js.map +1 -0
  403. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/package.json +49 -0
  404. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/fec-block-profile.ts +145 -0
  405. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/index.ts +6 -0
  406. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/mmtp.ts +892 -0
  407. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/net-constants.ts +17 -0
  408. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/ntp-short.ts +67 -0
  409. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/serial-number.ts +107 -0
  410. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/signalling.ts +862 -0
  411. package/node_modules/@blockcast/mmt-fec/package.json +79 -0
  412. package/node_modules/@blockcast/mmt-fec/src/block-manager.ts +371 -0
  413. package/node_modules/@blockcast/mmt-fec/src/block-processor.ts +439 -0
  414. package/node_modules/@blockcast/mmt-fec/src/cmaf-recovery.ts +127 -0
  415. package/node_modules/@blockcast/mmt-fec/src/cmaf-symbols.ts +68 -0
  416. package/node_modules/@blockcast/mmt-fec/src/fec-block-mapper.ts +310 -0
  417. package/node_modules/@blockcast/mmt-fec/src/index.ts +94 -0
  418. package/node_modules/@blockcast/mmt-fec/src/loc-symbols.ts +34 -0
  419. package/node_modules/@blockcast/mmt-fec/src/oti.ts +463 -0
  420. package/node_modules/@blockcast/mmt-fec/src/reorder-buffer.ts +101 -0
  421. package/node_modules/@blockcast/mmt-fec/src/repair-group.ts +195 -0
  422. package/node_modules/@blockcast/mmt-manifest-verify/dist/.build-stamp +0 -0
  423. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.d.ts +28 -0
  424. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.d.ts.map +1 -0
  425. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.js +128 -0
  426. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.js.map +1 -0
  427. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.d.ts +4 -0
  428. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.d.ts.map +1 -0
  429. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.js +4 -0
  430. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.js.map +1 -0
  431. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.d.ts +37 -0
  432. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.d.ts.map +1 -0
  433. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.js +179 -0
  434. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.js.map +1 -0
  435. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.d.ts +99 -0
  436. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.d.ts.map +1 -0
  437. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.js +492 -0
  438. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.js.map +1 -0
  439. package/node_modules/@blockcast/mmt-manifest-verify/package.json +45 -0
  440. package/node_modules/@blockcast/mmt-manifest-verify/src/admission.ts +171 -0
  441. package/node_modules/@blockcast/mmt-manifest-verify/src/index.ts +3 -0
  442. package/node_modules/@blockcast/mmt-manifest-verify/src/source-auth.ts +230 -0
  443. package/node_modules/@blockcast/mmt-manifest-verify/src/verifier.ts +669 -0
  444. package/node_modules/@noble/hashes/LICENSE +21 -0
  445. package/node_modules/@noble/hashes/README.md +521 -0
  446. package/node_modules/@noble/hashes/_assert.d.ts +17 -0
  447. package/node_modules/@noble/hashes/_assert.d.ts.map +1 -0
  448. package/node_modules/@noble/hashes/_assert.js +18 -0
  449. package/node_modules/@noble/hashes/_assert.js.map +1 -0
  450. package/node_modules/@noble/hashes/_blake.d.ts +14 -0
  451. package/node_modules/@noble/hashes/_blake.d.ts.map +1 -0
  452. package/node_modules/@noble/hashes/_blake.js +50 -0
  453. package/node_modules/@noble/hashes/_blake.js.map +1 -0
  454. package/node_modules/@noble/hashes/_md.d.ts +51 -0
  455. package/node_modules/@noble/hashes/_md.d.ts.map +1 -0
  456. package/node_modules/@noble/hashes/_md.js +162 -0
  457. package/node_modules/@noble/hashes/_md.js.map +1 -0
  458. package/node_modules/@noble/hashes/_u64.d.ts +55 -0
  459. package/node_modules/@noble/hashes/_u64.d.ts.map +1 -0
  460. package/node_modules/@noble/hashes/_u64.js +90 -0
  461. package/node_modules/@noble/hashes/_u64.js.map +1 -0
  462. package/node_modules/@noble/hashes/argon2.d.ts +32 -0
  463. package/node_modules/@noble/hashes/argon2.d.ts.map +1 -0
  464. package/node_modules/@noble/hashes/argon2.js +401 -0
  465. package/node_modules/@noble/hashes/argon2.js.map +1 -0
  466. package/node_modules/@noble/hashes/blake1.d.ts +106 -0
  467. package/node_modules/@noble/hashes/blake1.d.ts.map +1 -0
  468. package/node_modules/@noble/hashes/blake1.js +459 -0
  469. package/node_modules/@noble/hashes/blake1.js.map +1 -0
  470. package/node_modules/@noble/hashes/blake2.d.ts +116 -0
  471. package/node_modules/@noble/hashes/blake2.d.ts.map +1 -0
  472. package/node_modules/@noble/hashes/blake2.js +420 -0
  473. package/node_modules/@noble/hashes/blake2.js.map +1 -0
  474. package/node_modules/@noble/hashes/blake2b.d.ts +11 -0
  475. package/node_modules/@noble/hashes/blake2b.d.ts.map +1 -0
  476. package/node_modules/@noble/hashes/blake2b.js +14 -0
  477. package/node_modules/@noble/hashes/blake2b.js.map +1 -0
  478. package/node_modules/@noble/hashes/blake2s.d.ts +20 -0
  479. package/node_modules/@noble/hashes/blake2s.d.ts.map +1 -0
  480. package/node_modules/@noble/hashes/blake2s.js +24 -0
  481. package/node_modules/@noble/hashes/blake2s.js.map +1 -0
  482. package/node_modules/@noble/hashes/blake3.d.ts +54 -0
  483. package/node_modules/@noble/hashes/blake3.d.ts.map +1 -0
  484. package/node_modules/@noble/hashes/blake3.js +255 -0
  485. package/node_modules/@noble/hashes/blake3.js.map +1 -0
  486. package/node_modules/@noble/hashes/crypto.d.ts +2 -0
  487. package/node_modules/@noble/hashes/crypto.d.ts.map +1 -0
  488. package/node_modules/@noble/hashes/crypto.js +5 -0
  489. package/node_modules/@noble/hashes/crypto.js.map +1 -0
  490. package/node_modules/@noble/hashes/cryptoNode.d.ts +2 -0
  491. package/node_modules/@noble/hashes/cryptoNode.d.ts.map +1 -0
  492. package/node_modules/@noble/hashes/cryptoNode.js +18 -0
  493. package/node_modules/@noble/hashes/cryptoNode.js.map +1 -0
  494. package/node_modules/@noble/hashes/eskdf.d.ts +47 -0
  495. package/node_modules/@noble/hashes/eskdf.d.ts.map +1 -0
  496. package/node_modules/@noble/hashes/eskdf.js +166 -0
  497. package/node_modules/@noble/hashes/eskdf.js.map +1 -0
  498. package/node_modules/@noble/hashes/esm/_assert.d.ts +17 -0
  499. package/node_modules/@noble/hashes/esm/_assert.d.ts.map +1 -0
  500. package/node_modules/@noble/hashes/esm/_assert.js +15 -0
  501. package/node_modules/@noble/hashes/esm/_assert.js.map +1 -0
  502. package/node_modules/@noble/hashes/esm/_blake.d.ts +14 -0
  503. package/node_modules/@noble/hashes/esm/_blake.d.ts.map +1 -0
  504. package/node_modules/@noble/hashes/esm/_blake.js +45 -0
  505. package/node_modules/@noble/hashes/esm/_blake.js.map +1 -0
  506. package/node_modules/@noble/hashes/esm/_md.d.ts +51 -0
  507. package/node_modules/@noble/hashes/esm/_md.d.ts.map +1 -0
  508. package/node_modules/@noble/hashes/esm/_md.js +155 -0
  509. package/node_modules/@noble/hashes/esm/_md.js.map +1 -0
  510. package/node_modules/@noble/hashes/esm/_u64.d.ts +55 -0
  511. package/node_modules/@noble/hashes/esm/_u64.d.ts.map +1 -0
  512. package/node_modules/@noble/hashes/esm/_u64.js +67 -0
  513. package/node_modules/@noble/hashes/esm/_u64.js.map +1 -0
  514. package/node_modules/@noble/hashes/esm/argon2.d.ts +32 -0
  515. package/node_modules/@noble/hashes/esm/argon2.d.ts.map +1 -0
  516. package/node_modules/@noble/hashes/esm/argon2.js +392 -0
  517. package/node_modules/@noble/hashes/esm/argon2.js.map +1 -0
  518. package/node_modules/@noble/hashes/esm/blake1.d.ts +106 -0
  519. package/node_modules/@noble/hashes/esm/blake1.d.ts.map +1 -0
  520. package/node_modules/@noble/hashes/esm/blake1.js +452 -0
  521. package/node_modules/@noble/hashes/esm/blake1.js.map +1 -0
  522. package/node_modules/@noble/hashes/esm/blake2.d.ts +116 -0
  523. package/node_modules/@noble/hashes/esm/blake2.d.ts.map +1 -0
  524. package/node_modules/@noble/hashes/esm/blake2.js +413 -0
  525. package/node_modules/@noble/hashes/esm/blake2.js.map +1 -0
  526. package/node_modules/@noble/hashes/esm/blake2b.d.ts +11 -0
  527. package/node_modules/@noble/hashes/esm/blake2b.d.ts.map +1 -0
  528. package/node_modules/@noble/hashes/esm/blake2b.js +11 -0
  529. package/node_modules/@noble/hashes/esm/blake2b.js.map +1 -0
  530. package/node_modules/@noble/hashes/esm/blake2s.d.ts +20 -0
  531. package/node_modules/@noble/hashes/esm/blake2s.d.ts.map +1 -0
  532. package/node_modules/@noble/hashes/esm/blake2s.js +21 -0
  533. package/node_modules/@noble/hashes/esm/blake2s.js.map +1 -0
  534. package/node_modules/@noble/hashes/esm/blake3.d.ts +54 -0
  535. package/node_modules/@noble/hashes/esm/blake3.d.ts.map +1 -0
  536. package/node_modules/@noble/hashes/esm/blake3.js +251 -0
  537. package/node_modules/@noble/hashes/esm/blake3.js.map +1 -0
  538. package/node_modules/@noble/hashes/esm/crypto.d.ts +2 -0
  539. package/node_modules/@noble/hashes/esm/crypto.d.ts.map +1 -0
  540. package/node_modules/@noble/hashes/esm/crypto.js +2 -0
  541. package/node_modules/@noble/hashes/esm/crypto.js.map +1 -0
  542. package/node_modules/@noble/hashes/esm/cryptoNode.d.ts +2 -0
  543. package/node_modules/@noble/hashes/esm/cryptoNode.d.ts.map +1 -0
  544. package/node_modules/@noble/hashes/esm/cryptoNode.js +15 -0
  545. package/node_modules/@noble/hashes/esm/cryptoNode.js.map +1 -0
  546. package/node_modules/@noble/hashes/esm/eskdf.d.ts +47 -0
  547. package/node_modules/@noble/hashes/esm/eskdf.d.ts.map +1 -0
  548. package/node_modules/@noble/hashes/esm/eskdf.js +160 -0
  549. package/node_modules/@noble/hashes/esm/eskdf.js.map +1 -0
  550. package/node_modules/@noble/hashes/esm/hkdf.d.ts +36 -0
  551. package/node_modules/@noble/hashes/esm/hkdf.d.ts.map +1 -0
  552. package/node_modules/@noble/hashes/esm/hkdf.js +82 -0
  553. package/node_modules/@noble/hashes/esm/hkdf.js.map +1 -0
  554. package/node_modules/@noble/hashes/esm/hmac.d.ts +35 -0
  555. package/node_modules/@noble/hashes/esm/hmac.d.ts.map +1 -0
  556. package/node_modules/@noble/hashes/esm/hmac.js +86 -0
  557. package/node_modules/@noble/hashes/esm/hmac.js.map +1 -0
  558. package/node_modules/@noble/hashes/esm/index.d.ts +2 -0
  559. package/node_modules/@noble/hashes/esm/index.d.ts.map +1 -0
  560. package/node_modules/@noble/hashes/esm/index.js +33 -0
  561. package/node_modules/@noble/hashes/esm/index.js.map +1 -0
  562. package/node_modules/@noble/hashes/esm/legacy.d.ts +71 -0
  563. package/node_modules/@noble/hashes/esm/legacy.d.ts.map +1 -0
  564. package/node_modules/@noble/hashes/esm/legacy.js +281 -0
  565. package/node_modules/@noble/hashes/esm/legacy.js.map +1 -0
  566. package/node_modules/@noble/hashes/esm/package.json +10 -0
  567. package/node_modules/@noble/hashes/esm/pbkdf2.d.ts +23 -0
  568. package/node_modules/@noble/hashes/esm/pbkdf2.d.ts.map +1 -0
  569. package/node_modules/@noble/hashes/esm/pbkdf2.js +97 -0
  570. package/node_modules/@noble/hashes/esm/pbkdf2.js.map +1 -0
  571. package/node_modules/@noble/hashes/esm/ripemd160.d.ts +13 -0
  572. package/node_modules/@noble/hashes/esm/ripemd160.d.ts.map +1 -0
  573. package/node_modules/@noble/hashes/esm/ripemd160.js +13 -0
  574. package/node_modules/@noble/hashes/esm/ripemd160.js.map +1 -0
  575. package/node_modules/@noble/hashes/esm/scrypt.d.ts +34 -0
  576. package/node_modules/@noble/hashes/esm/scrypt.d.ts.map +1 -0
  577. package/node_modules/@noble/hashes/esm/scrypt.js +228 -0
  578. package/node_modules/@noble/hashes/esm/scrypt.js.map +1 -0
  579. package/node_modules/@noble/hashes/esm/sha1.d.ts +11 -0
  580. package/node_modules/@noble/hashes/esm/sha1.d.ts.map +1 -0
  581. package/node_modules/@noble/hashes/esm/sha1.js +11 -0
  582. package/node_modules/@noble/hashes/esm/sha1.js.map +1 -0
  583. package/node_modules/@noble/hashes/esm/sha2.d.ts +159 -0
  584. package/node_modules/@noble/hashes/esm/sha2.d.ts.map +1 -0
  585. package/node_modules/@noble/hashes/esm/sha2.js +375 -0
  586. package/node_modules/@noble/hashes/esm/sha2.js.map +1 -0
  587. package/node_modules/@noble/hashes/esm/sha256.d.ts +20 -0
  588. package/node_modules/@noble/hashes/esm/sha256.d.ts.map +1 -0
  589. package/node_modules/@noble/hashes/esm/sha256.js +20 -0
  590. package/node_modules/@noble/hashes/esm/sha256.js.map +1 -0
  591. package/node_modules/@noble/hashes/esm/sha3-addons.d.ts +142 -0
  592. package/node_modules/@noble/hashes/esm/sha3-addons.d.ts.map +1 -0
  593. package/node_modules/@noble/hashes/esm/sha3-addons.js +393 -0
  594. package/node_modules/@noble/hashes/esm/sha3-addons.js.map +1 -0
  595. package/node_modules/@noble/hashes/esm/sha3.d.ts +53 -0
  596. package/node_modules/@noble/hashes/esm/sha3.d.ts.map +1 -0
  597. package/node_modules/@noble/hashes/esm/sha3.js +234 -0
  598. package/node_modules/@noble/hashes/esm/sha3.js.map +1 -0
  599. package/node_modules/@noble/hashes/esm/sha512.d.ts +26 -0
  600. package/node_modules/@noble/hashes/esm/sha512.d.ts.map +1 -0
  601. package/node_modules/@noble/hashes/esm/sha512.js +26 -0
  602. package/node_modules/@noble/hashes/esm/sha512.js.map +1 -0
  603. package/node_modules/@noble/hashes/esm/utils.d.ts +161 -0
  604. package/node_modules/@noble/hashes/esm/utils.d.ts.map +1 -0
  605. package/node_modules/@noble/hashes/esm/utils.js +281 -0
  606. package/node_modules/@noble/hashes/esm/utils.js.map +1 -0
  607. package/node_modules/@noble/hashes/hkdf.d.ts +36 -0
  608. package/node_modules/@noble/hashes/hkdf.d.ts.map +1 -0
  609. package/node_modules/@noble/hashes/hkdf.js +88 -0
  610. package/node_modules/@noble/hashes/hkdf.js.map +1 -0
  611. package/node_modules/@noble/hashes/hmac.d.ts +35 -0
  612. package/node_modules/@noble/hashes/hmac.d.ts.map +1 -0
  613. package/node_modules/@noble/hashes/hmac.js +91 -0
  614. package/node_modules/@noble/hashes/hmac.js.map +1 -0
  615. package/node_modules/@noble/hashes/index.d.ts +1 -0
  616. package/node_modules/@noble/hashes/index.d.ts.map +1 -0
  617. package/node_modules/@noble/hashes/index.js +33 -0
  618. package/node_modules/@noble/hashes/index.js.map +1 -0
  619. package/node_modules/@noble/hashes/legacy.d.ts +71 -0
  620. package/node_modules/@noble/hashes/legacy.d.ts.map +1 -0
  621. package/node_modules/@noble/hashes/legacy.js +287 -0
  622. package/node_modules/@noble/hashes/legacy.js.map +1 -0
  623. package/node_modules/@noble/hashes/package.json +266 -0
  624. package/node_modules/@noble/hashes/pbkdf2.d.ts +23 -0
  625. package/node_modules/@noble/hashes/pbkdf2.d.ts.map +1 -0
  626. package/node_modules/@noble/hashes/pbkdf2.js +101 -0
  627. package/node_modules/@noble/hashes/pbkdf2.js.map +1 -0
  628. package/node_modules/@noble/hashes/ripemd160.d.ts +13 -0
  629. package/node_modules/@noble/hashes/ripemd160.d.ts.map +1 -0
  630. package/node_modules/@noble/hashes/ripemd160.js +16 -0
  631. package/node_modules/@noble/hashes/ripemd160.js.map +1 -0
  632. package/node_modules/@noble/hashes/scrypt.d.ts +34 -0
  633. package/node_modules/@noble/hashes/scrypt.d.ts.map +1 -0
  634. package/node_modules/@noble/hashes/scrypt.js +232 -0
  635. package/node_modules/@noble/hashes/scrypt.js.map +1 -0
  636. package/node_modules/@noble/hashes/sha1.d.ts +11 -0
  637. package/node_modules/@noble/hashes/sha1.d.ts.map +1 -0
  638. package/node_modules/@noble/hashes/sha1.js +14 -0
  639. package/node_modules/@noble/hashes/sha1.js.map +1 -0
  640. package/node_modules/@noble/hashes/sha2.d.ts +159 -0
  641. package/node_modules/@noble/hashes/sha2.d.ts.map +1 -0
  642. package/node_modules/@noble/hashes/sha2.js +384 -0
  643. package/node_modules/@noble/hashes/sha2.js.map +1 -0
  644. package/node_modules/@noble/hashes/sha256.d.ts +20 -0
  645. package/node_modules/@noble/hashes/sha256.d.ts.map +1 -0
  646. package/node_modules/@noble/hashes/sha256.js +23 -0
  647. package/node_modules/@noble/hashes/sha256.js.map +1 -0
  648. package/node_modules/@noble/hashes/sha3-addons.d.ts +142 -0
  649. package/node_modules/@noble/hashes/sha3-addons.d.ts.map +1 -0
  650. package/node_modules/@noble/hashes/sha3-addons.js +402 -0
  651. package/node_modules/@noble/hashes/sha3-addons.js.map +1 -0
  652. package/node_modules/@noble/hashes/sha3.d.ts +53 -0
  653. package/node_modules/@noble/hashes/sha3.d.ts.map +1 -0
  654. package/node_modules/@noble/hashes/sha3.js +239 -0
  655. package/node_modules/@noble/hashes/sha3.js.map +1 -0
  656. package/node_modules/@noble/hashes/sha512.d.ts +26 -0
  657. package/node_modules/@noble/hashes/sha512.d.ts.map +1 -0
  658. package/node_modules/@noble/hashes/sha512.js +29 -0
  659. package/node_modules/@noble/hashes/sha512.js.map +1 -0
  660. package/node_modules/@noble/hashes/src/_assert.ts +22 -0
  661. package/node_modules/@noble/hashes/src/_blake.ts +50 -0
  662. package/node_modules/@noble/hashes/src/_md.ts +176 -0
  663. package/node_modules/@noble/hashes/src/_u64.ts +91 -0
  664. package/node_modules/@noble/hashes/src/argon2.ts +497 -0
  665. package/node_modules/@noble/hashes/src/blake1.ts +534 -0
  666. package/node_modules/@noble/hashes/src/blake2.ts +486 -0
  667. package/node_modules/@noble/hashes/src/blake2b.ts +10 -0
  668. package/node_modules/@noble/hashes/src/blake2s.ts +20 -0
  669. package/node_modules/@noble/hashes/src/blake3.ts +272 -0
  670. package/node_modules/@noble/hashes/src/crypto.ts +9 -0
  671. package/node_modules/@noble/hashes/src/cryptoNode.ts +15 -0
  672. package/node_modules/@noble/hashes/src/eskdf.ts +187 -0
  673. package/node_modules/@noble/hashes/src/hkdf.ts +88 -0
  674. package/node_modules/@noble/hashes/src/hmac.ts +94 -0
  675. package/node_modules/@noble/hashes/src/index.ts +31 -0
  676. package/node_modules/@noble/hashes/src/legacy.ts +293 -0
  677. package/node_modules/@noble/hashes/src/pbkdf2.ts +122 -0
  678. package/node_modules/@noble/hashes/src/ripemd160.ts +12 -0
  679. package/node_modules/@noble/hashes/src/scrypt.ts +257 -0
  680. package/node_modules/@noble/hashes/src/sha1.ts +10 -0
  681. package/node_modules/@noble/hashes/src/sha2.ts +402 -0
  682. package/node_modules/@noble/hashes/src/sha256.ts +24 -0
  683. package/node_modules/@noble/hashes/src/sha3-addons.ts +499 -0
  684. package/node_modules/@noble/hashes/src/sha3.ts +258 -0
  685. package/node_modules/@noble/hashes/src/sha512.ts +34 -0
  686. package/node_modules/@noble/hashes/src/utils.ts +395 -0
  687. package/node_modules/@noble/hashes/utils.d.ts +161 -0
  688. package/node_modules/@noble/hashes/utils.d.ts.map +1 -0
  689. package/node_modules/@noble/hashes/utils.js +313 -0
  690. package/node_modules/@noble/hashes/utils.js.map +1 -0
  691. package/package.json +179 -8
  692. package/schemas/catalog.schema.json +357 -0
  693. package/src/align.ts +239 -0
  694. package/src/audio-init-parser.ts +189 -0
  695. package/src/audio-ring-buffer.ts +120 -0
  696. package/src/authored-timing.ts +63 -0
  697. package/src/block-policy.ts +903 -0
  698. package/src/carp-catalog.ts +1065 -0
  699. package/src/catalog-fec-params.ts +729 -0
  700. package/src/catalog-network-source.ts +74 -0
  701. package/src/catalog-validator.ts +34 -0
  702. package/src/cmaf-block-assembler.ts +1255 -0
  703. package/src/cmaf-group-batcher.ts +104 -0
  704. package/src/cmcd-producer.ts +329 -0
  705. package/src/codec-utils.ts +1984 -0
  706. package/src/conceal-timeline.ts +293 -0
  707. package/src/delivery-gap.ts +147 -0
  708. package/src/duplicate-ledger.ts +372 -0
  709. package/src/fec-codec.ts +61 -0
  710. package/src/fec-manager.ts +4298 -0
  711. package/src/fec-starvation-detector.ts +128 -0
  712. package/src/fec-state.ts +219 -0
  713. package/src/fec-worker-setup.ts +283 -0
  714. package/src/fmp4-completion.ts +47 -0
  715. package/src/fmp4-reader.ts +112 -0
  716. package/src/index.ts +590 -21
  717. package/src/iso-block-locator.ts +329 -0
  718. package/src/iso-bmff-timing.ts +813 -0
  719. package/src/loc.ts +128 -154
  720. package/src/manifest-auth-plan.ts +107 -0
  721. package/src/media-router.ts +841 -0
  722. package/src/mfu-reassembler.ts +3480 -155
  723. package/src/mfu-reassembly-policy.ts +134 -0
  724. package/src/mmtp-container-parser.ts +2380 -0
  725. package/src/mmtp-decode-pipeline.ts +406 -0
  726. package/src/mmtp-router.ts +4860 -0
  727. package/src/mmtp-to-cmaf.ts +977 -0
  728. package/src/multicast-auth-catalog.ts +340 -0
  729. package/src/multicast-input.ts +793 -0
  730. package/src/multicast-manager.ts +2783 -0
  731. package/src/multicast-transport.ts +1058 -0
  732. package/src/multicast-unwrap.ts +132 -0
  733. package/src/no-modules.ts +21 -0
  734. package/src/phase14-catalog-override.ts +336 -0
  735. package/src/repair-fetch.ts +772 -0
  736. package/src/repair-layers.ts +533 -0
  737. package/src/sample-combiner.ts +938 -0
  738. package/src/signaling.ts +78 -0
  739. package/src/source-auth.ts +1 -0
  740. package/src/switching-set-alignment.ts +109 -0
  741. package/src/timestamp-codec.ts +265 -0
  742. package/src/track-pipeline-setup.ts +205 -0
  743. package/src/track-pipeline.ts +332 -0
  744. package/src/track-session.ts +1010 -0
  745. package/src/transport-pool.ts +128 -0
  746. package/src/wasm-mfu-reassembler.ts +405 -0
  747. package/src/wasm-mmtp-container-parser.ts +23 -0
  748. package/test-vectors/align.json +199 -0
  749. package/test-vectors/catalog/README.md +106 -0
  750. package/test-vectors/catalog/negative/archive-origin-with-path.json +33 -0
  751. package/test-vectors/catalog/negative/archive-unknown-field.json +34 -0
  752. package/test-vectors/catalog/negative/fec-block-profile-missing.json +51 -0
  753. package/test-vectors/catalog/negative/fec-block-profile-unknown.json +52 -0
  754. package/test-vectors/catalog/negative/fec-enhancement-repair-removed.json +28 -0
  755. package/test-vectors/catalog/negative/fec-repair-layer-geometry.json +33 -0
  756. package/test-vectors/catalog/negative/fec-repair-layer-missing-symbols.json +31 -0
  757. package/test-vectors/catalog/negative/fec-repair-multiple-depends.json +32 -0
  758. package/test-vectors/catalog/negative/fec-repair-overlay-with-layer.json +34 -0
  759. package/test-vectors/catalog/negative/fec-repair-priority-out-of-band.json +24 -0
  760. package/test-vectors/catalog/negative/fec-repair-source-symbols-without-scope.json +33 -0
  761. package/test-vectors/catalog/negative/legacy-selection-params.json +15 -0
  762. package/test-vectors/catalog/negative/media-track-repair-layer.json +16 -0
  763. package/test-vectors/catalog/negative/missing-mmtp-fields.json +12 -0
  764. package/test-vectors/catalog/negative/multicast-auth-padded-key.json +37 -0
  765. package/test-vectors/catalog/negative/multicast-auth-reused-track-format.json +53 -0
  766. package/test-vectors/catalog/negative/multicast-auth-trailing-pad-bits.json +37 -0
  767. package/test-vectors/catalog/negative/multicast-endpoint-missing-protocol-and-source.json +24 -0
  768. package/test-vectors/catalog/negative/multicast-network-source-empty.json +44 -0
  769. package/test-vectors/catalog/negative/multicast-packet-id-signaling.json +26 -0
  770. package/test-vectors/catalog/negative/network-source-object.json +25 -0
  771. package/test-vectors/catalog/negative/raptorq-source-symbols-over-max.json +24 -0
  772. package/test-vectors/catalog/negative/raptorq-unaligned-symbol.json +27 -0
  773. package/test-vectors/catalog/negative/repair-track-legacy-shape.json +19 -0
  774. package/test-vectors/catalog/negative/track-role-empty.json +16 -0
  775. package/test-vectors/catalog/negative/track-role-non-string.json +16 -0
  776. package/test-vectors/catalog/positive/archive-origin.json +34 -0
  777. package/test-vectors/catalog/positive/fec-iso-ssbg1.json +52 -0
  778. package/test-vectors/catalog/positive/fec-layered-repair.json +106 -0
  779. package/test-vectors/catalog/positive/fec-multicast.json +52 -0
  780. package/test-vectors/catalog/positive/flat-av.json +30 -0
  781. package/test-vectors/catalog/positive/multicast-auth-rotation.json +55 -0
  782. package/test-vectors/catalog/positive/multicast-native-ssm-no-network-source.json +43 -0
  783. package/test-vectors/catalog/positive/ticks-and-mixed-timescales.json +39 -0
  784. package/test-vectors/catalog/positive/track-role.json +49 -0
  785. package/test-vectors/catalog/releases/moqcast-native-v16-v1/catalog.json +65 -0
  786. package/test-vectors/catalog/releases/moqcast-native-v16-v1.archive.sha256 +1 -0
  787. package/test-vectors/catalog-multicast-auth-v1.json +26 -0
  788. package/test-vectors/catalog-validation.json +732 -0
  789. package/test-vectors/catalog-validator-public-api.mjs +25 -0
  790. package/test-vectors/generate-source-auth-fixture.mjs +114 -0
  791. package/test-vectors/hevc-x265-repeat-headers-v1.json +40 -0
  792. package/test-vectors/source-auth-v1.json +61 -0
  793. package/test-vectors/source-contiguity-v1.json +131 -0
  794. package/test-vectors/subsample-placement-v1.json +230 -0
  795. package/dist/mmtp.d.ts +0 -73
  796. package/dist/mmtp.d.ts.map +0 -1
  797. package/dist/mmtp.js +0 -210
  798. package/dist/mmtp.js.map +0 -1
  799. package/src/mmtp.ts +0 -277
@@ -0,0 +1,1502 @@
1
+ /**
2
+ * @blockcast/fec-worker — Worker Entry Point
3
+ *
4
+ * Runs inside DedicatedWorkerGlobalScope. Receives FecWorkerCommand messages,
5
+ * drives the MmtFecDecoder WASM module, emits FecWorkerEvent messages back.
6
+ *
7
+ * WASM bindings are resolved via:
8
+ * 1. (self as any).__MMT_WASM_BINDINGS__ — set by consumer's loader/importmap
9
+ * 2. Falls back to error if not available (no defaults)
10
+ *
11
+ * This file must NOT import DOM globals incompatible with Worker scope.
12
+ */
13
+
14
+ import type {
15
+ FecWorkerCommand,
16
+ FecWorkerEvent,
17
+ FecTrackConfig,
18
+ FecTrackStats,
19
+ FecBlockSnapshot,
20
+ FrameMeta,
21
+ FecBlockProfile,
22
+ } from "./fec-worker-types.js";
23
+ import { toOwnedArrayBuffer } from "./transfer.js";
24
+ import {
25
+ blockPrecedes,
26
+ blocksBehind,
27
+ isoBlockContains,
28
+ isoBlocksBehind,
29
+ isoBlocksOverlap,
30
+ serialDistance,
31
+ serialPrecedes,
32
+ } from "./block-order.js";
33
+
34
+ // --- WASM binding types (from mmt-wasm) ---
35
+ // Signatures MUST match a freshly generated external mmt_wasm.d.ts exactly.
36
+ // The decoder derives SBN/ESI internally — do not pre-compute.
37
+ interface WasmFecDecoder {
38
+ /**
39
+ * ISO 23008-1 §C.5.2: ss_id is the flat 32-bit Source Symbol ID.
40
+ * `k-padded-v1`: data is the source symbol. `iso-ssbg1-v1`: data is the
41
+ * protected packet, unpadded; the decoder builds the ssbg_mode1 symbol and
42
+ * holds it until a repair names the block containing it.
43
+ */
44
+ add_source(
45
+ ss_id: number,
46
+ data: Uint8Array,
47
+ timestamp: number,
48
+ ): Uint8Array | undefined;
49
+ /**
50
+ * ISO 23008-1 §C.5.3. `k-padded-v1`: repair ESI = configured K + rs_id,
51
+ * SBN = ss_start / configured K. `iso-ssbg1-v1`: the block is
52
+ * `[ss_start, ss_start + ssb_length)` with K' = ssb_length and repair
53
+ * ESI = ssb_length + rs_id; a recovered block is ssb_length x T bytes of
54
+ * ssbg_mode1 symbols.
55
+ */
56
+ add_repair(
57
+ ss_start: number,
58
+ ssb_length: number,
59
+ rs_id: number,
60
+ data: Uint8Array,
61
+ timestamp: number,
62
+ ): Uint8Array | undefined;
63
+ configure_params(k: number, symbolSize: number): void;
64
+ /**
65
+ * Retire the named blocks (last-chance decode + stats), driven by the
66
+ * manager's exact deadlines. Nothing is synthesised for a lost block:
67
+ * concealment is the sink's hold-last-good-frame.
68
+ */
69
+ cleanup_blocks_detailed(sbns: Uint32Array): void;
70
+ /**
71
+ * `k-padded-v1` only (throws under `iso-ssbg1-v1`): remove decoder blocks
72
+ * below its configured live-SBN interleave horizon; returns their SBNs.
73
+ */
74
+ cleanup_by_sbn(current_sbn: number): Uint32Array;
75
+ /**
76
+ * `iso-ssbg1-v1` only: remove decoder blocks, and held unplaced sources,
77
+ * more than interleaveDepth `isoBlocksBehind` the live SS_ID; returns the
78
+ * SS_Starts of the blocks it removed.
79
+ */
80
+ cleanup_by_ss_start(current: number): Uint32Array;
81
+ /** The block profile the decoder was constructed with. */
82
+ get_block_profile(): string;
83
+ flush(): void;
84
+ pending_blocks(): number;
85
+ reset_stats(): void;
86
+ get_stats(): {
87
+ source_packets: bigint;
88
+ repair_packets: bigint;
89
+ blocks_complete: bigint;
90
+ blocks_recovered: bigint;
91
+ blocks_failed: bigint;
92
+ bytes_recovered: bigint;
93
+ recovery_rate(): number;
94
+ };
95
+ free(): void;
96
+ }
97
+
98
+ interface WasmBindings {
99
+ initSync(module: { module: WebAssembly.Module }): unknown;
100
+ MmtFecDecoder: new (interleaveDepth: number, blockProfile: FecBlockProfile) => WasmFecDecoder;
101
+ }
102
+
103
+ // --- Worker state (local to this Worker instance) ---
104
+
105
+ let wasmDecoder: WasmFecDecoder | null = null;
106
+ let config: FecTrackConfig | null = null;
107
+ let altaEnabled = false;
108
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
109
+ let altaVerifier: any = null; // JsAltaVerifier instance (loaded from ALTA WASM)
110
+ let snapshotTimer: ReturnType<typeof setInterval> | null = null;
111
+ // blockUpdate events are posted only after a client subscribes (BLO-36599).
112
+ let blockUpdatesSubscribed = false;
113
+
114
+ // Block tracking
115
+ const blocks = new Map<number, FecBlockSnapshot>();
116
+ const acceptedSourceEsis = new Map<number, Set<number>>();
117
+ const acceptedRepairEsis = new Map<number, Set<number>>();
118
+ // iso-ssbg1-v1: sources fed to the decoder before any repair named a block
119
+ // containing them, keyed by SS_ID with their arrival time. Map order is
120
+ // arrival order, which the horizon sweep relies on (retireUnplacedSources).
121
+ const unplacedSources = new Map<number, number>();
122
+ const UINT32_MAX = 0xffff_ffff;
123
+ const UINT24_MAX = 0xff_ffff;
124
+ const SS_ID_SPAN = 0x1_0000_0000;
125
+ // ISO/IEC 23008-1 C.3.3 ssbg_mode1: the two-octet protected-packet length field.
126
+ const SSBG1_LENGTH_FIELD_BYTES = 2;
127
+ // ISO/IEC 23008-1 AL-FEC signaling carries interleave_depth in one byte.
128
+ const MAX_INTERLEAVE_DEPTH = 0xff;
129
+ // Rust stores T as u16 and this decoder profile fixes RFC 6330 Al=8.
130
+ const MAX_SYMBOL_SIZE = 0xfff8;
131
+ // RFC 6330 systematic table limit, enforced by the vendored raptorq decoder.
132
+ const MAX_RAPTORQ_SOURCE_SYMBOLS_PER_BLOCK = 56_403;
133
+
134
+ // Aggregate counters
135
+ let sourceSymbols = 0;
136
+ let repairSymbols = 0;
137
+ let blocksComplete = 0;
138
+ let blocksRecovered = 0;
139
+ let blocksFailed = 0;
140
+ let altaVerified = 0;
141
+ let altaFailed = 0; // Crypto-confirmed tamper (MAC / sig / hash chain mismatch)
142
+ const altaFailedDetails: Array<{
143
+ seq: number;
144
+ error: string;
145
+ authLen: number;
146
+ payloadLen: number;
147
+ path: string; // "direct" | "recovered" | "pending-retry"
148
+ }> = [];
149
+ let altaPending = 0; // Unverifiable but not tamper (no trailer, verifier not ready, anchor missing)
150
+ let altaPendingEvictedCap = 0; // Incoming "pending" packets rejected because the queue was at cap after stale-sweep (flood indicator)
151
+ let altaPendingEvictedTtl = 0; // Deferred-queue entries dropped by TTL (retry window expired)
152
+ let altaError = 0; // verifyDetached threw — infrastructure flake, not tamper
153
+ let altaInitFailed = false; // ALTA configured but WASM init failed; altaEnabled was forced off
154
+ let relayBlocksVerified = 0;
155
+ let relayBlocksFailed = 0;
156
+ let liveSbn = 0;
157
+ // Whether liveSbn has been set by a symbol of this decoder epoch. Block order
158
+ // is serial on the 32-bit SS_ID (block-order.ts, BLO-35597 iso:5), where 0 is
159
+ // a real position, so "nothing seen yet" needs its own state.
160
+ let liveSbnKnown = false;
161
+ let repairSymbolSizeMismatch = 0;
162
+ // Source symbols dropped before WASM, by cause. All three sites below were
163
+ // silent, which left the "block settles at exactly K-1 source symbols" shape
164
+ // (BLO-35597 defect 2) with no evidence to attribute it to. A retired drop
165
+ // means a late symbol past retention; a terminal drop means the block had
166
+ // already settled — `state !== "collecting"`, so complete OR recovered OR
167
+ // FAILED, and the failed arm is the one this issue is chasing (a block aged out
168
+ // by synchronizeDecoderHorizon stays in `blocks` until its SBN retires, so a
169
+ // late symbol for a short block lands here); a duplicate drop means a repeated
170
+ // (SBN, ESI), which on this wire is usually a plain duplicate packet — the
171
+ // router's ledger has deliberate gaps — and only sometimes an SBN/ESI
172
+ // derivation collision.
173
+ //
174
+ // These narrow the cause; they do not uniquely attribute it. A symbol LOST IN
175
+ // TRANSIT never reaches handleFeedSource and increments nothing, so all three
176
+ // reading zero on a short block is NOT evidence of a publisher defect — wire
177
+ // loss is the null hypothesis here, and it produces an identical shape. The
178
+ // discriminator for that residual arm is `block.ssbLength` (set at the repair
179
+ // symbol's declared SSB_length, conflict-checked on arrival):
180
+ // ssbLength < k → genuinely short authored block
181
+ // ssbLength === k, sourceReceived < k → loss or a still-late symbol
182
+ // With no repair symbol yet seen, ssbLength is undefined and both stay open.
183
+ let sourceDroppedRetired = 0;
184
+ let sourceDroppedTerminal = 0;
185
+ let sourceDroppedDuplicate = 0;
186
+ let sourceUnplacedRetired = 0;
187
+
188
+ // Recovery timing
189
+ const recoveryTimes: number[] = [];
190
+
191
+ // ── Deferred ALTA verification queue (08-07 Path 3) ───────────────────────
192
+ //
193
+ // When an ALTA packet arrives whose MAC references point at sequences not yet
194
+ // in the verifier's ring buffer, we can't anchor trust — verify returns "No
195
+ // trust anchor". Typical cause: a predecessor packet was lost and hasn't been
196
+ // FEC-recovered yet. Rather than drop these, hold them in a bounded FIFO and
197
+ // retry verification after every successful verify (direct or FEC-recovered)
198
+ // that seeds the ring buffer with new raw bytes.
199
+ //
200
+ // Zero-copy (1B-B): the queue entry stores a Uint8Array view into the
201
+ // original `data` ArrayBuffer. JS GC keeps the buffer alive via the view
202
+ // reference; no bytes are copied on queue.
203
+ //
204
+ // Timeout entries (TTL exceeded) and cap-rejections count as `altaPending`
205
+ // (2C-B): they're not tamper indicators, they're just packets that couldn't
206
+ // be authenticated within the receive window — either loss or, in the cap
207
+ // case, a flood of unanchorable arrivals (real or attacker-driven) that
208
+ // exceeded the queue budget. See enqueuePendingVerify for the rate-limit
209
+ // protocol.
210
+ interface PendingVerify {
211
+ seq: number;
212
+ auth: Uint8Array;
213
+ payload: Uint8Array;
214
+ arrivalTs: number;
215
+ }
216
+ const pendingVerify = new Map<number, PendingVerify>();
217
+ let pendingVerifyCap = 0;
218
+ let pendingVerifyTtlMs = 0;
219
+
220
+ // --- Helpers ---
221
+
222
+ function emit(event: FecWorkerEvent, transfer?: Transferable[]): void {
223
+ self.postMessage(event, transfer ?? []);
224
+ }
225
+
226
+ function isUint32(value: number): boolean {
227
+ return Number.isInteger(value) && value >= 0 && value <= UINT32_MAX;
228
+ }
229
+
230
+ function isTerminalBlock(block: FecBlockSnapshot): boolean {
231
+ return block.state !== "collecting";
232
+ }
233
+
234
+ function retireAcceptedIds(sbn: number): void {
235
+ acceptedSourceEsis.delete(sbn);
236
+ acceptedRepairEsis.delete(sbn);
237
+ }
238
+
239
+ function clearBlockTracking(): void {
240
+ blocks.clear();
241
+ acceptedSourceEsis.clear();
242
+ acceptedRepairEsis.clear();
243
+ unplacedSources.clear();
244
+ pendingVerify.clear();
245
+ recoveryTimes.length = 0;
246
+ liveSbn = 0;
247
+ liveSbnKnown = false;
248
+ }
249
+
250
+ /**
251
+ * Whether `sbn` has fallen behind the decoder's retained interleave window:
252
+ * more than `interleaveDepth` blocks behind the live SBN. liveSbn only moves
253
+ * forward within a decoder epoch and configure() resets both, so this is the
254
+ * monotonic retirement frontier the decoder horizon implies.
255
+ */
256
+ function isRetiredSbn(sbn: number): boolean {
257
+ if (config === null || !liveSbnKnown) return false;
258
+ // iso-ssbg1-v1: `sbn` is an SS_ID (a block's SS_Start or a source's own
259
+ // SS_ID) and liveSbn the live SS_ID; K is only the maximum block length.
260
+ return config.blockProfile === "iso-ssbg1-v1"
261
+ ? isoBlocksBehind(sbn, liveSbn, config.k) > config.interleaveDepth
262
+ : blocksBehind(sbn, liveSbn, config.k) > config.interleaveDepth;
263
+ }
264
+
265
+ function isIsoProfile(): boolean {
266
+ return config?.blockProfile === "iso-ssbg1-v1";
267
+ }
268
+
269
+ /**
270
+ * iso-ssbg1-v1: the SS_IDs a known block certainly covers. Every block a
271
+ * repair created has its SSB_length; a cleanup tombstone for a block no
272
+ * repair declared certainly holds only its own SS_Start.
273
+ */
274
+ function isoKnownLength(block: FecBlockSnapshot): number {
275
+ return block.ssbLength ?? 1;
276
+ }
277
+
278
+ /** iso-ssbg1-v1: the known block whose SS_ID range contains `ssId` (ranges never overlap). */
279
+ function findIsoBlock(ssId: number): FecBlockSnapshot | undefined {
280
+ for (const block of blocks.values()) {
281
+ if (isoBlockContains(block.sbn, isoKnownLength(block), ssId)) return block;
282
+ }
283
+ return undefined;
284
+ }
285
+
286
+ /**
287
+ * Whether a terminal block's diagnostics may be deleted. Under iso-ssbg1-v1 a
288
+ * source carries no block identity, so a deleted block cannot refuse a late
289
+ * source of its own: that source would be admitted as unplaced and reopen the
290
+ * block in the decoder. The tombstone therefore stays until its LAST SS_ID is
291
+ * retired, after which the SS_ID retirement check refuses every member.
292
+ */
293
+ function isRetiredBlock(key: number, block: FecBlockSnapshot): boolean {
294
+ return isIsoProfile()
295
+ ? isRetiredSbn((key + isoKnownLength(block) - 1) % SS_ID_SPAN)
296
+ : isRetiredSbn(key);
297
+ }
298
+
299
+ /**
300
+ * Retire terminal diagnostics only after the decoder's monotonic interleave
301
+ * horizon proves that the SBN can no longer be valid out-of-order media.
302
+ * Admission keeps the compact frontier as the durable tombstone, so deletion
303
+ * never reopens a finalized block.
304
+ */
305
+ function trimBlockRetention(): void {
306
+ for (const [sbn, block] of blocks) {
307
+ if (!isTerminalBlock(block) || !isRetiredBlock(sbn, block)) continue;
308
+ blocks.delete(sbn);
309
+ retireAcceptedIds(sbn);
310
+ }
311
+ }
312
+
313
+ /** Advance the live position: an SBN (k-padded-v1) or an SS_ID / SS_Start (iso-ssbg1-v1). */
314
+ function advanceMediaHorizon(sbn: number): void {
315
+ if (!config) return;
316
+ if (liveSbnKnown) {
317
+ const ahead = config.blockProfile === "iso-ssbg1-v1"
318
+ ? serialPrecedes(liveSbn, sbn)
319
+ : blockPrecedes(liveSbn, sbn, config.k);
320
+ if (!ahead) return;
321
+ }
322
+ liveSbn = sbn;
323
+ liveSbnKnown = true;
324
+ trimBlockRetention();
325
+ }
326
+
327
+ function markBlockTerminal(
328
+ block: FecBlockSnapshot,
329
+ state: "complete" | "recovered" | "failed",
330
+ ): void {
331
+ block.state = state;
332
+ block.completedTs = performance.now();
333
+ retireAcceptedIds(block.sbn);
334
+ trimBlockRetention();
335
+ }
336
+
337
+ function synchronizeDecoderHorizon(): void {
338
+ if (!wasmDecoder || !liveSbnKnown) return;
339
+ const iso = isIsoProfile();
340
+ const expired = iso
341
+ ? wasmDecoder.cleanup_by_ss_start(liveSbn)
342
+ : wasmDecoder.cleanup_by_sbn(liveSbn);
343
+ for (const sbn of expired) {
344
+ const block = blocks.get(sbn);
345
+ if (!block || isTerminalBlock(block)) {
346
+ retireAcceptedIds(sbn);
347
+ continue;
348
+ }
349
+ markBlockTerminal(block, "failed");
350
+ blocksFailed++;
351
+ emitBlockUpdate(block);
352
+ }
353
+ if (iso) retireUnplacedSources();
354
+ trimBlockRetention();
355
+ }
356
+
357
+ /**
358
+ * iso-ssbg1-v1: retire unplaced sources behind the horizon, by the same
359
+ * isoBlocksBehind metric as a block start, sweeping in arrival order and
360
+ * stopping at the first live one (amortised O(1) per symbol). A source that
361
+ * arrived out of order behind a newer live one is retired when it reaches the
362
+ * front; until then nothing can place it, because any repair range holding it
363
+ * starts at or before it and is itself retired. One the live position moved
364
+ * at least half the SS_ID space past no longer precedes it serially; it is
365
+ * retired too rather than pinning the sweep.
366
+ */
367
+ function retireUnplacedSources(): void {
368
+ for (const ssId of unplacedSources.keys()) {
369
+ const unreachable = ssId !== liveSbn && !serialPrecedes(ssId, liveSbn);
370
+ if (!unreachable && !isRetiredSbn(ssId)) break;
371
+ unplacedSources.delete(ssId);
372
+ sourceUnplacedRetired++;
373
+ }
374
+ }
375
+
376
+ /**
377
+ * Run detached ALTA verify against the current verifier. Four-state result:
378
+ * - "valid": crypto passed, packet authentic. Caller increments altaVerified.
379
+ * - "failed": crypto confirmed tamper (MAC/sig mismatch, hash chain mismatch).
380
+ * Caller increments altaFailed. Not retryable.
381
+ * - "pending": anchor missing (MAC refs not in buffer, no signature, not
382
+ * early-warmup). Caller may enqueue for retry after the next
383
+ * successful verify seeds the ring buffer.
384
+ * - "error": verifier threw — WASM trap, unmarshal panic, OOM. Caller
385
+ * increments altaError (infrastructure flake, not tamper).
386
+ * The distinction matters for ops alerting: a spike in
387
+ * altaError indicates a runtime problem, not an attack.
388
+ */
389
+ let _lastVerifyError = "";
390
+ function runDetachedVerify(
391
+ auth: Uint8Array,
392
+ payload: Uint8Array,
393
+ ): "valid" | "failed" | "pending" | "error" {
394
+ if (!altaVerifier) return "pending";
395
+ try {
396
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
397
+ const r: any = altaVerifier.verifyDetached(auth, payload);
398
+ if (r.valid) {
399
+ _lastVerifyError = "";
400
+ return "valid";
401
+ }
402
+ const err = (r.error as string | undefined) ?? "";
403
+ _lastVerifyError = err;
404
+ // The bootstrap-friendly verifier returns this specific error string when
405
+ // the only obstacle to verification is a missing anchor (see alta-rs
406
+ // verifier.rs verify()). Distinguishing this from tamper cases keeps us
407
+ // from queuing packets that are definitively invalid.
408
+ if (err.startsWith("No trust anchor")) return "pending";
409
+ return "failed";
410
+ } catch (e) {
411
+ // Ratelimit: first ~N occurrences get logged. A sustained exception
412
+ // stream indicates a real problem — the altaError counter is the
413
+ // durable signal.
414
+ if (altaError < 5) {
415
+ console.warn("[FecWorker] verifyDetached threw:", e);
416
+ }
417
+ return "error";
418
+ }
419
+ }
420
+
421
+ /** Extract u32 BE sequence number from the ALTA authenticator header (offset 0..3). */
422
+ function seqFromAuth(auth: Uint8Array): number {
423
+ return (
424
+ ((auth[0]! << 24) | (auth[1]! << 16) | (auth[2]! << 8) | auth[3]!) >>> 0
425
+ );
426
+ }
427
+
428
+ /**
429
+ * Enqueue a packet whose ALTA verification returned "pending" (anchor
430
+ * missing). Dedupes by sequence number.
431
+ *
432
+ * Flood resistance (08-08 review): when the queue is at cap we do NOT
433
+ * evict the oldest entry. The oldest entry is typically the most
434
+ * load-bearing one — it's the packet whose anchor everything else in the
435
+ * queue is waiting on. Evict-oldest-on-insert let an attacker emitting a
436
+ * stream of "No trust anchor" packets flood out legitimate entries and
437
+ * prevent them from ever anchoring. Instead:
438
+ * 1. First try to free stale TTL'd entries (they'd be evicted on next
439
+ * drain anyway — pulling that forward is free).
440
+ * 2. If still full, reject the NEW entry. altaPendingEvictedCap counts
441
+ * these rejections (rate-limit trigger).
442
+ *
443
+ * Stores Uint8Array views into the original data + auth buffers — no copy.
444
+ * The views keep their underlying ArrayBuffers alive via GC references.
445
+ */
446
+ function enqueuePendingVerify(
447
+ auth: Uint8Array,
448
+ payload: Uint8Array,
449
+ ): void {
450
+ const seq = seqFromAuth(auth);
451
+ if (pendingVerify.has(seq)) return;
452
+
453
+ if (pendingVerify.size >= pendingVerifyCap) {
454
+ // Step 1: sweep stale entries so legitimate traffic isn't penalized
455
+ // by TTL'd junk that happens to still occupy slots.
456
+ const now = performance.now();
457
+ for (const [staleSeq, entry] of pendingVerify) {
458
+ if (now - entry.arrivalTs > pendingVerifyTtlMs) {
459
+ pendingVerify.delete(staleSeq);
460
+ altaPendingEvictedTtl++;
461
+ }
462
+ }
463
+ // Step 2: if the queue is still at cap it's a genuine flood —
464
+ // reject the new entry rather than evict an existing one. The
465
+ // rejected packet counts as altaPendingEvictedCap (which is
466
+ // surfaced up through stats for rate-limit alerting).
467
+ if (pendingVerify.size >= pendingVerifyCap) {
468
+ altaPendingEvictedCap++;
469
+ return;
470
+ }
471
+ }
472
+
473
+ pendingVerify.set(seq, {
474
+ seq,
475
+ auth,
476
+ payload,
477
+ arrivalTs: performance.now(),
478
+ });
479
+ }
480
+
481
+ /**
482
+ * Retry every queued entry. Called after every successful verify (direct
483
+ * source or FEC-recovered) — new raw bytes in the verifier's ring buffer may
484
+ * unblock previously-pending MAC references.
485
+ *
486
+ * Three outcomes per entry:
487
+ * - "valid" → remove from queue, altaVerified++
488
+ * - "failed" → remove from queue, altaFailed++ (tamper on retry)
489
+ * - "pending" → keep in queue, unless TTL expired (then evict as altaPending)
490
+ *
491
+ * Iteration order: Map preserves insertion order, so we drain oldest-first.
492
+ * No-op when the queue is empty or the verifier isn't ready.
493
+ */
494
+ function drainPendingVerify(): void {
495
+ if (pendingVerify.size === 0 || !altaVerifier) return;
496
+ const now = performance.now();
497
+ // Collect keys to avoid mutating Map during iteration
498
+ const keys = Array.from(pendingVerify.keys());
499
+ for (const seq of keys) {
500
+ const entry = pendingVerify.get(seq);
501
+ if (!entry) continue;
502
+ if (now - entry.arrivalTs > pendingVerifyTtlMs) {
503
+ pendingVerify.delete(seq);
504
+ altaPendingEvictedTtl++;
505
+ continue;
506
+ }
507
+ const result = runDetachedVerify(entry.auth, entry.payload);
508
+ if (result === "valid") {
509
+ pendingVerify.delete(seq);
510
+ altaVerified++;
511
+ } else if (result === "failed") {
512
+ pendingVerify.delete(seq);
513
+ altaFailed++;
514
+ if (altaFailedDetails.length < 16) {
515
+ altaFailedDetails.push({
516
+ seq: entry.seq,
517
+ error: _lastVerifyError,
518
+ authLen: entry.auth.length,
519
+ payloadLen: entry.payload.length,
520
+ path: "pending-retry",
521
+ });
522
+ }
523
+ } else if (result === "error") {
524
+ // Infrastructure failure — remove from queue, count as altaError.
525
+ // Keeping it around would just re-throw on every subsequent drain.
526
+ pendingVerify.delete(seq);
527
+ altaError++;
528
+ }
529
+ // "pending" → leave in queue for next drain
530
+ }
531
+ }
532
+
533
+ /**
534
+ * Look up or create a block. `isoSsbLength` creates an iso-ssbg1-v1 block,
535
+ * whose K' is its SSB_length (C.5.3); without it the block has the catalog K.
536
+ */
537
+ function getOrCreateBlock(sbn: number, isoSsbLength?: number): FecBlockSnapshot {
538
+ let block = blocks.get(sbn);
539
+ if (!block) {
540
+ if (!config) throw new Error("[FecWorker] not configured");
541
+ const now = performance.now();
542
+ block = {
543
+ sbn,
544
+ state: "collecting",
545
+ sourceReceived: 0,
546
+ repairReceived: 0,
547
+ k: isoSsbLength ?? config.k,
548
+ repairCount: config.repairCount,
549
+ firstSymbolTs: now,
550
+ lastSymbolTs: now,
551
+ deadlineTs: now + config.deliveryWindowMs,
552
+ altaVerified: 0,
553
+ relayBlockSigValid: null,
554
+ };
555
+ if (isoSsbLength !== undefined) block.ssbLength = isoSsbLength;
556
+ blocks.set(sbn, block);
557
+ trimBlockRetention();
558
+ }
559
+ return block;
560
+ }
561
+
562
+ function buildStatsSnapshot(): FecTrackStats {
563
+ if (!config) throw new Error("[FecWorker] not configured");
564
+ const decoderStats = wasmDecoder?.get_stats();
565
+ const decoder = decoderStats
566
+ ? {
567
+ sourcePackets: Number(decoderStats.source_packets),
568
+ repairPackets: Number(decoderStats.repair_packets),
569
+ blocksComplete: Number(decoderStats.blocks_complete),
570
+ blocksRecovered: Number(decoderStats.blocks_recovered),
571
+ blocksFailed: Number(decoderStats.blocks_failed),
572
+ bytesRecovered: Number(decoderStats.bytes_recovered),
573
+ pendingBlocks: wasmDecoder?.pending_blocks() ?? 0,
574
+ }
575
+ : undefined;
576
+
577
+ const avgRecoveryMs =
578
+ recoveryTimes.length > 0
579
+ ? recoveryTimes.reduce((a, b) => a + b, 0) / recoveryTimes.length
580
+ : 0;
581
+ const maxRecoveryMs =
582
+ recoveryTimes.length > 0 ? Math.max(...recoveryTimes) : 0;
583
+
584
+ // Cap block array to interleave window
585
+ const maxBlocks = Math.max(config.interleaveDepth * 3, 24);
586
+ const blockArray = Array.from(blocks.values())
587
+ .sort((a, b) => b.sbn - a.sbn)
588
+ .slice(0, maxBlocks);
589
+
590
+ return {
591
+ config,
592
+ sourceSymbols,
593
+ repairSymbols,
594
+ blocksComplete,
595
+ blocksRecovered,
596
+ blocksFailed,
597
+ recoveryRate:
598
+ blocksComplete + blocksRecovered > 0
599
+ ? Math.round(
600
+ (blocksRecovered / (blocksRecovered + blocksFailed)) * 100,
601
+ ) || 0
602
+ : 0,
603
+ altaVerified,
604
+ altaFailed,
605
+ altaPending,
606
+ altaPendingEvictedCap,
607
+ altaPendingEvictedTtl,
608
+ altaError,
609
+ altaInitFailed,
610
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
611
+ altaFailedDetails: altaFailedDetails.slice() as any,
612
+ relayBlocksVerified,
613
+ relayBlocksFailed,
614
+ liveSbn,
615
+ blocks: blockArray,
616
+ interleaveWindowBlocks: config.interleaveDepth,
617
+ avgRecoveryMs: Math.round(avgRecoveryMs * 100) / 100,
618
+ maxRecoveryMs: Math.round(maxRecoveryMs * 100) / 100,
619
+ repairSymbolSizeMismatch,
620
+ sourceDroppedRetired,
621
+ sourceDroppedTerminal,
622
+ sourceDroppedDuplicate,
623
+ ...(config.blockProfile === "iso-ssbg1-v1" ? { sourceUnplacedRetired } : {}),
624
+ decoder,
625
+ };
626
+ }
627
+
628
+ function emitSnapshot(): void {
629
+ if (!config) return;
630
+ emit({ type: "snapshot", stats: buildStatsSnapshot() });
631
+ }
632
+
633
+ function emitBlockUpdate(block: FecBlockSnapshot): void {
634
+ if (!blockUpdatesSubscribed) return;
635
+ emit({ type: "blockUpdate", block: { ...block } });
636
+ }
637
+
638
+ function emitRecoveredSymbols(
639
+ sbn: number,
640
+ recoveredBlock: Uint8Array,
641
+ block: FecBlockSnapshot,
642
+ ): void {
643
+ if (!config) return;
644
+ const symbolSize = config.symbolSize;
645
+ if (symbolSize <= 0) {
646
+ emit({
647
+ type: "error",
648
+ code: "recovered-symbol-size-unconfigured",
649
+ message: "[FecWorker] recovered block has no configured symbolSize",
650
+ });
651
+ return;
652
+ }
653
+ // The decoder returns only the block's authored source symbols
654
+ // (SSB_length x T); the encoder's zero padding is not media.
655
+ const ssbLength = block.ssbLength ?? config.k;
656
+ if (recoveredBlock.byteLength !== symbolSize * ssbLength) {
657
+ emit({
658
+ type: "error",
659
+ code: "recovered-block-too-short",
660
+ message: `[FecWorker] recovered block has the wrong length: got ${recoveredBlock.byteLength}B, expected ${symbolSize * ssbLength}B (SSB_length=${ssbLength})`,
661
+ });
662
+ return;
663
+ }
664
+
665
+ const iso = config.blockProfile === "iso-ssbg1-v1";
666
+ for (let i = 0; i < ssbLength; i++) {
667
+ const symbol = recoveredBlock.subarray(i * symbolSize, (i + 1) * symbolSize);
668
+ // ssbg_mode1 symbols are emitted whole: the router trims them by their
669
+ // length field. A field past T-2 or non-zero padding means the
670
+ // recovered symbol is not what was sent, so it is refused, not trimmed.
671
+ if (iso) {
672
+ const invalid = invalidSsbg1Symbol(symbol, symbolSize);
673
+ if (invalid !== null) {
674
+ emit({
675
+ type: "error",
676
+ code: "recovered-ssbg1-symbol-invalid",
677
+ message: `[FecWorker] Dropping recovered ssbg_mode1 symbol (ssStart=${sbn}, esi=${i}, SSB_length=${ssbLength}): ${invalid}`,
678
+ });
679
+ continue;
680
+ }
681
+ }
682
+
683
+ // Source authentication now runs once at router admission in
684
+ // @blockcast/mmt-manifest-verify. The worker only records that this
685
+ // legacy ALTA-enabled symbol bypassed hot-path verification.
686
+ const recoveredAltaVerified: boolean | null = null;
687
+ if (altaEnabled) altaPending++;
688
+
689
+ const buf = toOwnedArrayBuffer(symbol);
690
+ const meta: FrameMeta = {
691
+ altaVerified: recoveredAltaVerified,
692
+ recovered: true,
693
+ sbn,
694
+ esi: i,
695
+ ssbLength,
696
+ };
697
+ emit({ type: "frame", data: buf, meta }, [buf]);
698
+ }
699
+ }
700
+
701
+ /** Why a T-byte ssbg_mode1 symbol is malformed (C.3.3), or null when it is well formed. */
702
+ function invalidSsbg1Symbol(symbol: Uint8Array, symbolSize: number): string | null {
703
+ const length = (symbol[0]! << 8) | symbol[1]!;
704
+ const maxLength = symbolSize - SSBG1_LENGTH_FIELD_BYTES;
705
+ if (length > maxLength) {
706
+ return `length field ${length} exceeds T-2 = ${maxLength}`;
707
+ }
708
+ for (let j = SSBG1_LENGTH_FIELD_BYTES + length; j < symbolSize; j++) {
709
+ if (symbol[j] !== 0) return `non-zero padding at byte ${j} after a ${length}-byte packet`;
710
+ }
711
+ return null;
712
+ }
713
+
714
+ function startSnapshotTimer(): void {
715
+ if (snapshotTimer !== null) clearInterval(snapshotTimer);
716
+ snapshotTimer = setInterval(() => emitSnapshot(), 200);
717
+ }
718
+
719
+ function resolveWasmBindings(): WasmBindings {
720
+ const bindings = (self as unknown as Record<string, unknown>)
721
+ .__MMT_WASM_BINDINGS__ as WasmBindings | undefined;
722
+ if (!bindings) {
723
+ throw new Error(
724
+ "[FecWorker] WASM bindings not available. Set self.__MMT_WASM_BINDINGS__ = { initSync, MmtFecDecoder } before configure.",
725
+ );
726
+ }
727
+ return bindings;
728
+ }
729
+
730
+ /**
731
+ * Refuse a decoder that does not run the configured block profile: one that
732
+ * ignored or predates the constructor argument would apply one profile's
733
+ * block math to the other's wire.
734
+ */
735
+ function verifyDecoderProfile(decoder: WasmFecDecoder, expected: FecBlockProfile): void {
736
+ let actual: unknown;
737
+ try {
738
+ actual = decoder.get_block_profile();
739
+ } catch (err) {
740
+ decoder.free();
741
+ throw new Error(
742
+ `[FecWorker] configure: decoder cannot report its block profile (${err instanceof Error ? err.message : String(err)})`,
743
+ );
744
+ }
745
+ if (actual !== expected) {
746
+ decoder.free();
747
+ throw new Error(
748
+ `[FecWorker] configure: decoder runs block profile ${JSON.stringify(actual)}, not the configured "${expected}"`,
749
+ );
750
+ }
751
+ }
752
+
753
+ function acceptId(ids: Map<number, Set<number>>, key: number, id: number): void {
754
+ let set = ids.get(key);
755
+ if (!set) {
756
+ set = new Set<number>();
757
+ ids.set(key, set);
758
+ }
759
+ set.add(id);
760
+ }
761
+
762
+ /** A block the decoder returned: record its recovery and emit its source symbols. */
763
+ function recordRecovered(key: number, block: FecBlockSnapshot, result: Uint8Array): void {
764
+ const recovered = new Uint8Array(toOwnedArrayBuffer(result));
765
+ const recoveryMs = performance.now() - block.firstSymbolTs;
766
+ recoveryTimes.push(recoveryMs);
767
+ // Cap recovery times array at 1000 entries
768
+ if (recoveryTimes.length > 1000) recoveryTimes.shift();
769
+
770
+ if (block.state === "collecting") {
771
+ markBlockTerminal(block, "recovered");
772
+ blocksRecovered++;
773
+ emitBlockUpdate(block);
774
+ }
775
+ emitRecoveredSymbols(key, recovered, block);
776
+ }
777
+
778
+ /** Settle a block after one of its source symbols was accepted. */
779
+ function settleSourceArrival(
780
+ key: number,
781
+ block: FecBlockSnapshot,
782
+ result: Uint8Array | undefined,
783
+ ): void {
784
+ // Check if block is complete (all authored source symbols received:
785
+ // SSB_length once a repair symbol declared it, otherwise K)
786
+ const authoredLength = block.ssbLength ?? block.k;
787
+ if (
788
+ block.sourceReceived >= authoredLength &&
789
+ block.state === "collecting"
790
+ ) {
791
+ markBlockTerminal(block, "complete");
792
+ blocksComplete++;
793
+ emitBlockUpdate(block);
794
+ }
795
+
796
+ // A source-triggered return can be either clean block completion or recovery
797
+ // of earlier missing symbols after repair arrived. Clean completion is
798
+ // already routed by the source path, so only emit when the block did not
799
+ // reach K source arrivals.
800
+ if (result && result.byteLength > 0 && block.sourceReceived < authoredLength) {
801
+ recordRecovered(key, block, result);
802
+ }
803
+ synchronizeDecoderHorizon();
804
+ }
805
+
806
+ /** Settle a block after one of its repair symbols was accepted. */
807
+ function settleRepairArrival(
808
+ key: number,
809
+ block: FecBlockSnapshot,
810
+ ssbLength: number,
811
+ result: Uint8Array | undefined,
812
+ ): void {
813
+ // Every authored source symbol already arrived: knowing SSB_length makes
814
+ // the block clean. The decoder's return is the source already routed.
815
+ // Otherwise, returned data is the recovered source block: its SSB_length
816
+ // fixed-width authored symbols concatenated. Split it before emitting so
817
+ // MmtpRouter gets the same one-symbol shape as a direct source packet.
818
+ if (block.sourceReceived >= ssbLength && block.state === "collecting") {
819
+ markBlockTerminal(block, "complete");
820
+ blocksComplete++;
821
+ emitBlockUpdate(block);
822
+ } else if (result && result.byteLength > 0) {
823
+ recordRecovered(key, block, result);
824
+ }
825
+ synchronizeDecoderHorizon();
826
+ }
827
+
828
+ /**
829
+ * iso-ssbg1-v1 source admission. A source inside a known block is accounted
830
+ * at ESI = SS_ID - SS_Start. One no repair has placed yet is held unplaced,
831
+ * deduplicated by SS_ID, and still fed to the decoder, which holds it too,
832
+ * until a repair names a range containing it.
833
+ */
834
+ function feedIsoSource(
835
+ cmd: Extract<FecWorkerCommand, { type: "feedSource" }>,
836
+ view: Uint8Array,
837
+ decoder: WasmFecDecoder,
838
+ cfg: FecTrackConfig,
839
+ ): void {
840
+ const maxPacketLength = cfg.symbolSize - SSBG1_LENGTH_FIELD_BYTES;
841
+ if (view.byteLength > maxPacketLength) {
842
+ throw new Error(
843
+ `[FecWorker] feedSource: iso-ssbg1-v1 protected packet of ${view.byteLength}B exceeds T-2 = ${maxPacketLength}B`,
844
+ );
845
+ }
846
+ const block = findIsoBlock(cmd.ssId);
847
+ if (!block) {
848
+ if (isRetiredSbn(cmd.ssId)) {
849
+ sourceDroppedRetired++;
850
+ return;
851
+ }
852
+ if (unplacedSources.has(cmd.ssId)) {
853
+ sourceDroppedDuplicate++;
854
+ return;
855
+ }
856
+ const result = decoder.add_source(cmd.ssId, view, cmd.ts);
857
+ advanceMediaHorizon(cmd.ssId);
858
+ unplacedSources.set(cmd.ssId, performance.now());
859
+ sourceSymbols++;
860
+ if (altaEnabled) altaPending++;
861
+ // No repair has named this source's block, so the decoder cannot have
862
+ // one to recover; data here is a Worker/decoder disagreement that
863
+ // cannot be attributed to a block, and is refused rather than guessed.
864
+ if (result && result.byteLength > 0) {
865
+ emit({
866
+ type: "error",
867
+ code: "recovered-block-unplaced",
868
+ message: `[FecWorker] Dropping a ${result.byteLength}B recovered block returned for SS_ID ${cmd.ssId}, which no known block contains`,
869
+ });
870
+ }
871
+ synchronizeDecoderHorizon();
872
+ return;
873
+ }
874
+
875
+ const key = block.sbn;
876
+ if (isRetiredSbn(key)) {
877
+ sourceDroppedRetired++;
878
+ return;
879
+ }
880
+ if (isTerminalBlock(block)) {
881
+ sourceDroppedTerminal++;
882
+ return;
883
+ }
884
+ const esi = serialDistance(key, cmd.ssId);
885
+ if (acceptedSourceEsis.get(key)?.has(esi)) {
886
+ sourceDroppedDuplicate++;
887
+ return;
888
+ }
889
+
890
+ const result = decoder.add_source(cmd.ssId, view, cmd.ts);
891
+ advanceMediaHorizon(cmd.ssId);
892
+ acceptId(acceptedSourceEsis, key, esi);
893
+ block.sourceReceived++;
894
+ block.lastSymbolTs = performance.now();
895
+ sourceSymbols++;
896
+ if (altaEnabled) altaPending++;
897
+ settleSourceArrival(key, block, result);
898
+ }
899
+
900
+ /** iso-ssbg1-v1: a known block sharing an SS_ID with `[ssStart, ssStart + ssbLength)`. */
901
+ function findOverlappingIsoBlock(
902
+ ssStart: number,
903
+ ssbLength: number,
904
+ ): FecBlockSnapshot | undefined {
905
+ for (const block of blocks.values()) {
906
+ if (isoBlocksOverlap(block.sbn, isoKnownLength(block), ssStart, ssbLength)) return block;
907
+ }
908
+ return undefined;
909
+ }
910
+
911
+ /**
912
+ * iso-ssbg1-v1: create the block a repair first named, with K' = SSB_length,
913
+ * and move every unplaced source in its range into its accounting.
914
+ */
915
+ function placeIsoBlock(ssStart: number, ssbLength: number): FecBlockSnapshot {
916
+ const block = getOrCreateBlock(ssStart, ssbLength);
917
+ for (let esi = 0; esi < ssbLength; esi++) {
918
+ const ssId = (ssStart + esi) % SS_ID_SPAN;
919
+ const arrivalTs = unplacedSources.get(ssId);
920
+ if (arrivalTs === undefined) continue;
921
+ unplacedSources.delete(ssId);
922
+ acceptId(acceptedSourceEsis, ssStart, esi);
923
+ block.sourceReceived++;
924
+ if (arrivalTs < block.firstSymbolTs) block.firstSymbolTs = arrivalTs;
925
+ }
926
+ return block;
927
+ }
928
+
929
+ /**
930
+ * iso-ssbg1-v1 repair admission. The block is `[SS_Start, SS_Start +
931
+ * SSB_length)` keyed by SS_Start, with repair ESI = SSB_length + RS_ID. A
932
+ * known block's SSB_length is checked before deduplication because the
933
+ * dedup key depends on it.
934
+ */
935
+ function feedIsoRepair(
936
+ cmd: Extract<FecWorkerCommand, { type: "feedRepair" }>,
937
+ view: Uint8Array,
938
+ decoder: WasmFecDecoder,
939
+ ): void {
940
+ const key = cmd.ssStart;
941
+ if (isRetiredSbn(key)) return;
942
+ const existingBlock = blocks.get(key);
943
+ if (existingBlock && isTerminalBlock(existingBlock)) return;
944
+ if (existingBlock) {
945
+ if (existingBlock.ssbLength !== cmd.ssbLength) {
946
+ emit({
947
+ type: "error",
948
+ code: "repair-ssb-length-conflict",
949
+ message: `[FecWorker] Dropping repair symbol whose SSB_length ${cmd.ssbLength} conflicts with ${existingBlock.ssbLength} (ssStart=${key})`,
950
+ });
951
+ return;
952
+ }
953
+ } else {
954
+ const overlapped = findOverlappingIsoBlock(key, cmd.ssbLength);
955
+ if (overlapped) {
956
+ emit({
957
+ type: "error",
958
+ code: "repair-block-overlap",
959
+ message: `[FecWorker] Dropping repair symbol whose block (ssStart=${key}, SSB_length=${cmd.ssbLength}) overlaps known block (ssStart=${overlapped.sbn}, SSB_length=${isoKnownLength(overlapped)})`,
960
+ });
961
+ return;
962
+ }
963
+ }
964
+ const repairEsi = cmd.ssbLength + cmd.rsId;
965
+ if (acceptedRepairEsis.get(key)?.has(repairEsi)) return;
966
+
967
+ const result = decoder.add_repair(key, cmd.ssbLength, cmd.rsId, view, cmd.ts);
968
+ advanceMediaHorizon(key);
969
+ const block = existingBlock ?? placeIsoBlock(key, cmd.ssbLength);
970
+ acceptId(acceptedRepairEsis, key, repairEsi);
971
+ block.repairReceived++;
972
+ block.lastSymbolTs = performance.now();
973
+ repairSymbols++;
974
+ settleRepairArrival(key, block, cmd.ssbLength, result);
975
+ }
976
+
977
+ // --- Command handlers ---
978
+
979
+ async function handleConfigure(
980
+ cmd: Extract<FecWorkerCommand, { type: "configure" }>,
981
+ ): Promise<void> {
982
+ if (!cmd.wasmModule) {
983
+ throw new Error("[FecWorker] configure: wasmModule required");
984
+ }
985
+ if (!cmd.config) {
986
+ throw new Error("[FecWorker] configure: config required");
987
+ }
988
+ // The block profile is the one discriminator for two wire-identical block
989
+ // maths, so it is required and never defaulted (BLO-35597).
990
+ const blockProfile: unknown = cmd.config.blockProfile;
991
+ if (blockProfile === undefined) {
992
+ throw new Error(
993
+ '[FecWorker] configure: blockProfile is required: name the FEC block profile ("k-padded-v1" or "iso-ssbg1-v1")',
994
+ );
995
+ }
996
+ if (blockProfile !== "k-padded-v1" && blockProfile !== "iso-ssbg1-v1") {
997
+ throw new Error(
998
+ `[FecWorker] configure: blockProfile must be "k-padded-v1" or "iso-ssbg1-v1" (got ${JSON.stringify(blockProfile)})`,
999
+ );
1000
+ }
1001
+ if (
1002
+ !Number.isFinite(cmd.config.interleaveDepth) ||
1003
+ !Number.isInteger(cmd.config.interleaveDepth) ||
1004
+ cmd.config.interleaveDepth <= 0 ||
1005
+ cmd.config.interleaveDepth > MAX_INTERLEAVE_DEPTH
1006
+ ) {
1007
+ throw new Error(
1008
+ `[FecWorker] configure: interleaveDepth must be an integer in 1..${MAX_INTERLEAVE_DEPTH} (got ${cmd.config.interleaveDepth})`,
1009
+ );
1010
+ }
1011
+ if (
1012
+ !Number.isFinite(cmd.config.interleaveMs) ||
1013
+ cmd.config.interleaveMs <= 0
1014
+ ) {
1015
+ throw new Error(
1016
+ `[FecWorker] configure: interleaveMs must be finite and > 0 (got ${cmd.config.interleaveMs})`,
1017
+ );
1018
+ }
1019
+ if (
1020
+ !Number.isFinite(cmd.config.deliveryWindowMs) ||
1021
+ cmd.config.deliveryWindowMs <= 0
1022
+ ) {
1023
+ throw new Error(
1024
+ `[FecWorker] configure: deliveryWindowMs must be > 0 (got ${cmd.config.deliveryWindowMs})`,
1025
+ );
1026
+ }
1027
+ if (
1028
+ !Number.isInteger(cmd.config.k) ||
1029
+ cmd.config.k <= 0 ||
1030
+ cmd.config.k > MAX_RAPTORQ_SOURCE_SYMBOLS_PER_BLOCK
1031
+ ) {
1032
+ throw new Error(
1033
+ `[FecWorker] configure: k must be an integer in 1..${MAX_RAPTORQ_SOURCE_SYMBOLS_PER_BLOCK} (got ${cmd.config.k})`,
1034
+ );
1035
+ }
1036
+ if (
1037
+ !Number.isInteger(cmd.config.symbolSize) ||
1038
+ cmd.config.symbolSize <= 0 ||
1039
+ cmd.config.symbolSize > MAX_SYMBOL_SIZE ||
1040
+ cmd.config.symbolSize % 8 !== 0
1041
+ ) {
1042
+ throw new Error(
1043
+ `[FecWorker] configure: symbolSize must be an 8-byte-aligned integer in 8..${MAX_SYMBOL_SIZE} (got ${cmd.config.symbolSize})`,
1044
+ );
1045
+ }
1046
+
1047
+ const wasm = resolveWasmBindings();
1048
+
1049
+ // Initialize WASM from pre-compiled Module (no fetch, no re-compile)
1050
+ wasm.initSync({ module: cmd.wasmModule });
1051
+
1052
+ // A configure command starts a new decoder epoch. No terminal tombstone,
1053
+ // accepted-symbol ID, or active block from the previous decoder may affect it.
1054
+ wasmDecoder?.free();
1055
+ wasmDecoder = null;
1056
+ clearBlockTracking();
1057
+
1058
+ // Decoder policy and geometry are explicit at the epoch boundary. The WASM
1059
+ // module has no guessed K/T/interleave slow path.
1060
+ const decoder = new wasm.MmtFecDecoder(cmd.config.interleaveDepth, blockProfile);
1061
+ verifyDecoderProfile(decoder, blockProfile);
1062
+ wasmDecoder = decoder;
1063
+ config = cmd.config;
1064
+ altaEnabled = !!cmd.altaPublicKey;
1065
+
1066
+ // Size the deferred-verify queue strictly from catalog-derived FEC
1067
+ // parameters. CLAUDE.md bans Math.max(x, floor) magic — if catalog
1068
+ // values are zero/missing, throw at init rather than silently applying
1069
+ // a hardcoded floor. Consumers of an undersized queue cap would see
1070
+ // altaPendingEvictedCap spike with no actionable signal.
1071
+ // Cap: 1.5× interleave depth — wide enough to hold one full FEC recovery
1072
+ // window plus jitter, narrow enough to bound memory.
1073
+ pendingVerifyCap = Math.ceil(config.interleaveDepth * 1.5);
1074
+ // TTL: 1.5× block duration (K symbols at one per interleave tick) —
1075
+ // after this many ms, any unresolved entry has missed its FEC window.
1076
+ pendingVerifyTtlMs = Math.ceil(config.interleaveMs * config.k * 1.5);
1077
+
1078
+ // Initialize ALTA WASM verifier if public key and WASM module provided (D-05, D-09)
1079
+ altaVerifier = null;
1080
+ altaInitFailed = false;
1081
+ if (cmd.altaPublicKey && cmd.altaWasmModule) {
1082
+ try {
1083
+ // Load ALTA JS glue (wasm-bindgen output from wasm-pack build).
1084
+ // Mirrors the RaptorQ WASM pattern: initSync({ module }) then construct.
1085
+ // Dynamic import path matches the IWA static asset layout.
1086
+ // Cast via string to prevent TypeScript from attempting static resolution
1087
+ // of a runtime-only URL (same pattern as fec-worker-entry.js loading).
1088
+ const altaGlueUrl = '/wasm/alta/alta_rs.js' as string;
1089
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1090
+ const altaGlue: any = await import(/* webpackIgnore: true */ altaGlueUrl);
1091
+ altaGlue.initSync({ module: cmd.altaWasmModule });
1092
+ const keyBytes = new Uint8Array(cmd.altaPublicKey);
1093
+ altaVerifier = new altaGlue.JsAltaVerifier(keyBytes);
1094
+ } catch (e) {
1095
+ // T-08-15: WASM init failure must NOT silently look like "not-ready-yet".
1096
+ // Force altaEnabled = false so handleFeedSource takes the disabled
1097
+ // branch instead of continuously bumping altaPending under the
1098
+ // misleading "verifier not yet ready" comment. altaInitFailed
1099
+ // flag in the snapshot tells consumers this is an error state, not
1100
+ // an opt-out.
1101
+ console.warn('[FecWorker] ALTA WASM init failed, verification disabled:', e);
1102
+ altaVerifier = null;
1103
+ altaEnabled = false;
1104
+ altaInitFailed = true;
1105
+ }
1106
+ }
1107
+
1108
+ wasmDecoder.configure_params(config.k, config.symbolSize);
1109
+
1110
+ // Tell the client which optional commands this Worker accepts, so it can
1111
+ // batch symbols without risking a Worker that predates the batch command.
1112
+ emit({ type: "capabilities", commands: ["feedSymbols", "subscribeBlockUpdates"] });
1113
+
1114
+ // Start periodic snapshot emission (200ms floor)
1115
+ startSnapshotTimer();
1116
+
1117
+ // Emit initial snapshot
1118
+ emitSnapshot();
1119
+ }
1120
+
1121
+ function handleFeedSource(
1122
+ cmd: Extract<FecWorkerCommand, { type: "feedSource" }>,
1123
+ ): void {
1124
+ if (!wasmDecoder) {
1125
+ throw new Error("[FecWorker] feedSource: not configured");
1126
+ }
1127
+ if (!config) {
1128
+ throw new Error("[FecWorker] feedSource: config missing");
1129
+ }
1130
+ if (
1131
+ !isUint32(cmd.ssId) ||
1132
+ !(cmd.data instanceof ArrayBuffer)
1133
+ ) {
1134
+ throw new Error("[FecWorker] feedSource: invalid source symbol");
1135
+ }
1136
+
1137
+ const view = new Uint8Array(cmd.data);
1138
+ if (config.blockProfile === "iso-ssbg1-v1") {
1139
+ feedIsoSource(cmd, view, wasmDecoder, config);
1140
+ return;
1141
+ }
1142
+
1143
+ // Derive SBN locally for block-level accounting. WASM derives the same
1144
+ // value internally (ISO 23008-1 §C.5.2: SBN = floor(SS_ID / K)).
1145
+ const sbn = config.k > 0 ? Math.floor(cmd.ssId / config.k) : cmd.ssId;
1146
+ const esi = cmd.ssId % config.k;
1147
+ if (isRetiredSbn(sbn)) {
1148
+ sourceDroppedRetired++;
1149
+ return;
1150
+ }
1151
+ const existingBlock = blocks.get(sbn);
1152
+ if (existingBlock && isTerminalBlock(existingBlock)) {
1153
+ sourceDroppedTerminal++;
1154
+ return;
1155
+ }
1156
+ if (acceptedSourceEsis.get(sbn)?.has(esi)) {
1157
+ sourceDroppedDuplicate++;
1158
+ return;
1159
+ }
1160
+
1161
+ const result = wasmDecoder.add_source(cmd.ssId, view, cmd.ts);
1162
+ advanceMediaHorizon(sbn);
1163
+ let sourceEsis = acceptedSourceEsis.get(sbn);
1164
+ if (!sourceEsis) {
1165
+ sourceEsis = new Set<number>();
1166
+ acceptedSourceEsis.set(sbn, sourceEsis);
1167
+ }
1168
+ sourceEsis.add(esi);
1169
+
1170
+ // Update block state
1171
+ const block = getOrCreateBlock(sbn);
1172
+ block.sourceReceived++;
1173
+ block.lastSymbolTs = performance.now();
1174
+ sourceSymbols++;
1175
+
1176
+ // The FEC worker must remain decode-only on the per-symbol hot path. ALTA
1177
+ // WASM is retained temporarily for low-rate signaling users, but media is
1178
+ // authenticated by the canonical manifest verifier at router admission.
1179
+ if (altaEnabled) altaPending++;
1180
+
1181
+ settleSourceArrival(sbn, block, result);
1182
+ }
1183
+
1184
+ function handleFeedRepair(
1185
+ cmd: Extract<FecWorkerCommand, { type: "feedRepair" }>,
1186
+ ): void {
1187
+ if (!wasmDecoder) {
1188
+ throw new Error("[FecWorker] feedRepair: not configured");
1189
+ }
1190
+ if (!config) {
1191
+ throw new Error("[FecWorker] feedRepair: config missing");
1192
+ }
1193
+ // k-padded-v1 blocks start at multiples of K and repair ESI = K + RS_ID;
1194
+ // iso-ssbg1-v1 blocks start at any SS_ID and repair ESI = SSB_length + RS_ID.
1195
+ const iso = config.blockProfile === "iso-ssbg1-v1";
1196
+ if (
1197
+ !isUint32(cmd.ssStart) ||
1198
+ !isUint32(cmd.ssbLength) ||
1199
+ cmd.ssbLength <= 0 ||
1200
+ cmd.ssbLength > config.k ||
1201
+ (!iso && cmd.ssStart % config.k !== 0) ||
1202
+ !isUint32(cmd.rsId) ||
1203
+ (iso ? cmd.ssbLength : config.k) + cmd.rsId > UINT24_MAX ||
1204
+ !(cmd.data instanceof ArrayBuffer)
1205
+ ) {
1206
+ throw new Error("[FecWorker] feedRepair: invalid repair symbol");
1207
+ }
1208
+
1209
+ const view = new Uint8Array(cmd.data);
1210
+ if (config.symbolSize > 0 && view.byteLength !== config.symbolSize) {
1211
+ repairSymbolSizeMismatch++;
1212
+ if (repairSymbolSizeMismatch <= 5 || repairSymbolSizeMismatch % 100 === 0) {
1213
+ emit({
1214
+ type: "error",
1215
+ code: "repair-symbol-size-mismatch",
1216
+ message: `[FecWorker] Dropping repair symbol with invalid size: got ${view.byteLength}B, expected ${config.symbolSize}B (ssStart=${cmd.ssStart}, ssbLength=${cmd.ssbLength}, rsId=${cmd.rsId})`,
1217
+ });
1218
+ }
1219
+ return;
1220
+ }
1221
+ if (iso) {
1222
+ feedIsoRepair(cmd, view, wasmDecoder);
1223
+ return;
1224
+ }
1225
+ const sbn = Math.floor(cmd.ssStart / config.k);
1226
+ const repairEsi = config.k + cmd.rsId;
1227
+ // The repair path's mirror drops stay silent and uncounted: BLO-35597
1228
+ // defect 2 is a source-symbol shortfall, so only the source side needed
1229
+ // evidence. The asymmetry is deliberate, not an oversight — extend these
1230
+ // the same way if a repair-side shortfall ever needs attributing.
1231
+ if (isRetiredSbn(sbn)) return;
1232
+ const existingBlock = blocks.get(sbn);
1233
+ if (existingBlock && isTerminalBlock(existingBlock)) return;
1234
+ if (acceptedRepairEsis.get(sbn)?.has(repairEsi)) return;
1235
+ // One block has one authored SSB_length; a repair symbol naming another
1236
+ // is dropped before it reaches the decoder (which rejects it too).
1237
+ if (existingBlock?.ssbLength !== undefined && existingBlock.ssbLength !== cmd.ssbLength) {
1238
+ emit({
1239
+ type: "error",
1240
+ code: "repair-ssb-length-conflict",
1241
+ message: `[FecWorker] Dropping repair symbol whose SSB_length ${cmd.ssbLength} conflicts with ${existingBlock.ssbLength} (sbn=${sbn})`,
1242
+ });
1243
+ return;
1244
+ }
1245
+
1246
+ const result = wasmDecoder.add_repair(
1247
+ cmd.ssStart,
1248
+ cmd.ssbLength,
1249
+ cmd.rsId,
1250
+ view,
1251
+ cmd.ts,
1252
+ );
1253
+ advanceMediaHorizon(sbn);
1254
+ let repairEsis = acceptedRepairEsis.get(sbn);
1255
+ if (!repairEsis) {
1256
+ repairEsis = new Set<number>();
1257
+ acceptedRepairEsis.set(sbn, repairEsis);
1258
+ }
1259
+ repairEsis.add(repairEsi);
1260
+ const block = getOrCreateBlock(sbn);
1261
+ block.ssbLength = cmd.ssbLength;
1262
+ block.repairReceived++;
1263
+ block.lastSymbolTs = performance.now();
1264
+ repairSymbols++;
1265
+ settleRepairArrival(sbn, block, cmd.ssbLength, result);
1266
+ }
1267
+
1268
+ function handleRelayBlockSig(
1269
+ cmd: Extract<FecWorkerCommand, { type: "relayBlockSig" }>,
1270
+ ): void {
1271
+ // No-op AUTH stub: store sig on block, mark as not-yet-verified
1272
+ const block = blocks.get(cmd.sbn);
1273
+ if (block) {
1274
+ // Real verification deferred to ALTA milestone
1275
+ // For now, mark as null (not verified) — will be wired when ALTA is implemented
1276
+ block.relayBlockSigValid = null;
1277
+ emitBlockUpdate(block);
1278
+ }
1279
+ }
1280
+
1281
+ function handleCleanup(
1282
+ cmd: Extract<FecWorkerCommand, { type: "cleanup" }>,
1283
+ ): void {
1284
+ if (
1285
+ !Array.isArray(cmd.sbns) ||
1286
+ cmd.sbns.length === 0 ||
1287
+ !cmd.sbns.every(isUint32)
1288
+ ) {
1289
+ throw new Error("[FecWorker] cleanup: invalid SBNs");
1290
+ }
1291
+ const sbns = [...new Set(cmd.sbns)].sort((a, b) => a - b);
1292
+ const pendingSbns = sbns.filter((sbn) => !isRetiredSbn(sbn));
1293
+ try {
1294
+ if (!wasmDecoder) {
1295
+ throw new Error("[FecWorker] cleanup: not configured");
1296
+ }
1297
+
1298
+ // Call WASM cleanup only after all earlier symbol commands have drained,
1299
+ // and only for the exact SBNs the manager's deadlines named; command
1300
+ // coalescing must not infer ownership from the largest timed-out SBN.
1301
+ if (pendingSbns.length > 0) {
1302
+ wasmDecoder.cleanup_blocks_detailed(Uint32Array.from(pendingSbns));
1303
+ }
1304
+ // Manager deadlines are block-scoped and may expire a newer SBN while an
1305
+ // older interleaved block is still live. Only accepted media progression
1306
+ // advances the shared JS/Rust horizon; cleanup creates exact tombstones.
1307
+
1308
+ // Rust already finalized every SBN behind the media frontier. For each
1309
+ // remaining exact request, mirror the block-scoped WASM boundary immediately
1310
+ // so JS never retains state for a decoder block that is gone.
1311
+ for (const sbn of pendingSbns) {
1312
+ let currentBlock = blocks.get(sbn);
1313
+ if (currentBlock && isTerminalBlock(currentBlock)) {
1314
+ retireAcceptedIds(sbn);
1315
+ continue;
1316
+ }
1317
+ currentBlock ??= getOrCreateBlock(sbn);
1318
+ // k-padded-v1 compares with K, unchanged. iso-ssbg1-v1 compares with
1319
+ // the block's SSB_length; a key no repair declared has none, so a
1320
+ // block the Worker never knew can only have failed.
1321
+ const authoredLength = isIsoProfile() ? currentBlock.ssbLength : currentBlock.k;
1322
+ if (authoredLength !== undefined && currentBlock.sourceReceived >= authoredLength) {
1323
+ markBlockTerminal(currentBlock, "complete");
1324
+ blocksComplete++;
1325
+ } else {
1326
+ markBlockTerminal(currentBlock, "failed");
1327
+ blocksFailed++;
1328
+ }
1329
+ emitBlockUpdate(currentBlock);
1330
+ }
1331
+ } finally {
1332
+ // Message ordering makes this an acknowledgement that every source/repair
1333
+ // command posted before cleanup has completed. The main thread must not
1334
+ // commit failure accounting until it observes this event.
1335
+ emit({ type: "cleanupComplete", sbns });
1336
+ }
1337
+ }
1338
+
1339
+ function handleDispose(): void {
1340
+ // Clear snapshot timer (MUST clear to prevent leaks)
1341
+ if (snapshotTimer !== null) {
1342
+ clearInterval(snapshotTimer);
1343
+ snapshotTimer = null;
1344
+ }
1345
+
1346
+ // Free ALTA verifier WASM object (MUST call to release WASM memory —
1347
+ // Worker.terminate() fires immediately after dispose, so the FinalizationRegistry
1348
+ // callback has no chance to run and this memory would leak without explicit free)
1349
+ if (altaVerifier) {
1350
+ try {
1351
+ altaVerifier.free();
1352
+ } catch (_e) {
1353
+ // Best-effort — verifier may already be freed
1354
+ }
1355
+ altaVerifier = null;
1356
+ }
1357
+
1358
+ // Free WASM decoder (MUST call to release WASM memory)
1359
+ if (wasmDecoder) {
1360
+ wasmDecoder.free();
1361
+ wasmDecoder = null;
1362
+ }
1363
+
1364
+ // Clear deferred-verify queue so pinned ArrayBuffers drop.
1365
+ pendingVerify.clear();
1366
+ pendingVerifyCap = 0;
1367
+ pendingVerifyTtlMs = 0;
1368
+
1369
+ config = null;
1370
+ clearBlockTracking();
1371
+ }
1372
+
1373
+ function handleReset(): void {
1374
+ try {
1375
+ wasmDecoder?.flush();
1376
+ wasmDecoder?.reset_stats();
1377
+ } catch (err) {
1378
+ emit({
1379
+ type: "error",
1380
+ code: "reset-failed",
1381
+ message:
1382
+ err instanceof Error
1383
+ ? `[FecWorker] reset failed: ${err.message}`
1384
+ : `[FecWorker] reset failed: ${String(err)}`,
1385
+ });
1386
+ return;
1387
+ }
1388
+
1389
+ clearBlockTracking();
1390
+ sourceSymbols = 0;
1391
+ repairSymbols = 0;
1392
+ blocksComplete = 0;
1393
+ blocksRecovered = 0;
1394
+ blocksFailed = 0;
1395
+ altaVerified = 0;
1396
+ altaFailed = 0;
1397
+ altaFailedDetails.length = 0;
1398
+ altaPending = 0;
1399
+ altaPendingEvictedCap = 0;
1400
+ altaPendingEvictedTtl = 0;
1401
+ altaError = 0;
1402
+ relayBlocksVerified = 0;
1403
+ relayBlocksFailed = 0;
1404
+ repairSymbolSizeMismatch = 0;
1405
+ sourceDroppedRetired = 0;
1406
+ sourceDroppedTerminal = 0;
1407
+ sourceDroppedDuplicate = 0;
1408
+ sourceUnplacedRetired = 0;
1409
+ emit({ type: "resetComplete" });
1410
+ if (config) emitSnapshot();
1411
+ }
1412
+
1413
+ /**
1414
+ * A batch of symbols fed in one page task (BLO-36599). Each record is
1415
+ * handled exactly as its single-symbol command would be, in order, and a
1416
+ * failing record reports the same `command-failed` error that command would
1417
+ * without stopping the records after it.
1418
+ */
1419
+ function handleFeedSymbols(
1420
+ cmd: Extract<FecWorkerCommand, { type: "feedSymbols" }>,
1421
+ ): void {
1422
+ if (!Array.isArray(cmd.symbols)) {
1423
+ throw new Error("[FecWorker] feedSymbols: symbols must be an array");
1424
+ }
1425
+ for (const symbol of cmd.symbols) {
1426
+ try {
1427
+ if (symbol?.type === "feedSource") handleFeedSource(symbol);
1428
+ else if (symbol?.type === "feedRepair") handleFeedRepair(symbol);
1429
+ else {
1430
+ throw new Error(
1431
+ `[FecWorker] feedSymbols: unknown symbol type ${String((symbol as { type?: unknown } | null)?.type)}`,
1432
+ );
1433
+ }
1434
+ } catch (err) {
1435
+ emit({
1436
+ type: "error",
1437
+ code: "command-failed",
1438
+ message:
1439
+ err instanceof Error ? err.message : `[FecWorker] ${String(err)}`,
1440
+ });
1441
+ }
1442
+ }
1443
+ }
1444
+
1445
+ // --- Main message handler ---
1446
+
1447
+ async function dispatchCommand(cmd: FecWorkerCommand): Promise<void> {
1448
+ try {
1449
+ switch (cmd.type) {
1450
+ case "configure":
1451
+ // handleConfigure is async (ALTA WASM init uses dynamic import).
1452
+ // Await it so feed/cleanup messages cannot run against a partially
1453
+ // configured decoder.
1454
+ await handleConfigure(cmd);
1455
+ break;
1456
+ case "feedSource":
1457
+ handleFeedSource(cmd);
1458
+ break;
1459
+ case "feedRepair":
1460
+ handleFeedRepair(cmd);
1461
+ break;
1462
+ case "feedSymbols":
1463
+ handleFeedSymbols(cmd);
1464
+ break;
1465
+ case "relayBlockSig":
1466
+ handleRelayBlockSig(cmd);
1467
+ break;
1468
+ case "cleanup":
1469
+ handleCleanup(cmd);
1470
+ break;
1471
+ case "reset":
1472
+ handleReset();
1473
+ break;
1474
+ case "subscribeBlockUpdates":
1475
+ blockUpdatesSubscribed = true;
1476
+ break;
1477
+ case "dispose":
1478
+ handleDispose();
1479
+ break;
1480
+ default:
1481
+ emit({
1482
+ type: "error",
1483
+ code: "unknown-command",
1484
+ message: `Unknown command type: ${(cmd as Record<string, unknown>).type}`,
1485
+ });
1486
+ }
1487
+ } catch (err) {
1488
+ emit({
1489
+ type: "error",
1490
+ code: "command-failed",
1491
+ message:
1492
+ err instanceof Error ? err.message : `[FecWorker] ${String(err)}`,
1493
+ });
1494
+ }
1495
+ }
1496
+
1497
+ let commandQueue: Promise<void> = Promise.resolve();
1498
+
1499
+ self.onmessage = (e: MessageEvent<FecWorkerCommand>) => {
1500
+ const cmd = e.data;
1501
+ commandQueue = commandQueue.then(() => dispatchCommand(cmd));
1502
+ };