prismcast 1.12.1 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (655) hide show
  1. package/dist/app.d.ts +88 -7
  2. package/dist/app.js +295 -161
  3. package/dist/app.js.map +1 -1
  4. package/dist/browser/blockedPage.d.ts +0 -1
  5. package/dist/browser/browserSupervisor.d.ts +4 -4
  6. package/dist/browser/browserSupervisor.js.map +1 -1
  7. package/dist/browser/cdp.d.ts +8 -20
  8. package/dist/browser/cdp.js +11 -61
  9. package/dist/browser/cdp.js.map +1 -1
  10. package/dist/browser/channelSelection.d.ts +16 -4
  11. package/dist/browser/channelSelection.js +42 -31
  12. package/dist/browser/channelSelection.js.map +1 -1
  13. package/dist/browser/consent.d.ts +17 -4
  14. package/dist/browser/consent.js +25 -40
  15. package/dist/browser/consent.js.map +1 -1
  16. package/dist/browser/display.d.ts +0 -1
  17. package/dist/browser/hlsPlaylistObserver.d.ts +3 -2
  18. package/dist/browser/hlsPlaylistObserver.js +9 -4
  19. package/dist/browser/hlsPlaylistObserver.js.map +1 -1
  20. package/dist/browser/index.d.ts +179 -46
  21. package/dist/browser/index.js +473 -306
  22. package/dist/browser/index.js.map +1 -1
  23. package/dist/browser/launchGovernor.d.ts +0 -1
  24. package/dist/browser/login.d.ts +8 -7
  25. package/dist/browser/login.js +25 -16
  26. package/dist/browser/login.js.map +1 -1
  27. package/dist/browser/manifestInterceptor.d.ts +14 -5
  28. package/dist/browser/manifestInterceptor.js +25 -12
  29. package/dist/browser/manifestInterceptor.js.map +1 -1
  30. package/dist/browser/navigation.d.ts +28 -0
  31. package/dist/browser/navigation.js +57 -0
  32. package/dist/browser/navigation.js.map +1 -0
  33. package/dist/browser/pageStaleness.d.ts +0 -1
  34. package/dist/browser/precaching.d.ts +27 -11
  35. package/dist/browser/precaching.js +262 -107
  36. package/dist/browser/precaching.js.map +1 -1
  37. package/dist/browser/tabCapture.d.ts +7 -10
  38. package/dist/browser/tabCapture.js +37 -50
  39. package/dist/browser/tabCapture.js.map +1 -1
  40. package/dist/browser/tabNetworkObserver.d.ts +0 -1
  41. package/dist/browser/tabSelection.d.ts +5 -5
  42. package/dist/browser/tabSelection.js +11 -10
  43. package/dist/browser/tabSelection.js.map +1 -1
  44. package/dist/browser/tuning/cache.d.ts +0 -1
  45. package/dist/browser/tuning/comcastPolymer.d.ts +0 -1
  46. package/dist/browser/tuning/comcastPolymer.js +29 -31
  47. package/dist/browser/tuning/comcastPolymer.js.map +1 -1
  48. package/dist/browser/tuning/cox.d.ts +0 -1
  49. package/dist/browser/tuning/directv.d.ts +0 -1
  50. package/dist/browser/tuning/directv.js +11 -12
  51. package/dist/browser/tuning/directv.js.map +1 -1
  52. package/dist/browser/tuning/fox.d.ts +0 -1
  53. package/dist/browser/tuning/fox.js +2 -2
  54. package/dist/browser/tuning/fox.js.map +1 -1
  55. package/dist/browser/tuning/gridSearch.d.ts +0 -1
  56. package/dist/browser/tuning/hbo.d.ts +0 -1
  57. package/dist/browser/tuning/hbo.js +34 -17
  58. package/dist/browser/tuning/hbo.js.map +1 -1
  59. package/dist/browser/tuning/hulu.d.ts +14 -2
  60. package/dist/browser/tuning/hulu.js +144 -21
  61. package/dist/browser/tuning/hulu.js.map +1 -1
  62. package/dist/browser/tuning/shared.d.ts +0 -1
  63. package/dist/browser/tuning/sling.d.ts +0 -1
  64. package/dist/browser/tuning/sling.js +17 -22
  65. package/dist/browser/tuning/sling.js.map +1 -1
  66. package/dist/browser/tuning/spectrum.d.ts +0 -1
  67. package/dist/browser/tuning/thumbnailRow.d.ts +0 -1
  68. package/dist/browser/tuning/tileClick.d.ts +0 -1
  69. package/dist/browser/tuning/xfinity.d.ts +0 -1
  70. package/dist/browser/tuning/youtubeTv.d.ts +0 -1
  71. package/dist/browser/video.d.ts +22 -20
  72. package/dist/browser/video.js +102 -119
  73. package/dist/browser/video.js.map +1 -1
  74. package/dist/browser/windowSync.d.ts +0 -1
  75. package/dist/channels/index.d.ts +0 -1
  76. package/dist/channels/index.js +5 -5
  77. package/dist/channels/index.js.map +1 -1
  78. package/dist/cliHelp.d.ts +11 -0
  79. package/dist/cliHelp.js +148 -0
  80. package/dist/cliHelp.js.map +1 -0
  81. package/dist/config/channelForm.d.ts +0 -1
  82. package/dist/config/channelForm.js +5 -4
  83. package/dist/config/channelForm.js.map +1 -1
  84. package/dist/config/channelIdentity.d.ts +27 -0
  85. package/dist/config/channelIdentity.js +72 -0
  86. package/dist/config/channelIdentity.js.map +1 -0
  87. package/dist/config/channelSort.d.ts +25 -0
  88. package/dist/config/channelSort.js +101 -0
  89. package/dist/config/channelSort.js.map +1 -0
  90. package/dist/config/consistencyProbe.d.ts +2 -5
  91. package/dist/config/consistencyProbe.js +8 -51
  92. package/dist/config/consistencyProbe.js.map +1 -1
  93. package/dist/config/health.d.ts +3 -2
  94. package/dist/config/health.js +41 -28
  95. package/dist/config/health.js.map +1 -1
  96. package/dist/config/index.d.ts +107 -56
  97. package/dist/config/index.js +499 -267
  98. package/dist/config/index.js.map +1 -1
  99. package/dist/config/paths.d.ts +13 -9
  100. package/dist/config/paths.js +21 -25
  101. package/dist/config/paths.js.map +1 -1
  102. package/dist/config/persistence.context.d.ts +0 -1
  103. package/dist/config/persistence.d.ts +57 -12
  104. package/dist/config/persistence.js +127 -44
  105. package/dist/config/persistence.js.map +1 -1
  106. package/dist/config/presets.d.ts +0 -1
  107. package/dist/config/profiles.d.ts +9 -4
  108. package/dist/config/profiles.js +37 -3
  109. package/dist/config/profiles.js.map +1 -1
  110. package/dist/config/providerLineups.d.ts +0 -1
  111. package/dist/config/reactivity.d.ts +60 -37
  112. package/dist/config/reactivity.js +89 -116
  113. package/dist/config/reactivity.js.map +1 -1
  114. package/dist/config/servicePacks.d.ts +0 -1
  115. package/dist/config/services.d.ts +62 -73
  116. package/dist/config/services.js +140 -202
  117. package/dist/config/services.js.map +1 -1
  118. package/dist/config/sites.d.ts +0 -9
  119. package/dist/config/sites.js +5 -40
  120. package/dist/config/sites.js.map +1 -1
  121. package/dist/config/streamSettings.d.ts +24 -0
  122. package/dist/config/streamSettings.js +17 -0
  123. package/dist/config/streamSettings.js.map +1 -0
  124. package/dist/config/userChannels.d.ts +82 -80
  125. package/dist/config/userChannels.js +184 -248
  126. package/dist/config/userChannels.js.map +1 -1
  127. package/dist/config/userConfig.d.ts +225 -59
  128. package/dist/config/userConfig.js +352 -131
  129. package/dist/config/userConfig.js.map +1 -1
  130. package/dist/config/userProfiles.d.ts +2 -3
  131. package/dist/config/userProfiles.js +6 -2
  132. package/dist/config/userProfiles.js.map +1 -1
  133. package/dist/hdhr/channelMap.d.ts +0 -1
  134. package/dist/hdhr/discover.d.ts +7 -2
  135. package/dist/hdhr/discover.js +12 -6
  136. package/dist/hdhr/discover.js.map +1 -1
  137. package/dist/hdhr/getHandlers.d.ts +0 -1
  138. package/dist/hdhr/identity.d.ts +0 -1
  139. package/dist/hdhr/index.d.ts +51 -11
  140. package/dist/hdhr/index.js +108 -112
  141. package/dist/hdhr/index.js.map +1 -1
  142. package/dist/hdhr/protocol.d.ts +0 -1
  143. package/dist/hdhr/tunerState.d.ts +0 -1
  144. package/dist/hdhr/udp.d.ts +11 -5
  145. package/dist/hdhr/udp.js +25 -20
  146. package/dist/hdhr/udp.js.map +1 -1
  147. package/dist/identity.d.ts +0 -1
  148. package/dist/index.d.ts +0 -1
  149. package/dist/index.js +16 -143
  150. package/dist/index.js.map +1 -1
  151. package/dist/native/codecInference.d.ts +5 -4
  152. package/dist/native/codecInference.js +12 -7
  153. package/dist/native/codecInference.js.map +1 -1
  154. package/dist/native/decrypt.d.ts +10 -3
  155. package/dist/native/decrypt.js +9 -5
  156. package/dist/native/decrypt.js.map +1 -1
  157. package/dist/native/index.d.ts +25 -23
  158. package/dist/native/index.js +27 -20
  159. package/dist/native/index.js.map +1 -1
  160. package/dist/native/probe.d.ts +7 -10
  161. package/dist/native/probe.js +40 -36
  162. package/dist/native/probe.js.map +1 -1
  163. package/dist/native/proxy.d.ts +3 -4
  164. package/dist/native/proxy.js +122 -92
  165. package/dist/native/proxy.js.map +1 -1
  166. package/dist/native/tokenExpiry.d.ts +0 -1
  167. package/dist/routes/assets.d.ts +0 -1
  168. package/dist/routes/auth.d.ts +0 -1
  169. package/dist/routes/auth.js +1 -1
  170. package/dist/routes/auth.js.map +1 -1
  171. package/dist/routes/cdp.d.ts +18 -11
  172. package/dist/routes/cdp.js +99 -70
  173. package/dist/routes/cdp.js.map +1 -1
  174. package/dist/routes/channels.d.ts +0 -1
  175. package/dist/routes/clientActions.d.ts +0 -1
  176. package/dist/routes/components.d.ts +23 -6
  177. package/dist/routes/components.js +21 -4
  178. package/dist/routes/components.js.map +1 -1
  179. package/dist/routes/config/channels/endpoints/browse.d.ts +0 -1
  180. package/dist/routes/config/channels/endpoints/browse.js +39 -29
  181. package/dist/routes/config/channels/endpoints/browse.js.map +1 -1
  182. package/dist/routes/config/channels/endpoints/bulk.d.ts +0 -1
  183. package/dist/routes/config/channels/endpoints/bulk.js +3 -7
  184. package/dist/routes/config/channels/endpoints/bulk.js.map +1 -1
  185. package/dist/routes/config/channels/endpoints/crud.d.ts +0 -1
  186. package/dist/routes/config/channels/endpoints/crud.js +3 -2
  187. package/dist/routes/config/channels/endpoints/crud.js.map +1 -1
  188. package/dist/routes/config/channels/endpoints/importExport.d.ts +0 -1
  189. package/dist/routes/config/channels/endpoints/importExport.js +7 -3
  190. package/dist/routes/config/channels/endpoints/importExport.js.map +1 -1
  191. package/dist/routes/config/channels/endpoints/predefined.d.ts +0 -1
  192. package/dist/routes/config/channels/endpoints/predefined.js +5 -15
  193. package/dist/routes/config/channels/endpoints/predefined.js.map +1 -1
  194. package/dist/routes/config/channels/endpoints/prefs.d.ts +0 -1
  195. package/dist/routes/config/channels/endpoints/prefs.js +8 -7
  196. package/dist/routes/config/channels/endpoints/prefs.js.map +1 -1
  197. package/dist/routes/config/channels/endpoints/service.d.ts +0 -1
  198. package/dist/routes/config/channels/endpoints/service.js +13 -9
  199. package/dist/routes/config/channels/endpoints/service.js.map +1 -1
  200. package/dist/routes/config/channels/endpoints/tags.d.ts +0 -1
  201. package/dist/routes/config/channels/endpoints/tags.js +4 -16
  202. package/dist/routes/config/channels/endpoints/tags.js.map +1 -1
  203. package/dist/routes/config/channels/healthBridge.d.ts +0 -1
  204. package/dist/routes/config/channels/http/handler.d.ts +0 -1
  205. package/dist/routes/config/channels/http/playlistHint.d.ts +0 -1
  206. package/dist/routes/config/channels/http/serviceWarning.d.ts +0 -1
  207. package/dist/routes/config/channels/http/serviceWarning.js +1 -1
  208. package/dist/routes/config/channels/http/serviceWarning.js.map +1 -1
  209. package/dist/routes/config/channels/index.d.ts +0 -1
  210. package/dist/routes/config/channels/setup.d.ts +0 -1
  211. package/dist/routes/config/channels/table.d.ts +2 -2
  212. package/dist/routes/config/channels/table.js +13 -14
  213. package/dist/routes/config/channels/table.js.map +1 -1
  214. package/dist/routes/config/http/envelope.d.ts +6 -4
  215. package/dist/routes/config/http/envelope.js +18 -5
  216. package/dist/routes/config/http/envelope.js.map +1 -1
  217. package/dist/routes/config/index.d.ts +21 -14
  218. package/dist/routes/config/index.js +49 -35
  219. package/dist/routes/config/index.js.map +1 -1
  220. package/dist/routes/config/services.d.ts +0 -1
  221. package/dist/routes/config/services.js +4 -3
  222. package/dist/routes/config/services.js.map +1 -1
  223. package/dist/routes/config/settings.d.ts +43 -7
  224. package/dist/routes/config/settings.js +227 -126
  225. package/dist/routes/config/settings.js.map +1 -1
  226. package/dist/routes/config/vocabulary.d.ts +24 -0
  227. package/dist/routes/config/vocabulary.js +33 -0
  228. package/dist/routes/config/vocabulary.js.map +1 -0
  229. package/dist/routes/debug.d.ts +0 -1
  230. package/dist/routes/debug.js +28 -13
  231. package/dist/routes/debug.js.map +1 -1
  232. package/dist/routes/health.d.ts +1 -2
  233. package/dist/routes/health.js +5 -2
  234. package/dist/routes/health.js.map +1 -1
  235. package/dist/routes/hls.d.ts +0 -1
  236. package/dist/routes/icons.d.ts +0 -1
  237. package/dist/routes/index.d.ts +0 -1
  238. package/dist/routes/logs.d.ts +3 -2
  239. package/dist/routes/logs.js +4 -2
  240. package/dist/routes/logs.js.map +1 -1
  241. package/dist/routes/mpegts.d.ts +0 -1
  242. package/dist/routes/play.d.ts +0 -1
  243. package/dist/routes/playlist.d.ts +0 -1
  244. package/dist/routes/playlist.js +2 -1
  245. package/dist/routes/playlist.js.map +1 -1
  246. package/dist/routes/root/content.d.ts +0 -1
  247. package/dist/routes/root/content.js +33 -26
  248. package/dist/routes/root/content.js.map +1 -1
  249. package/dist/routes/root/index.d.ts +0 -1
  250. package/dist/routes/root/scripts/channels.d.ts +0 -1
  251. package/dist/routes/root/scripts/channels.js +25 -13
  252. package/dist/routes/root/scripts/channels.js.map +1 -1
  253. package/dist/routes/root/scripts/clientEscape.d.ts +0 -1
  254. package/dist/routes/root/scripts/clientUrl.d.ts +0 -1
  255. package/dist/routes/root/scripts/config.d.ts +1 -2
  256. package/dist/routes/root/scripts/config.js +132 -105
  257. package/dist/routes/root/scripts/config.js.map +1 -1
  258. package/dist/routes/root/scripts/index.d.ts +0 -1
  259. package/dist/routes/root/scripts/shared.d.ts +0 -1
  260. package/dist/routes/root/scripts/shared.js +34 -23
  261. package/dist/routes/root/scripts/shared.js.map +1 -1
  262. package/dist/routes/root/scripts/status.d.ts +0 -1
  263. package/dist/routes/root/scripts/status.handlers.d.ts +8 -14
  264. package/dist/routes/root/scripts/status.handlers.js +38 -23
  265. package/dist/routes/root/scripts/status.handlers.js.map +1 -1
  266. package/dist/routes/root/styles.d.ts +0 -1
  267. package/dist/routes/root/styles.js +13 -16
  268. package/dist/routes/root/styles.js.map +1 -1
  269. package/dist/routes/services.d.ts +0 -1
  270. package/dist/routes/services.js +13 -17
  271. package/dist/routes/services.js.map +1 -1
  272. package/dist/routes/sse.d.ts +3 -2
  273. package/dist/routes/sse.js +7 -3
  274. package/dist/routes/sse.js.map +1 -1
  275. package/dist/routes/streams.d.ts +4 -2
  276. package/dist/routes/streams.js +8 -5
  277. package/dist/routes/streams.js.map +1 -1
  278. package/dist/routes/theme.d.ts +0 -1
  279. package/dist/routes/theme.js +12 -12
  280. package/dist/routes/theme.js.map +1 -1
  281. package/dist/routes/ui.d.ts +4 -4
  282. package/dist/routes/ui.js +17 -13
  283. package/dist/routes/ui.js.map +1 -1
  284. package/dist/routes/upgrade.d.ts +0 -1
  285. package/dist/routes/upgrade.js +2 -1
  286. package/dist/routes/upgrade.js.map +1 -1
  287. package/dist/service/commands.context.d.ts +0 -1
  288. package/dist/service/commands.context.js +7 -1
  289. package/dist/service/commands.context.js.map +1 -1
  290. package/dist/service/commands.d.ts +0 -1
  291. package/dist/service/generators.context.d.ts +0 -1
  292. package/dist/service/generators.d.ts +0 -1
  293. package/dist/service/generators.js +12 -9
  294. package/dist/service/generators.js.map +1 -1
  295. package/dist/service/index.d.ts +0 -1
  296. package/dist/streaming/captureLock.d.ts +1 -2
  297. package/dist/streaming/captureLock.js +6 -5
  298. package/dist/streaming/captureLock.js.map +1 -1
  299. package/dist/streaming/captureSession.d.ts +4 -5
  300. package/dist/streaming/captureSession.js +6 -7
  301. package/dist/streaming/captureSession.js.map +1 -1
  302. package/dist/streaming/clients.d.ts +4 -3
  303. package/dist/streaming/clients.js +5 -4
  304. package/dist/streaming/clients.js.map +1 -1
  305. package/dist/streaming/codec.d.ts +12 -3
  306. package/dist/streaming/codec.js +40 -28
  307. package/dist/streaming/codec.js.map +1 -1
  308. package/dist/streaming/fmp4Segmenter.d.ts +16 -5
  309. package/dist/streaming/fmp4Segmenter.js +110 -89
  310. package/dist/streaming/fmp4Segmenter.js.map +1 -1
  311. package/dist/streaming/hls.d.ts +55 -15
  312. package/dist/streaming/hls.js +255 -162
  313. package/dist/streaming/hls.js.map +1 -1
  314. package/dist/streaming/hlsResume.d.ts +42 -16
  315. package/dist/streaming/hlsResume.js +75 -42
  316. package/dist/streaming/hlsResume.js.map +1 -1
  317. package/dist/streaming/hlsSegments.d.ts +9 -7
  318. package/dist/streaming/hlsSegments.js +19 -14
  319. package/dist/streaming/hlsSegments.js.map +1 -1
  320. package/dist/streaming/lifecycle.d.ts +5 -4
  321. package/dist/streaming/lifecycle.js +25 -13
  322. package/dist/streaming/lifecycle.js.map +1 -1
  323. package/dist/streaming/monitor.d.ts +8 -5
  324. package/dist/streaming/monitor.js +109 -93
  325. package/dist/streaming/monitor.js.map +1 -1
  326. package/dist/streaming/mp4Parser.d.ts +29 -5
  327. package/dist/streaming/mp4Parser.js +49 -7
  328. package/dist/streaming/mp4Parser.js.map +1 -1
  329. package/dist/streaming/mpegts.d.ts +0 -1
  330. package/dist/streaming/mpegts.js +23 -7
  331. package/dist/streaming/mpegts.js.map +1 -1
  332. package/dist/streaming/playlistBuilder.d.ts +1 -3
  333. package/dist/streaming/playlistBuilder.js +2 -3
  334. package/dist/streaming/playlistBuilder.js.map +1 -1
  335. package/dist/streaming/preroll.d.ts +76 -30
  336. package/dist/streaming/preroll.js +141 -89
  337. package/dist/streaming/preroll.js.map +1 -1
  338. package/dist/streaming/pretune.d.ts +3 -4
  339. package/dist/streaming/pretune.js +50 -46
  340. package/dist/streaming/pretune.js.map +1 -1
  341. package/dist/streaming/pretuneTimers.d.ts +16 -12
  342. package/dist/streaming/pretuneTimers.js +27 -37
  343. package/dist/streaming/pretuneTimers.js.map +1 -1
  344. package/dist/streaming/recovery.d.ts +36 -7
  345. package/dist/streaming/recovery.js +53 -11
  346. package/dist/streaming/recovery.js.map +1 -1
  347. package/dist/streaming/registry.d.ts +35 -14
  348. package/dist/streaming/registry.js +23 -8
  349. package/dist/streaming/registry.js.map +1 -1
  350. package/dist/streaming/setup.d.ts +66 -25
  351. package/dist/streaming/setup.js +305 -229
  352. package/dist/streaming/setup.js.map +1 -1
  353. package/dist/streaming/showInfo.d.ts +29 -13
  354. package/dist/streaming/showInfo.js +156 -140
  355. package/dist/streaming/showInfo.js.map +1 -1
  356. package/dist/streaming/statusEmitter.d.ts +16 -12
  357. package/dist/streaming/statusEmitter.js +25 -21
  358. package/dist/streaming/statusEmitter.js.map +1 -1
  359. package/dist/types/channels.d.ts +0 -1
  360. package/dist/types/config.d.ts +24 -14
  361. package/dist/types/config.js +5 -1
  362. package/dist/types/config.js.map +1 -1
  363. package/dist/types/index.d.ts +4 -4
  364. package/dist/types/index.js +2 -1
  365. package/dist/types/index.js.map +1 -1
  366. package/dist/types/profiles.d.ts +3 -3
  367. package/dist/types/selection.d.ts +19 -8
  368. package/dist/types/shared.d.ts +0 -1
  369. package/dist/types/streaming.d.ts +12 -6
  370. package/dist/types/streaming.js +12 -5
  371. package/dist/types/streaming.js.map +1 -1
  372. package/dist/upgrade/commands.context.d.ts +0 -1
  373. package/dist/upgrade/commands.d.ts +0 -1
  374. package/dist/upgrade/detection.context.d.ts +0 -1
  375. package/dist/upgrade/detection.d.ts +0 -1
  376. package/dist/upgrade/index.d.ts +0 -1
  377. package/dist/upgrade/lifecycle.context.d.ts +2 -1
  378. package/dist/upgrade/lifecycle.context.js +9 -4
  379. package/dist/upgrade/lifecycle.context.js.map +1 -1
  380. package/dist/upgrade/lifecycle.d.ts +0 -1
  381. package/dist/upgrade/pathHandle.d.ts +0 -1
  382. package/dist/utils/bootSession.context.d.ts +0 -1
  383. package/dist/utils/bootSession.context.js +2 -1
  384. package/dist/utils/bootSession.context.js.map +1 -1
  385. package/dist/utils/bootSession.d.ts +0 -1
  386. package/dist/utils/chromeFetch.d.ts +0 -1
  387. package/dist/utils/cliOutput.d.ts +0 -1
  388. package/dist/utils/debugFilter.d.ts +3 -3
  389. package/dist/utils/debugFilter.js +7 -5
  390. package/dist/utils/debugFilter.js.map +1 -1
  391. package/dist/utils/delay.d.ts +47 -25
  392. package/dist/utils/delay.js +74 -37
  393. package/dist/utils/delay.js.map +1 -1
  394. package/dist/{hdhr → utils}/deviceId.d.ts +1 -2
  395. package/dist/{hdhr → utils}/deviceId.js +11 -29
  396. package/dist/utils/deviceId.js.map +1 -0
  397. package/dist/utils/errors.d.ts +7 -1
  398. package/dist/utils/errors.js +12 -3
  399. package/dist/utils/errors.js.map +1 -1
  400. package/dist/utils/evaluate.d.ts +11 -3
  401. package/dist/utils/evaluate.js +4 -3
  402. package/dist/utils/evaluate.js.map +1 -1
  403. package/dist/utils/ffmpeg.context.d.ts +0 -1
  404. package/dist/utils/ffmpeg.d.ts +0 -1
  405. package/dist/utils/fileLogger.d.ts +26 -14
  406. package/dist/utils/fileLogger.js +89 -49
  407. package/dist/utils/fileLogger.js.map +1 -1
  408. package/dist/utils/format.d.ts +12 -3
  409. package/dist/utils/format.js +29 -11
  410. package/dist/utils/format.js.map +1 -1
  411. package/dist/utils/httpErrors.d.ts +13 -0
  412. package/dist/utils/httpErrors.js +22 -0
  413. package/dist/utils/httpErrors.js.map +1 -0
  414. package/dist/utils/index.d.ts +3 -2
  415. package/dist/utils/index.js +3 -1
  416. package/dist/utils/index.js.map +1 -1
  417. package/dist/utils/logEmitter.d.ts +0 -1
  418. package/dist/utils/logger.d.ts +9 -9
  419. package/dist/utils/logger.js +64 -20
  420. package/dist/utils/logger.js.map +1 -1
  421. package/dist/utils/m3u.d.ts +0 -1
  422. package/dist/utils/markup.d.ts +0 -1
  423. package/dist/utils/markup.js +8 -3
  424. package/dist/utils/markup.js.map +1 -1
  425. package/dist/utils/memo.d.ts +0 -1
  426. package/dist/utils/morganStream.d.ts +0 -1
  427. package/dist/utils/network.d.ts +0 -1
  428. package/dist/utils/never.d.ts +0 -1
  429. package/dist/utils/pid.d.ts +0 -1
  430. package/dist/utils/plainObject.d.ts +6 -0
  431. package/dist/utils/plainObject.js +18 -0
  432. package/dist/utils/plainObject.js.map +1 -0
  433. package/dist/utils/platform.d.ts +0 -1
  434. package/dist/utils/processInspector.context.d.ts +32 -2
  435. package/dist/utils/processInspector.context.js +117 -1
  436. package/dist/utils/processInspector.context.js.map +1 -1
  437. package/dist/utils/processInspector.d.ts +0 -32
  438. package/dist/utils/processInspector.js +10 -117
  439. package/dist/utils/processInspector.js.map +1 -1
  440. package/dist/utils/retry.d.ts +5 -4
  441. package/dist/utils/retry.js +43 -33
  442. package/dist/utils/retry.js.map +1 -1
  443. package/dist/utils/runtimeIdentity.context.d.ts +0 -1
  444. package/dist/utils/runtimeIdentity.context.js +4 -30
  445. package/dist/utils/runtimeIdentity.context.js.map +1 -1
  446. package/dist/utils/runtimeIdentity.d.ts +18 -7
  447. package/dist/utils/runtimeIdentity.js +51 -18
  448. package/dist/utils/runtimeIdentity.js.map +1 -1
  449. package/dist/utils/sanitize.d.ts +0 -1
  450. package/dist/utils/streamContext.d.ts +0 -1
  451. package/dist/utils/timing.d.ts +3 -4
  452. package/dist/utils/timing.js +4 -4
  453. package/dist/utils/timing.js.map +1 -1
  454. package/dist/utils/version.d.ts +5 -4
  455. package/dist/utils/version.js +35 -24
  456. package/dist/utils/version.js.map +1 -1
  457. package/package.json +9 -9
  458. package/dist/app.d.ts.map +0 -1
  459. package/dist/browser/blockedPage.d.ts.map +0 -1
  460. package/dist/browser/browserSupervisor.d.ts.map +0 -1
  461. package/dist/browser/cdp.d.ts.map +0 -1
  462. package/dist/browser/channelSelection.d.ts.map +0 -1
  463. package/dist/browser/consent.d.ts.map +0 -1
  464. package/dist/browser/display.d.ts.map +0 -1
  465. package/dist/browser/hlsPlaylistObserver.d.ts.map +0 -1
  466. package/dist/browser/index.d.ts.map +0 -1
  467. package/dist/browser/launchGovernor.d.ts.map +0 -1
  468. package/dist/browser/login.d.ts.map +0 -1
  469. package/dist/browser/manifestInterceptor.d.ts.map +0 -1
  470. package/dist/browser/pageStaleness.d.ts.map +0 -1
  471. package/dist/browser/precaching.d.ts.map +0 -1
  472. package/dist/browser/tabCapture.d.ts.map +0 -1
  473. package/dist/browser/tabNetworkObserver.d.ts.map +0 -1
  474. package/dist/browser/tabSelection.d.ts.map +0 -1
  475. package/dist/browser/tuning/cache.d.ts.map +0 -1
  476. package/dist/browser/tuning/comcastPolymer.d.ts.map +0 -1
  477. package/dist/browser/tuning/cox.d.ts.map +0 -1
  478. package/dist/browser/tuning/directv.d.ts.map +0 -1
  479. package/dist/browser/tuning/fox.d.ts.map +0 -1
  480. package/dist/browser/tuning/gridSearch.d.ts.map +0 -1
  481. package/dist/browser/tuning/hbo.d.ts.map +0 -1
  482. package/dist/browser/tuning/hulu.d.ts.map +0 -1
  483. package/dist/browser/tuning/shared.d.ts.map +0 -1
  484. package/dist/browser/tuning/sling.d.ts.map +0 -1
  485. package/dist/browser/tuning/spectrum.d.ts.map +0 -1
  486. package/dist/browser/tuning/thumbnailRow.d.ts.map +0 -1
  487. package/dist/browser/tuning/tileClick.d.ts.map +0 -1
  488. package/dist/browser/tuning/xfinity.d.ts.map +0 -1
  489. package/dist/browser/tuning/youtubeTv.d.ts.map +0 -1
  490. package/dist/browser/video.d.ts.map +0 -1
  491. package/dist/browser/windowSync.d.ts.map +0 -1
  492. package/dist/channels/index.d.ts.map +0 -1
  493. package/dist/config/channelForm.d.ts.map +0 -1
  494. package/dist/config/consistencyProbe.d.ts.map +0 -1
  495. package/dist/config/health.d.ts.map +0 -1
  496. package/dist/config/index.d.ts.map +0 -1
  497. package/dist/config/paths.d.ts.map +0 -1
  498. package/dist/config/persistence.context.d.ts.map +0 -1
  499. package/dist/config/persistence.d.ts.map +0 -1
  500. package/dist/config/presets.d.ts.map +0 -1
  501. package/dist/config/profiles.d.ts.map +0 -1
  502. package/dist/config/providerLineups.d.ts.map +0 -1
  503. package/dist/config/reactivity.d.ts.map +0 -1
  504. package/dist/config/servicePacks.d.ts.map +0 -1
  505. package/dist/config/services.d.ts.map +0 -1
  506. package/dist/config/sites.d.ts.map +0 -1
  507. package/dist/config/userChannels.d.ts.map +0 -1
  508. package/dist/config/userConfig.d.ts.map +0 -1
  509. package/dist/config/userProfiles.d.ts.map +0 -1
  510. package/dist/hdhr/channelMap.d.ts.map +0 -1
  511. package/dist/hdhr/deviceId.d.ts.map +0 -1
  512. package/dist/hdhr/deviceId.js.map +0 -1
  513. package/dist/hdhr/discover.d.ts.map +0 -1
  514. package/dist/hdhr/getHandlers.d.ts.map +0 -1
  515. package/dist/hdhr/identity.d.ts.map +0 -1
  516. package/dist/hdhr/index.d.ts.map +0 -1
  517. package/dist/hdhr/protocol.d.ts.map +0 -1
  518. package/dist/hdhr/tunerState.d.ts.map +0 -1
  519. package/dist/hdhr/udp.d.ts.map +0 -1
  520. package/dist/identity.d.ts.map +0 -1
  521. package/dist/index.d.ts.map +0 -1
  522. package/dist/native/codecInference.d.ts.map +0 -1
  523. package/dist/native/decrypt.d.ts.map +0 -1
  524. package/dist/native/index.d.ts.map +0 -1
  525. package/dist/native/probe.d.ts.map +0 -1
  526. package/dist/native/proxy.d.ts.map +0 -1
  527. package/dist/native/tokenExpiry.d.ts.map +0 -1
  528. package/dist/routes/assets.d.ts.map +0 -1
  529. package/dist/routes/auth.d.ts.map +0 -1
  530. package/dist/routes/cdp.d.ts.map +0 -1
  531. package/dist/routes/channels.d.ts.map +0 -1
  532. package/dist/routes/clientActions.d.ts.map +0 -1
  533. package/dist/routes/components.d.ts.map +0 -1
  534. package/dist/routes/config/channels/endpoints/browse.d.ts.map +0 -1
  535. package/dist/routes/config/channels/endpoints/bulk.d.ts.map +0 -1
  536. package/dist/routes/config/channels/endpoints/crud.d.ts.map +0 -1
  537. package/dist/routes/config/channels/endpoints/importExport.d.ts.map +0 -1
  538. package/dist/routes/config/channels/endpoints/predefined.d.ts.map +0 -1
  539. package/dist/routes/config/channels/endpoints/prefs.d.ts.map +0 -1
  540. package/dist/routes/config/channels/endpoints/service.d.ts.map +0 -1
  541. package/dist/routes/config/channels/endpoints/tags.d.ts.map +0 -1
  542. package/dist/routes/config/channels/healthBridge.d.ts.map +0 -1
  543. package/dist/routes/config/channels/http/handler.d.ts.map +0 -1
  544. package/dist/routes/config/channels/http/playlistHint.d.ts.map +0 -1
  545. package/dist/routes/config/channels/http/serviceWarning.d.ts.map +0 -1
  546. package/dist/routes/config/channels/index.d.ts.map +0 -1
  547. package/dist/routes/config/channels/setup.d.ts.map +0 -1
  548. package/dist/routes/config/channels/table.d.ts.map +0 -1
  549. package/dist/routes/config/http/envelope.d.ts.map +0 -1
  550. package/dist/routes/config/index.d.ts.map +0 -1
  551. package/dist/routes/config/services.d.ts.map +0 -1
  552. package/dist/routes/config/settings.d.ts.map +0 -1
  553. package/dist/routes/debug.d.ts.map +0 -1
  554. package/dist/routes/health.d.ts.map +0 -1
  555. package/dist/routes/hls.d.ts.map +0 -1
  556. package/dist/routes/icons.d.ts.map +0 -1
  557. package/dist/routes/index.d.ts.map +0 -1
  558. package/dist/routes/logs.d.ts.map +0 -1
  559. package/dist/routes/mpegts.d.ts.map +0 -1
  560. package/dist/routes/play.d.ts.map +0 -1
  561. package/dist/routes/playlist.d.ts.map +0 -1
  562. package/dist/routes/root/content.d.ts.map +0 -1
  563. package/dist/routes/root/index.d.ts.map +0 -1
  564. package/dist/routes/root/scripts/channels.d.ts.map +0 -1
  565. package/dist/routes/root/scripts/clientEscape.d.ts.map +0 -1
  566. package/dist/routes/root/scripts/clientUrl.d.ts.map +0 -1
  567. package/dist/routes/root/scripts/config.d.ts.map +0 -1
  568. package/dist/routes/root/scripts/index.d.ts.map +0 -1
  569. package/dist/routes/root/scripts/shared.d.ts.map +0 -1
  570. package/dist/routes/root/scripts/status.d.ts.map +0 -1
  571. package/dist/routes/root/scripts/status.handlers.d.ts.map +0 -1
  572. package/dist/routes/root/styles.d.ts.map +0 -1
  573. package/dist/routes/services.d.ts.map +0 -1
  574. package/dist/routes/sse.d.ts.map +0 -1
  575. package/dist/routes/streams.d.ts.map +0 -1
  576. package/dist/routes/theme.d.ts.map +0 -1
  577. package/dist/routes/ui.d.ts.map +0 -1
  578. package/dist/routes/upgrade.d.ts.map +0 -1
  579. package/dist/service/commands.context.d.ts.map +0 -1
  580. package/dist/service/commands.d.ts.map +0 -1
  581. package/dist/service/generators.context.d.ts.map +0 -1
  582. package/dist/service/generators.d.ts.map +0 -1
  583. package/dist/service/index.d.ts.map +0 -1
  584. package/dist/streaming/captureLock.d.ts.map +0 -1
  585. package/dist/streaming/captureSession.d.ts.map +0 -1
  586. package/dist/streaming/clients.d.ts.map +0 -1
  587. package/dist/streaming/codec.d.ts.map +0 -1
  588. package/dist/streaming/fmp4Segmenter.d.ts.map +0 -1
  589. package/dist/streaming/hls.d.ts.map +0 -1
  590. package/dist/streaming/hlsResume.d.ts.map +0 -1
  591. package/dist/streaming/hlsSegments.d.ts.map +0 -1
  592. package/dist/streaming/lifecycle.d.ts.map +0 -1
  593. package/dist/streaming/monitor.d.ts.map +0 -1
  594. package/dist/streaming/mp4Parser.d.ts.map +0 -1
  595. package/dist/streaming/mpegts.d.ts.map +0 -1
  596. package/dist/streaming/playlistBuilder.d.ts.map +0 -1
  597. package/dist/streaming/preroll.d.ts.map +0 -1
  598. package/dist/streaming/pretune.d.ts.map +0 -1
  599. package/dist/streaming/pretuneTimers.d.ts.map +0 -1
  600. package/dist/streaming/recovery.d.ts.map +0 -1
  601. package/dist/streaming/registry.d.ts.map +0 -1
  602. package/dist/streaming/setup.d.ts.map +0 -1
  603. package/dist/streaming/showInfo.d.ts.map +0 -1
  604. package/dist/streaming/statusEmitter.d.ts.map +0 -1
  605. package/dist/types/channels.d.ts.map +0 -1
  606. package/dist/types/config.d.ts.map +0 -1
  607. package/dist/types/index.d.ts.map +0 -1
  608. package/dist/types/profiles.d.ts.map +0 -1
  609. package/dist/types/selection.d.ts.map +0 -1
  610. package/dist/types/shared.d.ts.map +0 -1
  611. package/dist/types/streaming.d.ts.map +0 -1
  612. package/dist/upgrade/commands.context.d.ts.map +0 -1
  613. package/dist/upgrade/commands.d.ts.map +0 -1
  614. package/dist/upgrade/detection.context.d.ts.map +0 -1
  615. package/dist/upgrade/detection.d.ts.map +0 -1
  616. package/dist/upgrade/index.d.ts.map +0 -1
  617. package/dist/upgrade/lifecycle.context.d.ts.map +0 -1
  618. package/dist/upgrade/lifecycle.d.ts.map +0 -1
  619. package/dist/upgrade/pathHandle.d.ts.map +0 -1
  620. package/dist/utils/bootSession.context.d.ts.map +0 -1
  621. package/dist/utils/bootSession.d.ts.map +0 -1
  622. package/dist/utils/chromeFetch.d.ts.map +0 -1
  623. package/dist/utils/cliOutput.d.ts.map +0 -1
  624. package/dist/utils/clock.d.ts +0 -15
  625. package/dist/utils/clock.d.ts.map +0 -1
  626. package/dist/utils/clock.js +0 -31
  627. package/dist/utils/clock.js.map +0 -1
  628. package/dist/utils/debugFilter.d.ts.map +0 -1
  629. package/dist/utils/delay.d.ts.map +0 -1
  630. package/dist/utils/errors.d.ts.map +0 -1
  631. package/dist/utils/evaluate.d.ts.map +0 -1
  632. package/dist/utils/ffmpeg.context.d.ts.map +0 -1
  633. package/dist/utils/ffmpeg.d.ts.map +0 -1
  634. package/dist/utils/fileLogger.d.ts.map +0 -1
  635. package/dist/utils/format.d.ts.map +0 -1
  636. package/dist/utils/index.d.ts.map +0 -1
  637. package/dist/utils/logEmitter.d.ts.map +0 -1
  638. package/dist/utils/logger.d.ts.map +0 -1
  639. package/dist/utils/m3u.d.ts.map +0 -1
  640. package/dist/utils/markup.d.ts.map +0 -1
  641. package/dist/utils/memo.d.ts.map +0 -1
  642. package/dist/utils/morganStream.d.ts.map +0 -1
  643. package/dist/utils/network.d.ts.map +0 -1
  644. package/dist/utils/never.d.ts.map +0 -1
  645. package/dist/utils/pid.d.ts.map +0 -1
  646. package/dist/utils/platform.d.ts.map +0 -1
  647. package/dist/utils/processInspector.context.d.ts.map +0 -1
  648. package/dist/utils/processInspector.d.ts.map +0 -1
  649. package/dist/utils/retry.d.ts.map +0 -1
  650. package/dist/utils/runtimeIdentity.context.d.ts.map +0 -1
  651. package/dist/utils/runtimeIdentity.d.ts.map +0 -1
  652. package/dist/utils/sanitize.d.ts.map +0 -1
  653. package/dist/utils/streamContext.d.ts.map +0 -1
  654. package/dist/utils/timing.d.ts.map +0 -1
  655. package/dist/utils/version.d.ts.map +0 -1
