/** * @file Pre-join abort guard. * * The balancer patches `XMLHttpRequest.prototype` in the `CdnBalancer` * constructor, so the player's very first media requests are balancer-owned * before `/decision` has answered, before `setExternalProviders` has run and * before playback has joined. Aborting one of those is not a CDN switch — the * player never gets a startup segment, and on XHR HLS stacks (Clappr/hls.js on * Chrome Mobile) that is a fatal startup error rather than a retry. * * This gate is therefore consulted at every point where balancer code would * call `xhr.abort()` on the PLAYER's request. While it is closed the balancer * does not cut the player off; once playback has joined it opens and every * timeout / probe behaves exactly as before — unless `keepClosedAfterJoin` is * set, in which case it stays closed for the whole view (see the setting). * * Join is taken from the product's own definition — the first frame the viewer * sees. Three producers feed it, in decreasing order of accuracy: * * 1. Full plugin: the video adapters fire join off `playing` / `progress` / * `timeupdate` (e.g. `Hlsjs.js`), which reaches `contentListeners` * `_joinListener` and publishes the shared `isJoined` common variable. * 2. Standalone + `balancerAnalyticsEnabled`: `BalancerAnalytics.join()` * publishes the same variable. * 3. Standalone with no analytics at all — which is what the Globo POC ships, * since `CdnBalancerPlugin` bundles no adapters and only builds `video` * when `balancerAnalyticsEnabled === true`. Nothing would ever report * join, so the gate falls back to counting successful media segments the * balancer has already seen for itself. This floor is what keeps the * default working with no new integrator API (AC #6). */ export interface PreJoinAbortGuardSettings { /** When false the guard is inert and the balancer aborts as it always did. */ enabled: boolean; /** * Successful media segments that stand in for join when nothing reports it. * * Counted by file extension (`Utils.isMedia`), which an fMP4 / CMAF * initialization segment also satisfies — `init.mp4` / `init.m4s` carry no * playable frames but match `.mp4` / `.m4s`. With the default of 1 the gate * can therefore open on the init fetch, before any real media segment. Set * this to 2 or more for such streams (per account via `/decision` * `customData.options["preJoinAbortGuard.minMediaSegments"]`). MPEG-TS HLS * has no separate init file and is unaffected. */ minMediaSegments: number; /** * Keep the gate closed for the WHOLE view, not only until join: the balancer then never aborts * the player's XHR at any of its deadlines (connect / read / total / trial / first-chunk). Off by * default because it also switches off the reactive half of active switching — a slow host is * only left once the player's own timeout or an HTTP error hits it. Per profile via `/decision` * `customData.options["preJoinAbortGuard.keepClosedAfterJoin"]` * (devs-committee cdn-balancer/2026-09-02_plugin-js-no-player-request-abort-after-join, Switch 1). */ keepClosedAfterJoin: boolean; } export declare const DEFAULT_PRE_JOIN_ABORT_GUARD_SETTINGS: PreJoinAbortGuardSettings; /** * The single question every abort site asks: may the balancer abort the * player's XHR right now? Kept as a narrow interface so `Loader` and * `PeakBandwidthSampler` depend on the question, not on how it is answered. */ export interface PreJoinAbortGate { isOpen(): boolean; } export declare class JoinGate implements PreJoinAbortGate { private joined; private successfulMediaSegments; private readonly getSettings; constructor(getSettings?: () => Partial | undefined); /** Playback joined — reported by the adapters or by BalancerAnalytics. */ markJoined(): void; /** One media segment completed with a 2xx/3xx. Only counted before join. */ onMediaSegmentSuccess(): void; /** New video in the same page: protect its startup the same way. */ reset(): void; getSuccessfulMediaSegments(): number; /** * Switch 1 in force: the player's requests are off limits for the WHOLE view. Consulted where the * balancer would install its native call-timeout clamp on the player's XHR (`CdnBalancer.send`), * which is a balancer deadline too — the browser fires it on the player's request and the failure * path bans the host, exactly the cut the switch exists to stop (lab S2, 2026-09-08: 7 cuts at * 10.0 s with the guard closed, all from the clamp). */ playerRequestsOffLimits(): boolean; /** * True when balancer-initiated aborts of the player's XHR are allowed. * Latches: join is monotonic within a view, so once the gate opens it stays * open until `reset()`, which also keeps this off the hot path. */ isOpen(): boolean; /** * Reads the `isJoined` common variable published by whichever analytics * layer is present. Core is a singleton the balancer already reaches this * way (see `CDNLoader` and `decisionFinished`); a standalone build with no * analytics simply never has the variable set. */ private isJoinReportedExternally; } export default JoinGate;