prismcast 1.12.1 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.d.ts +0 -1
- package/dist/app.js +8 -7
- package/dist/app.js.map +1 -1
- package/dist/browser/blockedPage.d.ts +0 -1
- package/dist/browser/browserSupervisor.d.ts +4 -4
- package/dist/browser/browserSupervisor.js.map +1 -1
- package/dist/browser/cdp.d.ts +2 -3
- package/dist/browser/cdp.js +5 -4
- package/dist/browser/cdp.js.map +1 -1
- package/dist/browser/channelSelection.d.ts +16 -4
- package/dist/browser/channelSelection.js +42 -31
- package/dist/browser/channelSelection.js.map +1 -1
- package/dist/browser/consent.d.ts +17 -4
- package/dist/browser/consent.js +25 -40
- package/dist/browser/consent.js.map +1 -1
- package/dist/browser/display.d.ts +0 -1
- package/dist/browser/hlsPlaylistObserver.d.ts +3 -2
- package/dist/browser/hlsPlaylistObserver.js +9 -4
- package/dist/browser/hlsPlaylistObserver.js.map +1 -1
- package/dist/browser/index.d.ts +76 -26
- package/dist/browser/index.js +201 -101
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/launchGovernor.d.ts +0 -1
- package/dist/browser/login.d.ts +8 -7
- package/dist/browser/login.js +25 -16
- package/dist/browser/login.js.map +1 -1
- package/dist/browser/manifestInterceptor.d.ts +14 -5
- package/dist/browser/manifestInterceptor.js +25 -12
- package/dist/browser/manifestInterceptor.js.map +1 -1
- package/dist/browser/navigation.d.ts +28 -0
- package/dist/browser/navigation.js +57 -0
- package/dist/browser/navigation.js.map +1 -0
- package/dist/browser/pageStaleness.d.ts +0 -1
- package/dist/browser/precaching.d.ts +4 -2
- package/dist/browser/precaching.js +39 -32
- package/dist/browser/precaching.js.map +1 -1
- package/dist/browser/tabCapture.d.ts +7 -10
- package/dist/browser/tabCapture.js +35 -41
- package/dist/browser/tabCapture.js.map +1 -1
- package/dist/browser/tabNetworkObserver.d.ts +0 -1
- package/dist/browser/tabSelection.d.ts +5 -5
- package/dist/browser/tabSelection.js +11 -10
- package/dist/browser/tabSelection.js.map +1 -1
- package/dist/browser/tuning/cache.d.ts +0 -1
- package/dist/browser/tuning/comcastPolymer.d.ts +0 -1
- package/dist/browser/tuning/comcastPolymer.js +16 -17
- package/dist/browser/tuning/comcastPolymer.js.map +1 -1
- package/dist/browser/tuning/cox.d.ts +0 -1
- package/dist/browser/tuning/directv.d.ts +0 -1
- package/dist/browser/tuning/directv.js +11 -12
- package/dist/browser/tuning/directv.js.map +1 -1
- package/dist/browser/tuning/fox.d.ts +0 -1
- package/dist/browser/tuning/fox.js +2 -2
- package/dist/browser/tuning/fox.js.map +1 -1
- package/dist/browser/tuning/gridSearch.d.ts +0 -1
- package/dist/browser/tuning/hbo.d.ts +0 -1
- package/dist/browser/tuning/hulu.d.ts +14 -2
- package/dist/browser/tuning/hulu.js +144 -21
- package/dist/browser/tuning/hulu.js.map +1 -1
- package/dist/browser/tuning/shared.d.ts +0 -1
- package/dist/browser/tuning/sling.d.ts +0 -1
- package/dist/browser/tuning/sling.js +17 -22
- package/dist/browser/tuning/sling.js.map +1 -1
- package/dist/browser/tuning/spectrum.d.ts +0 -1
- package/dist/browser/tuning/thumbnailRow.d.ts +0 -1
- package/dist/browser/tuning/tileClick.d.ts +0 -1
- package/dist/browser/tuning/xfinity.d.ts +0 -1
- package/dist/browser/tuning/youtubeTv.d.ts +0 -1
- package/dist/browser/video.d.ts +22 -20
- package/dist/browser/video.js +102 -119
- package/dist/browser/video.js.map +1 -1
- package/dist/browser/windowSync.d.ts +0 -1
- package/dist/channels/index.d.ts +0 -1
- package/dist/config/channelForm.d.ts +0 -1
- package/dist/config/channelIdentity.d.ts +27 -0
- package/dist/config/channelIdentity.js +72 -0
- package/dist/config/channelIdentity.js.map +1 -0
- package/dist/config/channelSort.d.ts +25 -0
- package/dist/config/channelSort.js +101 -0
- package/dist/config/channelSort.js.map +1 -0
- package/dist/config/consistencyProbe.d.ts +0 -1
- package/dist/config/health.d.ts +3 -2
- package/dist/config/health.js +41 -28
- package/dist/config/health.js.map +1 -1
- package/dist/config/index.d.ts +0 -1
- package/dist/config/paths.d.ts +0 -1
- package/dist/config/persistence.context.d.ts +0 -1
- package/dist/config/persistence.d.ts +0 -1
- package/dist/config/presets.d.ts +0 -1
- package/dist/config/profiles.d.ts +9 -4
- package/dist/config/profiles.js +37 -3
- package/dist/config/profiles.js.map +1 -1
- package/dist/config/providerLineups.d.ts +0 -1
- package/dist/config/reactivity.d.ts +0 -1
- package/dist/config/servicePacks.d.ts +0 -1
- package/dist/config/services.d.ts +1 -47
- package/dist/config/services.js +3 -134
- package/dist/config/services.js.map +1 -1
- package/dist/config/sites.d.ts +0 -9
- package/dist/config/sites.js +5 -40
- package/dist/config/sites.js.map +1 -1
- package/dist/config/userChannels.d.ts +22 -28
- package/dist/config/userChannels.js +46 -60
- package/dist/config/userChannels.js.map +1 -1
- package/dist/config/userConfig.d.ts +4 -1
- package/dist/config/userConfig.js +4 -0
- package/dist/config/userConfig.js.map +1 -1
- package/dist/config/userProfiles.d.ts +2 -3
- package/dist/config/userProfiles.js +6 -2
- package/dist/config/userProfiles.js.map +1 -1
- package/dist/hdhr/channelMap.d.ts +0 -1
- package/dist/hdhr/deviceId.d.ts +0 -1
- package/dist/hdhr/discover.d.ts +0 -1
- package/dist/hdhr/getHandlers.d.ts +0 -1
- package/dist/hdhr/identity.d.ts +0 -1
- package/dist/hdhr/index.d.ts +0 -1
- package/dist/hdhr/protocol.d.ts +0 -1
- package/dist/hdhr/tunerState.d.ts +0 -1
- package/dist/hdhr/udp.d.ts +0 -1
- package/dist/identity.d.ts +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/native/codecInference.d.ts +5 -4
- package/dist/native/codecInference.js +12 -7
- package/dist/native/codecInference.js.map +1 -1
- package/dist/native/decrypt.d.ts +10 -3
- package/dist/native/decrypt.js +9 -5
- package/dist/native/decrypt.js.map +1 -1
- package/dist/native/index.d.ts +25 -23
- package/dist/native/index.js +23 -17
- package/dist/native/index.js.map +1 -1
- package/dist/native/probe.d.ts +7 -10
- package/dist/native/probe.js +40 -36
- package/dist/native/probe.js.map +1 -1
- package/dist/native/proxy.d.ts +2 -3
- package/dist/native/proxy.js +86 -64
- package/dist/native/proxy.js.map +1 -1
- package/dist/native/tokenExpiry.d.ts +0 -1
- package/dist/routes/assets.d.ts +0 -1
- package/dist/routes/auth.d.ts +0 -1
- package/dist/routes/auth.js +1 -1
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/cdp.d.ts +11 -5
- package/dist/routes/cdp.js +90 -62
- package/dist/routes/cdp.js.map +1 -1
- package/dist/routes/channels.d.ts +0 -1
- package/dist/routes/clientActions.d.ts +0 -1
- package/dist/routes/components.d.ts +0 -1
- package/dist/routes/config/channels/endpoints/browse.d.ts +0 -1
- package/dist/routes/config/channels/endpoints/bulk.d.ts +0 -1
- package/dist/routes/config/channels/endpoints/bulk.js +1 -1
- package/dist/routes/config/channels/endpoints/bulk.js.map +1 -1
- package/dist/routes/config/channels/endpoints/crud.d.ts +0 -1
- package/dist/routes/config/channels/endpoints/crud.js +3 -2
- package/dist/routes/config/channels/endpoints/crud.js.map +1 -1
- package/dist/routes/config/channels/endpoints/importExport.d.ts +0 -1
- package/dist/routes/config/channels/endpoints/importExport.js +7 -3
- package/dist/routes/config/channels/endpoints/importExport.js.map +1 -1
- package/dist/routes/config/channels/endpoints/predefined.d.ts +0 -1
- package/dist/routes/config/channels/endpoints/prefs.d.ts +0 -1
- package/dist/routes/config/channels/endpoints/prefs.js +1 -1
- package/dist/routes/config/channels/endpoints/prefs.js.map +1 -1
- package/dist/routes/config/channels/endpoints/service.d.ts +0 -1
- package/dist/routes/config/channels/endpoints/service.js +8 -5
- package/dist/routes/config/channels/endpoints/service.js.map +1 -1
- package/dist/routes/config/channels/endpoints/tags.d.ts +0 -1
- package/dist/routes/config/channels/healthBridge.d.ts +0 -1
- package/dist/routes/config/channels/http/handler.d.ts +0 -1
- package/dist/routes/config/channels/http/playlistHint.d.ts +0 -1
- package/dist/routes/config/channels/http/serviceWarning.d.ts +0 -1
- package/dist/routes/config/channels/http/serviceWarning.js +1 -1
- package/dist/routes/config/channels/http/serviceWarning.js.map +1 -1
- package/dist/routes/config/channels/index.d.ts +0 -1
- package/dist/routes/config/channels/setup.d.ts +0 -1
- package/dist/routes/config/channels/table.d.ts +0 -1
- package/dist/routes/config/channels/table.js +2 -1
- package/dist/routes/config/channels/table.js.map +1 -1
- package/dist/routes/config/http/envelope.d.ts +0 -1
- package/dist/routes/config/index.d.ts +0 -1
- package/dist/routes/config/index.js +2 -1
- package/dist/routes/config/index.js.map +1 -1
- package/dist/routes/config/services.d.ts +0 -1
- package/dist/routes/config/services.js +4 -3
- package/dist/routes/config/services.js.map +1 -1
- package/dist/routes/config/settings.d.ts +0 -1
- package/dist/routes/config/settings.js +2 -1
- package/dist/routes/config/settings.js.map +1 -1
- package/dist/routes/debug.d.ts +0 -1
- package/dist/routes/health.d.ts +1 -2
- package/dist/routes/health.js +5 -2
- package/dist/routes/health.js.map +1 -1
- package/dist/routes/hls.d.ts +0 -1
- package/dist/routes/icons.d.ts +0 -1
- package/dist/routes/index.d.ts +0 -1
- package/dist/routes/logs.d.ts +3 -2
- package/dist/routes/logs.js +4 -2
- package/dist/routes/logs.js.map +1 -1
- package/dist/routes/mpegts.d.ts +0 -1
- package/dist/routes/play.d.ts +0 -1
- package/dist/routes/playlist.d.ts +0 -1
- package/dist/routes/playlist.js +2 -1
- package/dist/routes/playlist.js.map +1 -1
- package/dist/routes/root/content.d.ts +0 -1
- package/dist/routes/root/index.d.ts +0 -1
- package/dist/routes/root/scripts/channels.d.ts +0 -1
- package/dist/routes/root/scripts/channels.js +2 -2
- package/dist/routes/root/scripts/channels.js.map +1 -1
- package/dist/routes/root/scripts/clientEscape.d.ts +0 -1
- package/dist/routes/root/scripts/clientUrl.d.ts +0 -1
- package/dist/routes/root/scripts/config.d.ts +0 -1
- package/dist/routes/root/scripts/config.js +5 -26
- package/dist/routes/root/scripts/config.js.map +1 -1
- package/dist/routes/root/scripts/index.d.ts +0 -1
- package/dist/routes/root/scripts/shared.d.ts +0 -1
- package/dist/routes/root/scripts/status.d.ts +0 -1
- package/dist/routes/root/scripts/status.handlers.d.ts +0 -1
- package/dist/routes/root/scripts/status.handlers.js +3 -2
- package/dist/routes/root/scripts/status.handlers.js.map +1 -1
- package/dist/routes/root/styles.d.ts +0 -1
- package/dist/routes/services.d.ts +0 -1
- package/dist/routes/services.js +1 -1
- package/dist/routes/services.js.map +1 -1
- package/dist/routes/sse.d.ts +3 -2
- package/dist/routes/sse.js +7 -3
- package/dist/routes/sse.js.map +1 -1
- package/dist/routes/streams.d.ts +4 -2
- package/dist/routes/streams.js +8 -5
- package/dist/routes/streams.js.map +1 -1
- package/dist/routes/theme.d.ts +0 -1
- package/dist/routes/ui.d.ts +0 -1
- package/dist/routes/upgrade.d.ts +0 -1
- package/dist/routes/upgrade.js +2 -1
- package/dist/routes/upgrade.js.map +1 -1
- package/dist/service/commands.context.d.ts +0 -1
- package/dist/service/commands.context.js +7 -1
- package/dist/service/commands.context.js.map +1 -1
- package/dist/service/commands.d.ts +0 -1
- package/dist/service/generators.context.d.ts +0 -1
- package/dist/service/generators.d.ts +0 -1
- package/dist/service/index.d.ts +0 -1
- package/dist/streaming/captureLock.d.ts +1 -2
- package/dist/streaming/captureLock.js +6 -5
- package/dist/streaming/captureLock.js.map +1 -1
- package/dist/streaming/captureSession.d.ts +0 -1
- package/dist/streaming/clients.d.ts +4 -3
- package/dist/streaming/clients.js +5 -4
- package/dist/streaming/clients.js.map +1 -1
- package/dist/streaming/codec.d.ts +0 -1
- package/dist/streaming/fmp4Segmenter.d.ts +2 -1
- package/dist/streaming/fmp4Segmenter.js +12 -11
- package/dist/streaming/fmp4Segmenter.js.map +1 -1
- package/dist/streaming/hls.d.ts +34 -1
- package/dist/streaming/hls.js +121 -54
- package/dist/streaming/hls.js.map +1 -1
- package/dist/streaming/hlsResume.d.ts +8 -5
- package/dist/streaming/hlsResume.js +10 -8
- package/dist/streaming/hlsResume.js.map +1 -1
- package/dist/streaming/hlsSegments.d.ts +9 -7
- package/dist/streaming/hlsSegments.js +19 -14
- package/dist/streaming/hlsSegments.js.map +1 -1
- package/dist/streaming/lifecycle.d.ts +5 -4
- package/dist/streaming/lifecycle.js +25 -13
- package/dist/streaming/lifecycle.js.map +1 -1
- package/dist/streaming/monitor.d.ts +3 -2
- package/dist/streaming/monitor.js +63 -57
- package/dist/streaming/monitor.js.map +1 -1
- package/dist/streaming/mp4Parser.d.ts +0 -1
- package/dist/streaming/mpegts.d.ts +0 -1
- package/dist/streaming/mpegts.js +23 -7
- package/dist/streaming/mpegts.js.map +1 -1
- package/dist/streaming/playlistBuilder.d.ts +0 -1
- package/dist/streaming/preroll.d.ts +34 -15
- package/dist/streaming/preroll.js +30 -28
- package/dist/streaming/preroll.js.map +1 -1
- package/dist/streaming/pretune.d.ts +2 -3
- package/dist/streaming/pretune.js +45 -45
- package/dist/streaming/pretune.js.map +1 -1
- package/dist/streaming/pretuneTimers.d.ts +16 -12
- package/dist/streaming/pretuneTimers.js +27 -37
- package/dist/streaming/pretuneTimers.js.map +1 -1
- package/dist/streaming/recovery.d.ts +7 -5
- package/dist/streaming/recovery.js +10 -7
- package/dist/streaming/recovery.js.map +1 -1
- package/dist/streaming/registry.d.ts +23 -5
- package/dist/streaming/registry.js +17 -3
- package/dist/streaming/registry.js.map +1 -1
- package/dist/streaming/setup.d.ts +39 -7
- package/dist/streaming/setup.js +130 -65
- package/dist/streaming/setup.js.map +1 -1
- package/dist/streaming/showInfo.d.ts +8 -4
- package/dist/streaming/showInfo.js +54 -45
- package/dist/streaming/showInfo.js.map +1 -1
- package/dist/streaming/statusEmitter.d.ts +1 -2
- package/dist/streaming/statusEmitter.js +1 -1
- package/dist/streaming/statusEmitter.js.map +1 -1
- package/dist/types/channels.d.ts +0 -1
- package/dist/types/config.d.ts +0 -1
- package/dist/types/index.d.ts +1 -2
- package/dist/types/index.js.map +1 -1
- package/dist/types/profiles.d.ts +1 -1
- package/dist/types/selection.d.ts +10 -1
- package/dist/types/shared.d.ts +0 -1
- package/dist/types/streaming.d.ts +0 -1
- package/dist/upgrade/commands.context.d.ts +0 -1
- package/dist/upgrade/commands.d.ts +0 -1
- package/dist/upgrade/detection.context.d.ts +0 -1
- package/dist/upgrade/detection.d.ts +0 -1
- package/dist/upgrade/index.d.ts +0 -1
- package/dist/upgrade/lifecycle.context.d.ts +2 -1
- package/dist/upgrade/lifecycle.context.js +9 -4
- package/dist/upgrade/lifecycle.context.js.map +1 -1
- package/dist/upgrade/lifecycle.d.ts +0 -1
- package/dist/upgrade/pathHandle.d.ts +0 -1
- package/dist/utils/bootSession.context.d.ts +0 -1
- package/dist/utils/bootSession.context.js +2 -1
- package/dist/utils/bootSession.context.js.map +1 -1
- package/dist/utils/bootSession.d.ts +0 -1
- package/dist/utils/chromeFetch.d.ts +0 -1
- package/dist/utils/cliOutput.d.ts +0 -1
- package/dist/utils/debugFilter.d.ts +0 -1
- package/dist/utils/delay.d.ts +47 -25
- package/dist/utils/delay.js +74 -37
- package/dist/utils/delay.js.map +1 -1
- package/dist/utils/errors.d.ts +0 -1
- package/dist/utils/evaluate.d.ts +11 -3
- package/dist/utils/evaluate.js +4 -3
- package/dist/utils/evaluate.js.map +1 -1
- package/dist/utils/ffmpeg.context.d.ts +0 -1
- package/dist/utils/ffmpeg.d.ts +0 -1
- package/dist/utils/fileLogger.d.ts +3 -2
- package/dist/utils/fileLogger.js +16 -8
- package/dist/utils/fileLogger.js.map +1 -1
- package/dist/utils/format.d.ts +12 -3
- package/dist/utils/format.js +29 -11
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/index.d.ts +0 -2
- package/dist/utils/index.js +0 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/logEmitter.d.ts +0 -1
- package/dist/utils/logger.d.ts +0 -1
- package/dist/utils/m3u.d.ts +0 -1
- package/dist/utils/markup.d.ts +0 -1
- package/dist/utils/memo.d.ts +0 -1
- package/dist/utils/morganStream.d.ts +0 -1
- package/dist/utils/network.d.ts +0 -1
- package/dist/utils/never.d.ts +0 -1
- package/dist/utils/pid.d.ts +0 -1
- package/dist/utils/platform.d.ts +0 -1
- package/dist/utils/processInspector.context.d.ts +32 -2
- package/dist/utils/processInspector.context.js +117 -1
- package/dist/utils/processInspector.context.js.map +1 -1
- package/dist/utils/processInspector.d.ts +0 -32
- package/dist/utils/processInspector.js +10 -117
- package/dist/utils/processInspector.js.map +1 -1
- package/dist/utils/retry.d.ts +5 -4
- package/dist/utils/retry.js +43 -33
- package/dist/utils/retry.js.map +1 -1
- package/dist/utils/runtimeIdentity.context.d.ts +0 -1
- package/dist/utils/runtimeIdentity.context.js +3 -1
- package/dist/utils/runtimeIdentity.context.js.map +1 -1
- package/dist/utils/runtimeIdentity.d.ts +1 -1
- package/dist/utils/runtimeIdentity.js +1 -1
- package/dist/utils/runtimeIdentity.js.map +1 -1
- package/dist/utils/sanitize.d.ts +0 -1
- package/dist/utils/streamContext.d.ts +0 -1
- package/dist/utils/timing.d.ts +3 -4
- package/dist/utils/timing.js +4 -4
- package/dist/utils/timing.js.map +1 -1
- package/dist/utils/version.d.ts +5 -4
- package/dist/utils/version.js +35 -24
- package/dist/utils/version.js.map +1 -1
- package/package.json +7 -7
- package/dist/app.d.ts.map +0 -1
- package/dist/browser/blockedPage.d.ts.map +0 -1
- package/dist/browser/browserSupervisor.d.ts.map +0 -1
- package/dist/browser/cdp.d.ts.map +0 -1
- package/dist/browser/channelSelection.d.ts.map +0 -1
- package/dist/browser/consent.d.ts.map +0 -1
- package/dist/browser/display.d.ts.map +0 -1
- package/dist/browser/hlsPlaylistObserver.d.ts.map +0 -1
- package/dist/browser/index.d.ts.map +0 -1
- package/dist/browser/launchGovernor.d.ts.map +0 -1
- package/dist/browser/login.d.ts.map +0 -1
- package/dist/browser/manifestInterceptor.d.ts.map +0 -1
- package/dist/browser/pageStaleness.d.ts.map +0 -1
- package/dist/browser/precaching.d.ts.map +0 -1
- package/dist/browser/tabCapture.d.ts.map +0 -1
- package/dist/browser/tabNetworkObserver.d.ts.map +0 -1
- package/dist/browser/tabSelection.d.ts.map +0 -1
- package/dist/browser/tuning/cache.d.ts.map +0 -1
- package/dist/browser/tuning/comcastPolymer.d.ts.map +0 -1
- package/dist/browser/tuning/cox.d.ts.map +0 -1
- package/dist/browser/tuning/directv.d.ts.map +0 -1
- package/dist/browser/tuning/fox.d.ts.map +0 -1
- package/dist/browser/tuning/gridSearch.d.ts.map +0 -1
- package/dist/browser/tuning/hbo.d.ts.map +0 -1
- package/dist/browser/tuning/hulu.d.ts.map +0 -1
- package/dist/browser/tuning/shared.d.ts.map +0 -1
- package/dist/browser/tuning/sling.d.ts.map +0 -1
- package/dist/browser/tuning/spectrum.d.ts.map +0 -1
- package/dist/browser/tuning/thumbnailRow.d.ts.map +0 -1
- package/dist/browser/tuning/tileClick.d.ts.map +0 -1
- package/dist/browser/tuning/xfinity.d.ts.map +0 -1
- package/dist/browser/tuning/youtubeTv.d.ts.map +0 -1
- package/dist/browser/video.d.ts.map +0 -1
- package/dist/browser/windowSync.d.ts.map +0 -1
- package/dist/channels/index.d.ts.map +0 -1
- package/dist/config/channelForm.d.ts.map +0 -1
- package/dist/config/consistencyProbe.d.ts.map +0 -1
- package/dist/config/health.d.ts.map +0 -1
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/paths.d.ts.map +0 -1
- package/dist/config/persistence.context.d.ts.map +0 -1
- package/dist/config/persistence.d.ts.map +0 -1
- package/dist/config/presets.d.ts.map +0 -1
- package/dist/config/profiles.d.ts.map +0 -1
- package/dist/config/providerLineups.d.ts.map +0 -1
- package/dist/config/reactivity.d.ts.map +0 -1
- package/dist/config/servicePacks.d.ts.map +0 -1
- package/dist/config/services.d.ts.map +0 -1
- package/dist/config/sites.d.ts.map +0 -1
- package/dist/config/userChannels.d.ts.map +0 -1
- package/dist/config/userConfig.d.ts.map +0 -1
- package/dist/config/userProfiles.d.ts.map +0 -1
- package/dist/hdhr/channelMap.d.ts.map +0 -1
- package/dist/hdhr/deviceId.d.ts.map +0 -1
- package/dist/hdhr/discover.d.ts.map +0 -1
- package/dist/hdhr/getHandlers.d.ts.map +0 -1
- package/dist/hdhr/identity.d.ts.map +0 -1
- package/dist/hdhr/index.d.ts.map +0 -1
- package/dist/hdhr/protocol.d.ts.map +0 -1
- package/dist/hdhr/tunerState.d.ts.map +0 -1
- package/dist/hdhr/udp.d.ts.map +0 -1
- package/dist/identity.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/native/codecInference.d.ts.map +0 -1
- package/dist/native/decrypt.d.ts.map +0 -1
- package/dist/native/index.d.ts.map +0 -1
- package/dist/native/probe.d.ts.map +0 -1
- package/dist/native/proxy.d.ts.map +0 -1
- package/dist/native/tokenExpiry.d.ts.map +0 -1
- package/dist/routes/assets.d.ts.map +0 -1
- package/dist/routes/auth.d.ts.map +0 -1
- package/dist/routes/cdp.d.ts.map +0 -1
- package/dist/routes/channels.d.ts.map +0 -1
- package/dist/routes/clientActions.d.ts.map +0 -1
- package/dist/routes/components.d.ts.map +0 -1
- package/dist/routes/config/channels/endpoints/browse.d.ts.map +0 -1
- package/dist/routes/config/channels/endpoints/bulk.d.ts.map +0 -1
- package/dist/routes/config/channels/endpoints/crud.d.ts.map +0 -1
- package/dist/routes/config/channels/endpoints/importExport.d.ts.map +0 -1
- package/dist/routes/config/channels/endpoints/predefined.d.ts.map +0 -1
- package/dist/routes/config/channels/endpoints/prefs.d.ts.map +0 -1
- package/dist/routes/config/channels/endpoints/service.d.ts.map +0 -1
- package/dist/routes/config/channels/endpoints/tags.d.ts.map +0 -1
- package/dist/routes/config/channels/healthBridge.d.ts.map +0 -1
- package/dist/routes/config/channels/http/handler.d.ts.map +0 -1
- package/dist/routes/config/channels/http/playlistHint.d.ts.map +0 -1
- package/dist/routes/config/channels/http/serviceWarning.d.ts.map +0 -1
- package/dist/routes/config/channels/index.d.ts.map +0 -1
- package/dist/routes/config/channels/setup.d.ts.map +0 -1
- package/dist/routes/config/channels/table.d.ts.map +0 -1
- package/dist/routes/config/http/envelope.d.ts.map +0 -1
- package/dist/routes/config/index.d.ts.map +0 -1
- package/dist/routes/config/services.d.ts.map +0 -1
- package/dist/routes/config/settings.d.ts.map +0 -1
- package/dist/routes/debug.d.ts.map +0 -1
- package/dist/routes/health.d.ts.map +0 -1
- package/dist/routes/hls.d.ts.map +0 -1
- package/dist/routes/icons.d.ts.map +0 -1
- package/dist/routes/index.d.ts.map +0 -1
- package/dist/routes/logs.d.ts.map +0 -1
- package/dist/routes/mpegts.d.ts.map +0 -1
- package/dist/routes/play.d.ts.map +0 -1
- package/dist/routes/playlist.d.ts.map +0 -1
- package/dist/routes/root/content.d.ts.map +0 -1
- package/dist/routes/root/index.d.ts.map +0 -1
- package/dist/routes/root/scripts/channels.d.ts.map +0 -1
- package/dist/routes/root/scripts/clientEscape.d.ts.map +0 -1
- package/dist/routes/root/scripts/clientUrl.d.ts.map +0 -1
- package/dist/routes/root/scripts/config.d.ts.map +0 -1
- package/dist/routes/root/scripts/index.d.ts.map +0 -1
- package/dist/routes/root/scripts/shared.d.ts.map +0 -1
- package/dist/routes/root/scripts/status.d.ts.map +0 -1
- package/dist/routes/root/scripts/status.handlers.d.ts.map +0 -1
- package/dist/routes/root/styles.d.ts.map +0 -1
- package/dist/routes/services.d.ts.map +0 -1
- package/dist/routes/sse.d.ts.map +0 -1
- package/dist/routes/streams.d.ts.map +0 -1
- package/dist/routes/theme.d.ts.map +0 -1
- package/dist/routes/ui.d.ts.map +0 -1
- package/dist/routes/upgrade.d.ts.map +0 -1
- package/dist/service/commands.context.d.ts.map +0 -1
- package/dist/service/commands.d.ts.map +0 -1
- package/dist/service/generators.context.d.ts.map +0 -1
- package/dist/service/generators.d.ts.map +0 -1
- package/dist/service/index.d.ts.map +0 -1
- package/dist/streaming/captureLock.d.ts.map +0 -1
- package/dist/streaming/captureSession.d.ts.map +0 -1
- package/dist/streaming/clients.d.ts.map +0 -1
- package/dist/streaming/codec.d.ts.map +0 -1
- package/dist/streaming/fmp4Segmenter.d.ts.map +0 -1
- package/dist/streaming/hls.d.ts.map +0 -1
- package/dist/streaming/hlsResume.d.ts.map +0 -1
- package/dist/streaming/hlsSegments.d.ts.map +0 -1
- package/dist/streaming/lifecycle.d.ts.map +0 -1
- package/dist/streaming/monitor.d.ts.map +0 -1
- package/dist/streaming/mp4Parser.d.ts.map +0 -1
- package/dist/streaming/mpegts.d.ts.map +0 -1
- package/dist/streaming/playlistBuilder.d.ts.map +0 -1
- package/dist/streaming/preroll.d.ts.map +0 -1
- package/dist/streaming/pretune.d.ts.map +0 -1
- package/dist/streaming/pretuneTimers.d.ts.map +0 -1
- package/dist/streaming/recovery.d.ts.map +0 -1
- package/dist/streaming/registry.d.ts.map +0 -1
- package/dist/streaming/setup.d.ts.map +0 -1
- package/dist/streaming/showInfo.d.ts.map +0 -1
- package/dist/streaming/statusEmitter.d.ts.map +0 -1
- package/dist/types/channels.d.ts.map +0 -1
- package/dist/types/config.d.ts.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/profiles.d.ts.map +0 -1
- package/dist/types/selection.d.ts.map +0 -1
- package/dist/types/shared.d.ts.map +0 -1
- package/dist/types/streaming.d.ts.map +0 -1
- package/dist/upgrade/commands.context.d.ts.map +0 -1
- package/dist/upgrade/commands.d.ts.map +0 -1
- package/dist/upgrade/detection.context.d.ts.map +0 -1
- package/dist/upgrade/detection.d.ts.map +0 -1
- package/dist/upgrade/index.d.ts.map +0 -1
- package/dist/upgrade/lifecycle.context.d.ts.map +0 -1
- package/dist/upgrade/lifecycle.d.ts.map +0 -1
- package/dist/upgrade/pathHandle.d.ts.map +0 -1
- package/dist/utils/bootSession.context.d.ts.map +0 -1
- package/dist/utils/bootSession.d.ts.map +0 -1
- package/dist/utils/chromeFetch.d.ts.map +0 -1
- package/dist/utils/cliOutput.d.ts.map +0 -1
- package/dist/utils/clock.d.ts +0 -15
- package/dist/utils/clock.d.ts.map +0 -1
- package/dist/utils/clock.js +0 -31
- package/dist/utils/clock.js.map +0 -1
- package/dist/utils/debugFilter.d.ts.map +0 -1
- package/dist/utils/delay.d.ts.map +0 -1
- package/dist/utils/errors.d.ts.map +0 -1
- package/dist/utils/evaluate.d.ts.map +0 -1
- package/dist/utils/ffmpeg.context.d.ts.map +0 -1
- package/dist/utils/ffmpeg.d.ts.map +0 -1
- package/dist/utils/fileLogger.d.ts.map +0 -1
- package/dist/utils/format.d.ts.map +0 -1
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/logEmitter.d.ts.map +0 -1
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/m3u.d.ts.map +0 -1
- package/dist/utils/markup.d.ts.map +0 -1
- package/dist/utils/memo.d.ts.map +0 -1
- package/dist/utils/morganStream.d.ts.map +0 -1
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/never.d.ts.map +0 -1
- package/dist/utils/pid.d.ts.map +0 -1
- package/dist/utils/platform.d.ts.map +0 -1
- package/dist/utils/processInspector.context.d.ts.map +0 -1
- package/dist/utils/processInspector.d.ts.map +0 -1
- package/dist/utils/retry.d.ts.map +0 -1
- package/dist/utils/runtimeIdentity.context.d.ts.map +0 -1
- package/dist/utils/runtimeIdentity.d.ts.map +0 -1
- package/dist/utils/sanitize.d.ts.map +0 -1
- package/dist/utils/streamContext.d.ts.map +0 -1
- package/dist/utils/timing.d.ts.map +0 -1
- package/dist/utils/version.d.ts.map +0 -1
package/dist/browser/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { LOG, boundedWait, evaluateWithAbort, formatError, isProcessRunning, listProcesses,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { LOG, boundedWait, evaluateWithAbort, formatError, isProcessRunning, listProcesses, setChromeUserAgent, startTimer } from "../utils/index.js";
|
|
2
|
+
import { TimerRegistry, systemClock } from "homebridge-plugin-utils";
|
|
3
|
+
import { clearLoginState, isLoginModeActive, setLoginDeps } from "./login.js";
|
|
4
|
+
import { getAllStreams, getStreamCount, hasActiveCaptureStreams, hasEstablishedStreams, isCaptureIdentity } from "../streaming/registry.js";
|
|
4
5
|
import { getCachedTabId, installStrayOpenTabReaper, onTabActivation } from "./tabSelection.js";
|
|
5
6
|
import { getChromeDataDir, getDataDir, getExtensionDir } from "../config/paths.js";
|
|
6
7
|
import { getExtensionPage, launch } from "puppeteer-stream";
|
|
@@ -18,7 +19,6 @@ import { getPresetViewport } from "../config/presets.js";
|
|
|
18
19
|
import path from "node:path";
|
|
19
20
|
import { launch as puppeteerLaunch } from "puppeteer-core";
|
|
20
21
|
import { startPrecaching } from "./precaching.js";
|
|
21
|
-
import { terminateStream } from "../streaming/lifecycle.js";
|
|
22
22
|
const { promises: fsPromises } = fs;
|
|
23
23
|
/* Global variables maintain the application's runtime state across all operations. We minimize global state where possible, but some values must be shared across
|
|
24
24
|
* the application lifecycle:
|
|
@@ -71,13 +71,13 @@ function buildRelaunchPolicy() {
|
|
|
71
71
|
}
|
|
72
72
|
/* The one browser capture-readiness supervisor for the process lifetime. It owns the lifecycle state (absent/launching/ready/degraded/trialing) that unifies the
|
|
73
73
|
* browser reference, launch promise, and launch timestamp, and routes every relaunch through one loop-safe governor. The adapter injects the impure
|
|
74
|
-
* ports: launchReadyBrowser (spawn Chrome and run the readiness gate), closeBrowserInstance (teardown),
|
|
74
|
+
* ports: launchReadyBrowser (spawn Chrome and run the readiness gate), closeBrowserInstance (teardown), systemClock.now (time), buildRelaunchPolicy (live config
|
|
75
75
|
* bounds), and onSupervisorStateChange (the loud degraded alarm and the recovery notice). All browser access flows through it: getCurrentBrowser is acquire(); the
|
|
76
76
|
* non-launching reads derive from current() and currentLaunchTime(). The injected ports are hoisted function declarations, so referencing them here is safe even
|
|
77
77
|
* though they are defined further down the module.
|
|
78
78
|
*/
|
|
79
|
-
const supervisor = createBrowserSupervisor({ close: closeBrowserInstance, launch: launchReadyBrowser, now:
|
|
80
|
-
policy: buildRelaunchPolicy });
|
|
79
|
+
const supervisor = createBrowserSupervisor({ close: closeBrowserInstance, launch: launchReadyBrowser, now: () => systemClock.now(),
|
|
80
|
+
onStateChange: onSupervisorStateChange, policy: buildRelaunchPolicy });
|
|
81
81
|
/**
|
|
82
82
|
* Observes supervisor lifecycle transitions purely for operator-visible signals; it never affects the transition (the supervisor treats it as best-effort, so a
|
|
83
83
|
* throwing logger cannot corrupt the lifecycle). It raises the loud degraded alarm when the relaunch governor trips, the capture-impairment alarm when a published
|
|
@@ -91,7 +91,7 @@ function onSupervisorStateChange(next, previous) {
|
|
|
91
91
|
// The governor just tripped: relaunches are paused while the browser's capture system cools down. We log loudly at ERROR with the cooldown horizon so the
|
|
92
92
|
// condition is never invisible - the enforceable form of "it is impossible to be silently un-tunable."
|
|
93
93
|
if ((next.kind === "degraded") && (previous.kind !== "degraded")) {
|
|
94
|
-
const cooldownMinutes = Math.max(1, Math.round((next.until -
|
|
94
|
+
const cooldownMinutes = Math.max(1, Math.round((next.until - systemClock.now()) / 60000));
|
|
95
95
|
LOG.error("The browser capture system has degraded and the relaunch governor has tripped: %s Relaunches are paused for approximately %d minute(s) while it " +
|
|
96
96
|
"cools down; new stream requests will receive a 503 back-off until it recovers.", next.reason, cooldownMinutes);
|
|
97
97
|
}
|
|
@@ -103,9 +103,9 @@ function onSupervisorStateChange(next, previous) {
|
|
|
103
103
|
else if ((next.kind === "ready") && (next.impairment !== null) && ((previous.kind !== "ready") || (previous.impairment === null))) {
|
|
104
104
|
// The published browser has been marked as unable to start captures. The transition carrying the mark happens once per instance, so the alarm fires once per
|
|
105
105
|
// instance too, and it says what an operator watching a stalled tune needs: the running captures are unaffected, new requests back off, and the cure arrives
|
|
106
|
-
// on its own
|
|
106
|
+
// on its own as soon as nothing on this browser holds a page.
|
|
107
107
|
LOG.error("The browser can no longer start captures (%s). Its running captures continue, new stream requests receive a 503 back-off, and it will relaunch as " +
|
|
108
|
-
"soon as
|
|
108
|
+
"soon as nothing holds a page on it.", next.impairment.reason);
|
|
109
109
|
}
|
|
110
110
|
// The browser's connectivity is part of the SSE system status, so emit when a ready browser is published. Readiness-loss emits are owned by handleBrowserDisconnect
|
|
111
111
|
// (genuine disconnect) and the shutdown path; emitSystemStatusChanged dedupes, so a redundant emit is a cheap no-op.
|
|
@@ -114,8 +114,9 @@ function onSupervisorStateChange(next, previous) {
|
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
/* The capture-readiness probe (capability tier of the launch gate). Null until streaming/setup.ts injects the real capture probe at module load, which the import
|
|
117
|
-
* order
|
|
118
|
-
* launchReadyBrowser refuses to publish a browser if it is somehow still null (see the call
|
|
117
|
+
* order places ahead of any launch: index.ts value-imports app.ts, app.ts value-imports streaming/hls.ts, and hls.ts value-imports streaming/setup.ts, so the
|
|
118
|
+
* injection has run by the time index.ts's own body calls startServer. launchReadyBrowser refuses to publish a browser if it is somehow still null (see the call
|
|
119
|
+
* site), rather than serving an unverified one.
|
|
119
120
|
*/
|
|
120
121
|
let captureProbe = null;
|
|
121
122
|
/**
|
|
@@ -126,9 +127,27 @@ let captureProbe = null;
|
|
|
126
127
|
export function setCaptureProbe(probe) {
|
|
127
128
|
captureProbe = probe;
|
|
128
129
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
/* The authoritative stream terminator. Null until streaming/lifecycle.ts injects terminateStream at module load, which the import order places ahead of any
|
|
131
|
+
* browser launch: index.ts value-imports app.ts, app.ts value-imports streaming/lifecycle.ts, and a module body finishes before the body of anything that
|
|
132
|
+
* imports it, so the injection has run by the time index.ts's own body calls startServer - the earliest moment a launch, and so a disconnect, can happen. A
|
|
133
|
+
* still-null terminator at readiness loss is reported at ERROR and the teardown carries on (see the call site), because losing the rest of the teardown on top
|
|
134
|
+
* of the stream cleanup would make a recoverable situation worse.
|
|
135
|
+
*/
|
|
136
|
+
let streamTerminator = null;
|
|
137
|
+
/**
|
|
138
|
+
* Injects the authoritative stream terminator used when browser readiness is lost. Called once from streaming/lifecycle.ts at module load. Separating the wiring
|
|
139
|
+
* from the call keeps browser/index.ts free of a streaming-layer import.
|
|
140
|
+
* @param terminator - The function that tears down one stream and all of the resources it owns.
|
|
141
|
+
*/
|
|
142
|
+
export function setStreamTerminator(terminator) {
|
|
143
|
+
streamTerminator = terminator;
|
|
144
|
+
}
|
|
145
|
+
// The identity of the periodic sweep on the stale-page owner's registry.
|
|
146
|
+
const STALE_PAGE_SWEEP_KEY = "sweep";
|
|
147
|
+
/* The stale-page owner's timers: the periodic sweep that closes browser pages no active stream is using, so a long session cannot exhaust resources. The registry
|
|
148
|
+
* is built on the clock the start receives and disposed by the stop, so the sweep can never outlive the owner. Null until started, and again once stopped.
|
|
149
|
+
*/
|
|
150
|
+
let stalePageTimers = null;
|
|
132
151
|
/* Opportunistic browser restart state. Chrome accumulates memory pressure, GPU process issues, and general flakiness over multi-hour sessions with continuous
|
|
133
152
|
* media playback. We proactively restart Chrome after it has been running for BROWSER_MAX_AGE, waiting for a quiet period with zero active streams before
|
|
134
153
|
* executing the restart. After the restart, a fresh browser is launched immediately so it is ready for the next stream request.
|
|
@@ -139,20 +158,23 @@ const BROWSER_MAX_AGE = 6 * 60 * 60 * 1000;
|
|
|
139
158
|
const BROWSER_RESTART_QUIET_PERIOD = 5 * 60 * 1000;
|
|
140
159
|
// How often to check whether the browser qualifies for a restart (30 seconds).
|
|
141
160
|
const BROWSER_RESTART_CHECK_INTERVAL = 30000;
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
|
|
161
|
+
// The identity of the periodic eligibility check on the restart owner's registry.
|
|
162
|
+
const RESTART_CHECK_KEY = "check";
|
|
163
|
+
// The identity of the quiet-period countdown on the restart owner's registry.
|
|
164
|
+
const RESTART_QUIET_KEY = "quiet";
|
|
165
|
+
/* The restart owner's timers, keyed on one registry built on the clock its start receives: the periodic eligibility check, and - while the browser has exceeded
|
|
166
|
+
* BROWSER_MAX_AGE and we are waiting for BROWSER_RESTART_QUIET_PERIOD to elapse with zero active streams - the quiet-period countdown, which a stream starting
|
|
167
|
+
* during the wait cancels. Holding both on one registry is what makes the stop drain the countdown along with the check. Null until the check is started, and
|
|
168
|
+
* again once it is stopped.
|
|
169
|
+
*/
|
|
170
|
+
let restartTimers = null;
|
|
145
171
|
/**
|
|
146
|
-
* Cancels the scheduled-restart quiet period if one is pending, so the countdown it was running cannot fire against a browser it no longer describes.
|
|
172
|
+
* Cancels the scheduled-restart quiet period if one is pending, so the countdown it was running cannot fire against a browser it no longer describes. Reads the
|
|
173
|
+
* module binding rather than a handed-in registry because executeBrowserRestart calls it as well, which also makes it a no-op once the owner has been stopped.
|
|
147
174
|
*/
|
|
148
175
|
function cancelRestartQuietTimer() {
|
|
149
|
-
|
|
150
|
-
clearTimeout(restartQuietTimer);
|
|
151
|
-
restartQuietTimer = null;
|
|
152
|
-
}
|
|
176
|
+
restartTimers?.clear(RESTART_QUIET_KEY);
|
|
153
177
|
}
|
|
154
|
-
// Interval handle for the periodic restart eligibility check.
|
|
155
|
-
let restartCheckInterval = null;
|
|
156
178
|
// Flag indicating that the browser is being closed intentionally via closeBrowser(). When true, the disconnect handler skips error logging and stream termination
|
|
157
179
|
// since these are handled by the shutdown code path. This prevents false "unexpected disconnect" errors during graceful shutdown.
|
|
158
180
|
let gracefulShutdownInProgress = false;
|
|
@@ -310,7 +332,7 @@ export async function confirmSharedWindowPlacement(page) {
|
|
|
310
332
|
return (await readPageWindow(page)) === sharedWindowId;
|
|
311
333
|
}
|
|
312
334
|
// Login mode management. State and functions live in login.ts; re-exported here so existing consumers don't need import path changes. clearLoginState,
|
|
313
|
-
// isLoginModeActive, and
|
|
335
|
+
// isLoginModeActive, and setLoginDeps are imported above; the first two for internal use, setLoginDeps for one-time initialization below.
|
|
314
336
|
export { clearLoginState, isLoginModeActive };
|
|
315
337
|
export { endLoginMode, getLoginPage, getLoginStatus, setLoginModeEndObserver, startLoginMode } from "./login.js";
|
|
316
338
|
// Re-export the supervisor's acquire() rejection classes through the browser surface so the stream-setup layer can map them to a 503 back-off without reaching into
|
|
@@ -329,7 +351,7 @@ export { BrowserCaptureImpairedError, BrowserSupersededError, BrowserUnavailable
|
|
|
329
351
|
* preference or as the first page the browser happened to report.
|
|
330
352
|
*
|
|
331
353
|
* The instance stays private to this module and every caller reaches it through the exported function just below. That split is what makes the symbol safe to
|
|
332
|
-
* reference at module-evaluation time: a function declaration is hoisted, so the
|
|
354
|
+
* reference at module-evaluation time: a function declaration is hoisted, so the setLoginDeps call further down - and any sibling module whose own body
|
|
333
355
|
* evaluates while this module is still evaluating, precaching.ts among them - resolves it whatever the declaration order turns out to be. A bare exported const
|
|
334
356
|
* would leave those readers in the temporal dead zone, which no placement inside this file can fix for a reader in another file.
|
|
335
357
|
*/
|
|
@@ -380,10 +402,10 @@ const windowVisibilitySync = createWindowVisibilitySync({
|
|
|
380
402
|
export async function syncWindowVisibility(page) {
|
|
381
403
|
return windowVisibilitySync(page);
|
|
382
404
|
}
|
|
383
|
-
// Inject
|
|
384
|
-
//
|
|
385
|
-
//
|
|
386
|
-
|
|
405
|
+
// Inject login's dependency set. This breaks the circular dependency (login needs getBrowserInstance and the window sync, index needs login functions) using the
|
|
406
|
+
// same setter/getter pattern as setChromeUserAgent in chromeFetch.ts. Both accessors are hoisted function declarations, so this call reads them at module
|
|
407
|
+
// evaluation time regardless of where they sit in the file. No clock is supplied, so login runs on the system clock the port defaults to.
|
|
408
|
+
setLoginDeps({ getBrowserInstance, syncWindowVisibility });
|
|
387
409
|
/**
|
|
388
410
|
* Computes the current system status and emits it to SSE subscribers. Called when browser state changes significantly or when streams are added/removed.
|
|
389
411
|
*/
|
|
@@ -642,15 +664,16 @@ export function killStaleChrome() {
|
|
|
642
664
|
}
|
|
643
665
|
/**
|
|
644
666
|
* Polls until the Chrome process with the given PID has exited, or the timeout expires. Uses process.kill(pid, 0) to check process existence - throws ESRCH
|
|
645
|
-
* when the process is gone.
|
|
667
|
+
* when the process is gone. The wait runs inside the synchronous exit handler, so it reads the port for its instants and sleeps synchronously between them
|
|
668
|
+
* using Atomics.wait() for cross-platform compatibility.
|
|
646
669
|
* @param pid - The Chrome process ID to wait for.
|
|
647
670
|
* @param timeoutMs - Maximum time to wait in milliseconds.
|
|
648
671
|
* @param pollIntervalMs - Time between existence checks in milliseconds.
|
|
649
672
|
* @returns True if the process exited within the timeout, false otherwise.
|
|
650
673
|
*/
|
|
651
674
|
function waitForChromeExit(pid, timeoutMs, pollIntervalMs) {
|
|
652
|
-
const deadline =
|
|
653
|
-
while (
|
|
675
|
+
const deadline = systemClock.now() + timeoutMs;
|
|
676
|
+
while (systemClock.now() < deadline) {
|
|
654
677
|
if (!isProcessRunning(pid)) {
|
|
655
678
|
return true;
|
|
656
679
|
}
|
|
@@ -973,14 +996,17 @@ export function mirrorPlacement(placement) {
|
|
|
973
996
|
/**
|
|
974
997
|
* Opens a page for a channel guide discovery walk in a browser window of its own, and marks it as belonging to that window.
|
|
975
998
|
*
|
|
976
|
-
* A document renders only while it
|
|
977
|
-
* no sign-in holds it on screen, with its selected tab belonging to the user. A guide walk needs its page to
|
|
978
|
-
* tiles from rendering updates,
|
|
979
|
-
*
|
|
999
|
+
* A document renders only while Chrome presents it: the active tab of a window the desktop is showing, or a page Chrome counts as captured. The shared window
|
|
1000
|
+
* rests minimized whenever nothing is capturing and no sign-in holds it on screen, with its selected tab belonging to the user. A guide walk needs its page to
|
|
1001
|
+
* render - an observer-driven channel rail fills its tiles from rendering updates, a virtualized grid re-renders as the walk scrolls it, and every wait the walk
|
|
1002
|
+
* makes polls on the page's animation frames - yet it is never captured, so it has no claim on the shared window's presentation and no business moving the
|
|
1003
|
+
* user's selection. A window of its own resolves the tab half: the page is that window's active tab from the moment it exists. The window is created in the
|
|
1004
|
+
* background, so Chrome shows it inactive and moves no focus (measured 2026-08-31), and such a window's document is one Chrome does not present on its own: it
|
|
1005
|
+
* reports itself hidden and unfocused and delivers no animation frame beyond a document load's first ones (measured 2026-09-13). Focus emulation resolves the
|
|
1006
|
+
* presentation half, for the page's whole life - Puppeteer's own emulateFocusedPage carries the mechanism, on the page's own session.
|
|
980
1007
|
*
|
|
981
|
-
* The window
|
|
982
|
-
* the
|
|
983
|
-
* the page and owns its registration, and closing the page closes the window with it.
|
|
1008
|
+
* The window opens at the shared window's own placement, so the window placement Chrome persists for the profile never changes - readWindowPlacement carries
|
|
1009
|
+
* the reasoning. The caller declares the layout surface on the page and owns its registration, and closing the page closes the window with it.
|
|
984
1010
|
* @param browser - The browser to open the window in.
|
|
985
1011
|
* @returns The page, as the active tab of its own window.
|
|
986
1012
|
*/
|
|
@@ -990,6 +1016,24 @@ export async function createDiscoveryPage(browser) {
|
|
|
990
1016
|
// The mark is set with nothing awaited between the creation and it, so no page read can observe this page as a candidate to carry a shared-window command.
|
|
991
1017
|
const page = await browser.newPage({ background: true, type: "window", windowBounds: placement ? mirrorPlacement(placement) : undefined });
|
|
992
1018
|
ownWindowPages.add(page);
|
|
1019
|
+
/* The page is presented from before its first load, so every wait a walk makes - a visible selector, a condition polled from inside the page - runs as it
|
|
1020
|
+
* would in a window the desktop is showing. Chrome counts a focus-emulated page as captured, which is what presents its document, and Puppeteer keeps the
|
|
1021
|
+
* state on the page's own session for as long as the page lives (measured 2026-09-13). The mark above stays first: nothing is awaited between the creation
|
|
1022
|
+
* and it. A page that refuses the emulation is one no walk can use, and nothing else holds it yet - the caller never receives it and the managed-page sweep
|
|
1023
|
+
* never sees it - so the creator closes it, which closes the window with it, before the failure propagates.
|
|
1024
|
+
*/
|
|
1025
|
+
try {
|
|
1026
|
+
await page.emulateFocusedPage(true);
|
|
1027
|
+
}
|
|
1028
|
+
catch (error) {
|
|
1029
|
+
try {
|
|
1030
|
+
await page.close();
|
|
1031
|
+
}
|
|
1032
|
+
catch {
|
|
1033
|
+
// The page is already gone, which is the state the close was asking for.
|
|
1034
|
+
}
|
|
1035
|
+
throw error;
|
|
1036
|
+
}
|
|
993
1037
|
LOG.debug("browser:lifecycle", "Opened the discovery page in a window of its own, %s.", placement ? "mirroring the shared window's placement" : "with no shared window to read a placement from");
|
|
994
1038
|
return page;
|
|
995
1039
|
}
|
|
@@ -1026,10 +1070,10 @@ const activationHeals = new WeakMap();
|
|
|
1026
1070
|
* @param page - The capture page this callback re-affirms.
|
|
1027
1071
|
* @param reaffirm - The re-issue to invoke. A parameter rather than a direct call so the callback's behavior can be driven on its own: what it does with the page
|
|
1028
1072
|
* it was built for, what schedule it keeps, and what it does with a rejection, is the whole of its contract.
|
|
1029
|
-
* @param clock - The time source the ladder's waits run on. Defaults to
|
|
1073
|
+
* @param clock - The time source the ladder's waits run on. Defaults to the system clock.
|
|
1030
1074
|
* @returns The callback, which never rejects. Its promise settles once the ladder is spent, which the page-side listener does not wait on.
|
|
1031
1075
|
*/
|
|
1032
|
-
export function makeFocusReaffirmCallback(page, reaffirm, clock =
|
|
1076
|
+
export function makeFocusReaffirmCallback(page, reaffirm, clock = systemClock) {
|
|
1033
1077
|
// The generation this closure hands out. It only ever increments: an invocation claims the next one and owns the rung schedule until a later invocation claims
|
|
1034
1078
|
// a higher one.
|
|
1035
1079
|
let generation = 0;
|
|
@@ -1048,7 +1092,7 @@ export function makeFocusReaffirmCallback(page, reaffirm, clock = realClock) {
|
|
|
1048
1092
|
let previousOffsetMs = 0;
|
|
1049
1093
|
for (const offsetMs of ACTIVATION_REAFFIRM_LADDER_MS) {
|
|
1050
1094
|
// eslint-disable-next-line no-await-in-loop -- The pacing is the point: each rung waits out the distance left to its own offset before anything else runs.
|
|
1051
|
-
await clock.
|
|
1095
|
+
await clock.delay(offsetMs - previousOffsetMs);
|
|
1052
1096
|
// A newer activation owns the schedule, and it carries its own rungs timed from its own moment, so this ladder has nothing left to contribute.
|
|
1053
1097
|
if (generation !== mine) {
|
|
1054
1098
|
return;
|
|
@@ -1060,7 +1104,7 @@ export function makeFocusReaffirmCallback(page, reaffirm, clock = realClock) {
|
|
|
1060
1104
|
};
|
|
1061
1105
|
}
|
|
1062
1106
|
// The production collaborators.
|
|
1063
|
-
const defaultActivationHealDeps = { clock:
|
|
1107
|
+
const defaultActivationHealDeps = { clock: systemClock, reaffirm: reaffirmCaptureSurface };
|
|
1064
1108
|
/**
|
|
1065
1109
|
* Installs the tab-activation heal on a capture page. Chrome composes the capture of a selected tab from the window's fitted presentation, so the instant a
|
|
1066
1110
|
* capture tab becomes the selected one that capture starts recording a clipped view of itself; the re-issue ladder this installs moves the composition back to
|
|
@@ -1298,10 +1342,21 @@ function relinquishBrowserReadiness(streamTerminationReason) {
|
|
|
1298
1342
|
if (clearLoginState() && !gracefulShutdownInProgress) {
|
|
1299
1343
|
LOG.info("Login mode ended due to browser readiness loss.");
|
|
1300
1344
|
}
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1345
|
+
/* Terminate every active stream through the streaming layer's authoritative terminator, so a readiness loss cleans up exactly the way every other termination
|
|
1346
|
+
* path does. Kept even during graceful shutdown as a defensive measure - termination is safe to call more than once, so streams the caller already tore down
|
|
1347
|
+
* leave this a harmless pass over an empty array.
|
|
1348
|
+
*/
|
|
1349
|
+
const activeStreams = getAllStreams();
|
|
1350
|
+
if (streamTerminator) {
|
|
1351
|
+
for (const streamInfo of activeStreams) {
|
|
1352
|
+
streamTerminator(streamInfo.id, streamInfo.info.storeKey, streamTerminationReason);
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
else {
|
|
1356
|
+
/* An unwired terminator means the streaming layer never loaded, so nothing here can run the streams' cleanup sequence. Report it and keep going: the page
|
|
1357
|
+
* tracking and status emission below are still worth doing, and abandoning them would compound a wiring failure with a half-finished teardown.
|
|
1358
|
+
*/
|
|
1359
|
+
LOG.error("Stream cleanup was skipped on browser readiness loss because no stream terminator is wired. Active streams left untouched: %s.", activeStreams.length);
|
|
1305
1360
|
}
|
|
1306
1361
|
// The session those streams captured on is over, so whatever page tracking survived their termination belongs to a browser that is gone.
|
|
1307
1362
|
clearPageTracking();
|
|
@@ -1325,24 +1380,29 @@ function handleBrowserDisconnect() {
|
|
|
1325
1380
|
relinquishBrowserReadiness("browser disconnect");
|
|
1326
1381
|
}
|
|
1327
1382
|
/**
|
|
1328
|
-
* Records that a still-connected browser can no longer start captures - a mid-life capture death that no "disconnected" event would surface - and
|
|
1383
|
+
* Records that a still-connected browser can no longer start captures - a mid-life capture death that no "disconnected" event would surface - and runs the
|
|
1329
1384
|
* relaunch that cures it. This is the single recovery action for a browser that is alive and still serving: the mark lives on the supervisor's ready state, so its
|
|
1330
1385
|
* running captures continue untouched, new stream requests are refused at acquire() with a 503 back-off, the recovery ladder stops offering tab replacement, and
|
|
1331
|
-
* the relaunch waits
|
|
1386
|
+
* the relaunch waits until nothing depends on the browser. Exported for the streaming layer to call once its probe or its wedge has produced the verdict.
|
|
1387
|
+
*
|
|
1388
|
+
* The returned promise settles once the relaunch this mark triggered has settled, which is what a caller that means to use the fresh browser awaits: the teardown
|
|
1389
|
+
* holds the supervisor in its draining state, where acquire() rejects rather than joins, so acquiring any earlier draws that rejection instead of the new
|
|
1390
|
+
* instance. A caller with nothing waiting on the outcome voids it and the relaunch proceeds on its own.
|
|
1332
1391
|
*
|
|
1333
1392
|
* The restart trigger uses this function's return value rather than the supervisor's transition observer, deliberately. The observer runs inside transition(), so a
|
|
1334
1393
|
* restart begun there would call noteReadinessLost re-entrantly while the marking transition's notification is still on the stack. The observer stays a reporter -
|
|
1335
1394
|
* the alarm and the status emit - and the caller that holds the verdict acts on it once the transition has completed.
|
|
1336
1395
|
* @param browser - The specific browser instance the caller verified as unable to start captures.
|
|
1337
1396
|
* @param reason - A short description of the evidence behind the verdict, carried in the alarm log and the impairment record.
|
|
1397
|
+
* @returns A promise settling once the relaunch this mark triggered has settled, or at once when the verdict landed on nothing.
|
|
1338
1398
|
*/
|
|
1339
|
-
export function noteBrowserCaptureImpaired(browser, reason) {
|
|
1399
|
+
export async function noteBrowserCaptureImpaired(browser, reason) {
|
|
1340
1400
|
// A false answer means the verdict landed on nothing: the instance was superseded by a disconnect and relaunch while the caller was confirming it, or the browser
|
|
1341
1401
|
// already carries a mark whose alarm and status emit have already fired. Either way there is no new state to act on.
|
|
1342
1402
|
if (!supervisor.noteCaptureImpaired(browser, reason)) {
|
|
1343
1403
|
return;
|
|
1344
1404
|
}
|
|
1345
|
-
restartBrowserIfImpairedAndIdle();
|
|
1405
|
+
await restartBrowserIfImpairedAndIdle();
|
|
1346
1406
|
}
|
|
1347
1407
|
/**
|
|
1348
1408
|
* Provides access to the capture-ready browser, launching one if needed. This is the single gated entry point for all browser access: it delegates to the
|
|
@@ -1430,8 +1490,9 @@ async function launchReadyBrowser() {
|
|
|
1430
1490
|
const gpuSuffix = gpu ? formatGpuSuffix(gpu) : "";
|
|
1431
1491
|
LOG.info("Chrome ready: %s%s.", chromeVersion, gpuSuffix);
|
|
1432
1492
|
LOG.debug("timing:browser", "Browser ready. Total: %sms.", browserElapsed());
|
|
1433
|
-
// Start background precaching of selected service channel lineups. Fire-and-forget - the
|
|
1434
|
-
// launch settles and the supervisor has published the ready browser, so its getCurrentBrowser() resolves immediately rather than re-entering this
|
|
1493
|
+
// Start background precaching of selected service channel lineups. Fire-and-forget - the delay startPrecaching() arms on its own clock defers the work until
|
|
1494
|
+
// after this launch settles and the supervisor has published the ready browser, so its getCurrentBrowser() resolves immediately rather than re-entering this
|
|
1495
|
+
// launch.
|
|
1435
1496
|
startPrecaching();
|
|
1436
1497
|
/* The reaper belongs to the browser instance the supervisor is about to publish: puppeteer drops a closed browser's listeners along with the instance, and
|
|
1437
1498
|
* a relaunch comes back through this same path and installs its own. It goes in ahead of the disconnect handler so that handler stays the last step before
|
|
@@ -1621,8 +1682,10 @@ export async function closeBrowser() {
|
|
|
1621
1682
|
* 2. Exclude pages associated with active streams, and pages an operation still holds in flight
|
|
1622
1683
|
* 3. Apply a grace period before closing (to handle race conditions)
|
|
1623
1684
|
* 4. Preserve at least one page to keep the browser alive
|
|
1685
|
+
* @param now - The instant the sweep judges staleness against, read from the interval's own clock so the staleness clocks this sweep starts and the ones it
|
|
1686
|
+
* later reads are stamped on one time source.
|
|
1624
1687
|
*/
|
|
1625
|
-
export async function cleanupStalePages() {
|
|
1688
|
+
export async function cleanupStalePages(now) {
|
|
1626
1689
|
// Guard against calling this when no ready browser is running.
|
|
1627
1690
|
const browser = supervisor.current();
|
|
1628
1691
|
if (!browser?.connected) {
|
|
@@ -1644,7 +1707,6 @@ export async function cleanupStalePages() {
|
|
|
1644
1707
|
}
|
|
1645
1708
|
}
|
|
1646
1709
|
}
|
|
1647
|
-
const now = Date.now();
|
|
1648
1710
|
// Project the browser's pages into the shape the decision core reads: the managed ids in the browser's own order, with undefined standing in for pages we
|
|
1649
1711
|
// did not create, plus a lookup back to the Page objects so the ids it returns can be resolved to something closable.
|
|
1650
1712
|
const idToPage = new Map();
|
|
@@ -1699,63 +1761,96 @@ export async function cleanupStalePages() {
|
|
|
1699
1761
|
}
|
|
1700
1762
|
}
|
|
1701
1763
|
/**
|
|
1702
|
-
* Starts the periodic stale page cleanup
|
|
1703
|
-
*
|
|
1764
|
+
* Starts the periodic stale page cleanup. This should be called once during server startup, after the browser is initialized. The sweep runs indefinitely until
|
|
1765
|
+
* stopStalePageCleanup() is called (typically during graceful shutdown). A second start while one is running changes nothing.
|
|
1766
|
+
* @param clock - The clock the sweep's interval arms on and whose reading each sweep judges staleness against, so the cadence and the judgment share one time
|
|
1767
|
+
* source. Defaults to the system clock.
|
|
1704
1768
|
*/
|
|
1705
|
-
export function startStalePageCleanup() {
|
|
1706
|
-
|
|
1769
|
+
export function startStalePageCleanup(clock = systemClock) {
|
|
1770
|
+
if (stalePageTimers) {
|
|
1771
|
+
return;
|
|
1772
|
+
}
|
|
1773
|
+
const timers = new TimerRegistry({ clock });
|
|
1774
|
+
timers.setInterval(STALE_PAGE_SWEEP_KEY, () => { void cleanupStalePages(clock.now()); }, CONFIG.recovery.stalePageCleanupInterval);
|
|
1775
|
+
stalePageTimers = timers;
|
|
1707
1776
|
}
|
|
1708
1777
|
/**
|
|
1709
|
-
* Stops the stale page cleanup
|
|
1710
|
-
*
|
|
1778
|
+
* Stops the periodic stale page cleanup. This should be called during graceful shutdown to prevent the sweep from running after we've started shutting down the
|
|
1779
|
+
* browser and streams. Disposing the registry drains the interval, and a stop with nothing running is a no-op.
|
|
1711
1780
|
*/
|
|
1712
1781
|
export function stopStalePageCleanup() {
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
stalePageCleanupInterval = null;
|
|
1716
|
-
}
|
|
1782
|
+
stalePageTimers?.dispose();
|
|
1783
|
+
stalePageTimers = null;
|
|
1717
1784
|
}
|
|
1718
|
-
|
|
1719
|
-
*
|
|
1720
|
-
*
|
|
1721
|
-
* streams, starts a quiet period timer. The quiet timer is cancelled if a stream starts, ensuring active viewers are never disrupted. When the timer expires, the
|
|
1722
|
-
* browser is closed and immediately re-launched.
|
|
1785
|
+
/**
|
|
1786
|
+
* Decides whether the browser may be torn down for the given cause, from facts read at the call. Each cause asks a different question of the same registry, so
|
|
1787
|
+
* the decision is stated once here rather than spelled out at each guard.
|
|
1723
1788
|
*
|
|
1724
|
-
*
|
|
1725
|
-
*
|
|
1726
|
-
*
|
|
1789
|
+
* Maintenance is opportunistic housekeeping, so it waits for an empty registry outright: a pending entry is a tune in progress, and replacing the browser under
|
|
1790
|
+
* one would fail it for nothing better than a fresher instance. Impairment is a repair the tunes themselves are waiting on, so it asks the narrower question of
|
|
1791
|
+
* what a teardown would actually destroy - a stream established on this browser, whose entry holds its page, or a page an operation still holds in flight. A tune
|
|
1792
|
+
* refused a capture start holds neither while it waits for the relaunch, so the very pending entry that maintenance would defer to is not a reason to leave an
|
|
1793
|
+
* unusable browser in place. A tune that has acquired the browser but not yet opened its page falls outside every dependency source for the same reason and
|
|
1794
|
+
* deliberately so: it holds nothing a relaunch would destroy, neither a page nor a capture, and its own capture start would meet the impaired browser in any
|
|
1795
|
+
* case, so the relaunch may run under it.
|
|
1796
|
+
* @param cause - Why the restart wants to run.
|
|
1797
|
+
* @param facts - The registry facts read at the call.
|
|
1798
|
+
* @returns True when the browser may be torn down for this cause.
|
|
1799
|
+
*/
|
|
1800
|
+
export function isBrowserIdleForRestart(cause, facts) {
|
|
1801
|
+
switch (cause) {
|
|
1802
|
+
case "impairment": {
|
|
1803
|
+
return !facts.establishedStreams && (facts.inFlightPages === 0);
|
|
1804
|
+
}
|
|
1805
|
+
case "maintenance": {
|
|
1806
|
+
return facts.streamCount === 0;
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
/**
|
|
1811
|
+
* Reads the registry facts the idleness decision is made from, at the moment of the call.
|
|
1812
|
+
* @returns The facts.
|
|
1727
1813
|
*/
|
|
1814
|
+
function readRestartFacts() {
|
|
1815
|
+
return { establishedStreams: hasEstablishedStreams(), inFlightPages: inFlightPageIds.size, streamCount: getStreamCount() };
|
|
1816
|
+
}
|
|
1728
1817
|
/**
|
|
1729
1818
|
* Relaunches a browser that can no longer start captures, as soon as nothing depends on it. Every trigger routes here - the mark itself, each stream termination,
|
|
1730
1819
|
* and the periodic restart check as the backstop for a moment when the other two could not act (login mode above all) - so the decision lives in one place rather
|
|
1731
1820
|
* than being re-derived by each. Idleness rather than age is the condition, because the mark makes the browser useless for new tunes immediately while its running
|
|
1732
|
-
* captures are still worth finishing, so the earliest safe moment is exactly the moment the last of
|
|
1821
|
+
* captures are still worth finishing, so the earliest safe moment is exactly the moment the last thing depending on this browser lets go of it.
|
|
1822
|
+
*
|
|
1823
|
+
* The returned promise settles once the relaunch has settled, for the caller that goes on to acquire the fresh browser; a trigger with nothing waiting on the
|
|
1824
|
+
* outcome voids it. An early return resolves at once, because there is nothing for such a caller to wait for.
|
|
1825
|
+
* @returns A promise settling once the relaunch has settled, or at once when no relaunch runs.
|
|
1733
1826
|
*/
|
|
1734
|
-
export function restartBrowserIfImpairedAndIdle() {
|
|
1827
|
+
export async function restartBrowserIfImpairedAndIdle() {
|
|
1735
1828
|
if (supervisor.captureImpairment() === null) {
|
|
1736
1829
|
return;
|
|
1737
1830
|
}
|
|
1738
1831
|
// A marked browser's restart belongs to this path, so a maintenance quiet period pending from before the mark is retired the first time any trigger observes the
|
|
1739
|
-
// mark - whether or not the
|
|
1832
|
+
// mark - whether or not the browser is idle yet - rather than being left to fire against the browser the relaunch will have replaced.
|
|
1740
1833
|
cancelRestartQuietTimer();
|
|
1741
|
-
if (
|
|
1834
|
+
if (!isBrowserIdleForRestart("impairment", readRestartFacts())) {
|
|
1742
1835
|
return;
|
|
1743
1836
|
}
|
|
1744
|
-
|
|
1837
|
+
await executeBrowserRestart("impairment");
|
|
1745
1838
|
}
|
|
1746
1839
|
/**
|
|
1747
1840
|
* Checks whether the browser qualifies for a restart. Called periodically by the restart check interval. The check skips when any of these conditions hold:
|
|
1748
1841
|
* graceful shutdown in progress, login mode active, browser not ready. A marked browser is handed to the impairment path and the tick ends there. Otherwise the
|
|
1749
1842
|
* tick drives the supervisor's health-gated governor reset and applies the maintenance rules: skip below the age threshold, cancel any pending quiet timer while
|
|
1750
1843
|
* active streams exist (streams started during the quiet period reset the countdown), and otherwise start a quiet timer if one is not already running.
|
|
1844
|
+
* @param timers - The restart owner's registry, whose quiet period this check arms and reads. Handed in by the interval closure, so the check never reads the
|
|
1845
|
+
* nullable module binding and needs no null branch of its own.
|
|
1751
1846
|
*/
|
|
1752
|
-
function checkBrowserRestart() {
|
|
1847
|
+
function checkBrowserRestart(timers) {
|
|
1753
1848
|
// Skip if the server is shutting down or login mode is active.
|
|
1754
1849
|
if (gracefulShutdownInProgress || isLoginModeActive()) {
|
|
1755
1850
|
return;
|
|
1756
1851
|
}
|
|
1757
1852
|
// Read the ready browser and its launch time from the supervisor. Both are non-null only in the ready state, so a single guard covers "no ready browser." The
|
|
1758
|
-
// launch time is
|
|
1853
|
+
// launch time is read from the supervisor's clock (systemClock.now), so age is measured against that same clock rather than against any other time source.
|
|
1759
1854
|
const browser = supervisor.current();
|
|
1760
1855
|
const launchTime = supervisor.currentLaunchTime();
|
|
1761
1856
|
if (!browser?.connected || (launchTime === null)) {
|
|
@@ -1767,7 +1862,7 @@ function checkBrowserRestart() {
|
|
|
1767
1862
|
* all. The quiet period this tick would otherwise cancel below is retired inside the call, so the early return leaves no timer unobserved.
|
|
1768
1863
|
*/
|
|
1769
1864
|
if (supervisor.captureImpairment() !== null) {
|
|
1770
|
-
restartBrowserIfImpairedAndIdle();
|
|
1865
|
+
void restartBrowserIfImpairedAndIdle();
|
|
1771
1866
|
return;
|
|
1772
1867
|
}
|
|
1773
1868
|
// Health-gated governor reset. On every eligible tick, tell the supervisor the browser is still ready; once it has been continuously ready for the policy's
|
|
@@ -1776,22 +1871,22 @@ function checkBrowserRestart() {
|
|
|
1776
1871
|
LOG.info("Browser capture readiness has been sustained; the relaunch governor has reset to its normal state.");
|
|
1777
1872
|
}
|
|
1778
1873
|
// Skip if the browser has not exceeded the maximum age.
|
|
1779
|
-
const age =
|
|
1874
|
+
const age = systemClock.now() - launchTime;
|
|
1780
1875
|
if (age < BROWSER_MAX_AGE) {
|
|
1781
1876
|
return;
|
|
1782
1877
|
}
|
|
1783
1878
|
// If there are active streams, cancel any pending quiet timer and return. Streams that start during the quiet period reset the countdown.
|
|
1784
1879
|
if (getStreamCount() > 0) {
|
|
1785
|
-
if (
|
|
1880
|
+
if (timers.has(RESTART_QUIET_KEY)) {
|
|
1786
1881
|
LOG.debug("browser:lifecycle", "Browser restart quiet period cancelled - streams are active.");
|
|
1787
1882
|
}
|
|
1788
1883
|
cancelRestartQuietTimer();
|
|
1789
1884
|
return;
|
|
1790
1885
|
}
|
|
1791
1886
|
// No active streams and the browser is old enough. Start the quiet timer if one is not already running.
|
|
1792
|
-
if (!
|
|
1887
|
+
if (!timers.has(RESTART_QUIET_KEY)) {
|
|
1793
1888
|
LOG.debug("browser:lifecycle", "Browser uptime exceeds threshold. Quiet period started - restart will proceed if no streams start within %s minutes.", Math.round(BROWSER_RESTART_QUIET_PERIOD / 60000));
|
|
1794
|
-
|
|
1889
|
+
timers.setTimeout(RESTART_QUIET_KEY, () => {
|
|
1795
1890
|
void executeBrowserRestart("maintenance");
|
|
1796
1891
|
}, BROWSER_RESTART_QUIET_PERIOD);
|
|
1797
1892
|
}
|
|
@@ -1807,14 +1902,16 @@ async function executeBrowserRestart(cause) {
|
|
|
1807
1902
|
// restart the fresh instance a second time.
|
|
1808
1903
|
cancelRestartQuietTimer();
|
|
1809
1904
|
// Final guard: re-check all preconditions. Conditions may have changed during the quiet period (e.g., a stream started just before the timer fired, login mode
|
|
1810
|
-
// was activated, or the browser disconnected on its own). Reading current()/currentLaunchTime() together keeps the ready-state check and the age source
|
|
1905
|
+
// was activated, or the browser disconnected on its own). Reading current()/currentLaunchTime() together keeps the ready-state check and the age source
|
|
1906
|
+
// consistent. The idleness question is asked for this restart's own cause, because what a maintenance sweep must defer to and what a repair must defer to are
|
|
1907
|
+
// not the same set of streams.
|
|
1811
1908
|
const browser = supervisor.current();
|
|
1812
1909
|
const launchTime = supervisor.currentLaunchTime();
|
|
1813
|
-
if (gracefulShutdownInProgress || isLoginModeActive() || (
|
|
1910
|
+
if (gracefulShutdownInProgress || isLoginModeActive() || !isBrowserIdleForRestart(cause, readRestartFacts()) || !browser?.connected || (launchTime === null)) {
|
|
1814
1911
|
LOG.debug("browser:lifecycle", "Browser restart aborted - preconditions no longer met.");
|
|
1815
1912
|
return;
|
|
1816
1913
|
}
|
|
1817
|
-
const age =
|
|
1914
|
+
const age = systemClock.now() - launchTime;
|
|
1818
1915
|
const hours = Math.floor(age / 3600000);
|
|
1819
1916
|
const minutes = Math.floor((age % 3600000) / 60000);
|
|
1820
1917
|
if (cause === "impairment") {
|
|
@@ -1861,22 +1958,25 @@ async function executeBrowserRestart(cause) {
|
|
|
1861
1958
|
}
|
|
1862
1959
|
}
|
|
1863
1960
|
/**
|
|
1864
|
-
* Starts the periodic browser restart eligibility check. This should be called once during server startup, after the browser is initialized. The
|
|
1865
|
-
* indefinitely until stopBrowserRestartChecking() is called (typically during graceful shutdown).
|
|
1961
|
+
* Starts the periodic browser restart eligibility check. This should be called once during server startup, after the browser is initialized. The check runs
|
|
1962
|
+
* indefinitely until stopBrowserRestartChecking() is called (typically during graceful shutdown). A second start while one is running changes nothing.
|
|
1963
|
+
* @param clock - The clock the check's interval and the quiet period it arms both run on. Defaults to the system clock.
|
|
1866
1964
|
*/
|
|
1867
|
-
export function startBrowserRestartChecking() {
|
|
1868
|
-
|
|
1965
|
+
export function startBrowserRestartChecking(clock = systemClock) {
|
|
1966
|
+
if (restartTimers) {
|
|
1967
|
+
return;
|
|
1968
|
+
}
|
|
1969
|
+
const timers = new TimerRegistry({ clock });
|
|
1970
|
+
timers.setInterval(RESTART_CHECK_KEY, () => { checkBrowserRestart(timers); }, BROWSER_RESTART_CHECK_INTERVAL);
|
|
1971
|
+
restartTimers = timers;
|
|
1869
1972
|
}
|
|
1870
1973
|
/**
|
|
1871
|
-
* Stops the browser restart
|
|
1872
|
-
*
|
|
1974
|
+
* Stops the periodic browser restart eligibility check. This should be called during graceful shutdown to prevent a restart from racing with server shutdown.
|
|
1975
|
+
* Disposing the registry drains the check and any pending quiet period together, and a stop with nothing running is a no-op.
|
|
1873
1976
|
*/
|
|
1874
1977
|
export function stopBrowserRestartChecking() {
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
restartCheckInterval = null;
|
|
1878
|
-
}
|
|
1879
|
-
cancelRestartQuietTimer();
|
|
1978
|
+
restartTimers?.dispose();
|
|
1979
|
+
restartTimers = null;
|
|
1880
1980
|
}
|
|
1881
1981
|
/* When running as a packaged executable (created by the `pkg` tool), the application is bundled into a single binary. Node modules like puppeteer-stream are
|
|
1882
1982
|
* included in the bundle, but Chrome cannot load extensions from within the packaged binary - it needs actual files on the filesystem.
|