@@ -2,14 +2,15 @@
2
2
  *
3
3
  * proxy.ts: Native HLS proxy - manifest polling, segment fetching, and playlist generation.
4
4
  */
5
- import { LOG, chromeFetch, realClock, startTimer } from "../utils/index.js";
6
- import { buildPrerollEntries, computePrerollWindow } from "../streaming/preroll.js";
5
+ import { LOG, chromeFetch, resolveUrl, startTimer, timeoutSignal } from "../utils/index.js";
6
+ import { buildPrerollEntries, buildPrerollInitUri, computePrerollWindow } from "../streaming/preroll.js";
7
7
  import { decryptSegment, deriveIvFromSequence, fetchDecryptionKey, parseExplicitIv } from "./decrypt.js";
8
8
  import { findNamedInitSegment, pruneNamedInitSegments, storeAudioSegment, storeNamedInitSegment, storeSegment, updateAudioPlaylist, updatePlaylist, updateVideoPlaylist } from "../streaming/hlsSegments.js";
9
+ import { CAPTURE_BASELINE_CODEC } from "../types/index.js";
9
10
  import { CONFIG } from "../config/index.js";
10
11
  import { buildPlaylist } from "../streaming/playlistBuilder.js";
11
12
  import { getStream } from "../streaming/registry.js";
12
- import { resolveUrl } from "./probe.js";
13
+ import { systemClock } from "homebridge-plugin-utils";
13
14
  /* This module implements the native HLS proxy that replaces Chrome screen capture for viable streams. It polls the service's variant manifest at regular intervals,
14
15
  * detects new segments by tracking #EXT-X-MEDIA-SEQUENCE, fetches each segment (optionally decrypting AES-128), stores them in the existing HLS segment system, and
15
16
  * generates an HLS playlist that faithfully propagates upstream metadata - discontinuity markers, program timestamps, and SCTE-35 ad signaling (cue-out, cue-in,
@@ -33,10 +34,10 @@ const MAX_MANIFEST_FAILURES = 3;
33
34
  // dropped connection on one segment) are more common here; this higher tolerance than MAX_MANIFEST_FAILURES avoids escalating on blips that would otherwise
34
35
  // self-resolve on the next fetch.
35
36
  const MAX_SEGMENT_FAILURES = 5;
36
- // Number of segment byte-fetches one track keeps in flight while its commit walk consumes the results in broadcast order. Three concurrent connections lift a
37
- // track's download throughput to roughly three times the per-connection pacing a CDN edge grants, which is what keeps a high-bitrate channel ahead of its own
38
- // production rate rather than accumulating a backlog that delays the next manifest poll. The bound is what keeps the gain cheap: at most three segments per
39
- // track are resident at once - six for a stream whose audio is a separate rendition, since each track runs its own window.
37
+ // Number of segment byte-fetches one track keeps in flight while its commit walk consumes the results in broadcast order. Running that many connections at once
38
+ // lifts a track's download throughput to roughly that many times the per-connection pacing a CDN edge grants, which is what keeps a high-bitrate channel ahead
39
+ // of its own production rate rather than accumulating a backlog that delays the next manifest poll. The bound is what keeps the gain cheap: at most
40
+ // PARALLEL_SEGMENT_FETCHES segments per track are resident at once - twice that when audio is a separate rendition, since each track runs its own window.
40
41
  const PARALLEL_SEGMENT_FETCHES = 3;
41
42
  // Manifest poll backoff base delay and cap. On success, the poll interval returns to the base delay (a fixed 3000ms, ~half a typical 6s segment). On failure, the delay
42
43
  // doubles on each consecutive failure up to the cap. Jitter of +/-20% prevents multiple streams from retrying in lockstep after a shared CDN outage.
@@ -427,7 +428,7 @@ async function resolveTrackInit(options) {
427
428
  servedName = ((track === "video") ? "init-v" : "init-a") + String(tracking.initCounter) + ".mp4";
428
429
  tracking.initCounter++;
429
430
  }
430
- storeNamedInitSegment(ctx.streamId, track, servedName, data);
431
+ storeNamedInitSegment(ctx.streamId, track, servedName, data, ctx.clock.now());
431
432
  tracking.lastUpstreamMapUri = mapUri;
432
433
  LOG.debug("native:proxy", "Initialization segment %s for %s (%s track, %s bytes, %s).", (existingName === undefined) ? "stored as " + servedName :
433
434
  "reused as " + servedName, ctx.channelName, track, data.length, mapUri.slice(0, 80));
@@ -659,21 +660,30 @@ async function pollAudioStream(ctx, audio, pipeline) {
659
660
  return false;
660
661
  }
661
662
  try {
662
- const response = await chromeFetch(audio.variantUrl, { signal: AbortSignal.timeout(SEGMENT_FETCH_TIMEOUT) });
663
- if (!response.ok) {
664
- audio.consecutiveManifestFailures++;
665
- ctx.stats.totalFetchErrors++;
666
- // Classify the failure via the shared threshold helper so the audio path escalates identically to the video path: 4xx uses the base threshold, 5xx and
667
- // network conditions get double the attempts.
668
- const effectiveThreshold = manifestFailureThreshold(response.status);
669
- LOG.debug("native:proxy", "Audio manifest poll failed for %s: HTTP %s (%s/%s).", ctx.channelName, response.status, audio.consecutiveManifestFailures, effectiveThreshold);
670
- if (audio.consecutiveManifestFailures >= effectiveThreshold) {
671
- escalateProxyFailure({ ctx, message: "audio manifest poll failed " + String(audio.consecutiveManifestFailures) + " times" });
663
+ const bound = timeoutSignal(SEGMENT_FETCH_TIMEOUT, { clock: ctx.clock });
664
+ let body;
665
+ try {
666
+ const response = await chromeFetch(audio.variantUrl, { signal: bound.signal });
667
+ if (!response.ok) {
668
+ audio.consecutiveManifestFailures++;
669
+ ctx.stats.totalFetchErrors++;
670
+ // Classify the failure via the shared threshold helper so the audio path escalates identically to the video path: 4xx uses the base threshold, 5xx and
671
+ // network conditions get double the attempts.
672
+ const effectiveThreshold = manifestFailureThreshold(response.status);
673
+ LOG.debug("native:proxy", "Audio manifest poll failed for %s: HTTP %s (%s/%s).", ctx.channelName, response.status, audio.consecutiveManifestFailures, effectiveThreshold);
674
+ if (audio.consecutiveManifestFailures >= effectiveThreshold) {
675
+ escalateProxyFailure({ ctx, message: "audio manifest poll failed " + String(audio.consecutiveManifestFailures) + " times" });
676
+ }
677
+ return false;
672
678
  }
673
- return false;
679
+ audio.consecutiveManifestFailures = 0;
680
+ body = await response.text();
681
+ }
682
+ finally {
683
+ // The bound covers this poll's own network wait and nothing past it: a bound is armed for exactly the wait it bounds and disposed the moment that wait
684
+ // settles, so the audio processing below runs with no timer of this poll's left pending on the proxy's clock.
685
+ bound.cancel();
674
686
  }
675
- audio.consecutiveManifestFailures = 0;
676
- const body = await response.text();
677
687
  return await processAudioStream(ctx, audio, body, audio.variantUrl, pipeline);
678
688
  }
679
689
  catch (error) {
@@ -741,10 +751,10 @@ async function processAudioStream(ctx, audio, body, baseUrl, pipeline) {
741
751
  // Composite Playlist Builder.
742
752
  /**
743
753
  * Builds a composite playlist with fMP4 preroll entries and the relayed source's real entries. Uses the same compositor and builder as the capture path's
744
- * generatePlaylist(), ensuring identical windowing behavior (maxPrerollInWindow cap, progressive falloff). The DISCONTINUITY tag at the preroll-to-real boundary
745
- * signals the transition between two independently-initialized runs of content, which is spec-compliant per RFC 8216 Section 4.3.3.3 - and when the relayed
746
- * source is itself fMP4, that boundary is also where its own initialization reference is re-emitted. VERSION:7 is used throughout to support EXT-X-MAP, and stays
747
- * backward-compatible with MPEG-TS entries once preroll falls off the window.
754
+ * generatePlaylist(), ensuring identical windowing behavior (the MAX_PREROLL_IN_WINDOW cap in streaming/preroll.ts, progressive falloff). The DISCONTINUITY
755
+ * tag at the preroll-to-real boundary signals the transition between two independently-initialized runs of content, which is spec-compliant per RFC 8216
756
+ * Section 4.3.3.3 - and when the relayed source is itself fMP4, that boundary is also where its own initialization reference is re-emitted. VERSION:7 is used
757
+ * throughout to support EXT-X-MAP, and stays backward-compatible with MPEG-TS entries once preroll falls off the window.
748
758
  *
749
759
  * @param options - Composite playlist configuration with segment data, preroll settings, and composite tracking state.
750
760
  * @returns The formatted composite m3u8 playlist string.
@@ -752,20 +762,22 @@ async function processAudioStream(ctx, audio, body, baseUrl, pipeline) {
752
762
  function buildCompositePlaylist(options) {
753
763
  const { composite, prerollBaseUrl, prerollCodec, prerollSegmentCount, segmentEntries, segmentIndex, targetDuration, videoMetadata } = options;
754
764
  // Compute the sliding window start index via the compositor. The three-way max prevents negative indices, enforces the sliding window rule, and caps preroll
755
- // entries at maxPrerollInWindow to force clients past preroll toward the live edge.
765
+ // entries at the preroll-in-window cap to force clients past preroll toward the live edge.
756
766
  const realSegmentCount = segmentEntries.length;
757
767
  const startIndex = computePrerollWindow({
758
768
  currentSegmentIndex: segmentIndex,
759
769
  maxSegments: CONFIG.hls.maxSegments,
760
770
  prerollSegmentCount,
771
+ // The relay numbers its composite from index 0, so its preroll starts there.
772
+ prerollStartIndex: 0,
761
773
  realSegmentCount
762
774
  });
763
- // Build fMP4 preroll entries for preroll indices still in the window. These reference the global /preroll/ routes with absolute URLs and .m4s extension.
775
+ // Build fMP4 preroll entries for preroll indices still in the window. These reference the global /preroll/ routes with absolute URLs.
764
776
  let prerollEntries = [];
765
777
  if (startIndex < prerollSegmentCount) {
766
- prerollEntries = buildPrerollEntries({ baseUrl: prerollBaseUrl, codec: prerollCodec, extension: ".m4s", prerollSegmentCount, startIndex });
778
+ prerollEntries = buildPrerollEntries({ baseUrl: prerollBaseUrl, codec: prerollCodec, prerollSegmentCount, startIndex });
767
779
  }
768
- // Build real MPEG-TS entries from the video metadata maps via the shared helper.
780
+ // Build the relayed source's real entries (MPEG-TS or fMP4) from the video metadata maps via the shared helper.
769
781
  const realEntries = segmentEntries.map((filename) => buildEntryFromMetadata(filename, videoMetadata, targetDuration));
770
782
  // Mark the preroll-to-real boundary on the first real entry when preroll entries are present in the window. This is a playlist-level concern (the stitching of
771
783
  // preroll before real content), not a segment-level property - so it's applied here in the composite builder rather than injected into videoMetadata at segment
@@ -807,7 +819,7 @@ function buildCompositePlaylist(options) {
807
819
  * has fallen out of the window, the real content's own initialization becomes the window-level reference directly.
808
820
  */
809
821
  const realInitialMapUri = annotateMapTransitions(realEntries, videoMetadata);
810
- let initialMapUri = (prerollEntries.length > 0) ? (prerollBaseUrl + "/preroll/" + prerollCodec + "/init.mp4") : undefined;
822
+ let initialMapUri = (prerollEntries.length > 0) ? buildPrerollInitUri(prerollBaseUrl, prerollCodec) : undefined;
811
823
  if (realInitialMapUri !== null) {
812
824
  if ((prerollEntries.length > 0) && firstRealEntry) {
813
825
  firstRealEntry.mapUri = realInitialMapUri;
@@ -831,12 +843,14 @@ function buildCompositePlaylist(options) {
831
843
  * @returns The native proxy interface.
832
844
  */
833
845
  export function createNativeProxy(options) {
834
- const { channelName, clock = realClock, container, encryption, keyUrl, onError, streamId } = options;
846
+ const { channelName, clock = systemClock, container, encryption, keyUrl, onError, streamId } = options;
835
847
  const hasAudio = options.audioVariantUrl !== null;
848
+ // The preroll codec is read only while a preroll is active, and every caller that activates one passes its codec, so the baseline stands in only for a proxy that
849
+ // never reads it.
850
+ const prerollCodec = options.prerollCodec ?? CAPTURE_BASELINE_CODEC;
836
851
  // Preroll segment index offset. When preroll is ready (prerollSegmentCount > 0), real segments start numbering after the preroll range (e.g., segmentN.ts where
837
852
  // N = prerollSegmentCount). This offset is unconditional - it reserves the index space for preroll regardless of whether the deferred preroll timer fires.
838
853
  // The composite playlist behavior (including preroll entries) is determined dynamically by checking stream.hls.prerollStartTime at playlist generation time.
839
- const prerollCodec = options.prerollCodec ?? "h264";
840
854
  const prerollSegmentCount = options.prerollSegmentCount ?? 0;
841
855
  /* Proxy state. These track segment storage, manifest polling, error thresholds, and playlist generation across the proxy's lifetime. Mutable variables are organized
842
856
  * into typed state objects by subsystem (lifecycle, video tracking, audio tracking, composite playlist, statistics) to clarify ownership and interaction boundaries.
@@ -904,7 +918,7 @@ export function createNativeProxy(options) {
904
918
  totalTokenRefreshes: 0
905
919
  };
906
920
  // Shared context for extracted module-level functions. Bundles immutable references with shared mutable state objects.
907
- const ctx = { channelName, lifecycle, onError, stats, streamId };
921
+ const ctx = { channelName, clock, lifecycle, onError, stats, streamId };
908
922
  /* The two tracks' pipeline bindings. Construction happens here, at the proxy's composition root, so the pipeline that serves both loops carries no branch on
909
923
  * which track it is running: everything that differs between them - where segments are stored, how many are stored, how they are named, which initialization
910
924
  * slot they read, and the extra bookkeeping video alone keeps - is data on these two objects.
@@ -912,7 +926,7 @@ export function createNativeProxy(options) {
912
926
  const videoPipeline = {
913
927
  afterCommit: ({ byteLength, storedBefore }) => {
914
928
  video.lastSegmentSize = byteLength;
915
- video.lastSegmentTime = Date.now();
929
+ video.lastSegmentTime = ctx.clock.now();
916
930
  // Log the first segment fetch latency for timing diagnostics.
917
931
  if (stats.totalSegmentsFetched === 1) {
918
932
  LOG.debug("timing:native", "First segment fetched for %s (%s bytes).", channelName, byteLength);
@@ -969,34 +983,44 @@ export function createNativeProxy(options) {
969
983
  return Math.round(lifecycle.manifestBackoffMs * jitter);
970
984
  }
971
985
  /**
972
- * Orchestrates the poll cycle: fetches video and audio manifests in parallel, stores segments, and generates playlists after both stores are updated.
986
+ * Orchestrates the poll cycle: fetches the video manifest first, then runs video segment processing in parallel with the audio poll (the audio manifest fetch
987
+ * and its segments), and generates playlists after both stores are updated.
973
988
  */
974
989
  async function pollManifest() {
975
990
  if (lifecycle.stopped) {
976
991
  return;
977
992
  }
978
- const pollElapsed = startTimer();
993
+ const pollElapsed = startTimer(clock);
979
994
  try {
980
- const response = await chromeFetch(video.variantUrl, { signal: AbortSignal.timeout(SEGMENT_FETCH_TIMEOUT) });
981
- if (!response.ok) {
982
- video.consecutiveManifestFailures++;
983
- stats.totalFetchErrors++;
984
- // Classify the error via the shared threshold helper. Client errors (4xx) use the base threshold; server errors (5xx) and network conditions get double the
985
- // attempts. This is the same logic the audio poll path uses, so video and audio escalate identically.
986
- const isClientError = (response.status >= 400) && (response.status < 500);
987
- const effectiveThreshold = manifestFailureThreshold(response.status);
988
- LOG.debug("native:proxy", "Manifest poll failed for %s: HTTP %s (%s, %s/%s).", channelName, response.status, isClientError ? "client" : "server", video.consecutiveManifestFailures, effectiveThreshold);
989
- if (video.consecutiveManifestFailures >= effectiveThreshold) {
990
- escalateProxyFailure({ ctx, message: "manifest poll failed " + String(video.consecutiveManifestFailures) + " times (HTTP " +
991
- String(response.status) + ")" });
995
+ const bound = timeoutSignal(SEGMENT_FETCH_TIMEOUT, { clock });
996
+ let body;
997
+ try {
998
+ const response = await chromeFetch(video.variantUrl, { signal: bound.signal });
999
+ if (!response.ok) {
1000
+ video.consecutiveManifestFailures++;
1001
+ stats.totalFetchErrors++;
1002
+ // Classify the error via the shared threshold helper. Client errors (4xx) use the base threshold; server errors (5xx) and network conditions get double
1003
+ // the attempts. This is the same logic the audio poll path uses, so video and audio escalate identically.
1004
+ const isClientError = (response.status >= 400) && (response.status < 500);
1005
+ const effectiveThreshold = manifestFailureThreshold(response.status);
1006
+ LOG.debug("native:proxy", "Manifest poll failed for %s: HTTP %s (%s, %s/%s).", channelName, response.status, isClientError ? "client" : "server", video.consecutiveManifestFailures, effectiveThreshold);
1007
+ if (video.consecutiveManifestFailures >= effectiveThreshold) {
1008
+ escalateProxyFailure({ ctx, message: "manifest poll failed " + String(video.consecutiveManifestFailures) + " times (HTTP " +
1009
+ String(response.status) + ")" });
1010
+ return;
1011
+ }
1012
+ schedulePoll(nextBackoffDelay());
992
1013
  return;
993
1014
  }
994
- schedulePoll(nextBackoffDelay());
995
- return;
1015
+ video.consecutiveManifestFailures = 0;
1016
+ lifecycle.manifestBackoffMs = MANIFEST_BACKOFF_BASE;
1017
+ body = await response.text();
1018
+ }
1019
+ finally {
1020
+ // The bound covers this poll's own network wait and nothing past it: a bound is armed for exactly the wait it bounds and disposed the moment that wait
1021
+ // settles, so the segment work below runs with no timer of this poll's left pending on the proxy's clock.
1022
+ bound.cancel();
996
1023
  }
997
- video.consecutiveManifestFailures = 0;
998
- lifecycle.manifestBackoffMs = MANIFEST_BACKOFF_BASE;
999
- const body = await response.text();
1000
1024
  LOG.debug("native:proxy", "Manifest poll for %s completed in %sms.", channelName, pollElapsed());
1001
1025
  LOG.debug("native:manifest", "Variant manifest for %s:\n%s", channelName, body);
1002
1026
  // Fetch and store video and audio segments in parallel so neither delays the other, and generate playlists after both stores are updated. This ensures video
@@ -1046,7 +1070,7 @@ export function createNativeProxy(options) {
1046
1070
  schedulePoll(nextBackoffDelay());
1047
1071
  return;
1048
1072
  }
1049
- // Schedule the next poll at MANIFEST_BACKOFF_BASE (3000ms, ~half a typical 6s segment) for timely detection of new segments.
1073
+ // Schedule the next poll at MANIFEST_BACKOFF_BASE for timely detection of new segments.
1050
1074
  lifecycle.manifestBackoffMs = MANIFEST_BACKOFF_BASE;
1051
1075
  schedulePoll(MANIFEST_BACKOFF_BASE);
1052
1076
  }
@@ -1114,7 +1138,7 @@ export function createNativeProxy(options) {
1114
1138
  * Fetches and optionally decrypts a segment. This is the shared core used by both video and audio fetch paths, for media segments and for the initialization
1115
1139
  * segments an fMP4 source references. It handles the HTTP fetch, AES-128 decryption with key caching, and IV derivation. A caller with no key URL gets a plain
1116
1140
  * fetch, since the decryption block below gates on the key and the sequence argument only feeds keyed-IV derivation. Error tracking is the caller's
1117
- * responsibility since video and audio have independent failure thresholds.
1141
+ * responsibility since video and audio keep separate consecutive-failure counts against the shared MAX_SEGMENT_FAILURES threshold.
1118
1142
  *
1119
1143
  * @param url - The segment URL.
1120
1144
  * @param sequence - The media sequence number (for IV derivation).
@@ -1124,15 +1148,23 @@ export function createNativeProxy(options) {
1124
1148
  * @returns The segment data (decrypted if necessary), or null on failure.
1125
1149
  */
1126
1150
  async function fetchAndDecryptSegment(url, sequence, ivHex, segKeyUrl, abortSignal) {
1127
- // The caller's cancellation and this fetch's own timeout are composed, so the segment ends on whichever arrives first and a caller with nothing to cancel
1128
- // gets the timeout alone.
1129
- const timeout = AbortSignal.timeout(SEGMENT_FETCH_TIMEOUT);
1130
- const response = await chromeFetch(url, { signal: abortSignal ? AbortSignal.any([abortSignal, timeout]) : timeout });
1131
- if (!response.ok) {
1132
- LOG.debug("native:proxy", "Segment fetch failed for %s: HTTP %s.", channelName, response.status);
1133
- return null;
1151
+ // The caller's cancellation and this fetch's own bound are composed, so the segment ends on whichever arrives first and a caller with nothing to cancel gets
1152
+ // the bound alone.
1153
+ const bound = timeoutSignal(SEGMENT_FETCH_TIMEOUT, { clock });
1154
+ let data;
1155
+ try {
1156
+ const response = await chromeFetch(url, { signal: abortSignal ? AbortSignal.any([abortSignal, bound.signal]) : bound.signal });
1157
+ if (!response.ok) {
1158
+ LOG.debug("native:proxy", "Segment fetch failed for %s: HTTP %s.", channelName, response.status);
1159
+ return null;
1160
+ }
1161
+ data = Buffer.from(await response.arrayBuffer());
1162
+ }
1163
+ finally {
1164
+ // The bound covers the segment's own fetch and body read. The key lookup below carries its own bound, and the decryption is pure work, so neither belongs
1165
+ // inside this one.
1166
+ bound.cancel();
1134
1167
  }
1135
- let data = Buffer.from(await response.arrayBuffer());
1136
1168
  // Decrypt if this segment has a key URL. The manifest's #EXT-X-KEY tag is authoritative - DAI streams can switch between clear and AES-128 mid-stream (e.g., ad
1137
1169
  // pods encrypted while main content is clear), so the initial probe classification cannot be relied upon. Segments before the first #EXT-X-KEY tag have
1138
1170
  // segKeyUrl === null (clear).
@@ -1142,7 +1174,7 @@ export function createNativeProxy(options) {
1142
1174
  if (!key) {
1143
1175
  // The key fetch receives the raw cancellation signal rather than the composed one above, so it gets its own full timeout budget instead of inheriting
1144
1176
  // whatever this segment's fetch has already spent.
1145
- key = await fetchDecryptionKey(segKeyUrl, abortSignal) ?? undefined;
1177
+ key = await fetchDecryptionKey(segKeyUrl, { clock, signal: abortSignal }) ?? undefined;
1146
1178
  if (!key) {
1147
1179
  LOG.debug("native:proxy", "Decryption key unavailable for %s (key URL: %s).", channelName, segKeyUrl.slice(0, 80));
1148
1180
  return null;
@@ -1193,10 +1225,10 @@ export function createNativeProxy(options) {
1193
1225
  }
1194
1226
  /**
1195
1227
  * Generates playlists from the stored segments. For streams without separate audio, generates a single variant playlist. When preroll is active (muxed audio only),
1196
- * produces a composite playlist with fMP4 preroll entries and MPEG-TS real entries bridged by a DISCONTINUITY tag. The preroll entries use the same fMP4 segments
1197
- * served during the standalone preroll phase, ensuring smooth MEDIA-SEQUENCE progression. For streams with separate audio, generates the video variant playlist
1198
- * and the master playlist referencing video.m3u8 and audio.m3u8 (no preroll - preroll is muxed and can't be split into separate renditions); the audio variant
1199
- * playlist is produced separately by generateAudioPlaylist(), which the poll loop calls alongside this function.
1228
+ * produces a composite playlist with fMP4 preroll entries and the relayed source's real entries (MPEG-TS or fMP4) bridged by a DISCONTINUITY tag. The preroll
1229
+ * entries use the same fMP4 segments served during the standalone preroll phase, ensuring smooth MEDIA-SEQUENCE progression. For streams with separate audio,
1230
+ * generates the video variant playlist and the master playlist referencing video.m3u8 and audio.m3u8 (no preroll - preroll is muxed and can't be split into
1231
+ * separate renditions); the audio variant playlist is produced separately by generateAudioPlaylist(), which the poll loop calls alongside this function.
1200
1232
  *
1201
1233
  * @param targetDuration - The #EXT-X-TARGETDURATION value from the service's manifest.
1202
1234
  */
@@ -1207,10 +1239,11 @@ export function createNativeProxy(options) {
1207
1239
  }
1208
1240
  const segmentEntries = Array.from(stream.hls.segments.keys());
1209
1241
  if (!hasAudio) {
1210
- if ((prerollSegmentCount > 0) && stream.hls.prerollStartTime && stream.hls.prerollBaseUrl) {
1211
- // Composite playlist with fMP4 preroll entries + MPEG-TS real entries. The prerollStartTime check ensures we only include preroll entries when the deferred
1212
- // timer has fired and the client is actually watching preroll. Without this check, fast native streams (where real content arrives before the preroll delay)
1213
- // would include unnecessary preroll entries. The compositor handles the sliding window with the maxPrerollInWindow cap.
1242
+ if ((prerollSegmentCount > 0) && (stream.hls.prerollStartTime !== null) && stream.hls.prerollBaseUrl) {
1243
+ // Composite playlist with fMP4 preroll entries and the relayed source's real entries (MPEG-TS or fMP4). The prerollStartTime check ensures we only
1244
+ // include preroll entries when the deferred timer has fired and the client is actually watching preroll. Without this check, fast native streams (where
1245
+ // real content arrives before the preroll delay) would include unnecessary preroll entries. The compositor handles the sliding window with the
1246
+ // preroll-in-window cap.
1214
1247
  updatePlaylist(streamId, buildCompositePlaylist({
1215
1248
  composite,
1216
1249
  prerollBaseUrl: stream.hls.prerollBaseUrl,
@@ -1220,19 +1253,19 @@ export function createNativeProxy(options) {
1220
1253
  segmentIndex: video.segmentIndex,
1221
1254
  targetDuration,
1222
1255
  videoMetadata: video.metadata
1223
- }));
1256
+ }), clock.now());
1224
1257
  }
1225
1258
  else {
1226
1259
  // No preroll active - standard variant playlist. The segment index may still be offset (starting at prerollSegmentCount) to reserve the index space, but no
1227
1260
  // preroll entries are included.
1228
- updatePlaylist(streamId, buildVariantPlaylist(segmentEntries, video.metadata, "segment", targetDuration));
1261
+ updatePlaylist(streamId, buildVariantPlaylist(segmentEntries, video.metadata, "segment", targetDuration), clock.now());
1229
1262
  }
1230
1263
  }
1231
1264
  else {
1232
1265
  // Separate audio - generate video variant playlist and master playlist. Preroll is not supported for separate audio streams (preroll is muxed).
1233
1266
  updateVideoPlaylist(streamId, buildVariantPlaylist(segmentEntries, video.metadata, "segment", targetDuration));
1234
1267
  // Estimate bandwidth from stored segment sizes and durations. Sum total bytes and total duration across video segments, then convert to bits per second.
1235
- // Falls back to 5 Mbps when no duration data is available (first segment before durations are populated).
1268
+ // Falls back to 5 Mbps when the video window holds no segment with a usable duration yet, such as an audio advance arriving before the first video segment.
1236
1269
  let bandwidth = 5000000;
1237
1270
  let totalBytes = 0;
1238
1271
  let totalDuration = 0;
@@ -1252,7 +1285,7 @@ export function createNativeProxy(options) {
1252
1285
  "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"audio\",NAME=\"English\",DEFAULT=YES,AUTOSELECT=YES,URI=\"audio.m3u8\"\n" +
1253
1286
  "#EXT-X-STREAM-INF:BANDWIDTH=" + String(bandwidth) + ",AUDIO=\"audio\"\n" +
1254
1287
  "video.m3u8\n";
1255
- updatePlaylist(streamId, masterPlaylist);
1288
+ updatePlaylist(streamId, masterPlaylist, clock.now());
1256
1289
  }
1257
1290
  // Release initialization segments the freshly-generated window no longer references. This runs after generation so the retain set reflects exactly the
1258
1291
  // segments the playlist just published.
@@ -1276,11 +1309,11 @@ export function createNativeProxy(options) {
1276
1309
  pruneTrackInits(streamId, "audio", audio.metadata, audioEntries);
1277
1310
  }
1278
1311
  /**
1279
- * Schedules the next manifest poll after a delay. The sleep is awaited through the injected Clock port (defaulting to realClock) so tests can virtualize the
1280
- * polling cadence without depending on real timers. Cancellation semantics: stop() flips lifecycle.stopped, and the post-sleep guard catches that flip before
1281
- * issuing the next poll. The in-flight sleep itself is not cancelled - in production a stopped proxy lingers for at most the currently scheduled poll delay,
1282
- * which can be as long as MANIFEST_BACKOFF_CAP once jittered, before the awaiter wakes and exits cleanly. This is the same shape as retryOperation in
1283
- * utils/retry.ts which already adopted the Clock port for the same reason.
1312
+ * Schedules the next manifest poll after a delay. The sleep is awaited through the injected Clock port (defaulting to the system clock) so tests drive the
1313
+ * polling cadence on a virtual timeline rather than against real timers. Cancellation semantics: stop() flips lifecycle.stopped, and the post-sleep guard
1314
+ * catches that flip before issuing the next poll. The in-flight sleep itself is not cancelled - in production a stopped proxy lingers for at most the currently
1315
+ * scheduled poll delay, which can be as long as MANIFEST_BACKOFF_CAP once jittered, before the awaiter wakes and exits cleanly. This is the same shape
1316
+ * retryOperation in utils/retry.ts takes, for the same reason.
1284
1317
  *
1285
1318
  * @param delayMs - Delay in milliseconds before the next poll.
1286
1319
  */
@@ -1289,28 +1322,27 @@ export function createNativeProxy(options) {
1289
1322
  return;
1290
1323
  }
1291
1324
  void (async () => {
1292
- await clock.sleep(delayMs);
1325
+ await clock.delay(delayMs);
1293
1326
  if (lifecycle.stopped) {
1294
1327
  return;
1295
1328
  }
1296
1329
  void pollManifest();
1297
1330
  })();
1298
1331
  }
1299
- // The proxy's teardown: flip the lifecycle flag the polling awaiter checks, and cancel the pending token-refresh timer. Defined as a const so it can be exposed
1300
- // both as stop() (the established domain verb) and as [Symbol.dispose] (the TC39 protocol), making the proxy a self-disposing node without duplicating the body.
1332
+ // The proxy's teardown: flip the lifecycle flag the polling awaiter checks, cancel the proxy's open fetches, and cancel the pending token-refresh timer.
1333
+ // Defined as a const so it can be exposed both as stop() (the established domain verb) and as [Symbol.dispose] (the TC39 protocol), making the proxy a
1334
+ // self-disposing node without duplicating the body.
1301
1335
  const stop = () => {
1302
1336
  lifecycle.stopped = true;
1303
1337
  // Cancel every fetch the proxy still has open - segments, initialization segments, and decryption keys - so teardown does not wait on a CDN and no
1304
1338
  // straggler write can land after the stream's state has been cleared.
1305
1339
  lifecycle.abortController.abort();
1306
- // The polling cadence sleep is owned by schedulePoll's awaiter and is not cancelled here - the awaiter checks lifecycle.stopped after clock.sleep resolves
1340
+ // The polling cadence sleep is owned by schedulePoll's awaiter and is not cancelled here - the awaiter checks lifecycle.stopped after clock.delay resolves
1307
1341
  // and exits before issuing the next poll. See schedulePoll's docblock for the cancellation contract.
1308
1342
  // Cancel the pending token refresh timer to prevent fire-after-termination. Without this, the timer fires on a stopped proxy and attempts to navigate a
1309
1343
  // potentially closed or reused page.
1310
- if (lifecycle.tokenRefreshTimer) {
1311
- clearTimeout(lifecycle.tokenRefreshTimer);
1312
- lifecycle.tokenRefreshTimer = null;
1313
- }
1344
+ lifecycle.tokenRefreshTimer?.[Symbol.dispose]();
1345
+ lifecycle.tokenRefreshTimer = null;
1314
1346
  LOG.debug("native:proxy", "Stopped native proxy for %s.", channelName);
1315
1347
  };
1316
1348
  return {
@@ -1345,9 +1377,7 @@ export function createNativeProxy(options) {
1345
1377
  * handle is live when the stream ends.
1346
1378
  */
1347
1379
  setTokenRefreshTimer: (timer) => {
1348
- if (lifecycle.tokenRefreshTimer) {
1349
- clearTimeout(lifecycle.tokenRefreshTimer);
1350
- }
1380
+ lifecycle.tokenRefreshTimer?.[Symbol.dispose]();
1351
1381
  lifecycle.tokenRefreshTimer = timer;
1352
1382
  },
1353
1383
  start: () => {